构造二叉树

思路:
  • 根据中序遍历找到根节点,在前序遍历或者后序遍历中计算左右子树的范围;
  • 之后递归实现。
例题:
  • 105. 从前序与中序遍历序列构造二叉树
  • 106. 从中序与后序遍历序列构造二叉树
  • 889. 根据前序和后序遍历构造二叉树(根据前序遍历找到左子树的根节点,在后序遍历中找到对应下标并计算左子树的范围

完全二叉树

958. 二叉树的完全性检验

两个条件:
  • 左孩子为空,右孩子不为空不是完全二叉树;
  • 遇到左孩子或者右孩子为空的节点,下一次遍历的时候以后的所有节点都应该没有左孩子和右孩子。
核心代码:
左孩子为空、右孩子不为空一定不是完全二叉树
if(node.left == null && node.right != null){
    return false;
}

如果叶子节点左孩子、右孩子不为空一定不是完全二叉树
if(flag && (node.left != null || node.right != null)){
     return false;
}

临界点、从该节点之后的所有节点都是叶子节点
if(node.left == null || node.right == null){
    flag = true;
}

剑指 Offer II 043. 往完全二叉树添加节点  /  919. 完全二叉树插入器

分解为:
  • 层序遍历二叉树,用list存储;
  • 根据二叉树的性质寻找当前节点的父节点的下标(i - 1)/ 2。
根据二叉树的性质寻找父节点的下标(i - 1)/ 2
TreeNode parent = list.get((list.size() - 2) / 2);

if(parent.left == null){
   parent.left = node;
}else if(parent.right == null){
   parent.right = node;
}

满二叉树

分解为:
  • 求二叉树的深度 h  + 求节点的个数 n;
  • 满足 n == 1 << h - 1;
二叉树的深度
public int height(TreeNode root){
    if(root == null){
        return 0;
    }
    
    int left = height(root.left);
    int right = height(root.right);
    return Math.max(left, right) + 1;
}

平衡二叉树

面试题 04.04. 检查平衡性 / 110. 平衡二叉树 / 剑指 Offer 55 - II. 平衡二叉树

分解为:
  • 求二叉树的左右子树的深度;
  • 递归到二叉树的左右子树。
求二叉树的深度同上;

二叉搜索树

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

思路:根据二叉搜索树的性质找到对应的叶子节点,比较此叶子节点与val进行左、右插入
核心代码:
class Solution {
    // 思路:根据二叉树的性质找到对应的叶子节点进行插入
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root == null){
            return new TreeNode(val);
        }

        TreeNode cur = root, parent = null;
        while(cur != null){
            parent = cur;
            if(cur.val < val){
                cur = cur.right;
            }else{
                cur = cur.left;
            }
        }

        TreeNode node = new TreeNode(val);
        if(parent.val < val){
            parent.right = node;
        }else{
            parent.left = node;
        }

        return root;
    }
}

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

思路:
  • 如果当前节点有右子树,找到右子树的最小值赋值给根节点,之后删除右子树的最小值;
  • 如果当前节点有左子树,找到左子树的最大值赋值给根节点,之后删除左子树的最大值。
核心代码:
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null){
            return root;
        }else if(root.val < key){
            root.right = deleteNode(root.right, key);
        }else if(root.val > key){
            root.left = deleteNode(root.left, key);
        }else{
            if(root.left == null && root.right == null){
                return null;
            }else if(root.left != null){
                root.val = leftMax(root);
                root.left = deleteNode(root.left, root.val);
            }else{
                root.val = rightMin(root);
                root.right = deleteNode(root.right, root.val);
            }
        }

        return root;
    }

    public int leftMax(TreeNode root){
        root = root.left;
        while(root.right != null){
            root = root.right;
        }
        return root.val;
    }

    public int rightMin(TreeNode root){
        root = root.right;
        while(root.left != null){
            root = root.left;
        }
        return root.val;
    }
}

510. 二叉搜索树中的中序后继 II

思路:
  • 如果当前节点有右子树,则寻找右子树中最左的节点;
  • 如果没有,说明当前节点为最右侧节点,则一直寻找祖先节点。
核心代码:
class Solution {
    public Node inorderSuccessor(Node node) {
        // 如果有右子树,但是是最左边的节点
        // 如果没有,需要一直向上找祖先节点
        if(node.right != null){
            node = node.right;
            while(node.left != null){
                node = node.left;
            }

            return node;
        }else{
            while(node.parent != null && node.parent.right == node){
                node = node.parent;
            }
            return node.parent;
        }
    }
}

预存上一个节点与当前节点比较

分解为:

  • 中序遍历;
  • 预存储前一个节点与当前节点比较,比较之后节点再次更新为当前节点。
