首页 > 其他分享 >每日一刷——二叉树的构建——12.12

每日一刷——二叉树的构建——12.12

时间:2024-12-12 21:29:42浏览次数:5  
标签:index right TreeNode val int 一刷 left 12.12 二叉树

第一题:最大二叉树

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

我的想法:

我感觉这个题目最开始大家都能想到的暴力做法就是遍历找到数组中的最大值,然后再遍历一遍,把在它左边的依次找到最大值,但是emmm,感觉效率很低,时间上肯定过不了

然后其实我会觉得这个题目跟大根堆和小根堆有一点像,,,就是先建立一个树来,然后如果大就上去,如果小就下来,但是有一点,怎么保证顺序??因为这样先建立一棵树来,再上移,好像不能保证??我浅浅画个图

那这样,是让6之后的全部向上移动吗,移动到6的右边? 

题目分析:

嗯,看了一圈,好像没有人用小根堆,大根堆写哈,有用单调栈写的,有用DFS写,然后其实我那个初始想法版本也行,因为我的题解给我的就是这样写的,但是有一点不一样的是:它分割了数组+递归,我就是憨憨的非要把每一步写出来哈,,其实每一个节点做的事情一样,就一定一定抽象出来然后递归

就是相当于先找到数组中的最大值,然后左右两边分别交给左右儿子再去做分割

其实这样我们可以总结一个思想就是:

其实每一个节点,都是首先把自己构造出来,然后想办法构造自己的左右子树,就相当于每一个人都有当孩子和当父亲的一天,你要在孩子时期规定此时要做的事,要在父亲时期规定要做的事,然后每一个人的框架都是如此,只不过最后做出来的成果可能不一样

我的错误题解:

/**
 * 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);
    }

    TreeNode build(int[] nums ,int lo,int hi){
        if(lo>hi)
            return null;
        
        int max=nums[lo];
        int index=lo;
        //找到数组中地最大值,然后构建这个节点,再分配左右儿子的数组
        for(int i=lo+1;i<=hi;i++){
            if(nums[i]>max){
                max=nums[i];
                index=i;
            }
        }

        //一般,数组,====>传递下标,而不是非得要找到这个数是多少

        //创建这个节点
        TreeNode root =new TreeNode(max);
        root.left=build(nums,lo,index-1);
        root.left=build(nums,index+1,hi);

        return root;
    }
}

其实这个错误原因很好分析,一看,我去,右边的好像都没进去,所以直接检查代码,发现root.right写错了,写成了root.left

题解:

/**
 * 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);
    }

    TreeNode build(int[] nums ,int lo,int hi){
        if(lo>hi)
            return null;
        
        int max=nums[lo];
        int index=lo;
        //找到数组中地最大值,然后构建这个节点,再分配左右儿子的数组
        for(int i=lo+1;i<=hi;i++){
            if(nums[i]>max){
                max=nums[i];
                index=i;
            }
        }

        //一般,数组,====>传递下标,而不是非得要找到这个数是多少

        //创建这个节点
        TreeNode root =new TreeNode(max);
        root.left=build(nums,lo,index-1);
        root.right=build(nums,index+1,hi);

        return root;
    }
}

这个题目想清楚了之后,代码写起来和思维都不是很难的

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

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

我的想法:

soga,这个!用前序和中序构建树,这个题目老师原来讲过,挺经典的题的

而且无论是前序还是后序,都必须要知道中序序列,才有唯一的二叉树确定

应该思路是这样的:

先从preorder里边开始,最开始是3  然后在inorder里边找3,3就把inorder分成了两半,左边是左子树里边的,右边就是右子树里边的,然后一直一直这样下去,最后这棵树就建好了(这里的思路有点小问题,看到后面就知道了)

我的题解:

/**
 * 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) {
        
        //1.建立头节点
        //拿到头节点在inorder中的位置,然后分割左右
        //左边就是新的inorder 右边也是新的inorder
        //2.建立左右子节点

        //3.什么时候结束?
        int index=0;
        return build(index,preorder,inorder,0,inorder.length-1);
        
    }

    public static TreeNode build(int index,int[] preorder, int[] inorder,int lf,int lr){
        if(lf<lr)
            return null;

        int findplace=0; 
        TreeNode root =new TreeNode(preorder[index-1]);
        index++;
        for(int i=0;i<inorder.length;i++){
            if(inorder[i]==root.val){
                findplace=i;
                break;
            }
        }
        //突然感觉index传的不对啊???  这个index是互通的吗??
        root.left=build(index,preorder,inorder,lf,findplace-1);
        root.right=build(index,preorder,inorder,findplace+1,lr);
        return root;
    }
}

为啥越界了???

题目分析:

for循环遍历确定index效率不高,可以考虑进一步优化:

因为题目说二叉树结点的值不重复,所以我们可以使用一个hashmap存储元素到索引的映射,这样就可以直接通过hashMap查到rootVal对应的index!!

我天!!!我知道了我的有一点的错误了!!

就是在preorder里边遍历,其实是不需要一个一个遍历,怎么说呢,因为你一直把index传给左右两颗子树,但是其实左右两颗子树里边需要的index并不相同,因为它们的元素就不相同,所以这样干,没有考虑周到,笑死我了,根据下面这张图应该能很明显的看到:preorder其实把它分成了这样两个部分

 也就是说现在想指向某个值在数组里边对应的下标的时候,可以不需要遍历,直接用hashmap映射就可以了,它的值就是它的下标

题解:

/**
 * 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 static HashMap<Integer,Integer> valToIndex =new HashMap<>();

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        
        //1.建立头节点
        //拿到头节点在inorder中的位置,然后分割左右
        //左边就是新的inorder 右边也是新的inorder
        //2.建立左右子节点

        //3.什么时候结束?

        //HashMap优化
        
        for(int i=0;i<preorder.length;i++){
            valToIndex.put(inorder[i],i);
        }
        
        return build(0,preorder.length-1,preorder,inorder,0,inorder.length-1);
        
    }

    public static TreeNode build(int preFirst,int preLast,int[] preorder, int[] inorder,int lf,int lr){
        if(preFirst>preLast)
            return null;

        //分步完善  rootVal   index   leftSize
        int rootVal =preorder[preFirst];

        int index=valToIndex.get(rootVal);

        int leftSize=index-lf;

        //构造父结点
        TreeNode root =new TreeNode(rootVal);

        /*有了hashmap这一步可以简化了
        for(int i=0;i<inorder.length;i++){
            if(inorder[i]==root.val){
                findplace=i;
                break;
            }
        } 
        */
        root.left=build(preFirst+1 , preFirst+leftSize , preorder , inorder , lf , index-1);
        root.right=build(preFirst+leftSize+1 , preLast , preorder , inorder , index+1 , lr);
        return root;
    }
}

