27 二叉搜索树与双向链表(BG面试栽了的题——遍历递归形式需要返回值)

题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
Again:
从BG某公司面试栽到这道题,重新做一遍发现还是不会,我一直考虑如何在遍历的同时就把指针给转换完毕。
第一种方法是这样:我使用一个数组保存中序遍历的结果,然后在数组中对它进行指针的转换即可。看到这个做法,惊掉了我的大牙!!!!我怎么就想不到先把它放进一个数组,之后再对其进行转换呢!!!哎,多好的一份实习,没了。。。。
对树的算法的套路:要么遍历完处理;要么遍历的同时处理。

中序遍历的时候,当当前节点为null的时候,弹出栈中的元素。

public class Solution {       
    ArrayList<TreeNode> list = new ArrayList();
     public void InOrder(TreeNode root){
         if(root != null){
             InOrder(root.left);         
             list.add(root);
             InOrder(root.right);       
         }

     }
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return null;        
        InOrder(pRootOfTree);
        if(list.size() == 1)
            return list.get(0);
        list.get(0).left = null;
        list.get(0).right = list.get(1);
        list.get(list.size()-1).left = list.get(list.size()-2);
        list.get(list.size()-1).right = null; 
        for(int i=1;i<list.size()-1;i++){
            list.get(i).left = list.get(i-1);
            list.get(i).right = list.get(i+1);
        }
        return list.get(0);
    }
}

第二种是在遍历的同时进行指针转换,有两点很重要:
(1)保存上一个访问的节点
(2)最后返回的节点不再是根节点,而是最左的那个节点

public class Solution {    

    ArrayList<TreeNode> list = new ArrayList();
     public TreeNode InOrder(TreeNode root){
         if(root == null)
             return null;
         TreeNode p = root;
         TreeNode lastNode = null;
         Stack<TreeNode> stack = new Stack();
         while(!stack.isEmpty() || p!= null){
             while(p != null){
                 stack.push(p);
                 p = p.left;
             }
             p = stack.pop();
             p.left = lastNode;
             if(lastNode != null)
                 lastNode.right = p;
             lastNode = p;
             p = p.right;
         }
         lastNode.right = null;
         while(root.left != null)
            root = root.left;
         return root;            
     }
    public TreeNode Convert(TreeNode pRootOfTree) { 

        return InOrder(pRootOfTree);

    }
}

而且在递归中改变指针也没想象中的那么难,根本就不用返回值。如果真的想返回值的话,在递归的形式中,一旦某孩子的节点为null时,那么返回的上一个节点直接就变为null了,会出现问题,不能正确的返回真正的值;而且一个左边,一个右边,返回的是哪一个呢?
解决方式就是,当其左右孩子不为null的时候,返回上一个节点。

import java.util.Stack;

public class Solution {       
      TreeNode lastNode = null; 
      public void InOrder(TreeNode root){
         if(root != null){
             InOrder(root.left);         
             root.left = lastNode;
             if(lastNode != null)
                 lastNode.right = root;
             lastNode = root;             
             InOrder(root.right);       
         }

     }
    public TreeNode Convert(TreeNode pRootOfTree) { 
        if(pRootOfTree == null)
            return null;
        InOrder(pRootOfTree);
        lastNode.right = null;
        while(pRootOfTree.left != null){
            pRootOfTree = pRootOfTree.left;
        }
        return pRootOfTree;        
    }
}

带着返回值的,是不是递归形式下需要返回值的都这么写呀?
使用lastNode指向已经转换好的链表的最后一个节点(也是值最大的节点)
为什么要判断root的左右还是是不是为null呢?如果左孩子为null,那么lastNode指向的就不再是转换好的链表的最后一个节点,而是null;尤其是右孩子,如果右孩子不为null,那么对右孩子递归,处理右孩子的left和上一个节点的right,然后lastNode就指向了这个右孩子(这一块建议画图)==》即右孩子不为null,lastNode指向的就是右孩子;如果右孩子为null了,lastNode本应该指向已经转换好的最后一个节点,但是,此时会直接返回null,lastNode就指向了null;那么这里之后就都错误了!

public class Solution {       
      TreeNode lastNode = null; 
      public TreeNode InOrder(TreeNode root){
         if(root != null){
             if(root.left != null)
                 lastNode = InOrder(root.left);         
             root.left = lastNode;
             if(lastNode != null)
                 lastNode.right = root;
             lastNode = root;             
             if(root.right != null)
                 lastNode = InOrder(root.right);       
         }
         return lastNode;
     }
    public TreeNode Convert(TreeNode pRootOfTree) { 
        if(pRootOfTree == null)
            return null;
        InOrder(pRootOfTree);
        lastNode.right = null;
        while(pRootOfTree.left != null){
            pRootOfTree = pRootOfTree.left;
        }
        return pRootOfTree;        
    }
}

在中序遍历中,当遍历到根节点的时候,说明左子树已经完成了所需的操作。这句话理解很重要。
———————————————–两次分割线—————————————————————

1、

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
import java.util.ArrayList;

