电信保温杯笔记——代码随想录 刷题攻略 二叉树

电信保温杯笔记——代码随想录 刷题攻略 二叉树

  • 电信保温杯笔记——代码随想录 刷题攻略
  • 1.关于二叉树,你该了解这些!
    • 二叉树代码
    • 测试代码
  • 2.二叉树:一入递归深似海,从此offer是路人
    • 144.二叉树的前序遍历
      • 方式一:递归
    • 94.二叉树的中序遍历
      • 方式一:递归
    • 145.二叉树的后序遍历
      • 方式一:递归
  • 3.二叉树:听说递归能做的,栈也能做!
    • 144.二叉树的前序遍历
      • 方式二:非递归(栈)非统一法
    • 94.二叉树的中序遍历
      • 方式二:非递归(栈)非统一法
        • 原方法
        • 修改
    • 145.二叉树的后序遍历
      • 方式二:非递归(栈)非统一法
        • 原方法
        • 修改
  • 4.二叉树:前中后序迭代方式的写法就不能统一一下么?
    • 144.二叉树的前序遍历
      • 方式三:非递归(栈)统一法
    • 94.二叉树的中序遍历
      • 方式三:非递归(栈)统一法
    • 145.二叉树的后序遍历
      • 方式三:非递归(栈)统一法
  • 5.二叉树:层序遍历登场!
    • 102.二叉树的层序遍历
      • 方式一:递归
        • 使用层数作为变量
        • 使用深度作为变量
      • 方式二:非递归(队列)非统一写法
      • 方式三:非递归(队列)统一写法
    • 补充题目
  • 6.二叉树:你真的会翻转二叉树么?
    • 226.翻转二叉树
      • 方式一:递归
      • 方式二:非递归(栈)非统一法
  • 7.本周小结!(二叉树)
  • 8.二叉树:我对称么?
    • 101. 对称二叉树
      • 方式一:递归
      • 方式二:非递归(单向队列)
    • 补充题目
  • 9.二叉树:看看这些树的最大深度
    • 104.二叉树的最大深度
      • 方式一:递归
        • 使用层数作为变量
        • 使用高度作为变量
      • 方式二:非递归
    • 补充题目
  • 10.二叉树:看看这些树的最小深度
    • 111.二叉树的最小深度
      • 方式一:递归
      • 方式二:非递归
  • 11.二叉树:我有多少个节点?
    • 222.完全二叉树的节点个数
      • 方式一:递归
      • 方式二:非递归
  • 12.二叉树:我平衡么?
    • 110.平衡二叉树
      • 方式一:递归
        • 使用辅助类Result
        • 不使用辅助类:
      • 方法二:非递归
        • 暴力迭代法
        • 优化
  • 13.二叉树:找我的所有路径?
    • 257. 二叉树的所有路径
      • 方式一:递归
      • 方式二:非递归
  • 14.本周总结!二叉树系列二
  • 15.二叉树:以为使用了递归,其实还隐藏着回溯
  • 16.二叉树:做了这么多题目了,我的左叶子之和是多少?
    • 404.左叶子之和
      • 方式一:递归
      • 方式二:非递归
  • 17.二叉树:我的左下角的值是多少?
    • 513.找树左下角的值
      • 方式一:递归
      • 方式二:非递归
  • 18.二叉树:路径总和
    • 112. 路径总和
      • 方式一:递归
      • 方式二:非递归
    • 113. 路径总和 II
  • 20.二叉树:构造一棵最大的二叉树
    • 106.从中序与后序遍历序列构造二叉树
    • 105.从前序与中序遍历序列构造二叉树
  • 21.本周小结!(二叉树系列三)
  • 22.二叉树:合并两个二叉树
    • 617.合并二叉树
      • 方式一:递归
        • 新建节点
        • 合并才新建节点
      • 方式二:非递归
        • 合并才新建节点
  • 23.二叉树:二叉搜索树登场!
    • 700.二叉搜索树中的搜索
      • 方式一:递归
      • 方式二:非递归
  • 24.二叉树:我是不是一棵二叉搜索树
    • 98.验证二叉搜索树
      • 方式一:递归
      • 方式二:非递归
  • 25.二叉树:搜索树的最小绝对差
    • 530.二叉搜索树的最小绝对差
      • 方式一:递归
      • 方式二:非递归
  • 26.二叉树:我的众数是多少?
    • 501.二叉搜索树中的众数
      • 方式一:递归
      • 方式二:非递归
  • 27.二叉树:公共祖先问题
    • 236. 二叉树的最近公共祖先
  • 28.本周小结!(二叉树系列四)
  • 29.二叉树:搜索树的公共祖先问题
    • 235. 二叉搜索树的最近公共祖先
      • 方式一:递归
      • 方式二:非递归
  • 30.二叉树:搜索树中的插入操作
    • 701.二叉搜索树中的插入操作
      • 方式一:递归
      • 方式二:非递归
  • 31.二叉树:搜索树中的删除操作
    • 450.删除二叉搜索树中的节点
  • 32.二叉树:修剪一棵搜索树
    • 669. 修剪二叉搜索树
      • 方式一:递归
  • 小结
  • 33.二叉树:构造一棵搜索树
    • 108.将有序数组转换为二叉搜索树
      • 方式一:递归
      • 方式二:非递归
  • 34.二叉树:搜索树转成累加树
    • 538.把二叉搜索树转换为累加树
      • 方式一:递归
      • 方式二:非递归
  • 35.二叉树:总结篇!(需要掌握的二叉树技能都在这里了)
  • 总结

电信保温杯笔记——代码随想录 刷题攻略

代码随想录 刷题攻略
电信保温杯笔记——代码随想录 刷题攻略
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第1张图片

1.关于二叉树,你该了解这些!

讲义地址

二叉树代码

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {
    }

    TreeNode(int val) {
        this.val = val;
    }

    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

测试代码

class Test{
    public static void main(String[] args) {
        TreeNode root = createTree("[2,1,3,null,4,null,7]");
        Solution solution = new Solution();
    }
    
