1.包含min函数的栈
时间限制:1秒 空间限制:32768K 热度指数:177188
本题知识点: 栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
解题思路:
- 如果我们只有一个栈,在栈里添加一个成员变量存放最小元素是不够的,为什么呢?
- 因为如果当前最小的元素被弹出栈顶了,那么如何获得下一个最小的元素呢?
- 所以我们需要在压入最小的元素之前将次小的元素保存起来,可以添加一个辅助栈
- {3,4,2,1,0}, 往空的数据栈里压入3,显然现在3是最小值,将这个最小值也压入辅助栈里;然后压入4,辅助栈压入3;压入2,辅助栈压入2,依次下去
- 当最小元素压入辅助栈里,就可以保证辅助栈的栈顶一直都是最小的元素
- 当栈顶的元素被弹出,同时弹出辅助栈的最小元素,辅助栈的新栈顶就是下一个最小值
class Solution {
public:
void push(int value) {
StackData.push(value);
if(StackMin.empty())
StackMin.push(value);
else if(StackMin.top() < value)
StackMin.push(StackMin.top());
else
StackMin.push(value);
}
void pop() {
if(!StackData.empty())
{
StackData.pop();
StackMin.pop();
}
}
int top() {
return StackData.top();
}
int min() {
return StackMin.top();
}
private:
stack<int> StackData;
stack<int> StackMin;
};
2.栈的压入、弹出序列
时间限制:1秒 空间限制:32768K 热度指数:212288
本题知识点: 栈
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
解题思路:
总结:
- 如果下一个弹出的数字刚好是下一个弹出的栈顶数字,就直接弹出;如果下一个弹出的数字不在栈顶,就把压栈序列中还没有入栈的数字压入栈里面,直到把下一个需要弹出的数字压入栈顶为止
- 如果所有的数字都压入栈后,仍然没有找到下一个弹出的数字,那么该序列就不可能是一个弹出序列
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(popV.empty())
return false;
int len = pushV.size();
int i=0,j=0;
stack<int> s;
s.push(pushV[0]);
while(i <len && j< len){
if(s.top()!=popV[j]){
s.push(pushV[++i]);
}else{
s.pop();
j++;
}
}
if(s.empty())
return true;
return false;
}
};
3.从上往下打印二叉树
时间限制:1秒 空间限制:32768K 热度指数:222428
题目描述从上往下打印出二叉树的每个节点,同层节点从左至右打印。
解题思路:
- 本题实质上是考树的遍历算法,是层序遍历
- 每次打印一个节点的时候,如果该节点有子结点,就把该节点的子节点入到一个队列的末尾
- 然后队列的头部是最早进入队列的结点,将它取出来
- 重复打印操作即可,直到队列中的所有节点都被打印出来
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> result; //存放最后的数组
result.clear(); //将数组先清空
queue<TreeNode*> tree_queue;
if(NULL != root){
tree_queue.push(root);
}
while(!tree_queue.empty())
{
if(tree_queue.front()->left != NULL)
{
tree_queue.push(tree_queue.front()->left);
}
if(tree_queue.front()->right != NULL)
{
tree_queue.push(tree_queue.front()->right);
}
result.push_back(tree_queue.front()->val);
tree_queue.pop();
}
return result;
}
};
4.二叉搜索树的后序遍历序列
时间限制:1秒 空间限制:32768K 热度指数:250141
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
解题思路:
- 以{5,7,6,9,11,10,8}为例,我们知道后序遍历中的根节点位于数组的最后一个,所以在数组中的根节点8前面的是它的左右子树序列
- 二叉搜索树的左子树的结点都小于根节点的值,右子树的结点的值都大于根节点的值
- 所以数组中的5,7,6是根节点8的左子树;9,11,10是根节点8的右子树
- 左子树中的根结点和8是一样的规律,左子树中的根节点6,左子树5小于6,右子树7大于6,符合规律
- 右子树中的根节点10,左子树9小于10,右子树11大于10,符合规律
- 可以采用递归实现
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
int len=sequence.size();
if(len<=0)
return false;
//找到根节点,为最后一个元素
int root=sequence[len-1];
//找左子树,小于根节点
int i=0;
vector<int> sequenceleft;
for(; i<len-1; ++i) {
if(sequence[i] > root)
break;
sequenceleft.push_back(sequence[i]);
}
//找右子树,大于根节点,有小于根节点的那么false
int j=i;
vector<int> sequenceright;
for(;j<len-1; ++j) {
if(sequence[j]<root) return false;
sequenceright.push_back(sequence[j]);
}
//判断左右子树是不是二叉搜索树
bool left=true;
if(i>0)
left=VerifySquenceOfBST(sequenceleft);
bool right=true;
if(i<len-1)
right=VerifySquenceOfBST(sequenceright);
if(left && right){
cout<<"Yes"<<endl;
return true;
}
else {
cout<<"No"<<endl;
return false;
}
}
};
5.二叉树中和为某一值得路径
解题思路:
- 由于路径总是从根节点开始遍历,而前中后三种遍历方式中只有前序遍历是从根结点开始的,所以按照前序遍历的方式
- 由于没有指向父节点的指针,所以我们还需要将当前节点的上一个节点保存起来,每访问一个节点,都把当前节点添加到路径中去
- 当达到节点5的时候,路径中包含结点10和结点5,然后遍历到节点4,将其也添加到路径中去,这时已经到达叶子结点,但是值不等于22,所以不是我们要找的路径
- 这时候我们回到节点5,要去遍历5的右结点7,此时要删除结点4,将7添加到路径中去,值为22,使我们要找的路径
- 再次回到节点5,删除节点7; 再接着回到节点10, 删除节点5; 将12添加到路径中去,符合要求
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def FindPath(self, root, expectNumber):
# write code here
if not root:
return []
if root and not root.left and not root.right and root.val == expectNumber:
return [[root.val]]
result = []
left = self.FindPath(root.left, expectNumber-root.val)
right = self.FindPath(root.right, expectNumber-root.val)
for i in left + right:
result.append([root.val] + i)
return result
6.复杂链表的复制
时间限制:1秒 空间限制:32768K 热度指数:221374
本题知识点: 链表
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
//1. 复制原始链表的任一节点N并创建新节点N',再把N'链接到N的后边
void CloneNodes(RandomListNode* pHead)
{
RandomListNode* pNode=pHead;
while(pNode!=NULL)
{
RandomListNode* pCloned=new RandomListNode(0);
pCloned->label=pNode->label;
pCloned->next=pNode->next;
pCloned->random=NULL;
pNode->next=pCloned;
pNode=pCloned->next;
}
}
//2. 如果原始链表上的节点N的random指向S,则对应的复制节点N'的random指向S的下一个节点S'
void ConnectRandomNodes(RandomListNode* pHead)
{
RandomListNode* pNode=pHead;
while(pNode!=NULL)
{
RandomListNode* pCloned=pNode->next;
if(pNode->random!=NULL)
pCloned->random=pNode->random->next;
pNode=pCloned->next;
}
}
//3. 把得到的链表拆成两个链表,奇数位置上的结点组成原始链表,偶数位置上的结点组成复制出来的链表
RandomListNode* ReConnectNodes(RandomListNode* pHead)
{
RandomListNode* pNode=pHead;
RandomListNode* pClonedHead=NULL;
RandomListNode* pClonedNode=NULL;
//初始化
if(pNode!=NULL)
{
pClonedHead=pClonedNode=pNode->next;
pNode->next=pClonedNode->next;
pNode=pNode->next;
}
//循环
while(pNode!=NULL)
{
pClonedNode->next=pNode->next;
pClonedNode=pClonedNode->next;
pNode->next=pClonedNode->next;
pNode=pNode->next;
}
return pClonedHead;
}
//4. 三步合一
RandomListNode* Clone(RandomListNode* pHead)
{
CloneNodes(pHead);
ConnectRandomNodes(pHead);
return ReConnectNodes(pHead);
}
};