public class Solution {
    ArrayList<TreeNode> list = new ArrayList();
    public void InOrder(TreeNode pRootOfTree){
        if(pRootOfTree != null){
            InOrder(pRootOfTree.left);
            list.add(pRootOfTree);
            InOrder(pRootOfTree.right);
        }
    }
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return null;
        InOrder(pRootOfTree);
        list.get(0).left = null;
        if(list.size() > 1){
            list.get(0).right = list.get(1);
             list.get(list.size() - 1).left =  list.get(list.size() - 2);
        }else{
            list.get(0).right = null; 
            //
        }
        list.get(list.size() - 1).right = null;
        for(int i=1;i<list.size()-1;i++){
            list.get(i).left = list.get(i-1);
            list.get(i).right = list.get(i+1);
        }
        return list.get(0);
    }
}

二叉树的中序遍历得到的是一个有序的序列!记录中序遍历过程中遍历的结点,即是一个排序的结点,然后给这些结点添加上前后指针即可!
2、使用在中序遍历的递归一次性的完成指针的修改

注:返回的这个lastNode是以当前节点为根的树的中序遍历访问的最后一个节点。

import java.util.ArrayList;

public class Solution {

    public TreeNode InOrder(TreeNode root,TreeNode lastNode){
        if(root == null)
            return null;
        TreeNode current = root;
        if(current.left != null)
            lastNode = InOrder(current.left,lastNode);
        current.left = lastNode;
        if(lastNode != null)
            lastNode.right = current;
        lastNode = current;
        if(current.right != null)
            lastNode = InOrder(current.right,lastNode);
        return lastNode;
    }
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return null;
        TreeNode lastNode = null;
        lastNode = InOrder(pRootOfTree,lastNode);
        while(lastNode.left != null){
            lastNode = lastNode.left;
        }
        return lastNode;
    }
}

在中序遍历中,当遍历到根节点的时候,说明左子树已经完成了所需的操作。所以设置一个lastNode作为左子树中的最后一个结点,只要当前结点的左子树不为空,那么对左子树进行递归,=找最左的那个孩子,【>最左边的结点的上一个结点为null,即设置它的左指针指向lastNode;如果此时的lastNode不为null,那么就设置lastNode的右指针指向当前结点(其实就是遍历到那个点的时候,设置和它相关的双向指针)】;于是最左边的这个结点访问结束;

补充:其实,这时候相当于左孩子访问完毕(左子树完成了所需的操作),那么应该开始访问当前节点了,让lastNode指向这个结点,作为另一个结点的上一个还有另一个方向;然后轮到右孩子:右孩子会再次进行上面的循环,先找到右孩子最左的节点,无非这个时候最左的节点有了上一个节点。。。。
3、非递归的做法:

import java.util.ArrayList;
import java.util.Stack;

public class Solution {    

    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return null;
        Stack<TreeNode> stack = new Stack();
        TreeNode p = pRootOfTree;
        TreeNode lastNode = null;

        while(!stack.isEmpty() || p != null){
            while(p != null){
                stack.push(p);
                p = p.left;
            }
            p = stack.pop();
            p.left = lastNode;
            if(lastNode != null)
                lastNode.right = p;
            lastNode = p;
            p = p.right;

        }
        while(pRootOfTree.left != null){
            pRootOfTree = pRootOfTree.left;
        }
        return pRootOfTree;
    }
}

这里先回顾一下中序遍历(非递归),首先申请一个变量P指向根节点,然后剩余的都在while循环中进行了!注意只有这一个地方压入栈操作,把节点的左孩子的左孩子的直到null。。。。。压入栈中;当这时候p为null的时候,出栈,其实就意味着回溯了,回到当前null的父节点,设为p,对p进行一系列的操作,然后令p指向它的右孩子。(注意这里只是把p指向了它的右孩子,没有入栈操作,入栈操作只有一次)

其实,中序遍历这么个写法也对,就是把while循环中的那个while变为if-else,不过我不习惯这种写法,知道就好,还是使用上面那种while的写法,不过下面的这种if的形式便于理解:


    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            return null;
        Stack<TreeNode> stack = new Stack();
        TreeNode p = pRootOfTree;
        TreeNode lastNode = null;

        while(!stack.isEmpty() || p != null){
            if(p != null){
                stack.push(p);
                p = p.left;
            }else{
                p = stack.pop();
                p.left = lastNode;
                if(lastNode != null)
                    lastNode.right = p;
                lastNode = p;
                p = p.right;
            }                       
        }
        while(pRootOfTree.left != null){
            pRootOfTree = pRootOfTree.left;
        }
        return pRootOfTree;
    }

在递归方法中,注意那个lastNode = InOrder(current.right,lastNode);一定要有返回值,否则会报错!null
注意,在递归的这种情况一定要写返回值@!!@!
参考:
https://www.cnblogs.com/keedor/p/4467040.html
http://blog.sina.com.cn/s/blog_911243f30102wvfi.html
https://blog.csdn.net/lilianforever/article/details/51853960

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值