leetcode树相关题目

树的基本操作包括前序、中序及后序的递归及迭代遍历(dfs)、树的层次遍历(bfs)一定要非常非常的熟练!因为树的题目基本都是在建立在这些操作之上的

树的相关题目会频繁地出现递归,递归的一个非常重要的点就是:不去管函数的内部细节是如何处理的,只看其函数作用以及输入与输出

假设树的节点结构为

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

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

树的前序、中序及后序遍历的递归版本比较简单

private List<Integer> res = new ArrayList<>();

public List<Integer> preOrder(TreeNode root) {
    if (root != null) {
        res.add(root.val);
        preOrder(root.left);
        preOrder(root.right);
    }
    
    return res;
}

1. 树的前序遍历(迭代)

leetcode144
前序遍历就是按照中左右的顺序遍历树

public List<Integer> preOrder(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if (root == null) return res;

    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);

    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        res.add(cur.val);

        if (cur.right != null) {
            stack.push(cur.right);
        }
        if (cur.left != null) {
            stack.push(cur.left);
        }
    }
    return res;
}

2. 树的中序遍历

2.1 树的迭代中序遍历

leetcode94
中序遍历就是按照左中右的顺序遍历树

public List<Integer> inOrder(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if (root == null) return res;
    Stack<TreeNode> stack = new Stack<>();

    while (!stack.isEmpty() || root != null) {
        while (root != null) {
            stack.push(root);
            root = root.left;
        }

        root = stack.pop();
        res.add(root.val);
        root = root.right;
    }

    return res;
}

2.2 变形题:验证二叉搜索树 TODO

leetcode98

private long pre = Long.MIN_VALUE;

public boolean isValidBST(TreeNode root) {
    if (root == null) return true;

    // 访问左子树
    if (!isValidBST(root.left)) return false;

    // 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回false
    if (root.val <= pre) return false;

    pre = root.val;
    // 访问右子树
    return isValidBST(root.right);
}

2.3 变形题:二叉搜索树中第K小的元素

leetcode230

public int kthSmallest(TreeNode root, int k) {
    Stack<TreeNode> stack = new Stack<>();

    while (!stack.isEmpty() || root != null) {
        while (root != null) {
            stack.push(root);
            root = root.left;
        }

        root = stack.pop();
        if (--k == 0) return root.val;
        root = root.right;
    }

    return -1;
}

2.4 变形题:二叉搜索树迭代器

leetcode173

private Queue<Integer> queue = new LinkedList<>();

public BSTIterator(TreeNode root) {
    inOrder(root);
}

private void inOrder(TreeNode root) {
    if (root != null) {
        inOrder(root.left);
        queue.offer(root.val);
        inOrder(root.right);
    }
}

public int next() {
    return queue.poll();
}

public boolean hasNext() {
    return !queue.isEmpty();
}

3. 树的后序遍历(迭代)

leetcode145
后序遍历就是按照左右中的顺序遍历树

public List<Integer> postOrder(TreeNode root) {
    // 注意要用LinkedList才行,List接口没有addFirst方法
    LinkedList<Integer> res = new LinkedList<>();
    if (root == null) return res;
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);

    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        res.addFirst(cur.val);

        if (cur.left != null) {
            stack.push(cur.left);
        }
        if (cur.right != null) {
            stack.push(cur.right);
        }
    }

    return res;
}

4. 树的层次遍历

leetcode102

4.1 迭代版本

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if (root == null) return res;

    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);

    int cur = 1;
    int next = 0;
    List<Integer> in = new ArrayList<>();

    while (!queue.isEmpty()) {
        TreeNode t = queue.poll();
        cur--;
        in.add(t.val);

        if (t.left != null) {
            queue.offer(t.left);
            next++;
        }
        if (t.right != null) {
            queue.offer(t.right);
            next++;
        }

        if (cur == 0) {
            res.add(new ArrayList<>(in));
            in.clear();
            cur = next;
            next = 0;
        }
    }

    return res;
}

4.2 递归版本

private List<List<Integer>> res = new ArrayList<>();

public List<List<Integer>> levelOrder(TreeNode root) {
    dfs(root, 0, res);
    return res;
}