第三题:根据前序和后序遍历构造二叉树

题目描述:889. 根据前序和后序遍历构造二叉树 - 力扣(LeetCode)

我的想法:

我没有想法,惹惹惹,我在想这两个题有啥区别

我的题解:

题目分析:

前两道题:可以通过前序或者后序遍历找到根节点,然后根据中序遍历结果确定左右子树

但这一道题,可以确定根节点,但是无法确切的知道左右子树有哪些节点

比如:

但是解法还是和前两道题差别不大,通过控制左右子树的索引来构建

1.首先把前序遍历结果的第一个元素或者后序遍历结果的最后一个元素作为根节点的值

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 {
    //依然是下标到数的hash映射
    HashMap<Integer,Integer> valToIndex =new HashMap<>();

    public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
        for(int i=0;i<postorder.length;i++){
            valToIndex.put(postorder[i],i);
        }

        return build(preorder,0,preorder.length-1,
                     postorder,0,postorder.length-1);

    }


    TreeNode build(int[] preorder,int preStart ,int preEnd,
                   int[] postorder,int postStart,int postEnd){

             if(preStart > preEnd)
                return null;

             if(preStart==preEnd)
                return new TreeNode(preorder[preStart]);

            //root节点对应的值就是前序遍历数组的第一个元素
            int rootVal =preorder[preStart];

            //root.left 的值是前序遍历元素第二个元素
            //通过前序和后序遍历构造二叉树的关键在于通过左子树的根节点
            //确定preorder  和postorder 中左右子树的元素区间
            int leftRootVal =preorder[preStart+1];  

            //leftRootVal 在后序遍历数组中的索引
            int index =valToIndex.get(leftRootVal);

            //左子树的元素个数
            int leftSize =index-postStart +1;

            //先构造出当前根节点
            TreeNode root =new TreeNode(rootVal);
            //递归构造左右子树
            //根据左子树的根节点索引和元素个数推导左右子树的索引边界
            root.left =build(preorder,preStart+1,preStart+leftSize,postorder,postStart,index);
            root.right =build(preorder,preStart+leftSize+1,preEnd,postorder,index+1,preEnd-1); //是因为左闭右开吗??         
            //好像因为要用到两个,preorder里边,一次性用两个,第一个为头节点,第二个为左孩子节点
    
            return root;
    }
}