98. 验证二叉搜索树的核心代码:
if(pre != null && pre.val >= root.val){
   flag = false;
}
pre = root;

99. 恢复二叉搜索树的核心代码:
inorder(root.left);
if(pre != null && pre.val > root.val){
  // 定位两个错误的节点
  if(a == null){
     a = pre;
  }
  b = root;
}
pre = root;
inorder(root.right);
  • 剑指 Offer II 053. 二叉搜索树中的中序后继
  • 285. 二叉搜索树中的中序后继
  • 面试题 04.06. 后继者
核心代码:
public void dfs(TreeNode root, TreeNode p) {
        if(root == null){
            return;
        }

        dfs(root.left, p);

        if(pre != null && pre.val == p.val){
            cur = root;
        }
        pre = root;

        dfs(root.right, p);
    }

剑指 Offer II 052. 展平二叉搜索树

思路:在中序遍历的时候构造一直向右的二叉树
核心代码:
public void dfs(TreeNode root){
   if(root == null){
      return;
   }
   dfs(root.left);
   // 添加节点
   node.right = new TreeNode(root.val);
   // 继续向右节点添加
   node = node.right;
   dfs(root.right);
}

剑指 Offer II 055. 二叉搜索树迭代器 / 173. 二叉搜索树迭代器

中序遍历:
public void serialize(List<Integer> deque, TreeNode root){
        if(root == null){
            return;
        }
        serialize(deque, root.left);
        deque.add(root.val);
        serialize(deque, root.right);
    }

剑指 Offer 36. 二叉搜索树与双向链表

中序遍历的时候构造前驱和后继
核心代码:
if(pre == null){
   当前cur没有左节点,也就是cur为头节点
   cur = root;
}else{
   正常情况
   pre.right = root;
}
root.left = pre;
pre = root;


构造搜索二叉树

数组篇:

  • 面试题 04.02. 最小高度树(转义为:构造二叉搜索树)
  • 108. 将有序数组转换为二叉搜索树
  • 1382. 将二叉搜索树变平衡(转义为:中序遍历 + 构造二叉搜索树)
核心代码:
private TreeNode buildTree(int[] nums, int left, int right) {
        if (left <= right) {
            int mid = left + (right - left) / 2;
            TreeNode root = new TreeNode(nums[mid]);
            root.left = buildTree(nums, left, mid - 1);
            root.right = buildTree(nums, mid + 1, right);
            return root;
        }
        
        return null;
    }
链表篇:
  • 109. 有序链表转换二叉搜索树
核心代码:快慢指针找到中间位置,之后再进行构造二叉树
public TreeNode sortedListToBST(ListNode left, ListNode right) {
        if(left == right){
            return null;
        }
        // 快慢指针
        ListNode fast = left, slow = left;
        while(fast.next != right && fast.next.next != right){
            fast = fast.next.next;
            slow = slow.next;
        }
    
        //构造二叉树
        TreeNode root = new TreeNode(slow.val);
        root.left = sortedListToBST(left, slow);
        root.right = sortedListToBST(slow.next, right);
        return root;
    }

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

分解为:二叉树的序列化 + 构造二叉树
题目:
通解:前序遍历)
  • 剑指 Offer 37. 序列化二叉树
  • 剑指 Offer II 048. 序列化与反序列化二叉树
  • 297. 二叉树的序列化与反序列化
  • 449. 序列化和反序列化二叉搜索树
核心代码:
public void serialize(TreeNode root, StringBuffer buffer) {
        if(root == null){
            buffer.append("null").append(",");
        }else{
            buffer.append(root.val).append(",");
            serialize(root.left, buffer);
            serialize(root.right, buffer);
        }
        
    }


public TreeNode deserialize(Deque<String> deque) {
        String temp = deque.poll();
        if(temp.equals("null")){
            return null;
        }
        TreeNode root = new TreeNode(Integer.valueOf(temp));
        root.left = deserialize(deque);
        root.right = deserialize(deque);
        return root;
    }
(层序遍历)在for循环里面递归
  • 428. 序列化和反序列化 N 叉树
核心代码:
public void serialize(StringBuffer buffer, Node root) {
        if(root == null){
            buffer.append("null").append(",");
        }else{
            // 层序遍历
            buffer.append(root.val).append(":").append(root.children.size()).append(",");
            for(Node node : root.children){
                serialize(buffer, node);
            }
        }
    }



public Node deserialize(Deque<String> deque) {
        if(deque.peek().equals("null")){
            return null;
        }

        String[] temp = deque.poll().split(":");
        int val = Integer.valueOf(temp[0]);
        int nums = Integer.valueOf(temp[1]);

        Node root = new Node(val);
        root.children = new ArrayList<>();
        for(int i = 0; i < nums; i++){
            root.children.add(deserialize(deque));
        }
        
        return root;
    }

