20210322笔试小记

本文探讨了如何在二叉树遍历中实现不同顺序(前序、中序、后序)的迭代方法,并介绍了如何在栈中以O(1)空间复杂度查询栈中最大值。此外,还讲解了如何使用常量空间和时间复杂度修改栈以查询最大值。同时,提供了寻找二叉树上从根节点到给定节点路径的递归和非递归实现。

emm

遇到的真实面试题,可惜太菜了,希望下次别再犯错吧

面试题1:栈怎么实现O(1)空间复杂度查询栈中最大值,可以修改栈的存储方式,push,pop操作,但是要保证O(1)时间复杂度,空间复杂度

参考:https://blog.csdn.net/taotaotheripper/article/details/8652665

思路:

  1. 想到使用辅助栈,用来存储当前栈的最大值,每次查询栈的最大值时,将对应栈的最大值一起弹出既可;

算法描述:

  1. 建立2个栈,1个当前存储栈,1个辅助最大值栈Sm
  2. 当push入栈的元素大于当前最大元素时,将该元素压入Sm;
  3. 当push入栈的元素小于Sm栈顶中最大元素时,将Sm栈顶元素再压入一次;
  4. 当当前最大元素被弹出时,辅助栈Sm栈顶的元素也对应弹出;
  5. 查询栈的最大值,也就是Sm栈顶的元素

进一步追问:如何使用常量空间O(1),常量时间O(1)完成这一操作

思路:使用一个标记量Max记录当前最大值,每次num入栈时,入栈值为(num-MAX),出栈时+max进行相应变换;

image-20210322213357666

前序遍历迭代实现

迭代算法中,每遇到一个结点A,就应该立即访问它,

每颗子树先访问先访问其根节点,对节点左右子树来说,一定是先访问根

在A的两棵子树中,遍历完左子树后,再遍历右子树

在访问完根结点后,需要将右子树入栈;

//模板
栈S;
p = root;
while(p != null || !S.isEmpty()){
    while(p != null){
        访问p结点;
        p的右子树入S;
        p = p的左子树;
    }
    p = S栈顶弹出
}

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(root != null || !stack.isEmpty()){
            while(root != null){
                stack.push(root.right);	//先将右子树入栈
                res.add(root.val);	//访问该节点
                root = root.left;
            }
            root = stack.pop();
        }
        return res;
    }   
}

后序遍历迭代实现

后序遍历:左右根,与前序遍历类似

每遇到一个结点 ,立即访问它,然后将左子树入栈,访问右子树—>这样得到的顺序为根右左,倒序后为左根右;


//目标
Stack s;
p = root;
while(p != null || !stack.isEmpty()){
    while(p != null){
        stack.push(p.left);
        访问p结点;
        p = p.right;
    }
    p = s.pop();
}
将结果逆序输出

//Coding
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode prev = null;
        while(root != null || !stack.isEmpty()){
            while(root != null)
            {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            if(root.right == null || root.right == prev){
                res.add(root.val);
                prev = root;
                root = null;
            }else{
                stack.push(root);
                root = root.right;
            }
        }
        return res;
    }
}

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(root != null || !stack.isEmpty()){
            while(root != null){
                stack.push(root.left);
                res.push(root.val);			//根右左
                root = root.right;
            }
            root = stack.pop();
        }
        Collections.reverse(res);	//反转为左右根
        return res;
    }
}

中序遍历迭代实现

思路:每到一个节点 A,因为根的访问在中间,将 A 入栈。然后遍历左子树,接着访问 A,最后遍历右子树。

在访问完 A 后,A 就可以出栈了。因为 A 和其左子树都已经访问完成。


//伪代码
栈S;
p = root;
while(p != null || S !=){
	while(p != null){
        p入S;
        p = p的左子树;
    }
    p = S.pop();
    访问p;
    p = p的右子树;
}

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(root != null || !stack.isEmpty()){
            while(root != null){
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            res.add(root.val);  //先访问左
            root = root.right;
        }
        return res;
    }
}

寻找二叉树上从根结点到给定结点的路径

