算法学习day06(二叉树)

一、路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

整体思路:到了哪一个结点 先更新一下TargetSum(TargetSum-=node.val),然后对结点进行分情况判断,叶子节点/非叶子节点

递归法:

1.返回值boolean 参数:TreeNode node  int TargetSum

2.终止条件:遇到叶子节点进行判断,如果targetSum==0 返回true 相反 返回false

3.递归逻辑:

叶子节点if(node.left==null&&node.right==null)return targetSum==0;

非叶子节点:向左递归 if(node.left!=null)xxx();向右递归:if(node.right!=null)

代码:

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null)
            return false;
        targetSum -= root.val;
        if (root.left == null && root.right == null)
            return targetSum == 0;
        if (root.left != null) {
            boolean left = hasPathSum(root.left,targetSum);
            if (left) { // 已经找到
                return true;
            }
        }
        if (root.right != null) {
            boolean right = hasPathSum(root.right,targetSum);
            if (right) { // 已经找到
                return true;
            }
        }
        return false;
    }

注意:在递归左右子节点(非叶子节点)的时候,为什么不能直接return hasPathSum(xxx);

如果返回的是一个false,你return的话,后面的代码就不会继续执行。如果返回的是true,那就找到了直接结束。

二、路径之和II(返回满足targetSum的路径)

仍然使用递归法,和第一题类似。

声明全局变量:List<List<Integer>> lists; List<Integer> list;

当遇到叶子节点的时候,要判断targetSum是否等于0,如果相等的话,那么就把list添加到lists中。然后左右子节点都要进行回溯。

代码:

class Solution {
    private List<List<Integer>> lists;
    private List<Integer> list;
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        lists=new ArrayList<>();
        list=new ArrayList<>();
        traversal(root,targetSum);
        return lists;
    }
    public void traversal(TreeNode node ,int targetSum){
        if(node==null)return;
        targetSum-=node.val;
        list.add(node.val);//先把节点的值放进去
        if(node.left==null&&node.right==null&&targetSum==0){
            lists.add(new ArrayList<>(list));
        }
        if(node.left!=null)traversal(node.left,targetSum);
        if(node.right!=null)traversal(node.right,targetSum);
        list.remove(list.size()-1);
    }
}

三、最大二叉树

题目描述:

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 

递归函数的返回值为TreeNode node int left  int right

递归函数的终止条件为(left==right)return null;

递归逻辑:首先要找到数组中的最大值,然后左边部分作为根节点的左子树右边部分作为根节点的右子树,然会返回根节点。但是left和right的规则一定要选好。我这里选的是左闭右开

代码:

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return tarversal(nums,0,nums.length);
    }
    public TreeNode tarversal(int[] nums,int start,int end){
        if(start==end)return null;//终止条件
        //找到最大值的下标
        int maxIndex=start;
        int max=nums[start];
        for(int i=start;i<end;i++){
            if(nums[i]>max){
                max=nums[i];
                maxIndex=i;
            }
        }
        TreeNode root=new TreeNode(nums[maxIndex]);
        //递归调用左子节点 比根节点的val值小的都在左边 从start->maxIndex 右开 
        root.left=tarversal(nums,start,maxIndex);
        //递归调用右子节点 比根节点的val值大的都在右边,从maxIndex+1->end 
        root.right=tarversal(nums,maxIndex+1,end);
        return root;
    }
}

四、根据中序和后序遍历构建二叉树

思路:

1.根据后序的最后一个节点->得知根节点

2.然后从中序数组中找到根节点->左边就是左子树,右边就是右子树

3.左子树递归该函数

  3.1左子树的后序数组的最后一个节点中->得知根节点

  3.2 然后从中序数组中找到根节点->左边就是左子树,右边就是右子树

4 右子树递归该函数

   4.1 右子树的后序的最后一个节点中->得知根节点

    4.2 然后从中序数组中找到根节点->左边就是左子树,右边就是右子树

有一些细节需要注意:当left==right时,证明没有节点了

