二叉树22:二叉搜索树与双向链表

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

思路:

方法一:关键是理解题目要求的意思。将二叉搜索树转变为排序的双向链表,首先链表中的结点要是有序的,然后各个结点之间要双向连接,显然对于搜索二叉树,必须进行中序遍历才可能将结点顺序排列,于是问题的解决办法就是对于二叉树进行中序遍历,在遍历的同时调整结点之间的指针,使之成为双向链表。

遍历时总是保留上一个结点lastNode,每遍历一个结点node,设置node.left=lastNode,并且设置lastNode.right=last;即总是将上一个结点的right赋值为当前结点,将当前结点的left赋值为上一个结点。于是使用left指针可以指向上一个结点,使用right指针可以指向下一个结点。初始时令lastNode=null,对于lastNode=null时,不需要设置lastNode.right=node.对于最后一个结点,当node=null时,不用设置node.left=lastNode,即在遍历中间结点时条件2个if条件即可。最后返回是新的双向链表的头结点,只需要在开始遍历第一个结点的时候,即lastNode==null的时候将当前的root结点赋值给成员变量head即可波流这个头结点,最后返回头结点即可。注意常识:head=root之后, head仅仅是一个符号,是一个指针而已,它指向的是一个具体的对象,这个对象所在的结点就存在与堆中,于是使用head就可以找到头结点对象。

同理, root.left=lastNode中,将当前的前一个结点lastNode赋值给了root.left,但是之后lastNode=root显然lastNode的值发生了变化,那么是否之前的root.left也会发生变化呢?其实不会,要注意,root.left=lastNode中lastNode在赋值运算的右边,而lastNode仅仅是一个符号,是一个指针,它指向的是一个堆中的地址,于是将root.left也引向这个地址对象后lastNode这个指针就失效了,之后再对lastNode附上新的值只是将lastNode的指针指向了新的地址,并不会影响root.left指向的具体的对象。因此,思考问题要从指针的角度来思考问题,其实Java指针和我感觉中的用法是一致的,可以按着感觉走。

public class Solution {
    TreeNode lastNode=null;
    TreeNode head;
    public TreeNode Convert(TreeNode pRootOfTree) {
        //特殊输入
        if(pRootOfTree==null) return null;
        //调用递归方法解决问题
        this.process(pRootOfTree);
        return head;
    }
    
    //递归方法,中序遍历
    private void process(TreeNode root){
        //边界条件
        if(root==null) return;
        //①先遍历左子树
        this.process(root.left);
        //②遍历当前中间结点
        if(lastNode!=null){
            lastNode.right=root;
        }else{
            head=root;
        }
        if(root!=null){
            root.left=lastNode;
        }
        //更新lastNoode
        lastNode=root;
        //③遍历右子树
        this.process(root.right);
    }
}

方法二:对于以root为根的二叉搜索树,对于根结点root,它的前一个结点是左子树上最大值,它的下一个结点是右子树上的最小值,因此只要找到root.left树上最后一个结点lastNode合root.right树上第一个结点nextNode,然后设置root.right=nextNode表示下一个结点,root.left=lastNode表示上一个结点就完成了根结点的双向关系的建立,显然,对于任何一个结点,都可以按照这个方法进行,先将其左右子树分别调整为双向链表,然后将左子树的左后一个结点和右子树的第一个结点与root进行连接即可。这是一个递归的过程。设计一个递归函数,功能是:输入二叉树的根结点root,将这个搜索二叉树调整为双向的有序链表,并返回第一个结点(最小)和最后一个结点(最大)。递归函数中的操作是,先调用递归函数将root.left和root.right传入进行调整,同时得到最小和最大的结点,然后将左子树最大结点与root连接,将右子树的最小结点与root连接。

方法一是利用了中序遍历,在中序遍历的同时调整前后结点之间的指针关系,方法二是重新设计了一个递归逻辑,将一个复杂问题分解成为一系列递归的小问题。

参考代码:

// 记录子树链表的最后一个节点,终结点只可能为只含左子树的非叶节点与叶节点
    protected TreeNode leftLast = null;
    public TreeNode Convert(TreeNode root) {
        if(root==null)
            return null;
        if(root.left==null&&root.right==null){
            leftLast = root;// 最后的一个节点可能为最右侧的叶节点
            return root;
        }
        // 1.将左子树构造成双链表,并返回链表头节点
        TreeNode left = Convert(root.left);
        // 3.如果左子树链表不为空的话,将当前root追加到左子树链表
        if(left!=null){
            leftLast.right = root;
            root.left = leftLast;
        }
        leftLast = root;// 当根节点只含左子树时,则该根节点为最后一个节点
        // 4.将右子树构造成双链表,并返回链表头节点
        TreeNode right = Convert(root.right);
        // 5.如果右子树链表不为空的话,将该链表追加到root节点之后
        if(right!=null){
            right.left = root;
            root.right = right;
        }
        return left!=null?left:root;       
    }



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值