private void dfs(TreeNode root, int level, List<List<Integer>> res) {
    if (root == null) return;
    if (level >= res.size()) res.add(new ArrayList<>());

    res.get(level).add(root.val);
    dfs(root.left, level + 1, res);
    dfs(root.right, level + 1, res);
}

4.3 变形题:树的锯齿形层次遍历

leetcode103

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if (root == null) return res;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);

    int cur = 1;
    int next = 0;
    boolean flag = false;
    List<Integer> in = new ArrayList<>();

    while (!queue.isEmpty()) {
        TreeNode t = queue.poll();
        in.add(t.val);
        cur--;

        if (t.left != null) {
            queue.offer(t.left);
            next++;
        }
        if (t.right != null) {
            queue.offer(t.right);
            next++;
        }

        if (cur == 0) {
            if (flag) Collections.reverse(in);
            res.add(in);
            flag = !flag;
            in = new ArrayList<>();
            cur = next;
            next = 0;
        }
    }

    return res;
}

4.4 变形题:树的右视图

leetcode199

public List<Integer> rightSideView(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if (root == null) return res;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);

    int cur = 1;
    int next = 0;

    while (!queue.isEmpty()) {
        TreeNode t = queue.poll();
        if (--cur == 0) {
            res.add(t.val);
        }

        if (t.left != null) {
            queue.offer(t.left);
            next++;
        }
        if (t.right != null) {
            queue.offer(t.right);
            next++;
        }

        if (cur == 0) {
            cur = next;
            next = 0;
        }
    }
    return res;
}

5. 重建二叉树

5.1 前序中序遍历构造二叉树

leetcode105

public TreeNode buildTree(int[] preorder, int[] inorder) {
    return buildTree(preorder, 0, inorder, inorder.length - 1, 0);
}

private TreeNode buildTree(int[] preorder, int idx, int[] inorder, int end, int start) {
    if (idx >= preorder.length || start > end) return null;
    int i = 0;
    for (i = end; i >= start; i--) {
        if (preorder[idx] == inorder[i]) break;
    }
    TreeNode root = new TreeNode(preorder[idx]);
    root.left = buildTree(preorder, idx + 1, inorder, i - 1, start);
    root.right = buildTree(preorder, idx + i - start + 1, inorder, end, i + 1);
    return root;
}

5.2 后序中序遍历构造二叉树

leetcode106

public TreeNode buildTree(int[] inorder, int[] postorder) {
    return buildTree(postorder, postorder.length - 1, inorder, inorder.length - 1, 0);
}

private TreeNode buildTree(int[] postorder, int idx, int[] inorder, int end, int start) {
    if (idx < 0 || start > end) return null;
    int i;
    for (i = end; i >= start; i--) {
        if (inorder[i] == postorder[idx]) break;
    }
    TreeNode root = new TreeNode(postorder[idx]);
    root.left = buildTree(postorder, idx - end + i - 1, inorder, i - 1, start);
    root.right = buildTree(postorder, idx - 1, inorder, end, i + 1);
    return root;
}

6. 树的公共祖先

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

leetcode235

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

6.2 二叉树的最近公共祖先

leetcode236

6.2.1 递归版

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);
    return left == null ? right : right == null ? left : root;
}

6.2.2 迭代版

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    Map<TreeNode, TreeNode> parent = new HashMap<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    parent.put(root, null);
    stack.push(root);

    while (!parent.containsKey(p) || !parent.containsKey(q)) {
        TreeNode node = stack.pop();
        if (node.left != null) {
            parent.put(node.left, node);
            stack.push(node.left);
        }
        if (node.right != null) {
            parent.put(node.right, node);
            stack.push(node.right);
        }
    }
    Set<TreeNode> ancestors = new HashSet<>();
    while (p != null) {
        ancestors.add(p);
        p = parent.get(p);
    }
    while (!ancestors.contains(q)) q = parent.get(q);
    return q;
}

7. 树的深度

7.1 树的最大深度

leetcode104

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

7.2 树的最小深度 TODO

leetcode111

public int minDepth(TreeNode root) {
    if (root == null) return 0;
    int m1 = minDepth(root.left);
    int m2 = minDepth(root.right);
    return root.left == null || root.right == null ? m1 + m2 + 1 : Math.min(m1, m2) + 1;
}