代码:

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder==null||postorder==null)return null;
        return buildHelper(inorder, 0, inorder.length, postorder, 0, postorder.length);
    }

    private TreeNode buildHelper(int[] inorder, int inorderStart, int inorderEnd, int[] postorder, int postorderStart, int postorderEnd){
        //判断是否为空 随便一个就行
        if(inorderStart==inorderEnd)return null;
        //创建根节点
        TreeNode root=new TreeNode(postorder[postorderEnd-1]);

        //在中序数组中找到根节点的下标
        int rootIndex=-1;
        for(int i=0;i<inorderEnd;i++){
            if(inorder[i]==root.val){
                rootIndex=i;
                break;
            }
        }
        //左子树 中序遍历和后序遍历的起点和终点
        int leftInorderStart=inorderStart;//左子树中序遍历的起点
        int leftInorderEnd=rootIndex;//左子树中序遍历的终点
        int leftPostorderStart=postorderStart;//左子树后序遍历的起点
        int leftPostorderEnd=postorderStart+(rootIndex-inorderStart);左子树后序遍历的终点
        root.left=buildHelper(inorder,leftInorderStart,leftInorderEnd,postorder,leftPostorderStart,leftPostorderEnd);
        //右子树 中序遍历和后序遍历的起点和终点
        int rightInorderStart=rootIndex+1;//右子树中序遍历的起点
        int rightInorderEnd=inorderEnd;//右子树中序遍历的终点
        int rightPostorderStart=leftPostorderEnd;//左子树后序遍历的起点
        int rightPostorderEnd=postorderEnd-1;//左子树后序遍历的终点
        root.right=buildHelper(inorder,rightInorderStart,rightInorderEnd,postorder,rightPostorderStart,rightPostorderEnd);
        return root;
    }
}

注意:所说的后序遍历的起点和终点指的是在数组中的位置,终点不是,左闭右开。

后序遍历的起点是根据中序遍历左子树节点的size来确定的。eg:

int leftPostorderEnd=postorderStart+(rootIndex-inorderStart);(后序遍历的起点)+size

五、合并二叉树

1.确定遍历方式 前序是最适合的 中左右

2.递归函数返回值:TreeNode 参数 TreeNode t1,TreeNode t2;

3.递归函数终止条件:

4.递归逻辑:

三种情况:左有右没,左没右有,左右都有。上面的return xx是下一层的left 或者 right

代码:

public TreeNode traversal(TreeNode node1,TreeNode node2){
    if(node1==null)return node2;
    if(node2==null)return node1;
    if(node1!=null&&node2!=null)node1.val+=node2.val;
    node1.left=traversal(node1.left,node2.left);
    node2.right=traversal(node1.right,node2.right);
    return node1
}

六、二叉搜索树中的搜索(递归法/迭代法)

二叉搜索树的性质:

  1. 非空左子树的所有键值小于其根结点的键值。
  2. 非空右子树的所有键值大于其根结点的键值。
  3. 左、右子树都是二叉搜索树。
递归法:

确定递归参数的返回值和参数:TreeNode node 参数:TreeNode node,int target

确定终止条件:node==null node.val==val

递归逻辑:node.val<val 向右边搜索 node.val>val 去左边搜索

代码:

    public TreeNode searchBST(TreeNode root, int val) {
        if(root==null)return null;
        //节点相等
        if(root.val==val)return root;
        //节点小
        if(root.val<val)return searchBST(root.right,val);
        //节点大
        if(root.val>val)return searchBST(root.left,val);
        return null;
    }
迭代法:
while(root!=null){
    if(root.val>val)root=root.left;
    if(root.val<val)root=root.right;
    if(root.val==val)return root;
}

七、验证二叉搜索树(中序遍历!!!)

粗糙的解法:先通过中序遍历将val值都添加到一个集合中,然后判断集合是否的单调递增的。

代码:

class Solution {
    List<Integer> list=new ArrayList<>();
    public boolean isValidBST(TreeNode root) {
        //终止条件
        traversal(root);
        for(int i=1;i<list.size();i++){
            if(list.get(i)<=list.get(i-1))return false;
        }
        return true;
    }
    public void traversal(TreeNode root){
        if(root==null)return ;
        if(root.left!=null)traversal(root.left);
        list.add(root.val);
        if(root.right!=null)traversal(root.right);
    }
}

