二叉搜索树与双向链表

Posted dazhu123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉搜索树与双向链表相关的知识,希望对你有一定的参考价值。

1:题目描述

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

 

为了让您更好地理解问题,以下面的二叉搜索树为例:

 技术图片

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

 技术图片

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

 

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2:题目分析

解题思路:对于二叉排序树而言,其中序遍历输出就是升序排序。那我们怎么将其做成双向链表尼?着手点肯定是中序遍历;在遍历的迭代逻辑中,我们要将前次迭代和本次迭代处理的节点,按照双向链表的要求进行处理。怎么处理尼?通过一个全局变量保存上次遍历的节点,然后再本次遍历的节点处理中进行如下操作:

preNode.right  = curNode;

curNode.left = preNode;

当所有节点都这样处理后我们看

技术图片

 

其原本是一个二叉排序树,遍历处理后,root指向尾部,我们这是只需要将head和tail相连就可以构成一个完整的双向链表了。root再处理后就是尾部,然而head我们可以通过循环的方式

Node newHead = root;
while(true){
    if(newHead.left == null){
        break;    
    }
    newHead = newHead.left;    
}

一直向左子节点取寻找,直到为null,就到达了最小的节点了,当然也可以再中序遍历中进行操作,因为中序遍历第一个遍历的就是最左子节点,这里把其保存下来就可以了。

这里的递归操作如下:

  1. 本轮递归终止条件:当前节点为null时,终止本轮递归,归来即可。在二叉树的体现就是遇到了子节点为null的节点处就不会继续深入递归了。
  2. 递归逻辑设计:借用中序遍历,在遍历操作用将preNode.rigth = curNode,curNode.left = preNode,这里要注意的是,第一次遍历到左下节点时,此时preNode为null,curNode为最小节点。无法进行上述操作,所以在处理开始情况时,要处理好,在第二次遍历的时才操作。
  3. 递归返回值:void

3:代码示例

package JianZhiOffer36;

/**
 * @author :dazhu
 * @date :Created in 2020/3/23 9:11
 * @description: 二叉搜索树与双向链表
 * @modified By:
 * @version: $
 */
public class Main {
    public static void main(String[]args){


    }
}


class Solution {
    public Node preNode = null;
    public Node newHead = null;

    public Node treeToDoublyList(Node root) {
        //边界条件
        if(root == null){
            return root;
        }
        middleOrder(root);
        //将处理后的双向链表的首尾相连
        newHead.left = preNode;
        preNode.right = newHead;
        return newHead;
    }

    //算法思路:因为二叉排序树的中序遍历的输出就是有序的列表,
    //我们改写中序遍历,通过定义一个preNode的全局变量保留当前节点的前一个节点。
    //preNode.right = curNode,curNode.left = preNode.
    public void middleOrder(Node root){
        //如果当前节点为null,则return null
        if(root == null){
            return ;
        }
        //中序遍历,
        middleOrder(root.left);
        //中序遍历,第一个遍历的节点就是最左下的最小节点。
        //即第一个访问的节点就为左下角节点。
        //如果出preNode,则指向此时root,即为最左下节点。用来第一次遍历时,初始化
        //第一次进入这里是最左子节点时,初始化所需节点指引。
        if(preNode == null){
            newHead = root;
            preNode = root;
        }//否则,
        else{
            //以preNode始终指向前一个节点,正好是比当前节点小的节点,
            //进行以下操作,将其转成双向链表。
            preNode.right = root;
            root.left = preNode;
            preNode = root;
        }
        middleOrder(root.right);
    }
}
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val,Node _left,Node _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};

 

以上是关于二叉搜索树与双向链表的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer-二叉搜索树与双向链表

剑指offer 27:二叉搜索树与双向链表

剑指offer二十七之二叉搜索树与双向链表

剑指offer 二叉搜索树与双向链表

剑指offer 二叉搜索树与双向链表

剑指 Offer 36. 二叉搜索树与双向链表