7.3 变形题:二叉树的直径

leetcode543

private int res = 0;

public int diameterOfBinaryTree(TreeNode root) {
    if (root == null) return 0;
    depth(root);
    return res;
}

private int depth(TreeNode root) {
    if (root == null) return 0;
    int left = depth(root.left);
    int right = depth(root.right);
    res = Math.max(res, left + right);
    return Math.max(left, right) + 1;
}

7.4 变形题:二叉树中的最大路径和 TODO

leetcode124

private int res = Integer.MIN_VALUE;

public int maxPathSum(TreeNode root) {
    findMax(root);
    return res;
}

private int findMax(TreeNode root) {
    if (root == null) return 0;
    int left = Math.max(0, findMax(root.left));
    int right = Math.max(0, findMax(root.right));
    res = Math.max(res, left + right + root.val);
    return Math.max(left, right) + root.val;
}

8. 树的对称、翻转、平衡、合并

8.1 对称二叉树

leetcode101

public boolean isSymmetric(TreeNode root) {
    return isMirror(root, root);
}

private boolean isMirror(TreeNode t1, TreeNode t2) {
    if (t1 == null && t2 == null) return true;
    if (t1 == null || t2 == null) return false;
    return t1.val == t2.val && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);
}

8.2 翻转二叉树

leetcode226

8.2.1 递归版

public TreeNode invertTree(TreeNode root) {
    if (root == null) return root;
    TreeNode left = invertTree(root.left);
    TreeNode right = invertTree(root.right);
    root.left = right;
    root.right = left;
    return root;
}

8.2.2 迭代版

public TreeNode invertTree(TreeNode root) {
    if (root == null) return null;

    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);

    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        TreeNode tmp = cur.right;
        cur.right = cur.left;
        cur.left = tmp;

        if (cur.left != null) stack.push(cur.left);
        if (cur.right != null) stack.push(cur.right);
    }

    return root;
}

8.2.3 递归版(不破坏原来的树)

public TreeNode invertTree(TreeNode root) {
    if (root == null) return null;

    TreeNode newNode = new TreeNode(root.val);
    newNode.left = invertTree(root.right);
    newNode.right = invertTree(root.left);
    return newNode;
}

8.2.4 迭代版(不破坏原来的树)

public TreeNode invertTree(TreeNode root) {
    if (root == null) return null;
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);

    Stack<TreeNode> newStack = new Stack<>();
    TreeNode newRoot = new TreeNode(root.val);
    newStack.push(newRoot);

    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        TreeNode newCur = newStack.pop();

        if (cur.right != null) {
            stack.push(cur.right);
            newCur.left = new TreeNode(cur.right.val);
            newStack.push(newCur.left);
        }
        if (cur.left != null) {
            stack.push(cur.left);
            newCur.right = new TreeNode(cur.left.val);
            newStack.push(newCur.right);
        }
    }
    return newRoot;
}

8.3 平衡二叉树

leetcode110

public boolean isBalanced(TreeNode root) {
    if (root == null) return true;
    int left = depth(root.left);
    int right = depth(root.right);
    return Math.abs(left - right) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}

private int depth(TreeNode root) {
    if (root == null) return 0;
    return Math.max(depth(root.left), depth(root.right)) + 1;
}

8.4 合并二叉树

leetcode617

public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
    if (t1 == null) return t2;
    if (t2 == null) return t1;

    TreeNode root = new TreeNode(t1.val + t2.val);
    root.left = mergeTrees(t1.left, t2.left);
    root.right = mergeTrees(t1.right, t2.right);
    return root;
}

9. 二叉树转链表

9.1 二叉树展开为链表

leetcode114

public void flatten(TreeNode root) {
    if (root == null) return;

    flatten(root.left);
    flatten(root.right);

    TreeNode tmp = root.right;
    root.right = root.left;
    root.left = null;
    while (root.right != null) root = root.right;
    root.right = tmp;
}

9.2 二叉搜索树展开为双向链表 TODO

牛客网

9.2.1 迭代版