灵活的解法:顶一个TreeNode pre 指向前一次遍历的节点,要求前一次遍历的节点要比现在的根节点的值要小 private TreeNode pre=null

Boolean leftFlag=traversal(node.left);

if(pre!=null&&pre.val>=node.val)return false;

pre=node;

Boolean rightFlag=traversal(node.right);

八、二叉搜索树的最小绝对差

最小绝对差一定出现在前后两个节点中。因为二叉搜索树在中序遍历之后,值是从小到大的。

解题思路跟上一个题一样,使用TreeNode pre=null;每次比较pre.val和root.val的差值.

代码:

class Solution {
    private int min=Integer.MAX_VALUE;
    private TreeNode pre=null;
    public int getMinimumDifference(TreeNode root) {
        traversal(root);
        return min;
    }
    public void  traversal(TreeNode root){
        if(root==null)return ;
        //中序遍历
        if(root.left!=null)traversal(root.left);

        if(pre!=null){
            if((root.val-pre.val)<min)min=root.val-pre.val;
        }
        pre=root;

        if(root.right!=null)traversal(root.right);
    }
}

九、二叉搜索树中的众数

使用MaxCount全局变量 记录出现次数最多的 count记录当前结点数值出现的次数 results

思路:(还是使用pre指向上一个结点) 遇到的情况分为以下几种:

1.pre指向空,那么count=1 (刚开始)

2.pre.val和root.val不相等 count=1

3.如果pre.val==root.val count++

将众数添加到int数组中的过程:

if(count==MaxCount)results.add(node.val);如果次数等于最多的次数 那么就把它添加到数组中

如果count>MaxCount;那么直接把lists中的元素清除,然后add(node.val);Maxcount=count;更新一下出现最多的次数

代码:

class Solution {
    private List<Integer> results;
    private TreeNode pre;
    private int count;
    private int MaxCount;

    public int[] findMode(TreeNode root) {
        results = new ArrayList<>();
        pre =   new TreeNode();
        count = 0;
        MaxCount = 0;
        traversal(root);
        return ListToArray(results);
    }

    public void traversal(TreeNode root) {
        if (root == null)
            return;
        // 中序遍历
        traversal(root.left);
        if (pre == null || root.val != pre.val)
            count = 1;// 如果pre为空的话 刚开始比较 那么次数为1
        if (pre != null && root.val == pre.val)
            count++;// pre不为空 并且值相等 那么++

    //把众数往集合加的逻辑
        if (count == MaxCount)
            results.add(root.val);
        if (count > MaxCount){
            results.clear();
            results.add(root.val);
            MaxCount=count;
        }
        pre = root;  
        traversal(root.right);
    }


    public int[] ListToArray(List<Integer> list) {
        int[] result = new int[list.size()];
        int index = 0;
        for (int i : list) {
            result[index++] = i;
        }
        return result;
    }
}

十、二叉树的最近公共祖先(后序遍历)

 递归法

1.确立递归函数的返回值TreeNode node,参数:TreeNode node,TreeNode p,TreeNode q

2.确立递归函数的终止条件:root==null root==q||root==p 直接返回root

3.递归逻辑:

1.如果root==p||root==q||root==null 就不用判断了 直接返回root

2.如果不等于的话 就要往下递归。

左:TreeNode left=traversal(left,p,q);看左子树有没有满足条件的节点

右:TreeNode right=traversal(right,p,q);右子树中有没有满足条件的节点

中:判断左右,两边都不为空 返回root

哪边不为空 返回哪边

代码:

class Solution {
    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);

        if(left == null && right == null) { // 若未找到节点 p 或 q
            return null;
        }else if(left == null && right != null) { // 若找到一个节点
            return right;
        }else if(left != null && right == null) { // 若找到一个节点
            return left;
        }else { // 若找到两个节点
            return root;
        }
    }
}

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值