由反序列化二叉树实现函数引起的二叉树节点递归调用问题
2017-08-31 17:02
507 查看
在刷剑指offer中遇到一个反序列化二叉树问题,将给定的二叉树前序遍历字符串(节点之间用“,”分割,空节点我用的“#”)。代码用递归很容易实现,由于递归中不保存int型数据,为了给字符串遍历计数,用了一个只有一个元素的数组(目前没有想到更好的方法,返回值其他值已被预订) 实现方法如下:
调试结果(调用递归函数后,pHead的值):
但是结果却不对,返回的pHead的值为1,子树为空!我调试进递归函数发现运算程序是对的呀。我一度开始怀疑对象在调用中不是直接引用的,而是像基本类型变量一样复制的。为此,做了个实验,在实验中同样参数为一个非空的节点,但是在调用函数中给节点参数加上非空子节点,并改变节点参数的值,然后看pHead子节点有没有改变。代码如下:
实验结果:
这里实验结果证明,调用函数里的对象是直接引用的,而不是复制的。反序列化二叉树的代码也没问题呀。后来不得不改变的策略:用递归函数返回头结点。代码其他地方完全没有变,只是加了返回值,然后将返回值赋给引用节点的左右节点。还是贴出完整代码(为了调试没有直接返回递归结果):
调试结果(调用递归函数后,pHead的值):
这次结果正确了。在普通函数中直接改变pHead的引用可以,为什么在没有返回值的递归中不行呢?pHead返回的节点值是1,而不是null说明,说明程序在逻辑上是正确的,与是否递归没有关系。在递归函数中直接用deseiriaImp(node.left,strArray,array1),认为下次递归的输入变量node就和上次的node.left关联。在递归遍历树的时候的确是这样,因为树已经存在,节点的子节点已经确定。但是如果node.left等于null的时候,它们将失去关联性,在子递归函数中改变node对上一递归函数的节点没有任何影响。deseiriaImp(node.left,strArray,array1)中的node.left并没有将下次递归中的node与本次node的左子树绑定。正确的方法是:只能显性的赋值,让node.left=deseiriaImp(node.left,strArray,array1),使节点关联起来。递归函数最后将头结点返回。
public class Demo { TreeNode Deserialize(String str) { if(str.length()<=0) return null; String[] strArray=str.split(","); TreeNode pHead=new TreeNode(Integer.parseInt(strArray[0])); int[] array1=new int [1]; array1[0]=0; deseiriaImp(pHead,strArray,array1); return pHead; } private void deseiriaImp(TreeNode node,String[] strArray, int[] array1) { if( strArray[array1[0]].equals("#") || (array1[0]==strArray.length)) { array1[0]++; return ; } node=new TreeNode(Integer.parseInt(strArray[array1[0]])); array1[0]++; deseiriaImp(node.left,strArray,array1); deseiriaImp(node.right,strArray,array1); } public static void main(String[] args) { TreeNode node1=new TreeNode(1); TreeNode node2=new TreeNode(2); TreeNode node3=new TreeNode(3); TreeNode node4=new TreeNode(4); TreeNode node5=new TreeNode(5); TreeNode node6=new TreeNode(6); node1.left= node2; node1.right=node3; node2.left= node4; node2.right=null ; node3.left=node5 ; node3.right= node6; node4.left= null; node4.right=null; node5.left= null; node5.right=null; node6.left= null; node6.right=null ; String seriaStr="1,2,4,#,#,#,3,5,#,#,6,#,#"; new Demo().Deserialize(seriaStr); } }
调试结果(调用递归函数后,pHead的值):
但是结果却不对,返回的pHead的值为1,子树为空!我调试进递归函数发现运算程序是对的呀。我一度开始怀疑对象在调用中不是直接引用的,而是像基本类型变量一样复制的。为此,做了个实验,在实验中同样参数为一个非空的节点,但是在调用函数中给节点参数加上非空子节点,并改变节点参数的值,然后看pHead子节点有没有改变。代码如下:
public class Test { void testNode(TreeNode node) { node.val=6; node.left=new TreeNode(2); node.right=new TreeNode(3); } public static void main(String[] args) { TreeNode node1=new TreeNode(1); new Test().testNode(node1); System.out.println(node1.val); } }
实验结果:
这里实验结果证明,调用函数里的对象是直接引用的,而不是复制的。反序列化二叉树的代码也没问题呀。后来不得不改变的策略:用递归函数返回头结点。代码其他地方完全没有变,只是加了返回值,然后将返回值赋给引用节点的左右节点。还是贴出完整代码(为了调试没有直接返回递归结果):
public class Solution { TreeNode Deserialize(String str) { if(str.length()<=0) return null; String[] strArray=str.split(","); TreeNode pHead=new TreeNode(Integer.parseInt(strArray[0])); int[] array1=new int [1]; array1[0]=0; TreeNode res=deseiriaImp(pHead,strArray,array1); return res; } private TreeNode deseiriaImp(TreeNode node,String[] strArray, int[] array1) { if( strArray[array1[0]].equals("#") || (array1[0]==strArray.length)) { array1[0]++; return null; } node=new TreeNode(Integer.parseInt(strArray[array1[0]])); array1[0]++; node.left=deseiriaImp(node.left,strArray,array1); node.right=deseiriaImp(node.right,strArray,array1); return node; } public static void main(String[] args) { TreeNode node1=new TreeNode(1); TreeNode node2=new TreeNode(2); TreeNode node3=new TreeNode(3); TreeNode node4=new TreeNode(4); TreeNode node5=new TreeNode(5); TreeNode node6=new TreeNode(6); node1.left= node2; node1.right=node3; node2.left= node4; node2.right=null ; node3.left=node5 ; node3.right= node6; node4.left= null; node4.right=null; node5.left= null; node5.right=null; node6.left= null; node6.right=null ; String seriaStr="1,2,4,#,#,#,3,5,#,#,6,#,#"; new Solution().Deserialize(seriaStr); } }
调试结果(调用递归函数后,pHead的值):
这次结果正确了。在普通函数中直接改变pHead的引用可以,为什么在没有返回值的递归中不行呢?pHead返回的节点值是1,而不是null说明,说明程序在逻辑上是正确的,与是否递归没有关系。在递归函数中直接用deseiriaImp(node.left,strArray,array1),认为下次递归的输入变量node就和上次的node.left关联。在递归遍历树的时候的确是这样,因为树已经存在,节点的子节点已经确定。但是如果node.left等于null的时候,它们将失去关联性,在子递归函数中改变node对上一递归函数的节点没有任何影响。deseiriaImp(node.left,strArray,array1)中的node.left并没有将下次递归中的node与本次node的左子树绑定。正确的方法是:只能显性的赋值,让node.left=deseiriaImp(node.left,strArray,array1),使节点关联起来。递归函数最后将头结点返回。
相关文章推荐
- SQL SERVER树型数据处理时,函数递归调用问题,查询根节点,子节点函数
- 试编写一个函数,返回一颗给定二叉树在中序遍历下的最后一个节点(分别用递归和非递归实现)
- 数据结构基础(6)--递归和函数调用--汉诺塔问题C语言实现
- 动态加载DLL实现不同DLL的相同导出函数调用问题
- 递归、函数的调用机制及汉诺塔问题
- python 使用 signal模块实现函数调用超时问题
- 利用函数递归调用实现汉诺塔
- java语言实现的二叉树的各种操作(包括递归与非递归遍历二叉树,求二叉树的高度,节点总数,叶子节点等)
- C语言调用库函数实现生产者消费者问题
- 2013-03-17---二叉树递归,非递归实现(附代码)深度,叶子节点数量,逐行打印二叉树
- C/C++:函数的调用约定(Calling Convention)和名称修饰(Decorated Name)以及两者不匹配引起的问题
- JavaScript Arguments 实现可变参数的函数,以及函数的递归调用
- 函数的调用机制_用递归实现栈(Function call machenism_Recursive calls stimulate stack)
- 一行代码设置TForm颜色的前世今生(属性赋值引起函数调用,然后发消息实现改变显示效果),TForm的初始颜色在dfm中设置了clBtnFace色
- 二叉树的各种操作的(递归非递归)的实现,如(递归非递归)先序后序中序层次遍历 二叉树的高度 叶子节点数,所有节点数
- C语言学习4: 函数返回值与传入参数,关于函数值传递和类型隐性转换,变量不同的作用域,static变量,多文件编译例如两个C文件,显示函数调用语句跳转,递归,斐波那契数列,多文件编译相同变量的问题。
- ASP 递归调用 已知节点查找根节点的函数
- 非递归实现交换二叉树的左右子节点
- 函数递归调用问题
- 递归和非递归实现计算二叉树叶子节点的个数