二叉搜索树转换成有序的双向链表

简介:

一,问题描述

给定一棵二叉搜索树,在二叉搜索树的基础上,将之转换成有序的双向链表。即,不需要额外的辅助空间。

 

二,问题分析

对于二叉搜索树而言,它的结点有左孩子和右孩子指针。这类似于双向链表中的 前向指针(指向前驱结点) 和 后向指针(指向下一个结点)。另外,二叉搜索树中序遍历是有序的。在中序遍历二叉搜索树时,将原来指向左子结点的指针调整为链表中的指向前一个结点的指针;将原来指向右子结点的指针调整为链表中的指向下一个结点的指针。

当遍历到根结点时,可以将树视为三个部分:根结点、根结点的左子树、根的右子树。找出根结点的左子树中的最大值,将根的左孩子指向它;找出根结点的右子树中的最小值,将根的右孩子指向它。而这个过程,其实就是二叉搜索树的中序递归遍历隐含的过程。

根据中序递归,当遍历转换到根结点时,根的左子树已经转换成一个有序的双向链表了,此时:双向链表的最后一个结点就是根的左子树中最大的结点。下一个待转换的结点就是根。当根转换完成后,就遍历根的右子树进行转换。

转换后:

 

三,代码实现

①首先需要构造一棵二叉排序树,由buildTree方法实现,可参考:二叉树的构造

②对结点进行转换,主要由convertNode(BinaryNode, BinaryNode)方法实现

复制代码
 1     /**
 2      * 采用中序遍历思想将二叉搜索树的结点转化为双向链表的结点
 3      * @param root 从二叉搜索树的根开始转换
 4      * @param lastNode 当前链表的最后一个结点
 5      * @return 链表的最后一个结点
 6      */
 7     private BinaryNode convertNode(BinaryNode root, BinaryNode lastNode){
 8         if(root == null)
 9             return null;
10         
11         BinaryNode currentNode = root;//当前待转换子树的根结点
12         if(root.left != null)
13             lastNode = convertNode(root.left, lastNode);//向根的左子树转换(类似于中序遍历中左子树遍历)
14         
15         currentNode.left = lastNode;
16         
17         if(lastNode != null)
18             lastNode.right = currentNode;
19         lastNode = currentNode;//将currentNode加入到链表中
20         
21         if(root.right != null)
22             lastNode = convertNode(root.right, lastNode);//往根的右子树转换(类似于中序遍历中右子树遍历)
23         
24         return lastNode;
25     }
复制代码

1)在转换的过程中,链表长度是不断增加的。第7行方法里面的第二个参数lastNode,用来记录:当前链表中的最后一个结点。

2)第11行的currentNode就是 当前正在转换的结点。第12、13行表示,转换currentNode的左子树。当currentNode的左子树转换完成之后,程序执行到第15行,将currentNode的左孩子赋值给当前链表中的最后一个结点。

3)第17行 有个if判断,主要是:对于双向链表中的第一个结点而言(即:二叉搜索树中的最左下结点(值最小的结点),比如那个权值为4的结点),它是左孩子是null的。因为它是值最小的结点啊。如果lastNode不为null,表明:至少已经将二叉搜索树中的部分结点转化成了双向链表中的结点了。因此,需要将双向链表中的最后一个结点的右孩子指针指向当前正在转换的结点。

4)第19行,表示当前正在转换的结点已经转换完成了,故它变成了双向链表中的最后一个结点。

5)第21行,继续递归转换当前结点的右子树。

6)第24行,返回整棵二叉搜索树转换完成之后的 得到的 双向链表的尾结点。

7)由于每个结点只会遍历一次,故算法的时间复杂度为O(N);由于直接在是原来的二叉搜索树中进行链表结点的转换,故空间复杂度为O(1)

 

四,完整代码

复制代码
//将二叉搜索树转化为有序的双向链表
public class BST2List {
    
    private class BinaryNode implements Comparable<BinaryNode>{
        int ele;
        BinaryNode left;
        BinaryNode right;
        
