算法训练营打卡Day18 | 二叉树part05

一、LC513.找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

层序遍历,最后一层的第一个元素就是最左下角的值。

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        if (root == null) {
            return 0;
        }

        Deque queue = new LinkedList<>();
        int result = 0;//记录当前层最左边的
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();

            for (int i = 0; i < size; i++) {
                TreeNode node = queue.pollFirst();
                //每层第一个节点
                if (i == 0) {
                    result = node.val;
                }
                
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
        }
        return result;
    }
}

二、LC112. 路径总和  

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

1.根节点为null,直接返回false

2.叶子节点,如果此时目标值target为0,返回true,不然返回false

3.中间节点,递归当前节点的左右子树,同时target = target - currentNode.val。如果有一个子树的返回值为true,说明已经找到了,不用再看右子树了。如果左右子树都没找到,返回false.

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        //根节点
        if (root == null) {
            return false;
        }

        //叶子节点
        if (root.left == null && root.right == null) {
            if (targetSum - root.val == 0) {
                return true;
            }
            return false;
        }

        //回溯
        if (root.left != null && hasPathSum(root.left, targetSum - root.val)) {
            return true;//如果左子树找到满足条件的,直接返回,不用再看右子树了
        }

        if (root.right != null && hasPathSum(root.right, targetSum - root.val)) {
            return true;
        }

        //左右子树都没找到,返回false
        return false;
    }
}
|| 表示如果第一个条件满足不会去判断第二个条件, | 表示如果第一个条件满足了也会去看第二个条件。

if(a==b||a==c){...},如果a= =b成立,不会去判断a= =c;
if(a==b|a==c){...},不管a= =b是否成立,都要去判断a= =c;
 

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        //根节点
        if (root == null) {
            return false;
        }

        //叶子节点
        if (root.left == null && root.right == null) {
            if (targetSum - root.val == 0) {
                return true;
            }
            return false;
        }

        //回溯
        return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
    }
}

三、LC.113.路径总和ii

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路同上,多了一个list保存所有合规路径。此外,因为要找到所有合规路径,所以不能找到一个就提前返回,必须遍历所有可能的路径。将合法路径加入result中时需要进行浅拷贝,不然回溯时list会持续变动,最后返回空路径。

class Solution {
    public List> pathSum(TreeNode root, int targetSum) {
        List> result = new ArrayList<>();
        List path = new ArrayList();
        helper(root, targetSum, result, path);
        return result;
    }

    public void helper(TreeNode root, int targetSum, List> result, List path) {
        //根节点
        if (root == null) {
            return;
        }
        //叶子节点
        if (root.left == null && root.right == null) {
            if (targetSum - root.val == 0) {
                path.add(root.val);
                result.add(new ArrayList<>(path));
                path.remove(path.size() - 1);//回溯
            }
        }

        //中间节点
        path.add(root.val);
        helper(root.left, targetSum - root.val, result, path);
        helper(root.right, targetSum - root.val, result, path);
        path.remove(path.size() - 1);//回溯
    }
}

Java中的浅拷贝与深拷贝_java深拷贝和浅拷贝_DS程序员的博客-CSDN博客

我们创建了一个Person对象person1,其中包含一个Address对象。接着,我们使用person1的clone()方法创建了一个新的Person对象person2,并使用“==”判断person1和person2是否是同一对象。结果为false,证明两个对象是不同的。接下来,我们使用“==”判断person1和person2中的address是否是同一对象,结果为true,即两个对象的address成员变量指向的是同一个地址。
 

浅拷贝只是将原始对象的引用类型成员变量复制到新的对象中,因此新对象中的引用类型成员变量与原始对象中的成员变量指向同一对象。如果原始对象中的引用类型成员变量发生变化,新对象中的引用类型成员变量也会随之变化。

四、LC106.从中序与后序遍历序列构造二叉树

可以通过 中序与后序遍历 或者 前序与中序遍历 构造二叉树,但不能通过前序和后序遍历构造二叉树,因为二者都只能确定中间节点位置,无法确定左右子树分割位置。

