算法——二叉树和二叉搜索树

!!!学习自labuladong

二叉树框架

void traverse(TreeNode root){
    //前序遍历
    traverse(root.left);
    //中序遍历
    traverse(root.right);
    //后序遍历
}

二叉树总结

  • 将题目细化为每个节点(root,left,right)需要做的事
  • 前序遍历还是后序遍历
    • 前序遍历:从上到下,以最下的分量思考
    • 后序遍历:从下到上,以最上的分量思考
  • 确定basecase

经典例题

226. 翻转二叉树

翻转一棵二叉树。

示例:

输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9

输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1
    public TreeNode invertTree(TreeNode root) {
        if(root==null) return null;
        TreeNode left = root.left;
        root.left=invertTree(root.right);
        root.right=invertTree(left);
        return root;
    }
116. 填充每个节点的下一个右侧节点指针

给定一个完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

    public Node connect(Node root) {
        //base case
        if(root==null) return null;
        //连接两个相邻节点
        connectTwoNode(root.left,root.right);
        return root;
    }
    void connectTwoNode(Node node1,Node node2){
        //base case
        if(node1==null||node2==null){
            return;
        }
        //连接两个节点
        node1.next=node2;
        //递归节点内和节点外的节点
        connectTwoNode(node1.left,node1.right);
        connectTwoNode(node2.left,node2.right);
        connectTwoNode(node1.right,node2.left);
    }
114. 二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
    public void flatten(TreeNode root) {
        //base case 
        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;
    }
654. 最大二叉树

给定一个不含重复元素的整数数组 nums 。一个以此数组直接递归构建的 最大二叉树 定义如下:

二叉树的根是数组 nums 中的最大元素。
左子树是通过数组中 最大值左边部分 递归构造出的最大二叉树。
右子树是通过数组中 最大值右边部分 递归构造出的最大二叉树。
返回有给定数组 nums 构建的 最大二叉树 。

    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums,0,nums.length-1);
    }
    TreeNode build(int[]nums,int low,int high){
        //base case
        if(low>high) return null;
        //遍历找到最大值
        int max_value=nums[low],max_index=low;
        for(int i=low+1;i<=high;i++){
            if(nums[i]>max_value){
                max_value = nums[i];
                max_index = i;
            }
        }
        TreeNode root = new TreeNode(max_value);
        root.left = build(nums,low,max_index-1);
        root.right = build(nums,max_index+1,high);
        return root;
    }
105. 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return build(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
    }
    TreeNode build(int[]preorder,int prelow,int prehigh,int[]inorder,int inlow,int inhigh){
        //base case
        if(prelow>prehigh) return null;
        //找到root在中序的坐标
        int preroot = prelow,inroot=0;
        int rootVal = preorder[preroot];
        for(int i=inlow;i<=inhigh;i++){
            if(inorder[i]==rootVal){
                inroot = i;
                break;
            }
        }
        //建立根节点
        TreeNode root = new TreeNode(rootVal);
        //找到左子树大小以确定pre坐标
        int leftsize = inroot-inlow;
        root.left = build(preorder,prelow+1,prelow+leftsize,inorder,inlow,inroot-1);
        root.right = build(preorder,prelow+leftsize+1,prehigh,inorder,inroot+1,inhigh);
        return root;
    }
652. 寻找重复的子树

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。

两棵树重复是指它们具有相同的结构以及相同的结点值。

示例 1:

    1
   / \
  2   3
 /   / \
4   2   4
   /
  4

下面是两个重复的子树:

  2
 /
4

4

因此,你需要以列表的形式返回上述重复子树的根结点。

    //记录结果
    List<TreeNode> results = new LinkedList<>();
    //记录子树结构出现次数
    HashMap<String,Integer> record = new HashMap<>();
    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        traverse(root);
        return results;
    }
    String traverse(TreeNode root){
        //base case
        if(root==null) return "#";
        String left = traverse(root.left);
        String right = traverse(root.right);
        //序列化表示树的结构
        String subtree = left+","+right+","+root.val;
        //判断是否出现过一次
        int cnt = record.getOrDefault(subtree,0);
        if(cnt==1){
            results.add(root);
        }
        record.put(subtree,cnt+1);
        return subtree;
    }
124. 二叉树中的最大路径和

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

img

输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
    int max = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        build(root);
        return max;
    }
    int build(TreeNode root){
        if(root==null) return 0;
        int leftMax = Math.max(0,build(root.left));
        int rightMax = Math.max(0,build(root.right));
        max = Math.max(max,root.val+leftMax+rightMax);
        return root.val+Math.max(leftMax,rightMax);
    }

二叉搜索树

  • 如果当前节点会对下面的子节点有整体影响,可以通过辅助函数增长参数列表,借助参数传递信息。

  • -特点:中序遍历是升序

//遍历框架
void BST(TreeNode root, int target) {
    if (root.val == target)
        // 找到目标,做点什么
    if (root.val < target) 
        BST(root.right, target);
    if (root.val > target)
        BST(root.left, target);
}

经典例题
98. 验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

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

//我的方法
    boolean flag = true;
    int pre =-1;
    public boolean isValidBST(TreeNode root) {
        if(root==null) return true;
        boolean left = isValidBST(root.left);
        if(flag){
            pre=root.val;
            flag = false;
        }else {
            if(root.val<=pre) {
                return false;
            }
        }
        pre = root.val;
        boolean right = isValidBST(root.right);
        return left&&right; 
    }
//另一种方法
    public boolean isValidBST(TreeNode root) {
        return find(root,null,null);
    }
    boolean find(TreeNode root,TreeNode min,TreeNode max){
        if(root==null) return true;
        if(min!=null&&root.val<=min.val) return false;
        if(max!=null&&root.val>=max.val) return false;
        return find(root.left,min,root)&&find(root.right,root,max);
    }
701. 二叉搜索树中的插入操作

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

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

    public TreeNode insertIntoBST(TreeNode root, int val) {
        if(root==null) return new TreeNode(val);
        if(root.val<val) root.right=insertIntoBST(root.right,val);
        if(root.val>val) root.left=insertIntoBST(root.left,val);
        return root;
    }
450. 删除二叉搜索树中的节点

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

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。

TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return null;
    if (root.val == key) {
        // 这两个 if 把情况 1 和 2 都正确处理了
        if (root.left == null) return root.right;
        if (root.right == null) return root.left;
        // 处理情况 3
        TreeNode minNode = getMin(root.right);
        root.val = minNode.val;
        root.right = deleteNode(root.right, minNode.val);
    } else if (root.val > key) {
        root.left = deleteNode(root.left, key);
    } else if (root.val < key) {
        root.right = deleteNode(root.right, key);
    }
    return root;
}

TreeNode getMin(TreeNode node) {
    // BST 最左边的就是最小的
    while (node.left != null) node = node.left;
    return node;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值