        public BinaryNode(int ele) {
            this.ele = ele;
            left = right = null;
        }
        
        @Override
        public int compareTo(BinaryNode node) {
            return this.ele - node.ele;
        }
        
        @Override
        public String toString() {
            return this.ele + " ";
        }
        
    }
    
    private BinaryNode root;
    
    public void buildTree(int[] ele){
        for (int i : ele) {
            insert(i);
        }
    }
    private void insert(int ele){
        root = insert(root, ele);
    }
    private BinaryNode insert(BinaryNode root, int ele){
        if(root == null)
            return new BinaryNode(ele);
        if(root.ele > ele)
            root.left = insert(root.left, ele);
        else if(root.ele < ele)
            root.right = insert(root.right, ele);
        return root;
    }
    
    //将二叉搜索树转化成双向链表,返回链表的头结点
    public BinaryNode bstConvert2List(BinaryNode root){
        if(root == null)
            return null;
        
        BinaryNode lastNode = null;
        lastNode = convertNode(root, lastNode);
        
        BinaryNode head = null;
        while(lastNode != null)
        {
            head = lastNode;
            lastNode = lastNode.left;
        }
        return head;
    }
    /**
     * 采用中序遍历思想将二叉搜索树的结点转化为双向链表的结点
     * @param root 从二叉搜索树的根开始转换
     * @param lastNode 当前链表的最后一个结点
     * @return 链表的最后一个结点
     */
    private BinaryNode convertNode(BinaryNode root, BinaryNode lastNode){
        if(root == null)
            return null;
        
        BinaryNode currentNode = root;//当前待转换子树的根结点
        if(root.left != null)
            lastNode = convertNode(root.left, lastNode);//向根的左子树转换(类似于中序遍历中左子树遍历)
        
        currentNode.left = lastNode;
        
        if(lastNode != null)
            lastNode.right = currentNode;
        lastNode = currentNode;//将currentNode加入到链表中
        
        if(root.right != null)
            lastNode = convertNode(root.right, lastNode);//往根的右子树转换(类似于中序遍历中右子树遍历)
        
        return lastNode;
    }
    
    public void printList(BinaryNode head){
        if(head == null)
            return;
        BinaryNode current = head;
        while(current != null)
        {
            System.out.print(current);
            current = current.right;
        }
    }
    
    //hapjin test
    public static void main(String[] args) {
        int[] eles = {10,6,14,4,8,12,16};
        BST2List obj = new BST2List();
        obj.buildTree(eles);
        
        BinaryNode head = obj.bstConvert2List(obj.root);
        obj.printList(head);
    }
}
复制代码本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/,如需转载请自行联系原作者

 

相关文章
|
2月前
|
算法
LeetCode刷题---21.合并两个有序链表(双指针)
LeetCode刷题---21.合并两个有序链表(双指针)
【 腾讯精选练习 50 题】12—合并两个有序链表【简单】
【 腾讯精选练习 50 题】12—合并两个有序链表【简单】
数据结构实验之链表六:有序链表的建立
数据结构实验之链表六:有序链表的建立
|
4月前
leetcode-21:合并两个有序链表
leetcode-21:合并两个有序链表
24 0
LeetCode | 21. 合并两个有序链表
LeetCode | 21. 合并两个有序链表
|
5月前
|
运维
数据结构实验之链表四:有序链表的归并
数据结构实验之链表四:有序链表的归并
|
3月前
|
存储 算法
头歌:第1关:有序单链表的插入操作
头歌:第1关:有序单链表的插入操作
130 0
|
27天前
【力扣】21. 合并两个有序链表
【力扣】21. 合并两个有序链表
|
2月前
|
C语言
反转链表、链表的中间结点、合并两个有序链表【LeetCode刷题日志】
反转链表、链表的中间结点、合并两个有序链表【LeetCode刷题日志】
|
3月前
Leecode之合并两个有序链表
Leecode之合并两个有序链表