递归方式:

思路:借助栈结构来保存路径上的节点,首先从根结点开始,一直往左找,如果左边找到就返回true;否则,如果左边找不到并且右子树不为空的情况下再继续往右子树找,如果左右子树都找不到,则返回false,方法运行完,栈中保存的就是从根到给定结点的路径


public static boolean searchNode(TreeNode root , Stack<TreeNode> s , TreeNode node)
{
    if(root == null)
        return false;
    s.push(root);
    if(root.val == node.val)
        return true;
    boolean b = false;
    //先找左子树
    if(root.left != null)
        b = searchNode(root.left , s , node);
    //左子树没找到,再去右子树找
    if(!b && root.right != null)
        b = searchNode(root.right, s, node);
    //左右都找不到
    if(!b)
        s.pop();
    return b;
}


非递归实现:

image-20210322215354049


public static boolean searchNode(TreeNode root, TreeNode node)
{
    if(root == null || node == null)
        return;
    Stack<TreeNode> s = new Stack<>();
    TreeNode p = root;
    TreeNode pre = null;	//上次出栈的节点
    while(p != null || !s.isEmpty())
    {
        while(p != null)
        {
            s.push(p);
            if(p.val == node.val){
                //如果找到了,则打印输出并返回
                for(TreeNode t : s){
                    System.out.print(treeNode.val + " ");
                }
                return;
            }
            p = p.left;
        }
        //左子树为null,开始往栈顶的右子树上找
        if(!s.isEmpty())
        {
            p = s.peek();
            //如果栈顶元素的左子树为null,那么久开始向栈顶元素的右子树上去找
            while(p.right == null || pre != null && p.right == pre)
            {
                pre = s.pop();
                p = s.peek();
            }
            //继续遍历p的右子树
            p = p.right;
        }
    }
}

力扣部分题

144.二叉树前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。



迭代实现:使用栈,

前序遍历:根左右,

入栈方式应该为:右左根

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;
        Deque<TreeNode> stack = new ArrayDeque<>();
        stack.push(root);
        while(!stack.isEmpty())
        {
            TreeNode node = stack.pop();
            res.add(node.val);
            //注意栈先进后出
            if(node.right != null)
                stack.push(node.right);
            if(node.left != null)
                stack.push(node.left);
        }
        return res;
    }
}


递归写法

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        dfs(root , res);
        return res;
    }
    private void dfs(TreeNode root, List<Integer> res)
    {
        if(root == null)
            return;        
        res.add(root.val);
        dfs(root.left , res);
        dfs(root.right , res);
    }
}

145.二叉树后续遍历

给定一个二叉树,返回它的 后序 遍历。



考虑它的迭代实现:

前序遍历:根-左-右

后序遍历:左右根;

修改前序遍历为:根-右-左,则其倒序就为后序遍历

Collections.reverse(List<?> list) : 翻转列表元素

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<Integer>();
        if(root == null)
            return ret;
        Deque<TreeNode> stack = new ArrayDeque<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            if(node == null)
                continue;
            ret.add(node.val);
            if(node.left != null)
                stack.push(node.left);
            if(node.right != null)
                stack.push(node.right);
        }
        Collections.reverse(ret);
        return ret;
    }
}

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        dfs(root , res);
        return res;
    }
    private void dfs(TreeNode root, List<Integer> res)
    {
        if(root == null)
            return;
        dfs(root.left , res);
        dfs(root.right , res);
        res.add(root.val);
    }
}

94.二叉树中序遍历

给定一个二叉树的根节点 root ,返回它的 中序 遍历。



迭代实现:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(root != null || !stack.isEmpty())
        {
            while(root != null)
            {
                stack.push(root);
                root = root.left;
            }
            TreeNode node = stack.pop();
            res.add(node.val);
            root = node.right;
        }
        return res;
    }
}

递归实现

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        dfs(root , res);
        return res;
    }
    private void dfs(TreeNode root , List<Integer> res)
    {
        if(root == null)
            return;
        dfs(root.left , res);
        res.add(root.val);
        dfs(root.right , res);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值