●day 18 第六章 二叉树 part05

 513. 找树左下角的值

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

假设二叉树中至少有一个节点。

递归:

class Solution {
    int curVal = 0;
    int curHeight = 0;

    public int findBottomLeftValue(TreeNode root) {
        dfs(root, 0);
        return curVal;
    }

    public void dfs(TreeNode root, int height) {
        if (root == null) {
            return;
        }
        height++;
        dfs(root.left, height);
        dfs(root.right, height);
        if (root.left == null && root.right == null) {
            if (height > curHeight) {
                curHeight = height;
                curVal = root.val;
            }
        }
    }
}
class Solution {
    private int Deep = -1;
    private int value = 0;
    public int findBottomLeftValue(TreeNode root) {
        value = root.val;
        findLeftValue(root,0);
        return value;
    }

    private void findLeftValue (TreeNode root,int deep) {
        if (root == null) return;
        if (root.left == null && root.right == null) {//只有符合条件的才会被判断
            if (deep > Deep) {
                value = root.val;
                Deep = deep;
            }
        }
        if (root.left != null) findLeftValue(root.left,deep + 1);//deep+1也是fn和f(n+1)的关系之一
        if (root.right != null) findLeftValue(root.right,deep + 1);
    }
}

 1.第一个是后序遍历第二个是前序遍历。

2.第一个根节点高度是1,而第二个是0.

3.引入 height(或在第二个代码中称为 deep)变量的目的是跟踪当前节点在树中的深度或层级。这对于确定哪些节点位于最底层是必要的,因为我们的目标是找到最底层的最左侧节点的值。

以下是使用 heightdeep 变量的主要原因:

  1. 区分节点的深度:在递归遍历树时,我们需要知道每个节点所处的深度。这样,当我们遇到一个叶子节点时,我们可以检查它是否位于比我们之前见过的任何叶子节点更深的位置。

  2. 更新最左侧值的条件:只有当当前叶子节点的深度超过之前记录的最大深度时,我们才更新存储的最左侧值。为此,我们需要有一个变量来跟踪当前节点的深度,并将其与已知的最大深度进行比较。

  3. 递归时的深度跟踪:在递归调用的过程中,我们需要为子节点更新深度值。例如,如果一个节点的深度是 d,那么它的子节点的深度就是 d+1

如果我们不使用 heightdeep 这样的变量,我们就无法确定每个节点的深度,从而无法确定哪个叶子节点位于最底层。因此,这个变量在解决这个问题时起着关键作用。

迭代: 

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        int ret = 0;
        Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode p = queue.poll();
            if (p.right != null) {
                queue.offer(p.right);
            }
            if (p.left != null) {
                queue.offer(p.left);
            }
            ret = p.val;
        }
        return ret;
    }
}

迭代就可以看出来,层序遍历也不是非要int n = que.size();不可,和深度遍历的更大区别就在于一个是栈一个是队列。需要int和内层循环的必然在内层循环进行了一些操作。 

112. 路径总和

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

叶子节点 是指没有子节点的节点。

 有一句话慢慢体会

if (cur->left) { // 左 (空节点不遍历)
    // 遇到叶子节点返回true,则直接返回true
    if (traversal(cur->left, count - cur->left->val)) return true; // 注意这里有回溯的逻辑
}
if (cur->right) { // 右 (空节点不遍历)
    // 遇到叶子节点返回true,则直接返回true
    if (traversal(cur->right, count - cur->right->val)) return true; // 注意这里有回溯的逻辑
}
return false;
if (cur->left) { // 左
    count -= cur->left->val; // 递归,处理节点;
    if (traversal(cur->left, count)) return true;
    count += cur->left->val; // 回溯,撤销处理结果
}
if (cur->right) { // 右
    count -= cur->right->val;
    if (traversal(cur->right, count)) return true;
    count += cur->right->val;
}
return false;

以上代码中是包含着回溯的,没有回溯,如何后撤重新找另一条路径呢。

回溯隐藏在traversal(cur->left, count - cur->left->val)这里, 因为把count - cur->left->val 直接作为参数传进去,函数结束,count的数值没有改变。

class solution {
    public boolean haspathsum(treenode root, int targetsum) {

        if (root == null) return false; // 为空退出

        // 叶子节点判断是否符合
        if (root.left == null && root.right == null) return root.val == targetsum;

        // 求两侧分支的路径和
        return haspathsum(root.left, targetsum - root.val) || haspathsum(root.right, targetsum - root.val);
    }
}
class solution {
   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;
    }
}

第一个代码其实就是把第二个代码后面的部分合并了一下。

使用逻辑“或”操作符 || 是因为我们只需要左子树或右子树中的任何一个满足条件就可以。如果左子树返回 true,那么整个表达式立即返回 true,不再检查右子树(这是 || 操作符的短路特性)。如果左子树返回 false,那么右子树的结果决定整个表达式的返回值。