public TreeNode Convert(TreeNode root) {
    if (root == null) return null;
    TreeNode head = null, cur = root, pre = null;
    Stack<TreeNode> stack = new Stack<>();

    while (!stack.isEmpty() || cur != null) {
        while (cur != null) {
            stack.push(cur);
            cur = cur.left;
        }
        if (!stack.isEmpty()) {
            cur = stack.pop();
            if (head == null) head = cur;
            if (pre != null) {
                pre.right = cur;
                cur.left = pre;
            }
            pre = cur;
            cur = cur.right;
        }
    }
    return head;
}

9.2.2 递归版

public TreeNode Convert(TreeNode root) {
    root = convertBST2DLL(root);
    while (root.left != null) root = root.left;
    return root;
}

private TreeNode convertBST2DLL(TreeNode root) {
    if (root == null || (root.left == null && root.right == null)) return root;
    TreeNode cur;
    if (root.left != null) {
        cur = convertBST2DLL(root.left);
        while (cur.right != null) cur = cur.right;
        cur.right = root;
        root.left = cur;
    }
    if (root.right != null) {
        cur = convertBST2DLL(root.right);
        while (cur.left != null) cur = cur.left;
        cur.left = root;
        root.right = cur;
    }
    return root;
}

10. 二叉树的序列化与反序列化

leetcode297

public String serialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    if (root == null) {
        sb.append("#,");
        return sb.toString();
    }

    sb.append(root.val + ",");
    sb.append(serialize(root.left));
    sb.append(serialize(root.right));

    return sb.toString();
}

public TreeNode deserialize(String data) {
    String[] arr = data.split(",");
    Queue<String> queue = new LinkedList<>();
    for (String str : arr) queue.offer(str);

    return deserialize(queue);
}

private TreeNode deserialize(Queue<String> queue) {
    String str = queue.poll();
    if (str.equals("#")) return null;

    TreeNode root = new TreeNode(Integer.parseInt(str));
    root.left = deserialize(queue);
    root.right = deserialize(queue);
    return root;
}

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

leetcode108

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

    return buildTree(nums, 0, nums.length - 1);
}

private TreeNode buildTree(int[] nums, int lo, int hi) {
    if (lo > hi) return null;
    int mid = (lo + hi) / 2;
    TreeNode root = new TreeNode(nums[mid]);
    root.left = buildTree(nums, lo, mid - 1);
    root.right = buildTree(nums, mid + 1, hi);
    return root;
}

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

leetcode450

public TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return null;

    if (key < root.val) {
        root.left = deleteNode(root.left, key);
    } else if (key > root.val) {
        root.right = deleteNode(root.right, key);
    } else {
        if (root.left == null) {
            return root.right;
        } else if (root.right == null) {
            return root.left;
        } else {
            TreeNode node = root.right;
            while (node.left != null) node = node.left;

            node.left = root.left;
            return root.right;
        }
    }

    return root;
}

13. 树的第K层节点个数(递归)

主要是理解递归的思想

public static int getNodeNumKthLevel(TreeNode root, int k) {
    if (root == null || k < 1) return 0;
    if (k == 1) return 1;
    return getNodeNumKthLevel(root.left, k - 1) + getNodeNumKthLevel(root.right, k - 1);
}

14. 树的叶子节点个数(递归)

public static int getNodeNumLeaf(TreeNode root) {
    if (root == null) return 0;
    if (root.left == null && root.right == null) return 1;
    return getNodeNumLeaf(root.left) + getNodeNumLeaf(root.right);
}

15. 判断树是否为完全二叉树(迭代)

public static boolean isCompleteBinaryTree(TreeNode root) {
    if (root == null) return false;

    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    boolean mustHaveNoChild = false;
    boolean result = true;

    while (!queue.isEmpty()) {
        TreeNode cur = queue.remove();
        if (mustHaveNoChild) {
            if (cur.left != null || cur.right != null) {
                result = false;
                break;
            }
        } else {
            if (cur.left != null && cur.right != null) {
                queue.add(cur.left);
                queue.add(cur.right);
            } else if (cur.left != null) {
                mustHaveNoChild = true;
                queue.add(cur.left);
            } else if (cur.right != null) {
                result = false;
                break;
            } else {
                mustHaveNoChild = true;
            }
        }
    }
    return result;
}
posted @ 2020-06-13 13:09  sakura1027  阅读(140)  评论(0编辑  收藏  举报