最近公共祖先节点

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

  • 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
  • 235. 二叉搜索树的最近公共祖先
核心代码:
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || p == root || q == root){
            return root;
        }

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

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

        return root;
    }
}

二叉树的最近公共祖先节点

  • 236. 二叉树的最近公共祖先
  • 剑指 Offer 68 - II. 二叉树的最近公共祖先
核心代码:
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || p == root || q == root){
            return root;
        }

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if(left != null && right != null){
            return root;
        }else if(left != null){
            return left;
        }else if(right != null){
            return right;
        }

        return null;
    }
}


改进求最近公共祖先

  • 1644. 二叉树的最近公共祖先 II
区别:增加判断节点是否在二叉树中
public boolean isExits(TreeNode root, TreeNode temp) {
        if(root == null){
            return false;
        }

        return root.val == temp.val || isExits(root.left, temp) || isExits(root.right, temp);
    }

  • 1650. 二叉树的最近公共祖先 III
区别:增加parent指针相当于链表
class Solution {
    public Node lowestCommonAncestor(Node p, Node q) {
        // 有parent节点也就相当于链表
        Node a = p, b = q;
        while(a != b){
            a = a.parent == null ? q : a.parent;
            b = b.parent == null ? p : b.parent;
        }

        return a;
    }
}

  • 1676. 二叉树的最近公共祖先 IV
区别:多个节点的公共祖先,判断条件更改为是否包含
public TreeNode lowestCommonAncestor(TreeNode root, Set<Integer> set) {
        if(root == null){
            return root;
        }
        if(set.contains(root.val)){
            return root;
        }
        
        TreeNode left = lowestCommonAncestor(root.left, set);
        TreeNode right = lowestCommonAncestor(root.right, set);
        if(left != null && right != null){
            return root;
        }else if(left != null){
            return left;
        }else if(right != null){
            return right;
        }
        return null;
    }


根据深度进行判断返回节点,感觉有些类似于搜索二叉树寻找最近公共祖先了

  • 865. 具有所有最深节点的最小子树
  • 1123. 最深叶节点的最近公共祖先
核心代码:
class Solution {
    public TreeNode subtreeWithAllDeepest(TreeNode root) {
        if(root == null){
            return root;
        }

        int left = deepth(root.left);
        int right = deepth(root.right);

        if(left == right){
            return root;
        }else if(left > right){
            return subtreeWithAllDeepest(root.left);
        }else{
            return subtreeWithAllDeepest(root.right);
        }
    }


    public int deepth(TreeNode root) {
        if(root == null){
            return 0;
        }

        int left = deepth(root.left);
        int right = deepth(root.right);

        return Math.max(left, right) + 1;
    }
}

折纸问题

分解为:
  • 中序遍历;
  • 左孩子:down,右孩子:up。
核心代码:
public void dfs(List<String> list, int i, int n, boolean flag) {
        // write code here
        if(i > n){
            return;
        }
        dfs(list, i + 1, n, true);
        list.add(flag ? "down" : "up");
        dfs(list, i + 1, n, false);
    }

层序遍历(广度优先遍历)

  • 515. 在每个树行中找最大值
  • 429. N 叉树的层序遍历
  • 637. 二叉树的层平均值
  • 199. 二叉树的右视图
  • 102. 二叉树的层序遍历
  • 107. 二叉树的层序遍历 II
核心代码:
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        queue.offer(root);
        
        while(!queue.isEmpty()){
            List<Integer> temp = new ArrayList<>();
            for(int i = queue.size(); i > 0; i--){
                TreeNode node = queue.poll();
                temp.add(node.val);
                if(node.left != null){
                    queue.offer(node.left);
                }
                if(node.right != null){
                    queue.offer(node.right);
                }
            }
            list.add(temp);
        }
        return list;
    }
}

  • 117. 填充每个节点的下一个右侧节点指针 II
  • 116. 填充每个节点的下一个右侧节点指针
核心代码:添加next指针
Node node = deque.poll();
if(i > 0){
    node.next = deque.peek();
}

深度优先遍历

  • 104. 二叉树的最大深度
  • 111. 二叉树的最小深度(多加了左、右子树是否为空的判断条件
核心代码:
public int minDepth(TreeNode root) {
        if(root == null){
            return 0;
        }

        if(root.left == null && root.right != null){
            return minDepth(root.right) + 1;
        }

        if(root.left != null && root.right == null){
            return minDepth(root.left) + 1;
        }

        int left = minDepth(root.left);
        int right = minDepth(root.right);
        return Math.min(left, right) + 1;
    }