中序:左中右

后序:左右中

前序:中左右

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

视频讲解:坑很多!来看看你掉过几次坑 | LeetCode:106.从中序与后序遍历序列构造二叉树_哔哩哔哩_bilibili代码随想录
 

说到一层一层切割,就应该想到了递归。

来看一下一共分几步:

  • 第一步:如果数组大小为零的话,说明是空节点了。

  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素 -> 定位到中间节点

  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点切割中序数组

  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

  • 第五步:切割后序数组,切成后序左数组和后序右数组 -> 中序数组的左数组长度=后序数组的左数组长度

  • 第六步:递归处理左区间和右区间

    class Solution {
        public TreeNode buildTree(int[] inorder, int[] postorder) {
            if (postorder.length == 0 || inorder.length == 0) {
                return null;
            }
    
            return helper(inorder, postorder, 0, inorder.length - 1, 0, postorder.length - 1);
        }
    
    // 中序区间:[inorderBegin, inorderEnd],后序区间:[postorderBegin, postorderEnd]
        public TreeNode helper(int[] inorder, int[] postorder, int inStart, int inEnd, int postStart, int postEnd) {
            if (inStart > inEnd || postStart > postEnd) {
                return null;
            }
    
            //定位中间节点位置
            int mid = postorder[postEnd];
            TreeNode root = new TreeNode(mid);
    
            //切割中序数组,找到左右子树
            int index = inStart;
            for (index = inStart; index <= inEnd; index++) {
                if (inorder[index] == mid) {
                    break;
                }
            }
            int leftInorderBegin = inStart;
            int leftInorderEnd = index - 1;
            int rightInorderBegin = index + 1;
            int rightInorderEnd = inEnd;
    
    
            //找到左子树长度
            int leftSize = index - inStart;
    
            //用左子树长度定位后序数组的切割点
            int leftPostorderBegin = postStart;
            int leftPostorderEnd = postStart + leftSize - 1;
            int rightPostorderBegin = postStart + leftSize;
            int rightPostorderEnd = postEnd - 1;
    
            //持续遍历,找左右子树
            root.left = helper(inorder, postorder, leftInorderBegin, leftInorderEnd, leftPostorderBegin, leftPostorderEnd);
            root.right = helper(inorder, postorder, rightInorderBegin, rightInorderEnd, rightPostorderBegin, rightPostorderEnd);
    
            return root;
        }
    }

五、LC105.从前序与中序遍历序列构造二叉树

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0 || inorder.length == 0) {
            return null;
        }
        return helper(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
    }

    //区间:左闭右闭
    public TreeNode helper(int[] preorder, int[] inorder, int preStart, int preEnd, int inStart, int inEnd) {
        //叶子节点为空
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }
        //定位中间节点位置 - 前序遍历 中左右
        int mid = preorder[preStart];
        TreeNode root = new TreeNode(mid);
        
        //切割中序数组,找到左右子树 - 左中右
        int index;
        for (index = inStart; index <= inEnd; index++) {
            if (inorder[index] == mid) {
                break;
            }
        }
        int leftInorderBegin = inStart;
        int leftInorderEnd = index - 1;
        int rightInorderBegin = index + 1;
        int rightInorderEnd = inEnd;

        //找到左子树长度
        int leftSize = index - inStart;

        //用左子树长度定位前序数组的切割点
        int leftPreorderBegin = preStart + 1;
        int leftPreorderEnd = preStart + leftSize;
        int rightPreorderBegin = leftPreorderEnd + 1;
        int rightPreorderEnd = preEnd;

        //持续遍历,找左右子树
        root.left = helper(preorder, inorder, leftPreorderBegin, leftPreorderEnd, leftInorderBegin, leftInorderEnd);
        root.right = helper(preorder, inorder, rightPreorderBegin, rightPreorderEnd, rightInorderBegin, rightInorderEnd);
        return root;
    }
}

你可能感兴趣的:(算法,数据结构)