二叉树的套路

1、二叉树基础

  • 二叉树的遍历模板
void traverse(TreeNode root) {
    // 前序遍历
    traverse(root.left)
    // 中序遍历
    traverse(root.right)
    // 后序遍历
}

快速排序相当于二叉树的前序遍历
归并排序相当于二叉树的后续遍历

  • 快速排序的框架
void sort(int[] nums, int lo, int hi) {
    /****** 前序遍历位置 ******/
    // 通过交换元素构建分界点 p
    int p = partition(nums, lo, hi);
    /************************/

    sort(nums, lo, p - 1);
    sort(nums, p + 1, hi);
}
  • 归并排序的框架
void sort(int[] nums, int lo, int hi) {
    int mid = (lo + hi) / 2;
    sort(nums, lo, mid);
    sort(nums, mid + 1, hi);

    /****** 后序遍历位置 ******/
    // 合并两个排好序的子数组
    merge(nums, lo, mid, hi);
    /************************/
}

1.1 递归的算法的诀窍

  • 写递归算法的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果,绝不要试图跳入递归
  • 写树相关的算法,先搞清楚当前root节点该做什么,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情

2、二叉树例题

2.1 翻转二叉树

二叉树的镜像

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        //跳出条件
        if(root==null)
            return null;
        /**前序遍历位置 */
        //交换的是节点的左右孩子树,不是具体的某一个节点
        TreeNode node = root.left;
        root.left = root.right;
        root.right = node;
        //让左右孩子继续做同样的事情
        mirrorTree(root.left);
        mirrorTree(root.right);
        return root;
    }
}

2.2 填充二叉树节点的右侧指针

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

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/
class Solution {
    public Node connect(Node root) {
        //一个节点无法解决非同父问题,转接为两个节点
        if(root==null)
            return null;
        root.next=null;
        fun(root.left,root.right);
        return root;
    }
    public static void fun(Node node1, Node node2){
        if(node1==null || node2==null)
            return;

        //两个节点先连接
        node1.next = node2;
        //两个节点的,左右孩子节点连接
        fun(node1.left,node1.right);
        fun(node2.left,node2.right);
        //两个节点的,孩子跨父连接
        fun(node1.right,node2.left);
    }
}

2.3 将二叉树展开为链表

二叉树展开为链表

/**
 * Definition for a binary tree node.
 * 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 Solution {
    public void flatten(TreeNode root) {
        if(root==null)
            return;
        flatten(root.left);
        flatten(root.right);

        /**   后续遍历   */
        //先标记节点的左右孩子
        TreeNode left = root.left;
        TreeNode right = root.right;
        //左孩子变为右孩子
        root.left=null;//一定要先断左孩子,否则会出现左右指针都指向左孩子
        root.right=left;//此时右孩子断掉
        
        //把原来的右孩子连接到最新的右孩子上
        TreeNode p = root;
        while(p.right!=null){
            p = p.right;
        }
        p.right = right;
    }
}

2.4 构造最大二叉树

构造最大二叉树

  • 对于每个根节点,只需要找到当前nums中的最大值和对应的索引,然后递归调用左右数组构造左右子树即可
/**
 * Definition for a binary tree node.
 * 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 Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums,0,nums.length-1);
    }
    
    public static TreeNode build(int[] nums,int i,int j){
        if(i>j)
            return null;

        //找到数组最大值和其索引
        int m = max(nums,i,j);
        TreeNode root = new TreeNode(nums[m]);
        
        //递归调用左右子树
        root.left = build(nums,i,m-1);
        root.right = build(nums,m+1,j);
        return root;
    }
    //求出给定数组的最大值,并返回下标
    public static int max(int[] nums,int front,int rear){
        int max =Integer.MIN_VALUE;
        int k = -1;
        for(int i=front; i<rear+1;i++){
            if(max < nums[i]){
                max = nums[i];
                k=i;
            }
        }
        return k;
    }
}

2.5 通过前序和中序遍历结果构造二叉树

通过前序和中序遍历结果构造二叉树

/**
 * Definition for a binary tree node.
 * 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 Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {

        //以中序为主构建二叉树,通过前序依次找到根节点
        return build(preorder,0,preorder.length-1,
            inorder,0,inorder.length-1);
    }
    public static TreeNode build(int[] preorder, int i,int j,int[] inorder,int m,int n){
        if(i>j)
            return null;
        //从前序中找到根
        int val = preorder[i];
        //根据根求出中序根的下标
        int q = index(inorder,m,n,val);
        int len = q-m;//左子树的长度

        TreeNode node = new TreeNode(val);
        node.left = build(preorder,i+1,i+len,inorder,m,q-1);
        node.right = build(preorder,i+len+1,j,inorder,q+1,n);
        return node;
    }
    //求出中序里根的下标
    public static int index(int[] inorder,int m,int n, int val){
        for(int i=m;i<=n;i++){
            if(inorder[i]==val)
                return i;
        }
        return -1;
    }
}

2.6 通过后序和中序遍历结果构造二叉树

通过后序和中序遍历结果构造二叉树

/**
 * Definition for a binary tree node.
 * 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 Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return build(inorder,0,inorder.length-1,postorder,0,postorder.length-1);
    }
    public static TreeNode build(int[] inorder,int i,int j, int[] postorder,int m,int n){
        if(m>n)
            return null;

        //找到第一个节点,即后续最后一个
        int val = postorder[n];
        //然后根据后续得到的值将中序分割,即返回该值在中序的下标
        int k = index(inorder,i,j,val);
        int len_left =  k-i;
        
        //创建节点并加入,再遍历左右孩子
        TreeNode node = new TreeNode(val);

        node.left = build(inorder,i,k-1,postorder,m,m+len_left-1);
        node.right = build(inorder,k+1,j,postorder,m+len_left,n-1);
        return node;
    }
    public static int index(int[] inorder,int i,int j,int val){
        for(int p=i;p<=j;p++){
            if(inorder[p]==val)
                return p;
        }
        return -1;
    }
}

2.7 寻找重复的子树

寻找重复的子树

以我为根的这棵二叉树(子树)长啥样?

  • 利用序列化成字符串来判断

以其他节点为根的子树都长啥样?

  • 保存在map中的key中
/**
 * Definition for a binary tree node.
 * 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 Solution {
    //记录所有子树以及出现的次数
    HashMap<String,Integer> map = new HashMap<>();
    //记录重复的子树根节点
    LinkedList<TreeNode> res = new LinkedList<>();
    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        traverse(root);
        return res;
    }
    public String traverse(TreeNode root){
        if(root==null)
            return "#";
        //后序遍历逐步得到节点,组成子树
        //将左右子树序列化成字符串
        String left = traverse(root.left);
        String right = traverse(root.right);

        //后序遍历的子树表示
        String subTree = left+","+right+","+root.val;

        //先获得子树出现的次数,第一次出现则加入最终结果,再次数加一
        int freq = map.getOrDefault(subTree,0);
        if(freq==1)
            res.add(root);
        map.put(subTree,freq+1);
        return subTree;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值