首页 > 试题广场 >

二叉搜索树与双向链表

[编程题]二叉搜索树与双向链表
  • 热度指数:643421 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示


数据范围:输入二叉树的节点数 ,二叉树中每个节点的值
要求:空间复杂度(即在原树上操作),时间复杂度

注意:
1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继
2.返回链表中的第一个节点的指针
3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构
4.你不用输出双向链表,程序会根据你的返回值自动打印输出

输入描述:
二叉树的根节点


输出描述:
双向链表的其中一个头节点。
示例1

输入

{10,6,14,4,8,12,16}

输出

From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;

说明

输入题面图中二叉树,输出的时候将双向链表的头节点返回即可。     
示例2

输入

{5,4,#,3,#,2,#,1}

输出

From left to right are:1,2,3,4,5;From right to left are:5,4,3,2,1;

说明

                    5
                  /
                4
              /
            3
          /
        2
      /
    1
树的形状如上图       

说明:本题目包含复杂数据结构TreeNode,点此查看相关信息
推荐
方法一:非递归版
解题思路:
1.核心是中序遍历的非递归算法。
2.修改当前遍历节点与前一遍历节点的指针指向。
    import java.util.Stack;
    public TreeNode ConvertBSTToBiList(TreeNode root) {
    	if(root==null)
    		return null;
    	Stack<TreeNode> stack = new Stack<TreeNode>();
    	TreeNode p = root;
    	TreeNode pre = null;// 用于保存中序遍历序列的上一节点
    	boolean isFirst = true;
    	while(p!=null||!stack.isEmpty()){
    		while(p!=null){
    			stack.push(p);
    			p = p.left;
    		}
    		p = stack.pop();
    		if(isFirst){
    			root = p;// 将中序遍历序列中的第一个节点记为root
    			pre = root;
    			isFirst = false;
    		}else{
    			pre.right = p;
    			p.left = pre;
    			pre = p;
    		}		
    		p = p.right;
    	}
    	return root;
    }
方法二:递归版
解题思路:
1.将左子树构造成双链表,并返回链表头节点。
2.定位至左子树双链表最后一个节点。
3.如果左子树链表不为空的话,将当前root追加到左子树链表。
4.将右子树构造成双链表,并返回链表头节点。
5.如果右子树链表不为空的话,将该链表追加到root节点之后。
6.根据左子树链表是否为空确定返回的节点。
    public TreeNode Convert(TreeNode root) {
    	if(root==null)
    		return null;
    	if(root.left==null&&root.right==null)
    		return root;
    	// 1.将左子树构造成双链表,并返回链表头节点
    	TreeNode left = Convert(root.left);
    	TreeNode p = left;
    	// 2.定位至左子树双链表最后一个节点
    	while(p!=null&&p.right!=null){
    		p = p.right;
    	}
    	// 3.如果左子树链表不为空的话,将当前root追加到左子树链表
    	if(left!=null){
    		p.right = root;
    		root.left = p;
    	}
    	// 4.将右子树构造成双链表,并返回链表头节点
    	TreeNode right = Convert(root.right);
    	// 5.如果右子树链表不为空的话,将该链表追加到root节点之后
    	if(right!=null){
    		right.left = root;
    		root.right = right;
    	}
		return left!=null?left:root;        
    }
方法三:改进递归版
解题思路:
思路与方法二中的递归版一致,仅对第2点中的定位作了修改,新增一个全局变量记录左子树的最后一个节点。
    // 记录子树链表的最后一个节点,终结点只可能为只含左子树的非叶节点与叶节点
    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;        
    }

编辑于 2015-08-18 23:36:07 回复(111)
morris遍历,空间O(1)
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        TreeNode res = null;
        TreeNode pre = null;
        TreeNode mostRight;
        TreeNode cur = pRootOfTree;
        while (cur != null) {
            if (cur.left != null) {
                mostRight = cur.left;
                while(mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }

                if (mostRight.right == null) {
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                }

                mostRight.right = null;

            }

            if (pre != null) {
                pre.right = cur;
                cur.left = pre;
            } else {
                res = cur;
            }
            pre = cur;
            cur = cur.right;
        }

        return res;
    }
}


发表于 2024-11-30 23:12:19 回复(0)
public class Solution {
    TreeNode pRoot=new TreeNode(0) ,p1=pRoot;

    public TreeNode Convert(TreeNode pRootOfTree) {
        dfs(pRootOfTree);
        if(pRoot.right!=null){
            pRoot.right.left=null;
        }
        return pRoot.right;
    }

