【leetCode 257】一道算法题延伸出的Java内存知识

文章目录

    • 题目描述
    • 题目分析
    • 两种解法
    • 总结

原题链接如下:
257. 二叉树的所有路径 - 力扣(LeetCode)

题目描述

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

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

【leetCode 257】一道算法题延伸出的Java内存知识_第1张图片

题目分析

虽然是一道简单题,但是本题值得讨论的问题如下:每次dfs返回时,怎么样才能让返回的String是当前递归中的值,而不是上面的递归传下来的值?(栈的视角)

两种解法

  1. 每层递归进dfs时,都新开一个path来保存当前进度。下面代码中dfs定义的第二个参数,就是专门为path准备的。

    class Solution {
        List<String> ans = new ArrayList<>();
        public List<String> binaryTreePaths(TreeNode root) {
            StringBuffer s = new StringBuffer();
            dfs(root , s);
            return ans;
        }
        void dfs(TreeNode root , StringBuffer sb){
            //在每次递归进dfs时,都新开一个path。
            StringBuffer path = new StringBuffer(sb.toString());
            if(root == null) return;
            if(root.left == null && root.right == null){
                path.append(root.val);
                ans.add(path.toString());
                return;
            }
                path.append(root.val);
                path.append("->");
                dfs(root.left , path);
                dfs(root.right , path);
        }
    }
    
  2. 使用一个额外的List变量,保存当前访问的节点。在我们退出递归时,记得将当前访问的节点从递归中删除。

    class Solution {
        List<String> ans = new ArrayList<>();
        ArrayList <Integer> list = new ArrayList<>();
        public List<String> binaryTreePaths(TreeNode root) {
            StringBuffer s = new StringBuffer();
            dfs(root);
            return ans;
        }
        void dfs(TreeNode root){
            list.add(root.val);
            if(root.left == null && root.right == null){
                StringBuilder sb = new StringBuilder();
                for(int i = 0 ; i < list.size() - 1 ; i ++){
                    sb.append(list.get(i)).append("->");
                }
                sb.append(list.get(list.size() - 1));
                ans.add(sb.toString());
            }
            if(root.left != null){
                dfs(root.left);
                //出dfs的位置,删除当前dfs中元素。
                list.remove(list.size() - 1);
            }
            if(root.right != null){
                dfs(root.right);
                //出dfs的位置,删除当前dfs中元素。
                list.remove(list.size() - 1);
            }
    
        }
    

总结

在递归遍历中,如果我们使用全局变量,则每次递归中大家都共享这个变量。在这类查找路径的问题中就会出现一些错误。也就是当前深度优先到了一条路径,在转到另一条路径时,之前的结果还在全局变量中保存,但是此时已经不是我们想要的路径了。
所以应对这种问题,我给出了两种方案

  1. 每个dfs方法中定义一个局部变量。局部变量是保存在栈帧的局部变量表中,方法的调用需要通过栈来进行传递,每次调用方法,都有一个对应的栈帧被压入栈中,每一个方法掉用结束后,都有一个栈帧被弹出。这样一来,就可以避免全局变量出现的问题。因为每个方法中的局部变量都是不同的。
  2. 另外定义一个List,用来保存访问过的变量。这里的List其实就是一个全局变量/实例变量,全局变量保存在堆中,随着对象的创建而创建。
    堆是Java虚拟机管理的内存中最大的一块。堆是线程共享的,在虚拟机启动时创建。
    所以在进入一个dfs方法时,List中加入该数据,一个dfs方法结束之后,要记得将List中的数据移出。

你可能感兴趣的:(日更计划,算法,leetcode,java)