CD48.【C++ Dev】栈和队列练习题(栈的压入弹出序列、二叉树的层序遍历(正序和逆序))

目录

1.STL的stack和queue的使用

2.栈的压入弹出序列

分析

代码

改成简洁版(去掉continue)

提交结果

3.二叉树的层序遍历(正序)

分析

 方法1:双队列

提交结果

方法2:单队列

提交结果

3.二叉树的层序遍历(逆序)

分析

代码

提交结果


1.STL的stack和queue的使用

参加以下文章复习:

C32.【C++ Cont】静态实现栈及STL库的栈

CC33.【C++ Cont】静态实现队列及STL库的队列

2.栈的压入弹出序列

注:LeetCode上也有同样的题:https://leetcode.cn/problems/validate-stack-sequences/description/

https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&tqId=11174&ru=/exam/oj

描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列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.二叉树的层序遍历(逆序)

https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/description/

给你二叉树的根节点 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;
    }
};

提交结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值