我要背题目!!阿巴

标签:index,right,TreeNode,val,int,一刷,left,12.12,二叉树
From: https://blog.csdn.net/2301_80073593/article/details/144434128

相关文章

  • 12.12 数据结构,创建顺序表
    1.思维导图2.创建顺序表程序代码:1>头文件seqList.h:#ifndef__SEQLIST_H__#define__SEQLIST_H__#include<stdio.h>#include<stdlib.h>#include<string.h>//数据类型重命名typedefintDataType;//宏定义线性表的最大容量#defineMAX30//定义顺序表的结构体......
  • 12.12 CW 模拟赛 T3. 消除贫困
    思路朴素容易发现一个人资金变化是这样的:对于\(op=1\)的情况,会将其直接变成\(x\)对于\(op=2\)的情况,将其变成\(\max(x,当前值)\)直接用线段树暴力的维护即可巧妙容易发现\(op=2\)相当于一个大保底,我们先倒着处理出每个人到\(i\)位置至少有多少......
  • 12.12 CW 模拟赛 T1. 理想路径
    前言作为一个别的不行抗伤无敌的\(\rm{man}\),区区反向\(\rm{rk\1}\)不足为惧\(\rm{HD0X}\)巨佬场切\(2700\),\(\%\%\%\)思路朴素先把考场上一些基础的想法搬过来考虑一个环什么时候会导致产生字典序负环,这个好像还比较显然,就是如果出去的那个点的字典序小......
  • 数字组合转字母&删除二叉树节点&字符串相乘&打家劫舍ii&无序数组第k大 &无序数组前k大
    一、数字串转换为字符串1-26个数字分别代表26个字符(A-z)输入"12326〞就可以拆分为【1,2,3,2,6】、(12,3,2,6].[1,23,2,6]【1,23,26】、【12,3,26】等,将每种组合转成成对应字母输出,输出所有可能的结果返回所有可能的转换结果//将数字串转换成字母串//将数字串转换成字母......
  • 每日一刷——二叉树——12.09
    第一题:二叉树的层序遍历题目描述:102.二叉树的层序遍历-力扣(LeetCode)我的思路:拿到这个题目,我首先想到的是利用队列来模拟,给我二叉树的根节点,然后我来返回每一层的各个节点,但是为啥我甚至觉得这个题目给的输入就是按照层序遍历来给的呢??然后感觉还有一个点就是咋返回成几......
  • 线索二叉树——c语言详细注释版
        线索二叉树是一种特殊的二叉树,主要用于高效地实现树的遍历。与普通的二叉树相比,线索二叉树通过在节点中增加“线索”指针来简化遍历过程。值得注意的是,线索化二叉树的过程仍然需要使用递归,而后续遍历效率才会提高,适合一次构造,多次调用的场景。前言一般的二叉树在......
  • 二叉树篇
    classSolution{publicTreeNodesortedArrayToBST(int[]nums){returnsortedArrayToBST(nums,0,nums.length);}publicTreeNodesortedArrayToBST(int[]nums,intleft,intright){if(left>=right){retur......
  • 代码随想录day14 | leetcode 226.翻转二叉树 101. 对称二叉树 104.二叉树的最大深度 1
    226.翻转二叉树前序和后序写法都可以我用的是前序错误写法classSolution{publicTreeNodeinvertTree(TreeNoderoot){if(root==null)returnnull;swap(root.left,root.right);invertTree(root.left);invertTree(root.r......
  • 124. 二叉树中的最大路径和
    问题描述二叉树中的路径被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中至多出现一次。该路径至少包含一个节点,且不一定经过根节点。路径和是路径中各节点值的总和。给你一个二叉树的根节点root,返回其最大路径和。分析树形D......
  • 236. 二叉树的最近公共祖先
    问题描述给定一个二叉树,找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树T的两个节点p、q,最近公共祖先表示为一个节点x,满足x是p、q的祖先且x的深度尽可能大(一个节点也可以是它自己的祖先)。”分析使用递归解决比较简单,但是不太......