    // string的输入格式为:"[2,1,3,null,4,null,7]"
    public static TreeNode createTree(String string){
        if (string.equals("[]")){
            return null;
        }
        ArrayList<Integer> list = new ArrayList<>();
        String temp = "";
        for (int i = 1; i < string.length(); i++) {
            char ch = string.charAt(i);
            if (ch == ',' || ch == ']') {
                if (temp.equals("null")) {
                    // null点先赋值为无穷小,后面再剪枝
                    list.add(Integer.MIN_VALUE);
                }else {
                    list.add(Integer.valueOf(temp));
                }
                temp = "";
            }else {
                temp += ch;
            }
        }
        if (list.get(0) == Integer.MIN_VALUE){
            return null;
        }
        TreeNode root = new TreeNode();


        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        TreeNode cur;

        for (int i = 0; i < list.size(); i++) {
            cur = queue.poll();
            cur.val = list.get(i);
            // null点先赋值为无穷小,后面再剪枝
            cur.left = new TreeNode(Integer.MIN_VALUE);
            cur.right = new TreeNode(Integer.MIN_VALUE);
            queue.offer(cur.left);
            queue.offer(cur.right);
        }

        // 剪枝,将值为无穷小的子节点删除
        queue.clear();
        queue.offer(root);
        while (!queue.isEmpty()) {
            cur = queue.poll();
            if (cur.left.val == Integer.MIN_VALUE) {
                cur.left = null;
            }else {
                queue.offer(cur.left);
            }
            if (cur.right.val == Integer.MIN_VALUE) {
                cur.right = null;
            }else {
                queue.offer(cur.right);
            }
        }
        return root;
    }
}

2.二叉树:一入递归深似海,从此offer是路人

讲义地址
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第2张图片

二叉树遍历有前序、中序、后序、层次遍历4种,其中前序、中序、后序的区别在于:左右节点的操作相对次序不变,中节点的操作在左右节点的前中后,就代表前序、中序、后序。
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第3张图片
一周刷爆LeetCode,算法da神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记的P6 5.二叉树中,基础第五课题目一:二叉树节点结构,有讲解先序中序后序遍历的递归与非递归方法。

下面三题的非递归方法在下两章中。

144.二叉树的前序遍历

leetcode地址