    public void dfs(TreeNode root){
        if(root==null){
            return;
        }
        dfs(root.left);
        p1.right=root;
        root.left=p1;
        p1=root;
        dfs(root.right);
    }

发表于 2024-11-10 16:33:10 回复(0)
最短代码
中序遍历访问节点的顺序,即是正向排序的顺序,每次从某个根向右时,根左边的都已完成操作且用不到了,故此时改指针指向刚访问的左节点即可
public class Solution {
    TreeNode beg=null;
    public void dfs(TreeNode p) {
        if(p==null)return;
        dfs(p.left);
        beg.right=p;
        p.left=beg;
        beg=p;
        dfs(p.right);
    }
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null)return null;
        TreeNode head = new TreeNode(-1);
        beg=head;
        dfs(pRootOfTree);
        head.right.left=null;
        return head.right;
    }
}


发表于 2024-11-04 17:09:04 回复(0)
public class Solution {
    TreeNode const_head = new TreeNode(999);
    TreeNode pre = const_head;

    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null) return null;
        tra(pRootOfTree);
        pre = const_head.right;
        pre.left = null;
        return pre;
    }
    public void tra(TreeNode node){
        if(node.left != null){
            tra(node.left);
        }
        pre.right = node;
        node.left = pre;
        pre = node;
        
        if(node.right != null){
            tra(node.right);
        } 
    }
}

发表于 2024-08-26 16:55:01 回复(0)
import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }

}
*/
public class Solution {
    // 封装结构 - 该子树的最大结点和最小结点
    // 这里不涉及到新建TreeNode结点,只用作记录已存在的TreeNode结点
    public class Info {
        // 该子树的最大结点
        TreeNode max;
        // 该子树的最小结点
        TreeNode min;

        public Info(TreeNode max, TreeNode min) {
            this.max = max;
            this.min = min;
        }
    }

    // 主方法
    public TreeNode Convert(TreeNode pRootOfTree) {
        // 根据左程云老师讲的【二叉树递归套路】,先弄明白三件事
        // 1.我要干什么:
        //        令root.left   <->   【左子树】的【最大结点】
        //        令root.right  <->   【右子树】的【最小结点】

        // 2.我需要什么信息:
        //      综上所述,我需要左右子树的【最小结点】和【最大结点】,可以把它们【封装】进一个数据结构中,方便递归函数的return

        // 3.我如何利用两个子树的信息加工出来我的信息:
        //      根据二叉搜索树的性质:
        //      root的【最大结点】 = 【右子树】的【最大结点】 或者 【root】
        //      root的【最小结点】 = 【左子树】的【最小结点】 或者 【root】


        // 调用递归函数生成双向链表,并返回这个双向链表的最左头结点
        Info resultInfo = process(pRootOfTree);

        return resultInfo.min;
    }

    // 递归序遍历二叉树函数 - 返回以root为根结点的子树的最大结点和最小结点
    public Info process(TreeNode root) {
        // 递归出口
        if (root == null) {
            // 当结点为空时,说明这一子树的max和min都是null
            return new Info(null, null);
        }

        // 记录左子树和右子树的最小结点和最大结点
        Info leftInfo = process(root.left);
        Info rightInfo = process(root.right);

        // 拿到左右子树的Info后做两件事:
        // 1.令root.left <-> 【左子树】的【最大结点】;root.right <-> 【右子树】的【最小结点】
        root.left = leftInfo.max;
        if (leftInfo.max != null) {
            leftInfo.max.right = root;
        }
        root.right = rightInfo.min;
        if (rightInfo.min != null) {
            rightInfo.min.left = root;
        }
        // 2.生成自己的Info信息并返回
        TreeNode myMax = (rightInfo.max != null) ? rightInfo.max : root;
        TreeNode myMin = (leftInfo.min != null) ? leftInfo.min : root;

        return new Info(myMax, myMin);
    }
}

发表于 2024-07-29 21:11:10 回复(0)
public class Solution {

    private TreeNode pre = null;

    public TreeNode Convert(TreeNode pRootOfTree) {
        if (null == pRootOfTree) {
            return null;
        }
        inorder(pRootOfTree);
        TreeNode head = pRootOfTree;
        while (head.left != null) {
            head = head.left;
        }
        return head;
    }

    private void  inorder(TreeNode root) {
        if (null == root) {
            return ;
        }
        inorder(root.left);
        if (pre != null) {
            pre.right = root;
            root.left = pre;
        }
        pre = root;
        inorder(root.right);
    }

}

发表于 2023-10-13 17:21:41 回复(0)
import java.util.*;

public class Solution {
    // 中序遍历可以输出二叉搜索树的顺序
    // 只需要将输出变成构建一条新的二叉树(双向链表)即可
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree == null) {
            return null;
        }
        Stack<TreeNode> stack = new Stack<>();
        boolean isFirst = true;
        // head是返回的头节点,temp是用于与新节点链接的指针
        TreeNode head = new TreeNode(0);
        TreeNode temp = head;
        while (pRootOfTree != null || !stack.isEmpty()) {
            while (pRootOfTree != null) {
                stack.push(pRootOfTree);
                pRootOfTree = pRootOfTree.left;
            }
            TreeNode peek = stack.peek();
            pRootOfTree = stack.pop();
            // 原本该是输出,现在变成构建二叉树
            if (isFirst) {
                head = peek;
                temp = peek;
                isFirst = false;
            } else {
                temp.right = peek;
                peek.left = temp;
                temp = temp.right;
            }
            pRootOfTree = pRootOfTree.right;

        }
        return head;
    }
}