这样,递归地检查每个节点,直到找到满足条件的路径或遍历完所有的节点为止。

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }
        Deque<TreeNode> stack = new ArrayDeque<>();
        Deque<Integer> stack1 = new ArrayDeque<>();
        stack.push(root);
        stack1.push(root.val);
        while (!stack.isEmpty()) {
            TreeNode pop = stack.pop();
            int sum = stack1.pop();
            if (pop.left == null && pop.right == null && sum == targetSum) {
                return true;
            }
            if (pop.left != null) {
                stack.push(pop.left);
                stack1.push(sum+pop.left.val);
            }
            if(pop.right!=null){
                stack.push(pop.right);
                stack1.push(sum+pop.right.val);
            }
        }
        return false;
    }
}

 113. 路径总和 II

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

class Solution {
    List<List<Integer>> ret = new LinkedList<List<Integer>>();
    Deque<Integer> path = new LinkedList<Integer>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        dfs(root, targetSum);
        return ret;
    }

    public void dfs(TreeNode root, int targetSum) {
        if (root == null) {
            return;
        }
        path.offerLast(root.val);
        targetSum -= root.val;
        if (root.left == null && root.right == null && targetSum == 0) {
            ret.add(new LinkedList<Integer>(path));
        }
        dfs(root.left, targetSum);
        dfs(root.right, targetSum);
        path.pollLast();
    }
}
class Solution {
    List<List<Integer>> ret = new LinkedList<List<Integer>>();
    Map<TreeNode, TreeNode> map = new HashMap<TreeNode, TreeNode>();

    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return ret;
        }

        Queue<TreeNode> queueNode = new LinkedList<TreeNode>();
        Queue<Integer> queueSum = new LinkedList<Integer>();
        queueNode.offer(root);
        queueSum.offer(0);

        while (!queueNode.isEmpty()) {
            TreeNode node = queueNode.poll();
            int rec = queueSum.poll() + node.val;

            if (node.left == null && node.right == null) {
                if (rec == targetSum) {
                    getPath(node);
                }
            } else {
                if (node.left != null) {
                    map.put(node.left, node);
                    queueNode.offer(node.left);
                    queueSum.offer(rec);
                }
                if (node.right != null) {
                    map.put(node.right, node);
                    queueNode.offer(node.right);
                    queueSum.offer(rec);
                }
            }
        }

        return ret;
    }

    public void getPath(TreeNode node) {
        List<Integer> temp = new LinkedList<Integer>();
        while (node != null) {
            temp.add(node.val);
            node = map.get(node);
        }
        Collections.reverse(temp);
        ret.add(new LinkedList<Integer>(temp));
    }
}
class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> result = new ArrayList<>();
        Stack<TreeNode> nodeStack = new Stack<>();
        Stack<Integer> sumStack = new Stack<>();
        Stack<ArrayList<Integer>> pathStack = new Stack<>();
        if(root == null)
            return result;
        nodeStack.add(root);
        sumStack.add(root.val);
        pathStack.add(new ArrayList<>());

        while(!nodeStack.isEmpty()){
            TreeNode currNode = nodeStack.peek();
            int currSum = sumStack.pop();
            ArrayList<Integer> currPath = pathStack.pop();
            if(currNode != null){
                nodeStack.pop();
                nodeStack.add(currNode);
                nodeStack.add(null);
                sumStack.add(currSum);
                currPath.add(currNode.val);
                pathStack.add(new ArrayList(currPath));
                if(currNode.right != null){
                    nodeStack.add(currNode.right);
                    sumStack.add(currSum + currNode.right.val);
                    pathStack.add(new ArrayList(currPath));
                }
                if(currNode.left != null){
                    nodeStack.add(currNode.left);
                    sumStack.add(currSum + currNode.left.val);
                    pathStack.add(new ArrayList(currPath));
                }
            }else{
                nodeStack.pop();
                TreeNode temp = nodeStack.pop();
                if(temp.left == null && temp.right == null && currSum == targetSum)
                    result.add(new ArrayList(currPath));
            }
        }
        return result;
    }
}

105. 从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

public TreeNode buildTree(int[] preOrder, int[] inOrder) {
    if (preOrder.length == 0) {
        return null;
    }

    // 获取根节点值
    int rootValue = preOrder[0];
    TreeNode root = new TreeNode(rootValue);

    // 区分左右子树
    for (int i = 0; i < inOrder.length; i++) {
        if (inOrder[i] == rootValue) {
            // 0 ~ i-1 左子树
            // i+1 ~ inOrder.length -1 右子树
            int[] inLeft = Arrays.copyOfRange(inOrder, 0, i); // [4,2]
            int[] inRight = Arrays.copyOfRange(inOrder, i + 1, inOrder.length); // []

            int[] preLeft = Arrays.copyOfRange(preOrder, 1, i + 1); // [2,4]
            int[] preRight = Arrays.copyOfRange(preOrder, i + 1, inOrder.length);

            root.left = buildTree(preLeft, inLeft); // 2
            root.right = buildTree(preRight, inRight); // 3
            break;
        }
    }

    return root;
}

 

