目录
1.STL的stack和queue的使用
参加以下文章复习:
2.栈的压入弹出序列
注:LeetCode上也有同样的题:https://leetcode.cn/problems/validate-stack-sequences/description/
描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
1. 0<=pushV.length == popV.length <=1000
2. -1000<=pushV[i]<=1000
3. pushV 的所有数字均不相同
示例1
输入:
[1,2,3,4,5],[4,5,3,2,1]返回值:true说明:
可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop() 这样的顺序得到[4,5,3,2,1]这个序列,返回true示例2
输入:
[1,2,3,4,5],[4,3,5,1,2]返回值:false说明:
由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false
分析
本题没有什么规律可言,直接使用模拟算法来模拟一遍流程
给了两个序列:压栈序列和弹出序列,使用一个栈stack<int> st去模拟,分别用指针i和j去遍历压栈序列和弹出序列
初始情况下栈是空的,先压入一个数,看看弹出序列中有没有,如果没有则继续压入数,
1.i和j不能越界
2.如果栈为空,不能弹出数,要先入栈
3.出栈问题:栈顶元素==popV[j],则出栈(有可能存在一直出栈的情况)
4.栈顶元素!=popV[j],如果i没有遍历完,则入栈(有可能存在一直入栈的情况)
注意到入栈是有两种情况的:2和5
5.返回值的问题:
返回true:i和j全都变量完各自的序列,那么最终结果栈为空,一定符合要求
//不用再增加st.empty()的条件 if (i==pushV.size()&&j==popV.size()) return true;
返回false:i遍历完了,但j无法继续遍历(栈顶元素不等于popV[j])
if (i == pushV.size() && st.top()!=popV[j]) return false;
外面加个死循环一直判断上面5个条件即可
代码
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pushV int整型vector
* @param popV int整型vector
* @return bool布尔型
*/
bool IsPopOrder(vector<int>& pushV, vector<int>& popV)
{
stack<int> st;
int i=0,j=0;
while(true)
{
if (st.empty())//如果栈为空,不能弹出数,要先入栈
{
st.push(pushV[i++]);
}
if (st.top()==popV[j])//栈顶元素==popV[j],则出栈
{
st.pop();
j++;
}
else
{
if (i<pushV.size())/栈顶元素!=popV[j],如果i没有遍历完,则入栈
st.push(pushV[i++]);
}
if (i==pushV.size()&&j==popV.size())i和j全都变量完各自的序列,且最终结果栈为空
return true;
if (i == pushV.size() && st.top()!=popV[j])
return false;
}
return true;//随便返回
}
};
下面这个写法更简单:
#include <iterator>
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pushV int整型vector
* @param popV int整型vector
* @return bool布尔型
*/
bool IsPopOrder(vector<int>& pushV, vector<int>& popV)
{
stack<int> st;
int i=0,j=0;
while(i<pushV.size())
{
st.push(pushV[i++]);
if (st.top()!=popV[j])
continue;
else
{
while(!st.empty()&&st.top()==popV[j])//可能连续出栈
{
st.pop();
j++;
}
}
}
return st.empty();
}
};
改成简洁版(去掉continue)
#include <iterator>
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pushV int整型vector
* @param popV int整型vector
* @return bool布尔型
*/
bool IsPopOrder(vector<int>& pushV, vector<int>& popV)
{
stack<int> st;
int i=0,j=0;
while(i<pushV.size())
{
//整体上就两个操作:入栈和出栈
st.push(pushV[i++]);
while(!st.empty()&&st.top()==popV[j])
{
st.pop();
j++;
}
}
return st.empty();
}
};
提交结果
3.二叉树的层序遍历(正序)
给你二叉树的根节点
root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]]示例 2:
输入:root = [1] 输出:[[1]]示例 3:
输入:root = [] 输出:[]提示:
- 树中节点数目在范围
[0, 2000]
内-1000 <= Node.val <= 1000
分析
知识回顾:之前在C语言专栏的109.【C语言】数据结构之二叉树层序遍历文章提到过层序遍历的方法,但和这道题有区别:
(109.【C语言】数据结构之二叉树层序遍历文章只打印遍历的结果,并没有区分各个层的节点)
(LeetCode要求返回二维数组,显然要区分各个层的节点)
方法1:双队列
设队列qnode和qlevel,节点入队列qnode时,节点对应的层数入队列qlevel
设最小层为第一层,发现树中节点数目在范围 [0, 2000]
内,因此root有可能为空,需要分类讨论
如果root不为空,入root到qnode,qnode的层数1到qlevel
接下来一直循环
1.用cur_level控制当前层(为tmp尾插准备)
while (!qlevel.empty() &&qlevel.front() == cur_level)//短路运算 { //...... }
2.出队头节点的指针ptr前要先入ptr->left和ptr->right到qnode,入ptr->left->val和ptr->right->val到qlevel,注意是先入左节点再入右节点
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
if (root == nullptr)
return {};
queue<TreeNode*> qnode;
queue<int> qlevel;
vector<vector<int>> ret;
qnode.push(root);
qlevel.push(1);
int cur_level;
while (!qnode.empty())
{
cur_level = qlevel.front();
vector<int> tmp;
while (!qlevel.empty() &&qlevel.front() == cur_level)//短路运算
{
TreeNode* head = qnode.front();
if (head->left)
{
qnode.push(head->left);
qlevel.push(qlevel.front() + 1);
}
if (head->right)
{
qnode.push(head->right);
qlevel.push(qlevel.front() + 1);
}
tmp.push_back(qnode.front()->val);
qnode.pop();
qlevel.pop();
}
ret.push_back(tmp);
}
return ret;
}
};
附:手动调试测试用例的代码
例如:
可自行用本地IDE测试:
int main()
{
TreeNode n1(3);
TreeNode n2(9);
TreeNode n3(20);
TreeNode n4(15);
TreeNode n5(7);
(&n1)->left = &n2;
(&n1)->right = &n3;
(&n3)->left = &n4;
(&n3)->right = &n5;
(&n2)->right = nullptr;
(&n2)->left = nullptr;
(&n4)->right = nullptr;
(&n4)->left = nullptr;
(&n5)->right = nullptr;
(&n5)->left = nullptr;
vector<vector<int>> ret = Solution().levelOrder(&n1);
return 0;
}
提交结果
方法2:单队列
核心:使用levelsize(表示当前层节点的个数)来控制一层一层出
控制一层一层出的框架代码:
i==level_size表示这一层出完了
for (int i=0;i<level_size;i++)
{
//......
}
这一层出完了就更新level_size为qnode的大小,继续进行下一层出队,因此外面还要套个循环
while (level_size)//层数不为0就继续出队
{
//......
for (int i = 0; i < level_size; i++)
{
//......
}
//......
level_size = qnode.size();
}
完整代码:
注意:队头出队前要先将队头的非空左右节点入队
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
if (root == nullptr)
return {};
queue<TreeNode*> qnode;
vector<vector<int>> ret;
qnode.push(root);
int level_size=qnode.size();
while(level_size!=0)
{
vector<int> tmp;
for (int i=0;i<level_size;i++)
{
TreeNode* qhead=qnode.front();
if (qhead->left)
qnode.push(qhead->left);
if (qhead->right)
qnode.push(qhead->right);
tmp.push_back(qhead->val);
qnode.pop();
}
level_size=qnode.size();
ret.push_back(tmp);
}
return ret;
}
};
提交结果
3.二叉树的层序遍历(逆序)
给你二叉树的根节点
root
,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[15,7],[9,20],[3]]示例 2:
输入:root = [1] 输出:[[1]]示例 3:
输入:root = [] 输出:[]提示:
- 树中节点数目在范围
[0, 2000]
内-1000 <= Node.val <= 1000
分析
采用正难则反的策略,复用第3题的代码,将最后的结果逆置(reverse函数)即可
代码
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
if (root == nullptr)
return {};
queue<TreeNode*> qnode;
vector<vector<int>> ret;
qnode.push(root);
int level_size=qnode.size();
while(level_size!=0)
{
vector<int> tmp;
for (int i=0;i<level_size;i++)
{
TreeNode* qhead=qnode.front();
if (qhead->left)
qnode.push(qhead->left);
if (qhead->right)
qnode.push(qhead->right);
tmp.push_back(qhead->val);
qnode.pop();
}
level_size=qnode.size();
ret.push_back(tmp);
}
reverse(ret.begin(),ret.end());
return ret;
}
};