发表于 2023-08-15 10:38:52 回复(0)
import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }

}
*/
public class Solution {
    TreeNode res = null;
    TreeNode pre = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        midOrder(pRootOfTree);
        return res;
    }
    public void midOrder(TreeNode root) {
        if(root != null) {
            midOrder(root.left);
            if(res == null) {
                res = root;
                pre = root;
            } else {
                pre.right = root;
                root.left = pre;
                pre = root;
            }
            midOrder(root.right);
        }
    }
}
发表于 2023-08-07 11:02:00 回复(0)
import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }

}
*/
public class Solution {
    TreeNode pre = null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree == null) return null;
        InOrder(pRootOfTree);
        TreeNode p = pRootOfTree;
        while (p.left != null) {
            p = p.left;
        }
        return p;
    }
    public void InOrder(TreeNode Root) {
        if (Root == null) {
            return;
        }
        InOrder(Root.left);
        Root.left = pre;
        if (pre != null) {
            pre.right = Root;
        }
        pre = Root;
        InOrder(Root.right);
    }
}

发表于 2023-07-24 20:24:00 回复(0)
import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }

}
*/
public class Solution {

    public TreeNode prev = null;

    public void convertChild(TreeNode pCur) {
        if(pCur == null) {
            return;
        }
        convertChild(pCur.left);

        pCur.left = prev;
        if(prev != null) {
            prev.right = pCur;
        }
        prev = pCur;
        convertChild(pCur.right);

    }

    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null) {
            return null;
        }
        convertChild(pRootOfTree);
        TreeNode head = pRootOfTree;

        while(head.left != null) {
            head = head.left;
        }
        return head;
    }
}
发表于 2023-07-16 17:13:18 回复(0)
public class Solution {
    TreeNode head=null;//赋初值为null
    TreeNode pre=null;
    //双向链表必然要定义head、pre
    public TreeNode Convert(TreeNode pRootOfTree) {
        build(pRootOfTree);
        return head;
    }
    public void build(TreeNode root){
        //必然是中序遍历
        if(root==null) return ;
        build(root.left);
        if(pre==null){
            pre=root;
            head=root;
        }else{
            pre.right=root;//这里head、pre定义为TreeNode  所以这里是pre.right  head.left
            root.left=pre;//root来回倒
            pre=root;
        }
        build(root.right);

    }

发表于 2023-07-13 22:21:11 回复(0)
import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }
}
*/

public class Solution {
    public TreeNode pre=null;
    public TreeNode head=null;
    public TreeNode Convert(TreeNode root) {
        //如果根节点为空则为空
        if(root==null){
            return null;
        }
        //用来保存创建的双向链表
        ArrayList<TreeNode> tree=new ArrayList<>();
        //中序遍历
        mid(root,tree);
        //清空tree节点每个左右分支
        for(int i=0;i<tree.size();i++){
            TreeNode tmp=tree.get(i);
            tmp.left=null;
            tmp.right=null;
        }
        //开始构建从左到右单向链表
        for(int i=0;i<tree.size()-1;i++){
            TreeNode tmp=tree.get(i);
            tmp.right=tree.get(i+1);
        }
        //开始构建从右到左的单向链表
        for(int i=tree.size()-1;i>0;i--){
            TreeNode tmp=tree.get(i);
            tmp.left=tree.get(i-1);
        }
        //返回第一个tree节点,就是双向链表的头节点
        return tree.get(0);
    }
    public void mid(TreeNode root,ArrayList<TreeNode> tree){
        if(root==null){
            return;
        }
        mid(root.left,tree);
        tree.add(root);
        mid(root.right,tree);
    }
}

发表于 2023-05-09 08:48:46 回复(0)
这叫排好序?

发表于 2023-04-24 16:58:08 回复(0)
递归直接建立链表
    public TreeNode hair = new TreeNode(-1);
    public TreeNode temp = hair;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null){
            return null;
        }
        convertSub(pRootOfTree);
        hair.right.left = null;
        return hair.right;
    }

    public void convertSub(TreeNode pRootOfTree) {
        if(pRootOfTree == null){
            return;
        }
        convertSub(pRootOfTree.left);
        temp.right = pRootOfTree;
        pRootOfTree.left = temp;
        temp = pRootOfTree;
        convertSub(pRootOfTree.right);
    }


发表于 2023-03-23 21:18:53 回复(0)