• 【代码随想录】二叉树刷题


    理论基础

    满二叉树:一棵二叉树只有度为 0 的结点和度为 2 的结点,并且度为 0 的结点在同一层

    完全二叉树:对节点从上至下、左至右开始编号,其所有编号都能与 相同高度的满二叉树中的编号对应

    二叉搜索树:它存储的元素必须具备 可比较性

    • 任意一个节点的值都 大于子树 所有节点的值
    • 任意一个节点的值都 小于子树 所有节点的值
    • 它的左右子树也是一棵 二叉搜索树

    平衡二叉搜索树 (AVL 树):它是一棵空树,或它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树

    二叉树的存储方式

    链式存储:

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    顺序存储:

    a
    ------
    |    |
    b    c
    --------
    | |  | |
    d e  f g
    
    下标 i 从 0 开始
    - 左子树: 2i + 1
    - 右子树: 2i + 2
    -------------------
    0  1  2  3  4  5  6
    a, b, c, d, e, f, g
    -------------------
    
    下标 i 从 1 开始
    - 左子树: 2i
    - 右子树: 2i + 1
    ----------------------
    0  1  2  3  4  5  6  7
    -  a, b, c, d, e, f, g
    ----------------------
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    二叉树的遍历

    前中后序遍历

    递归的本质:

    • 每一次递归调用都会把函数的 局部变量参数值返回地址 等压入调用栈中
    • 递归返回的时候,从栈顶弹出上一次递归的各项参数
    • 这就是递归为什么可以返回上一层位置的原因

    先序遍历144. 二叉树的前序遍历

    // 递归实现
    class Solution {
        public List<Integer> preorderTraversal(TreeNode root) {
            List<Integer> list = new ArrayList<>(); 
            dfs(root, list);
            return list;
        }
    
        void dfs(TreeNode node, List<Integer> list) {
            if (node == null) return;
            list.add(node.val); // 根
            dfs(node.left, list); // 左
            dfs(node.right, list); // 右
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // 迭代实现
    class Solution {
        public List<Integer> preorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
            if (root != null) 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    后序遍历145. 二叉树的后序遍历

    // 递归实现
    class Solution {
        public List<Integer> postorderTraversal(TreeNode root) {
            List<Integer> list = new ArrayList<>();
            dfs(root, list);
            return list;
        }
    
        void dfs(TreeNode node, List<Integer> list) {
            if (node == null) return;
            dfs(node.left, list); // 左
            dfs(node.right, list); // 右
            list.add(node.val); // 根
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // 迭代实现
    public List<Integer> postorderTraversal(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	if (root == null) return res;
    
    	Stack<TreeNode> stack = new Stack<>();
    	stack.add(root);
    
    	while (!stack.isEmpty()) {
    		TreeNode node = stack.pop();
    		res.add(node.val);
    		if (node.left != null) stack.add(node.left); // 左
    		if (node.right != null) stack.add(node.right); // 右
    	}
    
    	Collections.reverse(res); // 反转
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    中序遍历94. 二叉树的中序遍历

    // 递归实现
    class Solution {
        List<Integer> res = new ArrayList<>();
    
        public List<Integer> inorderTraversal(TreeNode root) {
            dfs(root);
            return res;
        }
    
        void dfs(TreeNode node) {
            if (node == null) return;
            dfs(node.left); // 左
            res.add(node.val); // 根
            dfs(node.right); // 右
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    中序遍历 的 迭代实现不太容易记,建议记后面的 迭代写法统一

    // 迭代实现1
    public List<Integer> inorderTraversal(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	Stack<TreeNode> st = new Stack<>();
    	TreeNode cur = root;
    	while (cur != null || !st.isEmpty()) {
    		if (cur != null) { // *
    			st.push(cur);
    			cur = cur.left;
    		} else { // *
    			cur = st.pop();
    			res.add(cur.val);
    			cur = cur.right;
    		}
    	}
    	return res;
    }
    
    // 迭代实现2
    public List<Integer> inorderTraversal(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	Stack<TreeNode> st = new Stack<>();
    	TreeNode cur = root;
    	while (cur != null || !st.isEmpty()) {
    		while (cur != null) { // *
    			st.push(cur);
    			cur = cur.left;
    		} 
    		cur = st.pop();
    		res.add(cur.val);
    		cur = cur.right;
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    迭代写法统一*

    这种写法下,三种遍历只需要调整代码顺序即可实现:

    // 中序遍历
    public List<Integer> inorderTraversal(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	if (root == null) return res;
    	Stack<Object> st = new Stack<>();
    	st.push(root);
    	while (!st.empty()) {
    		Object o = st.pop();
    		if (o instanceof TreeNode) {
    			TreeNode node = (TreeNode) o;
    			if (node.right != null) st.push(node.right); // *
    			st.push(node.val); // *
    			if (node.left != null) st.push(node.left); // *
    		} else {
    			res.add((Integer) o);
    		}
    	}
    	return res;
    }
    
    // 先序遍历
    public List<Integer> preorderTraversal(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	if (root == null) return res;
    	Stack<Object> st = new Stack<>();
    	st.push(root);
    	while (!st.empty()) {
    		Object o = st.pop();
    		if (o instanceof TreeNode) {
    			TreeNode node = (TreeNode) o; // *
    			if (node.right != null) st.push(node.right); // *
    			if (node.left != null) st.push(node.left); // *
    			st.push(node.val);
    		} else {
    			res.add((Integer) o);
    		}
    	}
    	return res;
    }
    
    // 后序遍历
    public List<Integer> postorderTraversal(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	if (root == null) return res;
    	TreeNode cur = root;
    	Stack<Object> st = new Stack<>();
    	st.push(cur);
    	while (!st.isEmpty()) {
    		Object o = st.pop();
    		if (o instanceof TreeNode) {
    			TreeNode node = (TreeNode) o;
    			st.push(node.val); // *
    			if (node.right != null) st.push(node.right); // *
    			if (node.left != null) st.push(node.left); // *
    		} else {
    			res.add((Integer)o);
    		}
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    层序遍历

    题目:102. 二叉树的层序遍历 - 力扣(LeetCode)

    public List<List<Integer>> levelOrder(TreeNode root) {
    	List<List<Integer>> res = new ArrayList<>();
    	if (root == null) return res;
    	Queue<TreeNode> q = new LinkedList<>();
    	q.add(root);
    	while (!q.isEmpty()) {
    		List<Integer> tmpList = new ArrayList<>();
    		for (int i = q.size(); i > 0; i--) {
    			TreeNode node = q.poll();
    			tmpList.add(node.val);
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    		res.add(tmpList);
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    层序遍历相关题目

    二叉树的层序遍历 II

    题目:107. 二叉树的层序遍历 II - 力扣(LeetCode)

    给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

    public List<List<Integer>> levelOrderBottom(TreeNode root) {
    	List<List<Integer>> res = new ArrayList<>();
    	if (root == null) return res;
    	Deque<TreeNode> q = new ArrayDeque<>();
    	q.add(root);
    	while (!q.isEmpty()) {
    		List<Integer> tmpList = new ArrayList<>();
    		for (int i = q.size(); i > 0; i--) {
    			TreeNode node = q.poll();
    			tmpList.add(node.val);
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    		res.add(0, tmpList); // 往头部放
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    二叉树的右视图

    题目:199. 二叉树的右视图 - 力扣(LeetCode)

    给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

    每次将层次遍历的最后一个值添加到结果中:

    public List<Integer> rightSideView(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	if (root == null) return res;
    	Queue<TreeNode> q = new LinkedList<>();
    	q.add(root);
    	while (!q.isEmpty()) {
    		for (int i = q.size(); i > 0; i--) {
    			TreeNode node = q.poll();
    			if (i == 1) res.add(node.val);
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    dfs 将第一次访问该深度的右边元素添加到结果中:

    class Solution {
        List<Integer> res = new ArrayList<>();
    
        public List<Integer> rightSideView(TreeNode root) {
            dfs(root, 0);
            return res;
        }
    
        void dfs(TreeNode node, int depth) {
            if (node == null) return;
            // 第一次访问这个深度
            if (depth == res.size()) res.add(node.val);
            dfs(node.right, depth + 1);
            dfs(node.left, depth + 1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    二叉树的层平均值

    题目:637. 二叉树的层平均值 - 力扣(LeetCode)

    给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10^-5 以内的答案可以被接受。

    public List<Double> averageOfLevels(TreeNode root) {
    	List<Double> res = new ArrayList<>();
    	if (root == null) return res;
    	Queue<TreeNode> q = new LinkedList<>();
    	q.add(root);
    	while (!q.isEmpty()) {
    		double sum = 0;
    		int n = q.size();
    		for (int i = n; i > 0; i--) {
    			TreeNode node = q.poll();
    			sum += node.val;
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    		res.add(sum / n);
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    N 叉树的层序遍历

    题目:429. N 叉树的层序遍历 - 力扣(LeetCode)

    public List<List<Integer>> levelOrder(Node root) {
    	List<List<Integer>> res = new ArrayList<>();
    	if (root == null) return res;
    	Queue<Node> q = new LinkedList<>();
    	q.add(root);
    	while (!q.isEmpty()) {
    		List<Integer> tmpList = new ArrayList<>();
    		for (int i = q.size(); i > 0; i--) {
    			Node node = q.poll();
    			tmpList.add(node.val);
    			for (Node n : node.children) q.add(n);
    		}
    		res.add(tmpList);
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在每个树行中找最大值

    题目:515. 在每个树行中找最大值 - 力扣(LeetCode)

    给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。

    public List<Integer> largestValues(TreeNode root) {
    	List<Integer> res = new ArrayList<>();
    	if (root == null) return res;
    	Queue<TreeNode> q = new LinkedList<>();
    	q.add(root);
    	while (!q.isEmpty()) {
    		int max = Integer.MIN_VALUE;
    		for (int i = q.size(); i > 0; i--) {
    			TreeNode node = q.poll();
    			max = Math.max(max, node.val);
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    		res.add(max);
    	}
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    填充每个节点的下一个右侧节点指针

    题目:116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode)

    层序遍历:

    public Node connect(Node root) {
    	if (root == null) return root;
    	Queue<Node> q = new LinkedList<>();
    	q.add(root);
    	while (!q.isEmpty()) {
    		for (int i = q.size(); i > 0; i--) {
    			Node node = q.poll();
    			if (i > 1) node.next = q.peek(); // 除最后一个元素,都指向下一元素
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    	}
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    DFS:

    public Node connect(Node root) {
    	if (root == null) return root;
    	if (root.left != null) {
    		root.left.next = root.right; // 亲兄弟建立连接
    		if (root.next != null) // 堂兄弟建立连接
    			root.right.next = root.next.left;
    	}
    	connect(root.left);
    	connect(root.right);
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    填充每个节点的下一个右侧节点指针 II

    题目:117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)

    这题用层序遍历的解法的话,和 116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode) 一模一样。

    DFS 的做法:

    class Solution {
        public Node connect(Node root) {
            if (root == null) return root;
            // 左右子树都不为 null, 直接连接
            if (root.left != null && root.right != null)
                root.left.next = root.right;
            // 左不为 null, 右为 null, 则右子树的 next 由根的 next 得出
            if (root.left != null && root.right == null)
                root.left.next = getNext(root.next);
            // 右不为 null
            if (root.right != null)
                root.right.next = getNext(root.next);
            connect(root.right); // 必须先 right 再 left
            connect(root.left);
            return root;
        }
        // 找到 node 最左的第一个节点
        Node getNext(Node node) {
            if (node == null) return null;
            // 先尝试找左节点
            if (node.left != null) return node.left;
            // 再尝试找右节点
            if (node.right != null) return node.right;
            // 左右节点都不存在,尝试对 node.next 进行相同操作
            if (node.next != null) return getNext(node.next);
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    二叉树的最大深度

    题目:104. 二叉树的最大深度 - 力扣(LeetCode)

    给定一个二叉树,找出其最大深度。
    二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

    递归:

    public int maxDepth(TreeNode root) {
    	if (root == null) return 0;
    	return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
    
    • 1
    • 2
    • 3
    • 4

    DFS:

    class Solution {
        int maxLevel = 0;
    
        public int maxDepth(TreeNode root) {
            dfs(root, 1);
            return maxLevel;
        }
    
        void dfs(TreeNode node, int level) {
            if (node == null) return;
            if (level > maxLevel) maxLevel = level;
            dfs(node.left, level + 1);
            dfs(node.right, level + 1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    BFS:

    public int maxDepth(TreeNode root) {
    	if (root == null) return 0;
    	Deque<TreeNode> q = new ArrayDeque<>();
    	q.add(root);
    	int cnt = 0;
    	while (!q.isEmpty()) {
    		for (int i = q.size(); i > 0; i--) {
    			TreeNode node = q.poll();
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    		cnt ++ ;
    	}
    	return cnt;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    二叉树的最小深度

    题目:111. 二叉树的最小深度 - 力扣(LeetCode)

    给定一个二叉树,找出其最小深度。
    最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

    层序遍历:最快

    public int minDepth(TreeNode root) {
    	if (root == null) return 0;
    	Queue<TreeNode> q = new LinkedList<>();
    	q.add(root);
    	int level = 1;
    	while (!q.isEmpty()) {
    		for (int i = q.size(); i > 0; i--) {
    			TreeNode node = q.poll();
    			// 层序遍历时, 遇到第一个叶子节点,返回当前树的高度
    			if (node.left == null && node.right == null)
    				return level;
    			if (node.left != null) q.add(node.left);
    			if (node.right != null) q.add(node.right);
    		}
    		level++;
    	}
    	return level;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    DFS:

    class Solution {
        int minLevel = Integer.MAX_VALUE;
    
        public int minDepth(TreeNode root) {
            if (root == null) return 0;
            dfs(root, 1);
            return minLevel;
        }
    
        void dfs(TreeNode node, int level) {
            if (node == null) return;
            if (node.left == null && node.right == null)
                minLevel = Math.min(minLevel, level);
            dfs(node.left, level + 1);
            dfs(node.right, level + 1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    递归:

    public int minDepth(TreeNode root) {
    	if (root == null) return 0;
    	// 处理只有一个子节点的情况
    	if (root.left == null && root.right != null)
    		return minDepth(root.right) + 1;
    	if (root.right == null && root.left != null)
    		return minDepth(root.left) + 1;
    	return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    二叉树题目

    翻转二叉树

    题目:226. 翻转二叉树 - 力扣(LeetCode)

    给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

    递归:

    public TreeNode invertTree(TreeNode root) {
    	if (root == null) return null;
    
    	// 交换左右子节点
    	TreeNode tmpNode = root.left;
    	root.left = root.right;
    	root.right = tmpNode;
    
    	invertTree(root.left);
    	invertTree(root.right);
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    BFS:

    public TreeNode invertTree(TreeNode root) {
    	if (root == null) return null;
    	Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(root);
    	while (!queue.isEmpty()) {
    		TreeNode node = queue.poll();
    		// 交换子树
    		TreeNode t = node.right;
    		node.right = node.left;
    		node.left = t;
    		if (node.left != null) queue.offer(node.left);
    		if (node.right != null) queue.offer(node.right);
    	}
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    DFS:

    public TreeNode invertTree(TreeNode root) {
    	if (root == null) return null;
    	// 交换子树
    	TreeNode t = invertTree(root.left);
    	root.left = invertTree(root.right);
    	root.right = t;
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对称二叉树

    题目:101. 对称二叉树 - 力扣(LeetCode)

    给你一个二叉树的根节点 root , 检查它是否轴对称。

    递归:

    class Solution {
        public boolean isSymmetric(TreeNode root) {
            if (root == null) return true;
            return helper(root.left, root.right);
        }
    
        boolean helper(TreeNode n1, TreeNode n2) {
            if (n1 == null && n2 == null) return true;
            if (n1 == null || n2 == null || n1.val != n2.val) return false;
            return helper(n1.left, n2.right) && helper(n1.right, n2.left); 
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    迭代:层序遍历

    public boolean isSymmetric(TreeNode root) {
    	Queue<TreeNode> q = new LinkedList<>();
    	q.offer(root.left);
    	q.offer(root.right);
    	while (!q.isEmpty()) {
    		TreeNode n1 = q.poll();
    		TreeNode n2 = q.poll();
    		if (n1 == null && n2 == null) continue;
    		if (n1 == null || n2 == null || n1.val != n2.val)
    			return false;
    		q.offer(n1.left);
    		q.offer(n2.right);
    		q.offer(n1.right);
    		q.offer(n2.left);
    	}
    	return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    完全二叉树的节点个数*

    题目:222. 完全二叉树的节点个数 - 力扣(LeetCode)

    给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

    利用完全二叉树性质的做法:

    class Solution {
        public int countNodes(TreeNode root) {
            if (root == null) return 0;
            int l = getHeight(root.left); // 左子树的高度
            int r = getHeight(root.right); // 右子树的高度
            if (l == r) {
                // 左右子树高度相等,表示左子树一定是满二叉树
                // 用公式计算左子树的节点个数:2^l - 1
                // 递归计算右子树的节点个数 countNodes(root.right)
                // 再 + 1 (当前节点)
                return countNodes(root.right) + (1 << l); // 1 << l == 2^l
            } else {
                // 最后一层不满,但是倒数第二层是满的,右子树一定是满二叉树
                // 公式计算右子树的节点个数: 2^r - 1
                // 递归计算左子树的节点个数 countNodes(root.left)
                // 再 + 1 (当前节点)
                return countNodes(root.left) + (1 << r); // 1 << r == 2^r
            }
        }
    
        // 完全二叉树计算高度
        int getHeight(TreeNode node) {
            int height = 0;
            while (node != null) {
                node = node.left;
                height++;
            }
            return height;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    递归:

    public int countNodes(TreeNode root) {
    	if (root == null) return 0;
    	return countNodes(root.left) + countNodes(root.right) + 1;
    }
    
    • 1
    • 2
    • 3
    • 4

    DFS:

    class Solution {
        int num = 0;
    
        public int countNodes(TreeNode root) {
            dfs(root);
            return num;
        }
    
        void dfs(TreeNode node) {
            if (node == null) return;
            num++;
            dfs(node.left);
            dfs(node.right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    层序遍历:

    public int countNodes(TreeNode root) {
    	if (root == null) return 0;
    	Queue<TreeNode> q = new LinkedList<>();
    	q.offer(root);
    	int count = 0;
    	while (!q.isEmpty()) {
    		TreeNode node = q.poll();
    		if (node.left != null) q.offer(node.left);
    		if (node.right != null) q.offer(node.right);
    		count++;
    	}
    	return count;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    平衡二叉树*

    题目:110. 平衡二叉树 - 力扣(LeetCode)

    给定一个二叉树,判断它是否是高度平衡的二叉树。

    本题中,一棵高度平衡二叉树定义为:一个二叉树 每个节点 的左右两个子树的高度差的绝对值不超过 1 。

    递归:

    class Solution {
        public boolean isBalanced(TreeNode root) {
            if (root == null) return true;
            if (Math.abs(getHeight(root.left) - getHeight(root.right)) > 1) return false;
            return isBalanced(root.left) && isBalanced(root.right); 
        }
        int getHeight(TreeNode node) {
            if (node == null) return 0;
            int leftH = getHeight(node.left);
            int rightH = getHeight(node.right);
            return Math.max(leftH, rightH) + 1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    带剪枝的递归:

    class Solution {
        public boolean isBalanced(TreeNode root) {
            return getHeight(root) != -1;
        }
    
        int getHeight(TreeNode node) {
            if (node == null) return 0;
            int leftH = getHeight(node.left); // 左子树高度
            int rightH = getHeight(node.right); // 右子树高度
            // 剪枝:左子树 或 右子树 不满足条件 或 当前节点不满足条件,直接返回 -1
            if (leftH == -1 || rightH == -1 || Math.abs(leftH - rightH) > 1) return -1;
            // 左右子树都满足条件,返回当前节点的高度
            return Math.max(leftH, rightH) + 1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    二叉树的所有路径

    题目:257. 二叉树的所有路径 - 力扣(LeetCode)

    给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

    DFS + StringBuilder:

    class Solution {
        List<String> res = new ArrayList<>();
        
        public List<String> binaryTreePaths(TreeNode root) {
            dfs(new StringBuilder(), root); 
            return res;
        }
    
        void dfs(StringBuilder path, TreeNode node) {
            if (node == null) return;
            path.append(node.val); 
            if (node.left == null && node.right == null) {
                res.add(path.toString());
                return;
            }
            dfs(new StringBuilder(path).append("->"), node.left);
            dfs(new StringBuilder(path).append("->"), node.right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    另一棵树的子树

    题目:572. 另一棵树的子树 - 力扣(LeetCode)

    给你两棵二叉树 rootsubRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false
    二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

    class Solution {
        public boolean isSubtree(TreeNode root, TreeNode subRoot) {
            if (root == null) return false; 
            if (isSameTree(root, subRoot)) return true;
            return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
        }
        // 判断 n1 和 n2 是否是相同的树
        boolean isSameTree(TreeNode n1, TreeNode n2) {
            if (n1 == null && n2 == null) return true;
            if (n1 == null || n2 == null || n1.val != n2.val) return false;
            return isSameTree(n1.left, n2.left) && isSameTree(n1.right, n2.right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    左叶子之和

    题目:404. 左叶子之和 - 力扣(LeetCode)

    给定二叉树的根节点 root ,返回所有左叶子之和。

    DFS:

    class Solution {
        int res = 0;
        public int sumOfLeftLeaves(TreeNode root) {
            if (root.left == null && root.right == null) return 0;
            dfs(root, null);
            return res;
        }
        // DFS 时传入 parent 节点用来判断是否是左叶子
        void dfs(TreeNode node, TreeNode parent) {
            if (node == null) return;
            // 判断是左叶子
            if (node.left == null && node.right == null && parent.left == node)
                res += node.val;
            dfs(node.left, node);
            dfs(node.right, node);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    递归:

    public int sumOfLeftLeaves(TreeNode root) {
    	if (root == null) return 0;
    	int res = 0;
    	if (root.left != null && root.left.left == null && root.left.right == null)
    		res += root.left.val;
    	return  sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right) + res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    找树左下角的值

    题目:513. 找树左下角的值 - 力扣(LeetCode)

    给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

    层序遍历:每一层更新 res 为当前层第一个节点的值

    public int findBottomLeftValue(TreeNode root) {
    	Queue<TreeNode> q = new LinkedList<>();
    	q.offer(root);
    	int res = root.val;
    	while (!q.isEmpty()) {
    		int size = q.size();
    		for (int i = size; i > 0; i--) {
    			TreeNode node = q.poll();
    			if (node.left != null) q.offer(node.left);
    			if (node.right != null) q.offer(node.right);
    			if (i == size) res = node.val; // 每次更新为当前层第一个节点的值
    		}
    	} 
    	return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    DFS:遍历同时求出树的深度

    // DFS:遍历同时求出树的深度
    class Solution {
        int maxLevel = Integer.MIN_VALUE;
        int res;
    
        public int findBottomLeftValue(TreeNode root) {
            dfs(root, 0);     
            return res;
        }
        
        void dfs(TreeNode node, int level) {
            if (node == null) return;
            if (node.left == null && node.right == null)
                if (level > maxLevel) {
                    maxLevel = level;
                    res = node.val;
                }
            dfs(node.left, level + 1);
            dfs(node.right, level + 1); 
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    路径总和

    题目:112. 路径总和 - 力扣(LeetCode)

    给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false

    DFS 模板:

    class Solution {
        public boolean hasPathSum(TreeNode root, int targetSum) {
            return dfs(root, 0, targetSum);
        }
    
        boolean dfs(TreeNode node, int sum, int targetSum) {
            if (node == null) return false;
            if (node.left == null && node.right == null && sum + node.val == targetSum)
                return true;
            return dfs(node.left, node.val + sum, targetSum)
                    || dfs(node.right, node.val + sum, targetSum);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用 - 操作优化掉额外参数:

    public boolean hasPathSum(TreeNode root, int targetSum) {
    	if (root == null) return false;
    	if (root.left == null && root.right == null && root.val == targetSum)
    		return true;
    	return hasPathSum(root.left, targetSum - root.val)
    			|| hasPathSum(root.right, targetSum - root.val);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    题目:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

    给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

    参考题解:图解构造二叉树之中序+后序

    递归:

    public TreeNode buildTree(int[] inorder, int[] postorder) {
    	if (inorder.length == 0) return null;
    	if (inorder.length == 1) return new TreeNode(inorder[0]);
    	// 初始化根节点
    	TreeNode root = new TreeNode(postorder[postorder.length - 1]);
    	// 在 inorder[] 中找到根的位置
    	for (int i = 0; i < inorder.length; i++) {
    		if (inorder[i] == root.val) {
    			root.left = buildTree(
    				Arrays.copyOfRange(inorder, 0, i),
    				Arrays.copyOfRange(postorder, 0, i));
    			root.right = buildTree(
    				Arrays.copyOfRange(inorder, i + 1, inorder.length),
    				Arrays.copyOfRange(postorder, i, postorder.length - 1));
    			break;
    		}
    	}
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    递归优化:传入索引进行优化 + 数组模拟哈希

    class Solution {
        int[] inorder_map = new int[6001]; // 数组模拟哈希
        public TreeNode buildTree(int[] inorder, int[] postorder) {
            if (inorder.length == 1) return new TreeNode(inorder[0]);
            // 下标映射
            for (int i = 0; i < inorder.length; i++) 
                inorder_map[inorder[i] + 3000] = i; // 映射防越界
            return help(inorder, 0, inorder.length - 1, 
                          postorder, 0, postorder.length - 1);
        }
    
        // inorder [s1, e1] postorder [s2, e2]
        TreeNode help(int[] inorder, int s1, int e1, int[] postorder, int s2, int e2) {
            if (s1 > e1 || s2 > e2) return null;
            int rootVal = postorder[e2]; // 根节点的值
            TreeNode root = new TreeNode(rootVal); // 初始化根节点
            int rootValIdx = inorder_map[rootVal + 3000]; // 中序遍历中根节点值的下标
            // 左子树长度 = rootValIx - s1 -1
            // s2 + rootValIdx - s1 - 1 表示计算 后序数组 的结束位置,
            root.left = help(inorder, s1, rootValIdx - 1,
                             postorder, s2, s2 + rootValIdx - s1 - 1);
            root.right = help(inorder, rootValIdx + 1, e1,
                              postorder, s2 + rootValIdx - s1, e2 - 1);
            return root;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

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

    题目:105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

    给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

    public TreeNode buildTree(int[] preorder, int[] inorder) {
    	// preorder = [3,9,20,15,7] -> [3] [9] [20, 15, 7] 根左右
    	// inorder = [9,3,15,20,7] -> [9] [3] [15, 20, 7] 左根右
    	if (preorder.length == 0) return null;
    	TreeNode root = new TreeNode(preorder[0]);
    	for (int i = 0; i < inorder.length; i++) {
    		if (inorder[i] == root.val) {
    			root.left = buildTree(
    				Arrays.copyOfRange(preorder, 1, i + 1),
    				Arrays.copyOfRange(inorder, 0, i)
    			);
    			root.right = buildTree(
    				Arrays.copyOfRange(preorder, i + 1, preorder.length),
    				Arrays.copyOfRange(inorder, i + 1, inorder.length)
    			);
    			break;
    		}
    	}
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    最大二叉树

    题目:654. 最大二叉树 - 力扣(LeetCode)

    给定一个不重复的整数数组 nums最大二叉树 可以用下面的算法从 nums 递归地构建:

    1. 创建一个根节点,其值为 nums 中的最大值。
    2. 递归地在最大值 左边子数组前缀上 构建左子树。
    3. 递归地在最大值 右边子数组后缀上 构建右子树。

    返回 nums 构建的 最大二叉树

    递归:

    public TreeNode constructMaximumBinaryTree(int[] nums) {
    	if (nums.length == 0) return null;
    	int maxIdx = 0;
    	for (int i = 0; i < nums.length; i++) 
    		if (nums[i] > nums[maxIdx]) maxIdx = i;
    	TreeNode root = new TreeNode(nums[maxIdx]);
    	root.left = constructMaximumBinaryTree(Arrays.copyOfRange(nums, 0, maxIdx));
    	root.right = constructMaximumBinaryTree(Arrays.copyOfRange(nums, maxIdx + 1, nums.length));
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    利用索引优化数组切片操作:

    class Solution {
        public TreeNode constructMaximumBinaryTree(int[] nums) {
            return help(nums, 0, nums.length - 1);
        }
        
        TreeNode help(int[] nums, int l, int r) {
            if (l > r) return null;
            int maxIdx = l;
            for (int i = l; i <= r; i++)
                if (nums[i] > nums[maxIdx]) maxIdx = i;
            TreeNode root = new TreeNode(nums[maxIdx]);
            root.left = help(nums, l, maxIdx - 1);
            root.right = help(nums, maxIdx + 1, r);
            return root;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    合并二叉树

    题目:617. 合并二叉树 - 力扣(LeetCode)

    给你两棵二叉树: root1root2
    想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
    注意:合并过程必须从两个树的根节点开始。

    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
    	if (root1 == null) return root2;
    	if (root2 == null) return root1;
    	TreeNode root = new TreeNode(root1.val + root2.val);
    	root.left = mergeTrees(root1.left, root2.left);
    	root.right = mergeTrees(root1.right, root2.right);
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二叉搜索树题目

    验证二叉搜索树

    题目:98. 验证二叉搜索树 - 力扣(LeetCode)

    给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
    有效 二叉搜索树定义如下:

    • 节点的左子树只包含 小于 当前节点的数。
    • 节点的右子树只包含 大于 当前节点的数。
    • 所有左子树和右子树自身必须也是二叉搜索树。

    中序 DFS:

    class Solution {
        long preVal = Long.MIN_VALUE;
    
        public boolean isValidBST(TreeNode root) {
            if (root == null) return true;
            // 左
            if (!isValidBST(root.left)) return false;
            // 中
            if (root.val <= preVal) return false; 
            preVal = root.val; // 保存上一次遍历时的值
            // 右
            return isValidBST(root.right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    二叉搜索树的最小绝对差

    题目:530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)

    给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值
    差值是一个正数,其数值等于两值之差的绝对值。

    中序 DFS:

    class Solution {
        int res = Integer.MAX_VALUE;
        int prev = -100010; // 记录上一个节点的值
        
        public int getMinimumDifference(TreeNode root) {
            dfs(root);
            return res;
        }
    
        void dfs(TreeNode node) {
            if (node == null) return;
    		// 左
            dfs(node.left);
    		// 中
            res = Math.min(res, node.val - prev);
            prev = node.val; // 记录上一个节点的值
    		// 右
            dfs(node.right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    二叉搜索树中的众数

    题目:501. 二叉搜索树中的众数 - 力扣(LeetCode)

    给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
    如果树中有不止一个众数,可以按 任意顺序 返回。
    假定 BST 满足如下定义:

    • 结点左子树中所含节点的值 小于等于 当前节点的值
    • 结点右子树中所含节点的值 大于等于 当前节点的值
    • 左子树和右子树都是二叉搜索树

    对通用的二叉树的处理:

    class Solution {
        final int N = 100010;
        int[] map = new int[N + N]; // 某个数字的出现次数
        int maxCount = 0; // 某个数字出现最多的次数
        public int[] findMode(TreeNode root) {
            dfs(root);
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < map.length; i++)
                if (map[i] == maxCount)  list.add(i - N);
            int[] res = new int[list.size()];
            int k = 0;
            for (Integer i : list) res[k++] = i;
            return res;
        }
        void dfs(TreeNode root) {
            if (root == null) return;
            dfs(root.left);
            map[root.val + N]++; // 映射为正值
            maxCount = Math.max(maxCount, map[root.val + N]);
            dfs(root.right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    针对二叉搜索树进行优化:

    class Solution {
        List<Integer> list = new ArrayList<>();
        int preVal = 0; // 上一个值
        int curTimes = 0; // 当前值的出现次数
        int  maxTimes = 0; // 最大的出现次数
    
        public int[] findMode(TreeNode root) {
            dfs(root);
            int[] res = new int[list.size()];
            int k = 0;
            for (Integer i : list) res[k++] = i;
            return res;
        }
    
        void dfs(TreeNode node) {
            if (node == null) return;
            dfs(node.left);
            // 判断 当前值 与 上一个值 的关系
            if (preVal == node.val) curTimes++;
            else {
                preVal = node.val;
                curTimes = 1;
            }
            // 判断 当前数量 和 最大数量 的关系
            if (curTimes == maxTimes) list.add(node.val);
            else if (curTimes > maxTimes) {
                list.clear();
                list.add(node.val);
                maxTimes = curTimes;
            }
            dfs(node.right);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    二叉树的最近公共祖先**

    题目:236. 二叉树的最近公共祖先 - 力扣(LeetCode)

    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    	if (root == null || root == p || root == q) return root;
    	TreeNode left = lowestCommonAncestor(root.left, p, q);
    	TreeNode right = lowestCommonAncestor(root.right, p, q);
    	// root 左右两边都能找到,分别为 p, q,则 root 是最近公共祖先
    	if (left != null && right != null) return root;
    	// 只有 root 左边 能找到
    	if (left != null) return left;
    	// 只有 root 右边 能找到
    	if (right != null) return right;
    	// 左右都找不到
    	return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    题目:235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)

    给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    	// 如果 p, q 值都小于 root, 说明 p, q 在 root 的左子树中
    	if (root.val > p.val && root.val > q.val)
    		return lowestCommonAncestor(root.left, p, q);
    	// 如果 p, q 值都大于 root, 说明 p, q 在 root 的右子树中
    	if (root.val < p.val && root.val < q.val)
    		return lowestCommonAncestor(root.right, p, q);
    	// p, q 分布在 root 左右,则 root 为最近公共祖先
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    二叉搜索树中的插入操作

    题目:701. 二叉搜索树中的插入操作 - 力扣(LeetCode)

    给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

    递归:

    public TreeNode insertIntoBST(TreeNode root, int val) {
    	if (root == null) return new TreeNode(val);
    	if (root.val > val) root.left = insertIntoBST(root.left, val);
    	else root.right = insertIntoBST(root.right, val);
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    迭代:

    public TreeNode insertIntoBST(TreeNode root, int val) {
    	if (root == null) return new TreeNode(val);
    	TreeNode node = root, parent = root; // 记录上一个节点
    	while (node != null) {
    		parent = node;
    		if (val > node.val) node = node.right;
    		else node = node.left;
    	}
    	if (val > parent.val) parent.right = new TreeNode(val); 
    	else parent.left = new TreeNode(val);
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    删除二叉搜索树中的节点**

    题目:450. 删除二叉搜索树中的节点 - 力扣(LeetCode)

    给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

    删除的三种情况:

    1. 度为 0 的节点(叶子节点),直接删除
    2. 度为 1 的节点:用子节点替代该节点
    3. 度为 2 的节点:用 前驱 / 后继 覆盖它,再删除 前驱 / 后继
    public TreeNode deleteNode(TreeNode root, int key) {
    	if (root == null) return null;
    	// 目标值 < 当前节点值, 往左找
    	if (root.val > key) root.left = deleteNode(root.left, key);
    	// 目标值 > 当前节点值, 往右找
    	else if (root.val < key) root.right = deleteNode(root.right, key);
    	// 目标值 == 当前节点值, 进行删除操作
    	else {
    		// 情况 1: 度为 0 的节点 (叶子节点): 直接删除
    		if (root.left == null && root.right == null) return null;
    		// 情况 2: 度为 1 的节点: 用子节点替代该节点
    		else if (root.right == null) return root.left;
    		else if (root.left == null) return root.right;
    		// 情况 3: 度为 2 的节点: 用 前驱/后继 覆盖它, 再删除 前驱/后继
    		else {
    			// 前驱: 左节点的最右节点
    			TreeNode pre = root.left;
    			while (pre.right != null) pre = pre.right; // 寻找前驱
    			root.val = pre.val; // 用前驱覆盖当前节点
    			root.left = deleteNode(root.left, pre.val); // 删除前驱
    			
    			// 后继: 右节点的最左节点
    			// TreeNode suc = root.right;
    			// while (suc.left != null) suc = suc.left; // 寻找后继
    			// root.val = suc.val; // 用后继覆盖当前节点
    			// root.right = deleteNode(root.right, suc.val); // 删除后继
    		}
    	}
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    修剪二叉搜索树

    题目:669. 修剪二叉搜索树 - 力扣(LeetCode)

    给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案

    public TreeNode trimBST(TreeNode root, int low, int high) {
    	if (root == null) return null;
    	// root.val < low 则 root.left 下面的节点值都 < low
    	if (root.val < low) return trimBST(root.right, low, high);
    	// root.val > high 则 root.right 下面的值都 > high
    	if (root.val > high) return trimBST(root.left, low, high);
    	// 递归调用左右子树
    	root.left = trimBST(root.left, low, high);
    	root.right = trimBST(root.right, low, high);
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    题目:108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)

    给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

    高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

    好理解的递归:

    public TreeNode sortedArrayToBST(int[] nums) {
    	if (nums.length == 0) return null;
    	int mid = nums.length >> 1;
    	TreeNode root = new TreeNode(nums[mid]);
    	root.left = sortedArrayToBST(Arrays.copyOfRange(nums, 0, mid));
    	root.right = sortedArrayToBST(Arrays.copyOfRange(nums, mid + 1, nums.length));
    	return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    利用索引优化上面递归中的数组拷贝:

    class Solution {
        public TreeNode sortedArrayToBST(int[] nums) {
            return helper(nums, 0, nums.length - 1);
        }
    
        TreeNode helper(int[] nums, int l, int r) {
            if (l > r ) return null;
            int mid = (l + r) >> 1;
            TreeNode root = new TreeNode(nums[mid]);
            root.left = helper(nums, l, mid - 1);
            root.right = helper(nums, mid + 1, r);
            return root;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Go 语言记录

    Go 的先序递归实现:

    func preorderTraversal(root *TreeNode) []int {
    	res := make([]int, 0)
    	if root == nil {
    		return res
    	}
    
    	st := list.New()
    	st.PushBack(root)
    
    	for st.Len() > 0 {
    		node := st.Remove(st.Back()).(*TreeNode)
    
    		res = append(res, node.Val)
    		if node.Right != nil {
    			st.PushBack(node.Right)
    		}
    		if node.Left != nil {
    			st.PushBack(node.Left)
    		}
    	}
    	return res
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    Go 的中序递归实现:

    // go - 全局变量
    var arr []int
    
    func inorderTraversal(root *TreeNode) []int {
        arr = make([]int, 0)
        dfs(root)
        return arr
    }
    
    func dfs(root *TreeNode) {
        if root != nil {
            dfs(root.Left)
            arr = append(arr, root.Val)
            dfs(root.Right)
        }
    }
    
    // go - 闭包
    func inorderTraversal(root *TreeNode) []int {
        arr := make([]int, 0)
    	
        var dfs func(*TreeNode)
        dfs = func(root *TreeNode) {
            if root == nil {
                return
            }
            dfs(root.Left)
            arr = append(arr, root.Val)
            dfs(root.Right)
        }
        
        dfs(root)
        return arr
    }
    
    // go - 切片当参数传递
    func inorderTraversal(root *TreeNode) []int {
    	arr := make([]int, 0)
    	dfs(root, &arr)
    	return arr
    }
    
    func dfs(root *TreeNode, arr *[]int) {
    	if root == nil {
            return
    	}
    	dfs(root.Left, arr)
    	*arr = append(*arr, root.Val)
    	dfs(root.Right, arr)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  • 相关阅读:
    car audio service详解
    Yarn 配置管理
    基于Web的在线学习平台设计与实现(源码+lw+部署文档+讲解等)
    react 或者 vue,如何做 SEO 优化?
    [洛谷] P1097 [NOIP2007 提高组] 统计数字
    设计模式——19. 访问者模式
    什么是浏览器指纹识别
    2023服务端测试开发必备技能:Mock测试
    若依前后端分离如何解决匿名注解启动报错?
    LeetCode 53.最大子数组和17.电话号码的字母组合
  • 原文地址:https://blog.csdn.net/weixin_43734095/article/details/126697337