二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
1.方法一:临时路径使用String存储
class Solution {
List<String> res = new ArrayList<String>();
public List<String> binaryTreePaths(TreeNode root) {
if(root == null){
return res;
}
// String init = "";
// dfs(root, init); //上面这两行ok.
dfs(root, "");
return res;
}
public void dfs(TreeNode root,String s) {
//public void dfs(TreeNode root , Strng s){
if(root == null){
return;
}
if(root.left == null && root.right == null){
//String temps = new String(root.val);
s = s+root.val; // ok
//s = s + String.valueOf(root.val); ok
//res.add(new String(s)); ok
res.add(s); //ok 此处是否是new String都行
}
s = s + root.val;
s = s+ "->";
dfs(root.left, s); // s是String类型,在left这一侧会被操作改动,例如
dfs(root.right,s);
}
}
2.错误的方法二: 临时路径使用StringBuffer存储
如果我们直接把方法一中的代码粘贴过来,然后把String的地方全部改为StringBuffer,会发现答案不正确。
要清楚错误原因:我们先了解一下StringBuffer和String的区别:
1)String类是不可变类,其对象是不可更改的常量。而StringBuffer是可变类,它的对象是可以扩充和修改的。
2)对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新对象的地址赋给原引用。
3)StringBuffer是一个可变对象,当对他进行修改时候,不会像String那样重新建立对象。
知道上面区别之后我们通俗递讲解一下,上面方法一中的代码为什么可以使用String,而不能使用StringBuffer了。
重点看这几行代码:
s = s+ root.val; (例如目前存放的是"qwer")
dfs(root.left, s);
dfs(root.right,s);
我们的代码逻辑是:把当前存放的“qwer”去左子树遍历,把当前存放的“qwer”去右子树遍历。
由于我们的String每次修改,例如 s = s+ root.val,都是创建一个新对象,
所以s="qwer"进入左子树之后,原始的s = "qwer"不会变, 只是会新生成一个s,所以原始的"qwer"
还是会以"qwer"的值进入右子树。
而当我们使用的是StringBuffer,StringBuffer是一个可变对象,原始s="qwer"进入左子树之后,左子树里面的逻辑会改变原始s的值了。
所以进入右子树的s就不再是“qwer”了。所以左子树产生的结果,这样当然就不符合我们的逻辑。
3.正确的方法二:使用StringBuffer存储。
但是我们可以事先对s = "qwer"进行一下备份s_copy,让s = "qwer"进入左子树,
让s_copy = "qwer"进入右子树,这样就可以符合整体逻辑了
class Solution {
List<String> res = new ArrayList<String>();
public List<String> binaryTreePaths(TreeNode root) {
if(root == null) return res;
dfs(root,new StringBuffer());
return res;
}
public void dfs(TreeNode root , StringBuffer s){
if(root == null) return;
if(root.left == null && root.right == null){
s.append(root.val);
res.add(s.toString());
}
s.append(root.val);
s.append("->");
StringBuffer s_copy = new StringBuffer(s);
dfs(root.left , s);
dfs(root.right, s_copy);
}
}
核心一句代码:StringBuffer s_copy = new StringBuffer(s);注意必须使用new StringBuffer()这种产生对象的方法,不能直接
使用StringBuffer s_copy = s这种方法,因为这种方法还是没有产生新对象,还是会错误。
4.总结补充
1)我们借助这个样例可以很好的对是否创新一个新对象,以及创建新对象对代码逻辑产生的影响
有一个很好的认知了。以前总是停留在文字概念层面,现在深化到理解运用层面了。
2)在二叉树,树的遍历这一块我们经常使用的是String,但是在回溯那一块,我们StringBuffer用的比较多
我们要具体问题具体分析,在理解的基础上形成肌肉记忆,不要混乱,尽量从逻辑底层出发思考。