由反序列化二叉树实现函数引起的二叉树节点递归调用问题

来源:互联网 发布:python plot alpha 编辑:程序博客网 时间:2024/04/16 06:54

          在刷剑指offer中遇到一个反序列化二叉树问题,将给定的二叉树前序遍历字符串(节点之间用“,”分割,空节点我用的“#”)。代码用递归很容易实现,由于递归中不保存int型数据,为了给字符串遍历计数,用了一个只有一个元素的数组(目前没有想到更好的方法,返回值其他值已被预订)   实现方法如下:

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),使节点关联起来。递归函数最后将头结点返回。