class Solution {
    Map<Integer, Integer> map;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        map = new HashMap<>();
        for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
            map.put(inorder[i], i);
        }

        return findNode(preorder, 0, preorder.length, inorder,  0, inorder.length);  // 前闭后开
    }

    public TreeNode findNode(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd) {
        // 参数里的范围都是前闭后开
        if (preBegin >= preEnd || inBegin >= inEnd) {  // 不满足左闭右开,说明没有元素,返回空树
            return null;
        }
        int rootIndex = map.get(preorder[preBegin]);  // 找到前序遍历的第一个元素在中序遍历中的位置
        TreeNode root = new TreeNode(inorder[rootIndex]);  // 构造结点
        int lenOfLeft = rootIndex - inBegin;  // 保存中序左子树个数,用来确定前序数列的个数(这个很容易想,因为两个坐标相减就是包含两个数的个数减1,因为减去前一个坐标是包含两个数总共有几个数)
        root.left = findNode(preorder, preBegin + 1, preBegin + lenOfLeft + 1,
                            inorder, inBegin, rootIndex);
        root.right = findNode(preorder, preBegin + lenOfLeft + 1, preEnd,
                            inorder, rootIndex + 1, inEnd);

        return root;
    }
}

 106

class Solution {
   Map<Integer, Integer> map = new HashMap<>();

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        return findNode(postorder, 0, postorder.length, inorder, 0, inorder.length);
    }

    public TreeNode findNode(int[] postOrder, int postBegin, int postEnd, int[] inorder, int inBegin, int inEnd) {
        if (inBegin >= inEnd || postBegin >= postEnd) {  // 不满足左闭右开,说明没有元素,返回空树
            return null;
        }
        int rootIndex = map.get(postOrder[postEnd - 1]);
        TreeNode root = new TreeNode(inorder[rootIndex]);
        int lenOfLeft = rootIndex - inBegin;
        root.left = findNode(postOrder, postBegin, postBegin + lenOfLeft, inorder, inBegin, rootIndex);
        root.right = findNode(postOrder, postBegin + lenOfLeft, postEnd - 1, inorder, rootIndex + 1, inEnd);
        return root;
    }
}

变量能不能被别的方法用和它所在的方法是不是public定义的无关对吗

ChatGPT

是的,你说得对。一个变量是否能被其他方法使用与它所在的方法是否被声明为 public 是没有关系的。让我们更详细地分析这个问题:

  1. 局部变量:局部变量是在方法、构造器或块中声明的变量。它们的作用域只限于声明它们的代码块。一旦超出这个代码块,这个变量就会失效,不能被其他方法访问。局部变量的生命周期仅限于方法的执行时间。方法是否是 public 对此没有影响。

  2. 成员变量:成员变量(也称为实例变量)是在类的内部但在方法、构造器或任何块之外声明的。它们的访问权限由访问修饰符(如 publicprivateprotected 或默认)决定。其他方法可以访问成员变量,只要这些方法的作用域允许它们这么做。

例如,如果一个成员变量被声明为 private,那么只有同一类中的方法可以访问它。如果它被声明为 public,任何其他类都可以访问它,前提是它们有这个类的实例。

总结:方法是否是 public 只决定了其他类是否可以调用这个方法。它与方法内部的局部变量的可见性或类的成员变量的可见性无关。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 什么是二叉树二叉树是一种树形数据结构,其中每个节点最多有两个子节点,分别称为左子节点和右子节点。 2. 什么是二叉搜索树? 二叉搜索树是一种特殊的二叉树,其中左子节点的值小于等于父节点的值,右子节点的值大于等于父节点的值。 3. 什么是完全二叉树? 完全二叉树是一种特殊的二叉树,除了最后一层外,每一层都必须填满,且最后一层从左到右依次填入节点。 4. 什么是平衡二叉树? 平衡二叉树是一种特殊的二叉搜索树,其左右子树高度差不超过1。 5. 什么是二叉树的遍历? 二叉树的遍历是指按照某种顺序依次访问二叉树中的每个节点,常见的遍历方式包括前序遍历、中序遍历和后序遍历。 6. 什么是二叉树的前序遍历? 二叉树的前序遍历是指按照根节点、左子树、右子树的顺序依次访问二叉树中的每个节点。 7. 什么是二叉树的中序遍历? 二叉树的中序遍历是指按照左子树、根节点、右子树的顺序依次访问二叉树中的每个节点。 8. 什么是二叉树的后序遍历? 二叉树的后序遍历是指按照左子树、右子树、根节点的顺序依次访问二叉树中的每个节点。 9. 什么是二叉树的层序遍历? 二叉树的层序遍历是指按照从上到下、从左到右的顺序依次访问二叉树中的每个节点。 10. 如何判断一个二叉树是否为二叉搜索树? 可以通过中序遍历得到二叉树中所有节点的值,然后判断这些值是否按照升序排列即可判断是否为二叉搜索树。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值