方式一:递归

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        preOrderRecurrent(root, res);
        return res;
    }

    public void preOrderRecurrent(TreeNode node, List<Integer> res){
        if (node == null){
            return;
        }
        res.add(node.val);
        preOrderRecurrent(node.left, res);
        preOrderRecurrent(node.right, res);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第4张图片

94.二叉树的中序遍历

leetcode地址

方式一:递归

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inOrderRecurrent(root, res);
        return res;
    }

    public void inOrderRecurrent(TreeNode node, List<Integer> res){
        if (node == null){
            return;
        }
        inOrderRecurrent(node.left, res);
        res.add(node.val);
        inOrderRecurrent(node.right, res);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第5张图片

145.二叉树的后序遍历

leetcode地址

方式一:递归

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postOrderRecurrent(root, res);
        return res;
    }

    public void postOrderRecurrent(TreeNode node, List<Integer> res){
        if (node == null){
            return;
        }
        postOrderRecurrent(node.left, res);
        postOrderRecurrent(node.right, res);
        res.add(node.val);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第6张图片

3.二叉树:听说递归能做的,栈也能做!

讲义地址

非递归方法又分为统一法和非统一法。
统一法:先序中序后序的代码结构相同,其中几条代码顺序不同;
非统一法:3者代码风格不能做到统一写法,只有前序和后序是相似的,中序的逻辑与另外两种不同。

统一法在下一章中介绍。

144.二叉树的前序遍历

leetcode地址

方式二:非递归(栈)非统一法

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.add(root);
        while (!stack.empty()){
            TreeNode temp = stack.pop();
            res.add(temp.val);
            if (temp.right != null){
                stack.push(temp.right);
            }
            if (temp.left != null){
                stack.push(temp.left);
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第7张图片

94.二叉树的中序遍历

leetcode地址

方式二:非递归(栈)非统一法

原方法

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.empty()){
            if (cur != null){
                stack.push(cur);
                cur = cur.left;
            }else{
                cur = stack.pop();
                res.add(cur.val);
                cur = cur.right;
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第8张图片
左节点需要遍历到底,才会开始遍历右节点,右节点再重复遍历自身左节点的操作。

里面的if-else可以改为while

修改

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

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第9张图片

145.二叉树的后序遍历

leetcode地址

方式二:非递归(栈)非统一法

原方法

这是先序遍历的基础上修改,将先序变成中右左,然后再倒序得到的,并不是真正的后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.empty()){
            TreeNode temp = stack.pop();
            res.add(temp.val);
            if (temp.left != null){
                // 放入左节点
                stack.push(temp.left);
            }
            if (temp.right != null){
                // 放入右节点
                stack.push(temp.right);
            }
        }
        Collections.reverse(res);
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第10张图片
如果不使用reverse操作,可以使用多一个stack来收集val,然后将stack吐出到res即可。

修改

真正的后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;// 这个指针用于添加节点
        TreeNode temp = null;// 这个指针用于对stack顶节点进行判断
        TreeNode pre = null;// 这个指针用于判断当前节点右节点是否遍历过
        while (cur != null || !stack.empty()){
            while (cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            temp = stack.peek();
            // 查看当前节点的右节点是否遍历过,null相对于遍历了
            if (temp.right == null || temp.right == pre){
            	// 右节点已经遍历过
                pre =stack.pop();
                res.add(pre.val);
                cur = null;
            }else {
            	//没有遍历过
                cur = temp.right;
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第11张图片

4.二叉树:前中后序迭代方式的写法就不能统一一下么?

讲义地址

非递归(栈)统一法效率低,因为它加入了大量的null记号作为中节点的标记,虽然记一种当三种用,不过效率低,面试意义不大。

这个方法的核心思想:以先序为例,将栈节点弹出,化栈节点为3个节点,将这三个节点压进去栈,顺序是右左中null,弹出顺序就是null中左右,以null为中节点的标记,意思就是中节点的左右子节点已经遍历完了。

144.二叉树的前序遍历

leetcode地址

方式三:非递归(栈)统一法

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode cur = root.left;
        while (!stack.empty()){
            TreeNode temp = stack.pop();
            if (temp != null){
            	// 由于先序的出栈顺序是中左右,所以入栈顺序是右左中
                if (temp.right != null){
                	// 放入右节点
                    stack.push(temp.right);
                }
                if (temp.left != null){
                	// 放入左节点
                    stack.push(temp.left);
                }
            	// 放入中节点和null,null作为中节点的标记
                stack.push(temp);
                stack.push(null);
            }else {
                temp = stack.pop();
                res.add(temp.val);
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第12张图片

94.二叉树的中序遍历

leetcode地址

方式三:非递归(栈)统一法

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode cur = root.left;
        while (!stack.empty()){
            TreeNode temp = stack.pop();
            if (temp != null){
            	// 由于中序的出栈顺序是左中右,所以入栈顺序是右中左
                if (temp.right != null){
                	// 放入右节点
                    stack.push(temp.right);
                }
            	// 放入中节点和null,null作为中节点的标记
                stack.push(temp);
                stack.push(null);
                if (temp.left != null){
                	// 放入左节点
                    stack.push(temp.left);
                }
            }else {
                temp = stack.pop();
                res.add(temp.val);
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第13张图片

145.二叉树的后序遍历

leetcode地址

方式三:非递归(栈)统一法

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null){
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode cur = root.left;
        while (!stack.empty()){
            TreeNode temp = stack.pop();
            if (temp != null){
            	// 由于后序的出栈顺序是左右中,所以入栈顺序是中右左
            	// 放入中节点和null,null作为中节点的标记
                stack.push(temp);
                stack.push(null);
                if (temp.right != null){
                	// 放入右节点
                    stack.push(temp.right);
                }
                if (temp.left != null){
                	// 放入左节点
                    stack.push(temp.left);
                }
            }else {
                temp = stack.pop();
                res.add(temp.val);
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第14张图片

5.二叉树:层序遍历登场!

讲义地址

层次遍历,实质就是先序遍历。但并不是输出结果的数组拼接起来就是先序遍历的顺序,因为并不是每一层的数组填满了再填下一层的数组。
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第15张图片

102.二叉树的层序遍历

leetcode地址

方式一:递归

使用层数作为变量

根节点代表第0层。level代表node所在的层。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        ArrayList res = new ArrayList<>();
        levelOrderRecurrent(root, 0, res);
        return res;
    }

    public void levelOrderRecurrent(TreeNode node, int level, List<List<Integer>> res) {
        if (node == null) {
            return;
        }

        if (res.size() <= level) {
            res.add(new ArrayList<>());
        }
        res.get(level).add(node.val);

        levelOrderRecurrent(node.left, level + 1, res);
        levelOrderRecurrent(node.right, level + 1, res);
        return;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第16张图片

使用深度作为变量

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        ArrayList res = new ArrayList<>();
        levelOrderRecurrent(root, 1, res);
        return res;
    }

    public void levelOrderRecurrent(TreeNode node, int depth, List<List<Integer>> res) {
        if (node == null) {
            return;
        }

        if (res.size() < depth) {
            res.add(new ArrayList<>());
        }
        res.get(depth - 1).add(node.val);

        levelOrderRecurrent(node.left, depth + 1, res);
        levelOrderRecurrent(node.right, depth + 1, res);
        return;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第17张图片

方式二:非递归(队列)非统一写法

用一个变量len记录该层的长度,也就是当前队列的长度。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        ArrayList res = new ArrayList<>();
        if (root == null){
            return res;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        ArrayList<Integer> list = null;
        TreeNode temp = null;
        int len = 0;
        while (!queue.isEmpty()){
            len = queue.size();
            list = new ArrayList<>();
            while (len > 0){
                temp = queue.pop();
                list.add(temp.val);
                if (temp.left != null){
                    queue.add(temp.left);
                }
                if (temp.right != null){
                    queue.add(temp.right);
                }
                len--;
            }
            res.add(list);
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第18张图片

方式三:非递归(队列)统一写法

以null作为该层结尾的标记。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        ArrayList res = new ArrayList<>();
        if (root == null){
            return res;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        queue.add(null);
        ArrayList<Integer> list = new ArrayList<>();
        TreeNode temp = null;
        while (!queue.isEmpty()){
            temp = queue.pop();
            if (temp != null){
                list.add(temp.val);
                if (temp.left != null){
                    queue.add(temp.left);
                }
                if (temp.right != null){
                    queue.add(temp.right);
                }
                if (queue.peek() == null){
                    queue.add(null);
                }
            }else {
                if (queue.isEmpty()){
                    break;
                }
                res.add(list);
                list = new ArrayList<Integer>();
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第19张图片

补充题目

107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的层序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104.二叉树的最大深度
111.二叉树的最小深度

6.二叉树:你真的会翻转二叉树么?

讲义地址

226.翻转二叉树

leetcode地址

先序后序遍历都可以,也是各有三种方法,无非是先交换还是先遍历,唯独中序不行,不能遍历中穿插交换。

下面是先序的做法。

方式一:递归

class Solution {
    public TreeNode invertTree(TreeNode root) {
        preOrderRecurrentFun(root);
        return root;
    }
    public void preOrderRecurrentFun(TreeNode node){
        if (node == null){
            return;
        }
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
        preOrderRecurrentFun(node.left);
        preOrderRecurrentFun(node.right);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第20张图片

方式二:非递归(栈)非统一法

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null){
            return root;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode cur = null;
        TreeNode temp = null;
        while (!stack.empty()){
            cur = stack.pop();
            temp = cur.left;
            cur.left = cur.right;
            cur.right = temp;
            if (cur.right != null){
                stack.push(cur.right);
            }
            if (cur.left != null){
                stack.push(cur.left);
            }
        }
        return root;
    }
}

7.本周小结!(二叉树)

讲义地址

8.二叉树:我对称么?

讲义地址

101. 对称二叉树

leetcode地址

有点像先序遍历。有三种方法。

方式一:递归

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left, root.right);
    }

    private boolean compare(TreeNode left, TreeNode right) {

        if (left == null && right != null) {
            return false;
        }
        if (left != null && right == null) {
            return false;
        }
        if (left == null && right == null) {
            return true;
        }
        if (left.val != right.val) {
            return false;
        }
        // 比较外侧
        boolean compareOutside = compare(left.left, right.right);
        // 比较内侧
        boolean compareInside = compare(left.right, right.left);
        return compareOutside && compareInside;
    }
}

方式二:非递归(单向队列)

class Solution {
    public boolean isSymmetric(TreeNode root) {
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root.left);
        queue.add(root.right);
        TreeNode left = null;
        TreeNode right = null;
        while (!queue.isEmpty()){
            left = queue.pop();
            right = queue.pop();
            if (left == null && right == null){
                continue;
            }
            if (left == null || right == null || right.val != left.val){
                return false;
            }
            queue.add(left.left);
            queue.add(right.right);
            queue.add(left.right);
            queue.add(right.left);
        }
        return true;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第21张图片

补充题目

100.相同的树
572.另一个树的子树

9.二叉树:看看这些树的最大深度

讲义地址

104.二叉树的最大深度

leetcode地址

方式一:递归

使用层数作为变量

根节点为第0层,它的子节点为第1层,如此类推,level代表当前层,maxLevel代表遇到的最大层。采用顺序记录的树结构:
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第22张图片

class Solution {
    public int maxDepth(TreeNode root) {
        return preOrderRecurrentFun(root, 0, -1) + 1;
    }
    public int preOrderRecurrentFun(TreeNode node, int level, int maxLevel){
        if (node == null){
            return maxLevel;
        }
        maxLevel = Integer.max(level, maxLevel);
        int leftMaxLevel = preOrderRecurrentFun(node.left, level + 1, maxLevel);
        int rightMaxLevel = preOrderRecurrentFun(node.right, level + 1, maxLevel);
        maxLevel =Integer.max(leftMaxLevel, rightMaxLevel);
        return maxLevel;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第23张图片

使用高度作为变量

还有逆序记录的树状结构,记录当前节点子树的最大高度height:
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第24张图片
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第25张图片

class Solution {
    public int maxDepth(TreeNode root) {
        return preOrderRecurrentFun(root);
    }
    public int preOrderRecurrentFun(TreeNode node){
        if (node == null){
            return 0;
        }
        int leftHeight = preOrderRecurrentFun(node.left);
        int rightHeight = preOrderRecurrentFun(node.right);
        return Integer.max(leftHeight, rightHeight) + 1;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第26张图片

方式二:非递归

层次遍历

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null){
            return 0;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        TreeNode temp = null;
        int depth = 0;
        int len = 0;
        while (!queue.isEmpty()){
            len = queue.size();
            while (len > 0){
                temp = queue.poll();
                if (temp.left != null){
                    queue.offer(temp.left);
                }
                if (temp.right != null){
                    queue.offer(temp.right);
                }
                len--;
            }
            depth++;
        }
        return depth;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第27张图片

补充题目

559.n叉树的最大深度

10.二叉树:看看这些树的最小深度

讲义地址

111.二叉树的最小深度

leetcode地址

方式一:递归

后序遍历。

class Solution {
    public int minDepth(TreeNode root) {
        return postOrderRecurrentFun(root);
    }
    public int postOrderRecurrentFun(TreeNode node){
        if (node == null){
            return 0;
        }
        int leftMinDepth = postOrderRecurrentFun(node.left);
        int rightMinDepth = postOrderRecurrentFun(node.right);
        if (leftMinDepth == 0){
            return rightMinDepth + 1;
        }
        if(rightMinDepth == 0){
            return leftMinDepth + 1;
        }
        return Integer.min(leftMinDepth, rightMinDepth) + 1;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第28张图片

方式二:非递归

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null){
            return 0;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int depth = 0;
        TreeNode temp = null;
        int len = 0;
        while (!queue.isEmpty()){
            len = queue.size();
            depth++;
            while (len > 0){
                temp = queue.poll();
                // 碰到叶节点返回
                if (temp.left == null && temp.right == null){
                    return depth;
                }
                if (temp.left != null){
                    queue.offer(temp.left);
                }
                if (temp.right != null){
                    queue.offer(temp.right);
                }
                len--;
            }
        }
        return depth;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第29张图片

11.二叉树:我有多少个节点?

讲义地址

222.完全二叉树的节点个数

leetcode地址

方式一:递归

class Solution {
    public int countNodes(TreeNode root) {
        return recurrentFun(root);
    }
    public int recurrentFun(TreeNode node){
        if (node == null){
            return 0;
        }
        return recurrentFun(node.left) + recurrentFun(node.right) + 1;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第30张图片

方式二:非递归

层次遍历。

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null){
            return 0;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int res = 0;
        TreeNode temp = null;
        int len = 0;
        while (!queue.isEmpty()){
            len = queue.size();
            while (len > 0){
                temp = queue.poll();
                res++;
                if (temp.left != null){
                    queue.offer(temp.left);
                }
                if (temp.right != null){
                    queue.offer(temp.right);
                }
                len--;
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第31张图片

12.二叉树:我平衡么?

讲义地址

110.平衡二叉树

leetcode地址

方式一:递归

使用辅助类Result

后序遍历。

class Solution {
    public boolean isBalanced(TreeNode root) {
        return recurrent(root).isBalanced;
    }
    public Result recurrent(TreeNode node){
        Result res = new Result();
        if (node == null){
            return res;
        }
        Result leftRes = recurrent(node.left);
        Result rightRes = recurrent(node.right);
        res.height = Integer.max(leftRes.height, rightRes.height) + 1;
        boolean isBalanced = true;
        if (Math.abs(leftRes.height - rightRes.height) > 1){
            isBalanced = false;
        }
        res.isBalanced = isBalanced && leftRes.isBalanced && rightRes.isBalanced;
        
        return res;
    }
}
class Result{
    int height = 0;
    boolean isBalanced = true;

    public Result() {
    }

    public Result(int height, boolean isBalanced) {
        this.height = height;
        this.isBalanced = isBalanced;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第32张图片

不使用辅助类:

其实这个也是后序遍历,只是可以提前返回而已,只要将这行代码

        if (leftHeight == -1) {
            return -1;
        }

下移一行,其实就是后序遍历。

class Solution {
   /**
     * 递归法
     */
    public boolean isBalanced(TreeNode root) {
        return getHeight(root) != -1;
    }

    private int getHeight(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.left);
        if (leftHeight == -1) {
            return -1;
        }
        int rightHeight = getHeight(root.right);
        if (rightHeight == -1) {
            return -1;
        }
        // 左右子树高度差大于1,return -1表示已经不是平衡树了
        if (Math.abs(leftHeight - rightHeight) > 1) {
            return -1;
        }
        return Math.max(leftHeight, rightHeight) + 1;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第33张图片

方法二:非递归

因为要比较左右节点的高度,所以因为使用真正的后序遍历。

暴力迭代法

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null){
            return true;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;// 这个指针用于添加节点
        TreeNode temp = null;// 这个指针用于对stack顶节点进行判断
        TreeNode pre = null;// 这个指针用于判断当前节点右节点是否遍历过
        while (cur != null || !stack.empty()){
            while (cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            temp = stack.peek();
            // 查看当前节点的右节点是否遍历过,null相对于遍历了
            if (temp.right == null || temp.right == pre){
                // 右节点已经遍历过
                if (Math.abs(getHeight(temp.left) - getHeight(temp.right)) > 1){
                    return false;
                }
                pre =stack.pop();
                cur = null;
            }else {
                //没有遍历过
                cur = temp.right;
            }
        }
        return true;
    }
    public int getHeight(TreeNode root){
        if (root == null){
            return 0;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int len;
        int depth = 0;
        TreeNode temp;
        while (!queue.isEmpty()){
            len = queue.size();
            while (len > 0) {
                temp = queue.poll();
                if (temp.left != null){
                    queue.offer(temp.left);
                }
                if (temp.right != null) {
                    queue.offer(temp.right);
                }
                len--;
            }
            depth++;
        }
        return depth;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第34张图片

优化

针对暴力迭代法的getHeight方法做优化,利用TreeNode.val来保存当前结点的高度,但个人认为这样会破坏了树本身的值。

13.二叉树:找我的所有路径?

讲义地址

257. 二叉树的所有路径

leetcode地址

方式一:递归

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        ArrayList<String> res = new ArrayList<>();
        recurrent(root, "", res);
        return res;
    }
    public void recurrent(TreeNode node, String path, List<String> res){
        if (node == null){
            return;
        }
        path = path + String.valueOf(node.val);
        recurrent(node.left, path + "->" , res);
        recurrent(node.right, path + "->" , res);
        if (node.left == null && node.right == null){
            res.add(path);
        }
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第35张图片

方式二:非递归

层次遍历。

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        ArrayList<String> res = new ArrayList<>();
        if (root == null){
            res.add("");
            return res;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        ArrayList<String> curPaths;
        ArrayList<String> oldPaths = new ArrayList<>();
        oldPaths.add("");
        queue.offer(root);
        int len;
        TreeNode temp;
        String path;
        while (!queue.isEmpty()){
            len = queue.size();
            curPaths = new ArrayList<>();
            for (int i = 0; i < len; i++) {
                temp = queue.poll();
                path = oldPaths.get(i)+ String.valueOf(temp.val);
                if (temp.left == null && temp.right == null) {
                    res.add(path);
                    continue;
                }
                if (temp.left != null){
                    queue.offer(temp.left);
                    curPaths.add(path + "->");
                }
                if (temp.right != null){
                    queue.offer(temp.right);
                    curPaths.add(path + "->");
                }
            }
            oldPaths = curPaths;
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第36张图片

14.本周总结!二叉树系列二

讲义地址

15.二叉树:以为使用了递归,其实还隐藏着回溯

讲义地址

16.二叉树:做了这么多题目了,我的左叶子之和是多少?

讲义地址

404.左叶子之和

leetcode地址

方式一:递归

和先序后序中序层次遍历无关,主要是遍历的过程中如何判断是左叶子节点。

判断条件:当前点a的左子节点不为空,并且左子节点的左右子节点都为空,那么a的左子节点为左叶节点。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        return recurrent(root);
    }
    public int recurrent(TreeNode node){
        // level是node的上一层,根节点为0层,根节点的上一层为-1层
        if (node == null) {
            return 0;
        }
        int res = 0;
        if (node.left != null) {
            if (node.left.left == null && node.left.right == null){
                res += node.left.val;
            }
        }
        res += recurrent(node.left);
        res += recurrent(node.right);
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第37张图片

方式二:非递归

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if (root == null){
            return 0;
        }
        int res = 0;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode temp;
        while (!stack.empty()) {
            temp = stack.pop();
            if (temp.right != null){
                stack.push(temp.right);
            }
            if (temp.left != null){
                stack.push(temp.left);
                if (temp.left.left == null && temp.left.right == null) {
                    res += temp.left.val;
                }
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第38张图片

17.二叉树:我的左下角的值是多少?

讲义地址

513.找树左下角的值

leetcode地址

方式一:递归

层次遍历。

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        if (root == null){
            return 0;
        }
        ArrayList<List<Integer>> res = new ArrayList<>();
        levelOrderRecurrent(root, 1, res);
        return res.get(res.size() - 1).get(0);
    }

    public void levelOrderRecurrent(TreeNode node, int depth, List<List<Integer>> res) {
        if (node == null) {
            return;
        }

        if (res.size() < depth) {
            res.add(new ArrayList<Integer>());
        }
        res.get(depth - 1).add(node.val);

        levelOrderRecurrent(node.left, depth + 1, res);
        levelOrderRecurrent(node.right, depth + 1, res);
        return;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第39张图片

方式二:非递归

层次遍历。

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        if (root == null){
            return 0;
        }
        int res = 0;
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int len;
        TreeNode temp;
        while (!queue.isEmpty()) {
            len = queue.size();
            res = queue.peek().val;
            while (len > 0){
                temp = queue.poll();
                if (temp.left != null){
                    queue.offer(temp.left);
                }
                if (temp.right != null) {
                    queue.offer(temp.right);
                }
                len--;
            }
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第40张图片

18.二叉树:路径总和

讲义地址

112. 路径总和

leetcode地址

方式一:递归

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null){
            return false;
        }
        return preOrderRecurrent(root, 0, targetSum);
    }
    public boolean preOrderRecurrent(TreeNode node, int sum, int targetSum){
        sum += node.val;
        if (sum == targetSum && node.left == null && node.right == null) {
            return true;
        }
        boolean b1 = false;
        boolean b2 = false;
        if (node.left != null){
            b1 = preOrderRecurrent(node.left, sum, targetSum);
            // 左子节点已经找到路径了,右节点就不用找了
            if (b1 == true){
                return true;
            }
        }
        if (node.right != null){
            b2 = preOrderRecurrent(node.right, sum, targetSum);
        }
        return b1 || b2;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第41张图片

方式二:非递归

层次遍历。

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        ArrayList<Integer> curSums;
        ArrayList<Integer> oldSums = new ArrayList<>();
        oldSums.add(0);
        queue.offer(root);
        int len;
        int curSum;
        TreeNode temp;
        while (!queue.isEmpty()) {
            len = queue.size();
            curSums = new ArrayList<>();
            for (int i = 0; i < len; i++) {
                temp = queue.poll();
                curSum = oldSums.get(i) + temp.val;
                if (curSum == targetSum && temp.left == null && temp.right == null) {
                    return true;
                }
                // oldSum.get(i) < targetSum
                if (temp.left != null) {
                    queue.offer(temp.left);
                    curSums.add(curSum);
                }
                if (temp.right != null) {
                    queue.offer(temp.right);
                    curSums.add(curSum);
                }
            }
            oldSums = curSums;
        }
        return false;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第42张图片

113. 路径总和 II

leetcode地址

1

20.二叉树:构造一棵最大的二叉树

讲义地址

106.从中序与后序遍历序列构造二叉树

leetcode地址
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第43张图片
新左数组:
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第44张图片

新右数组:
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第45张图片

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return Recurrent(inorder,0, inorder.length,
                postorder, 0, postorder.length);
    }
    // 变量都是左闭右开的,left指向数组起始位置,right指向末尾元素的下一个位置
    public TreeNode Recurrent(int[] inorder, int inLeft, int inRight,
                              int[] postorder, int postLeft, int postRight){
        if (inRight - inLeft == 0){
            return null;
        }
        // 1.找出中序数组中,中节点的位置
        int inMid = 0;
        int postMid = postRight -1;// 后序数组中,中节点的位置
        for (int i = inLeft; i < inRight; i++) {
            if (inorder[i] == postorder[postMid]){
                inMid = i;
                break;
            }
        }
        TreeNode node = new TreeNode(postorder[postMid]);

        // 2.划分后,左数组信息,
        // 如果先序数组只有一个元素,即inLeft + 1 = inRight,
        // 那么划分后,左数组的 newInLeft == newInRight,即左数组没有元素。
        // 这些变量只是方便阅读,实际中可以直接将新的值放到函数中
        int newInLeft = inLeft;
        int newInRight = inMid;
        int newPostLeft = postLeft;
        int newPostRight = postLeft + inMid - inLeft;
        node.left = Recurrent(inorder, newInLeft, newInRight,
                postorder, newPostLeft, newPostRight);

        // 3.划分后,右节点信息
        newInLeft = inMid + 1;
        newInRight = inRight;
        newPostLeft = postLeft + inMid - inLeft;
        newPostRight = postMid;
        node.right = Recurrent(inorder, newInLeft, newInRight,
                postorder, newPostLeft, newPostRight);
        return node;
    }
}

105.从前序与中序遍历序列构造二叉树

leetcode地址

1

21.本周小结!(二叉树系列三)

讲义地址

22.二叉树:合并两个二叉树

讲义地址

617.合并二叉树

leetcode地址

跟先序中序后续层次遍历无关。

方式一:递归

新建节点

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        return recurrent(root1, root2);
    }
    public TreeNode recurrent(TreeNode node1, TreeNode node2){
        if (node1 == null && node2 == null){
            return null;
        }
        TreeNode node = new TreeNode();
        if (node1 == null && node2 != null){
            node.val = node2.val;
            node.left = recurrent(null, node2.left);
            node.right = recurrent(null, node2.right);
        }
        if (node1 != null && node2 == null) {
            node.val = node1.val;
            node.left = recurrent(null, node1.left);
            node.right = recurrent(null, node1.right);
        }
        if (node1 != null && node2 != null) {
            node.val = node1.val + node2.val;
            node.left = recurrent(node1.left, node2.left);
            node.right = recurrent(node1.right, node2.right);
        }
        return node;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第46张图片

合并才新建节点

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        return recurrent(root1, root2);
    }

    public TreeNode recurrent(TreeNode node1, TreeNode node2) {
        if (node1 == null) {
            return node2;
        }
        if (node2 == null) {
            return node1;
        }
        TreeNode node = new TreeNode(node1.val + node2.val);
        node.left = recurrent(node1.left, node2.left);
        node.right = recurrent(node1.right, node2.right);
        return node;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第47张图片

方式二:非递归

合并才新建节点

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if (root1 == null){
            return root2;
        }
        if (root2 == null) {
            return root1;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode root = new TreeNode();
        stack.push(root2);
        stack.push(root1);
        stack.push(root);
        TreeNode node1;
        TreeNode node2;
        TreeNode cur;
        while (!stack.empty()) {
            cur = stack.pop();
            node1 = stack.pop();
            node2 = stack.pop();
            cur.val = node1.val + node2.val;

            // 处理当前节点的右节点
            if (node1.right == null) {
                cur.right = node2.right;
            } else if (node2.right == null) {
                cur.right = node1.right;
            }else {
                // node1.right != null && node1.right != null
                stack.push(node2.right);
                stack.push(node1.right);
                cur.right = new TreeNode();
                stack.push(cur.right);
            }
            // 处理当前节点的左节点
            if (node1.left == null) {
                cur.left = node2.left;
            } else if (node2.left == null) {
                cur.left = node1.left;
            }else {
                // node1.left != null && node1.left != null
                stack.push(node2.left);
                stack.push(node1.left);
                cur.left = new TreeNode();
                stack.push(cur.left);
            }
        }
        return root;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第48张图片

23.二叉树:二叉搜索树登场!

讲义地址

700.二叉搜索树中的搜索

leetcode地址

和遍历无关。

方式一:递归

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        return preOrderRecurrent(root, val);
    }

    public TreeNode preOrderRecurrent(TreeNode node, int val) {
        if (node == null) {
            return null;
        }
        if (node.val == val) {
            return node;
        }
        TreeNode left = preOrderRecurrent(node.left, val);
        if (left != null) {
            return left;
        }
        TreeNode right = preOrderRecurrent(node.right, val);
        return right;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第49张图片

方式二:非递归

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if (root == null){
            return null;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.add(root);
        while (!stack.empty()){
            TreeNode temp = stack.pop();
            if (temp.val == val){
                return temp;
            }
            if (temp.right != null){
                stack.push(temp.right);
            }
            if (temp.left != null){
                stack.push(temp.left);
            }
        }
        return null;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第50张图片

24.二叉树:我是不是一棵二叉搜索树

讲义地址

98.验证二叉搜索树

leetcode地址

中序遍历的节点,如果是升序排列,那么这棵树就是二叉搜索树,这是中序的性质。要在遍历中进行比较,最主要是要记录上一个节点的值。

方式一:递归

class Solution {
    TreeNode min;

    public boolean isValidBST(TreeNode root) {
        return recurrent(root);
    }

    public boolean recurrent(TreeNode node) {
        if (node == null) {
            return true;
        }

        boolean leftRes = recurrent(node.left);
        // 如果左子树不满足,则提起终止
        if (!leftRes) {
            return false;
        }

        if (min != null && min.val >= node.val) {
            return false;
        }
        min = node;
        
        boolean rightRes = recurrent(node.right);
        
        return rightRes;
    }
}

方式二:非递归

class Solution {
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode min = null;
        while (cur != null || !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            if (min != null && min.val >= cur.val) {
                return false;
            }
            min = cur;
            cur = cur.right;
        }
        return true;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第51张图片

25.二叉树:搜索树的最小绝对差

讲义地址

530.二叉搜索树的最小绝对差

leetcode地址

中序遍历。

方式一:递归

class Solution {
    TreeNode last;
    int res = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        inOrderRecurrent(root);
        return res;
    }
    public void inOrderRecurrent(TreeNode node){
        if (node == null){
            return;
        }
        inOrderRecurrent(node.left);

        if (last != null){
            res = Integer.min(res, Math.abs(last.val - node.val));
        }
        last = node;
        inOrderRecurrent(node.right);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第52张图片

方式二:非递归

class Solution {
    public int getMinimumDifference(TreeNode root) {
        if (root == null || (root.left == null && root.right == null)) {
            return 0;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode last = null; // 记录上一个节点
        int res = Integer.MAX_VALUE;
        while (cur != null || !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            if (last != null) {
                res = Integer.min(res, Math.abs(last. val - cur.val));
            }
            last = cur;
            cur = cur.right;
        }
        return res;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第53张图片

26.二叉树:我的众数是多少?

讲义地址

501.二叉搜索树中的众数

leetcode地址

中序遍历。

方式一:递归

class Solution {
    ArrayList<Integer> resList = new ArrayList<>();
    int maxCount = 0;
    int count = 0;
    TreeNode pre = null;

    public int[] findMode(TreeNode root) {
        inOrderRecurrent(root);
        int len = resList.size();
        int[] res = new int[len];
        for (int i = 0; i < len; i++) {
            res[i] = resList.get(i);
        }
        return res;
    }

    public void inOrderRecurrent(TreeNode node) {
        if (node == null) {
            return;
        }

        inOrderRecurrent(node.left);

        int rootValue = node.val;
        // 计数
        if (pre == null || rootValue != pre.val) {
            count = 1;
        } else {
            count++;
        }
        // 更新结果以及maxCount
        if (count > maxCount) {
            resList.clear();
            resList.add(rootValue);
            maxCount = count;
        } else if (count == maxCount) {
            resList.add(rootValue);
        }

        pre = node;

        inOrderRecurrent(node.right);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第54张图片

方式二:非递归

class Solution {
    public int[] findMode(TreeNode root) {
        ArrayList<Integer> resList = new ArrayList<>();
        int maxCount = 0;
        int count = 0;
        TreeNode pre = null;

        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                // 计数
                if (pre == null || cur.val != pre.val) {
                    count = 1;
                } else {
                    count++;
                }
                // 更新结果
                if (count > maxCount) {
                    maxCount = count;
                    resList.clear();
                    resList.add(cur.val);
                } else if (count == maxCount) {
                    resList.add(cur.val);
                }
                pre = cur;
                cur = cur.right;
            }
        }

        int len = resList.size();
        int[] res = new int[len];
        for (int i = 0; i < len; i++) {
            res[i] = resList.get(i);
        }
        return res;
//        return result.stream().mapToInt(Integer::intValue).toArray();
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第55张图片

27.二叉树:公共祖先问题

讲义地址

236. 二叉树的最近公共祖先

leetcode地址

前提条件:节点不重复。

此题属于回溯,非递归法不适合模拟回溯的过程。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return postOrderRecurrent(root, p, q);
    }

    public TreeNode postOrderRecurrent(TreeNode node, TreeNode p, TreeNode q) {
        if (node == null || node == p || node == q) {// 递归结束条件
            return node;
        }

        TreeNode left = postOrderRecurrent(node.left, p, q);
        TreeNode right = postOrderRecurrent(node.right, p, q);

        if (left == null && right == null) {// 若未找到节点 p 或 q
            return null;
        }
        if (left != null && right == null) {// 若找到一个节点
            return left;
        }
        if (left == null && right != null) {// 若找到一个节点
            return right;
        }
        // 两个节点都找到
        return node;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第56张图片

28.本周小结!(二叉树系列四)

讲义地址

29.二叉树:搜索树的公共祖先问题

讲义地址

235. 二叉搜索树的最近公共祖先

leetcode地址

前提条件:节点不重复,节点2个输入节点都是存在于树中。

与遍历顺序无关。

节点q,p的关系无非下面三种:相互不包含,q包含p,p包含q
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第57张图片
那么公共节点s一定在区间【p,q】或者【q,p】里面。

当s为父节点的左节点时,那么它一定比右边的那些节点的值要小:
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第58张图片
同理,当s为父节点的右节点时,那么它一定比左边的那些节点的值要小:
电信保温杯笔记——代码随想录 刷题攻略 二叉树_第59张图片

方式一:递归

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return recurrent(root, p, q);
    }

    public TreeNode recurrent(TreeNode node, TreeNode p, TreeNode q) {
        if (node.val > p.val && node.val > q.val) {
            return recurrent(node.left, p, q);
        }
        if (node.val < p.val && node.val < q.val) {
            return recurrent(node.right, p, q);
        }
        return node;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第60张图片

方式二:非递归

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode temp = root;
        while (true) {
            if (temp.val > p.val && temp.val > q.val) {
                temp = temp.left;
            }else if (temp.val < p.val && temp.val < q.val) {
                temp = temp.right;
            }else {
                return temp;
            }
        }
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第61张图片

30.二叉树:搜索树中的插入操作

讲义地址

701.二叉搜索树中的插入操作

leetcode地址

方式一:递归

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        return recurrent(root,val);
    }
    public TreeNode recurrent(TreeNode node, int val){
        if (node == null){
            return new TreeNode(val);
        }
        if (node.val > val) {
            node.left = recurrent(node.left, val);
        }
        if (node.val < val) {
            node.right = recurrent(node.right,val);
        }
        return node;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第62张图片

方式二:非递归

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) {
            return root = new TreeNode(val);
        }
        TreeNode cur = root;
        while (true) {
            if (cur.val > val) {
                if (cur.left == null) {
                    cur.left = new TreeNode(val);
                    return root;
                }else {
                    cur = cur.left;
                }
            }else {
                if (cur.right == null) {
                    cur.right = new TreeNode(val);
                    return root;
                }else {
                    cur = cur.right;
                }
            }
        }
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第63张图片

31.二叉树:搜索树中的删除操作

讲义地址

450.删除二叉搜索树中的节点

leetcode地址

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        return delete(root, key);
    }

    private TreeNode delete(TreeNode node, int key) {
        if (node == null) {
            return null;
        }
        if (node.val > key) {
            node.left = delete(node.left, key);
        } else if (node.val < key) {
            node.right = delete(node.right, key);
        } else {
            // node.val == key,找到
            if (node.left == null) {
                return node.right;
            }
            if (node.right == null) {
                return node.left;
            }
            // 左右子节点都不为空
            TreeNode temp = node.right;
            // 找右子树的最小值
            while (temp.left != null) {
                temp = temp.left;
            }
            // 最小值移动到删除节点的位置
            node.val = temp.val;
            // 删除原来的最小值
            node.right = delete(node.right, temp.val);
        }
        return node;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第64张图片
非递归法不适合。

32.二叉树:修剪一棵搜索树

讲义地址

669. 修剪二叉搜索树

leetcode地址

方式一:递归

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        return recurrent(root, low, high);
    }
    public TreeNode recurrent(TreeNode node, int low, int high){
        if (node == null) {
            return null;
        }
        if (node.val > high) {
            return recurrent(node.left, low, high);
        }
        if (node.val < low){
            return recurrent(node.right, low, high);
        }
        node.left = recurrent(node.left, low, high);
        node.right = recurrent(node.right, low, high);
        
        return node;
    }
}

小结

30,31,32这三章的思路非常相似。

33.二叉树:构造一棵搜索树

讲义地址

108.将有序数组转换为二叉搜索树

leetcode地址

方式一:递归

左闭右开 [left,right),先序遍历。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return recurrent(nums, 0, nums.length);
    }

    public TreeNode recurrent(int[] nums, int left, int right) {
        if (left >= right) {
            return null;
        }
        if (right - left == 1) {
            return new TreeNode(nums[left]);
        }
        int mid = left + ((right - left) >> 1);
        TreeNode node = new TreeNode(nums[mid]);

        node.left = recurrent(nums, left, mid);
        node.right = recurrent(nums, mid + 1, right);

        return node;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第65张图片

方式二:非递归

左闭右开 [left,right),层次遍历。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if (nums.length == 0) return null;

        //根节点初始化
        TreeNode root = new TreeNode();
        Queue<TreeNode> nodeQueue = new LinkedList<>();
        Queue<Integer> leftQueue = new LinkedList<>();
        Queue<Integer> rightQueue = new LinkedList<>();

        // 根节点入队列
        nodeQueue.offer(root);
        // 0为左区间下标初始位置
        leftQueue.offer(0);
        // nums.size()为右区间下标初始位置
        rightQueue.offer(nums.length);

        while (!nodeQueue.isEmpty()) {
            TreeNode cur = nodeQueue.poll();
            int left = leftQueue.poll();
            int right = rightQueue.poll();
            int mid = left + ((right - left) >> 1);

            // 将mid对应的元素给中间节点
            cur.val = nums[mid];

            // 处理左区间
            if (left < mid) {
                cur.left = new TreeNode();
                nodeQueue.offer(cur.left);
                leftQueue.offer(left);
                rightQueue.offer(mid);
            }

            // 处理右区间
            if (right > mid + 1) {
                cur.right = new TreeNode();
                nodeQueue.offer(cur.right);
                leftQueue.offer(mid + 1);
                rightQueue.offer(right);
            }
        }
        return root;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第66张图片
用多了2个队列去模拟递归中的输入参数。

34.二叉树:搜索树转成累加树

讲义地址

538.把二叉搜索树转换为累加树

leetcode地址

右中左的中序遍历。

方式一:递归

class Solution {
    int sum = 0;

    public TreeNode convertBST(TreeNode root) {
        recurrent(root);
        return root;
    }

    public void recurrent(TreeNode node) {
        if (node == null) {
            return;
        }
        recurrent(node.right);
        sum += node.val;
        node.val = sum;
        recurrent(node.left);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第67张图片

方式二:非递归

class Solution {
    public TreeNode convertBST(TreeNode root) {
        int sum = 0;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.right;
            }
            cur = stack.pop();
            sum += cur.val;
            cur.val = sum;
            cur = cur.left;
        }
        return root;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 二叉树_第68张图片

35.二叉树:总结篇!(需要掌握的二叉树技能都在这里了)

讲义地址

总结

if (与遍历有关) {
    先序中序后序层次遍历;
    各种遍历的性质:
    比如:中序遍历的序列如果是降序排列的,那么它就是搜索二叉树
    if (递归) {
         1. 选择输入参数
         2. 终止条件
         3. 本轮递归的操作
         4. 返回值
    }else {
         非递归
    }
}else {
    与遍历无关
}

if (自顶向下) {
    比如计算深度、层数,单路径往下的
}else {
    自底向上
    比如向上递归返回子树的高度
}

各种结构的判断条件:
比如:叶节点的条件:一个节点的子节点存在,且子节点的左右子节点都为空;

你可能感兴趣的:(算法与数据结构,leetcode,算法,深度优先)