Java 将有序二叉树转换为双循环链接列表

Java 将有序二叉树转换为双循环链接列表,java,binary-tree,doubly-linked-list,Java,Binary Tree,Doubly Linked List,叶子指向空。我必须创建一个双链接列表,它应该像 I have a ordered binary tree: 4 | |-------| 2 5 | |-------| 1 3 正如您所看到的,双链接列表也是有序的 问题:我必须在不使用任何额外指针的情况下从树中创建链表。树的左指针应该是列表的上一个指针,树的右指针应该是列表的下一个指

叶子指向空。我必须创建一个双链接列表,它应该像

I have a ordered binary tree:
              4
              |
          |-------|
          2       5
          |
      |-------|
      1       3
正如您所看到的,双链接列表也是有序的

问题:我必须在不使用任何额外指针的情况下从树中创建链表。树的
指针应该是列表的
上一个
指针,树的
指针应该是列表的
下一个
指针

我的想法:因为树是一个有序的树,所以按顺序遍历会给我一个排序的列表。但在进行顺序遍历时,我无法看到在何处以及如何移动指针以形成双链接列表


p.S我检查了这个问题的一些变体,但没有一个给我任何线索。

按顺序遍历列表,将遇到的每个列表项添加到双链接列表中。完成后,在第一个和最后一个项目之间添加显式链接


2012年3月6日更新:由于必须重用已有的节点对象,因此在将节点对象放入列表后,可以迭代列表并重置节点对象的左指针和右指针以指向其同级。一旦完成,您就可以摆脱列表,只需返回第一个节点对象。

听起来您需要一个方法,该方法接受对树根的
节点
引用,并返回对循环列表头的
节点
引用,其中不创建新的
节点
对象。我将从简单的树开始递归地处理这个问题:

class Node {
    Node left;
    Node right;
    int value;

    public Node(int value)
    {
        this.value = value;
        left = null;
        right = null;
    }
}
   2
   |
|-----|
1     3
您没有说明树是否保证已满,因此我们需要允许
1
和/或
3
null
。以下方法应适用于此简单树:

class Node {
    Node left;
    Node right;
    int value;

    public Node(int value)
    {
        this.value = value;
        left = null;
        right = null;
    }
}
   2
   |
|-----|
1     3
一旦清楚了这是如何工作的,就不难将其推广到适用于任何树的递归方法。这是一种非常相似的方法:

Node simpleTreeToList(Node root) {
    if (root == null) {
        return null;
    }
    Node left = root.left;
    Node right = root.right;
    Node head;
    if (left == null) {
        head = root;
    } else {
        head = left;
        left.right = root;
        // root.left is already correct
    }
    if (right == null) {
        head.left = root;
        root.right = head;
    } else {
        head.left = right;
        right.right = head;
        right.left = root;
    }
    return head;
}

如果我正确地完成了所有链接分配,这应该是您所需要的全部内容。

将以下方法添加到您的
节点中

Node treeToList(Node root) {
    if (root == null) {
        return null;
    }
    Node leftTree = treeToList(root.left);
    Node rightTree = treeToList(root.right);
    Node head;
    if (leftTree == null) {
        head = root;
    } else {
        head = leftTree;
        leftTree.left.right = root;
        root.left = leftTree.left;
    }
    if (rightTree == null) {
        head.left = root;
        root.right = head;
    } else {
        head.left = rightTree.left;
        rightTree.left.right = head;
        rightTree.left = root;
        root.right = rightTree;
    }
    return head;
}
编辑通过保持由
toLinked()
返回的列表具有正确形式的不变量,您可以轻松获得子树递归调用返回的子列表中最左边和最右边的节点

,这也应该起作用:

public Node toLinked() {
    Node leftmost = this, rightmost = this;
    if (right != null) {
        right = right.toLinked();
        rightmost = right.left;
        right.left = this;
    }
    if (left != null) {
        leftmost = left.toLinked();
        left = leftmost.left;
        left.right = this;
    }
    leftmost.left = rightmost;
    rightmost.right = leftmost;
    return leftmost;
}

让您的递归返回形成的列表的左端和右端。然后将当前节点链接到左侧列表的最后一个和右侧列表的第一个。基本情况是,当没有左或右元素时,这两个元素都是它自己的节点。完成所有操作后,可以链接第一个和最后一个最终结果。下面是java代码

/*  input: root of BST. Output: first node of a doubly linked sorted circular list. **Constraints**: do it in-place.     */

public static Node transform(Node root){

        if(root == null){
            return null;
        }

        if(root.isLeaf()){

            root.setRight(root);
            root.setLeft(root);
            return root;

        }

        Node firstLeft = transform(root.getLeft());
        Node firstRight = transform(root.getRight());
        Node lastLeft = firstLeft == null ? null : firstLeft.getLeft();
        Node lastRight=  firstRight == null ? null : firstRight.getLeft();

        if(firstLeft != null){

           lastLeft.setRight(root);
           root.setLeft(lastLeft);

           if(lastRight == null){

               firstLeft.setLeft(root);

           }
           else{

               firstLeft.setLeft(lastRight);
               root.setRight(firstRight);
           }
        }

        if(firstRight != null){

           root.setRight(firstRight);
           firstRight.setLeft(root);       

           if(lastLeft == null){

               root.setLeft(lastRight);
               lastRight.setLeft(root);
               firstLeft = root;

           }
           else{

               root.setLeft(lastLeft);
               lastRight.setRight(firstLeft);
           }
        }

        return firstLeft;
}

为什么你不能使用额外的指针?听起来不太现实。如果是练习,你应该把它标记为家庭作业。这不是家庭作业。我在为我的采访而阅读时发现了这个问题。这正是问题所在。当你说“不使用任何额外指针”时,它实际上是指“在适当的位置,不创建任何新的
节点
对象”?是的,这正是我想说的。我认为挑战是在适当的位置使用现有的
节点
对象,你能解释一下为什么它的
//case rightree=无效的head.left=rightree.left.right
?它不应该是头。左=右树。右。右
?伟大的帖子,顺便说一句。@KodeSeeker-事实上,这是一个bug(现已修复),应该是
head.left=rightree.left
(不是
rightree.left.right
,它只是
rightree
)。如果
rightree
不是
null
,则它是右树中节点的循环列表(的头部)。
head
的前一个节点需要是
rightree
列表的最后一个节点;该节点为
rightree.left
。(我知道我需要最后的警告!)在这行“rightree.left=root;”之后,我想我们需要调用“root.right=rightree;”。正确吗?回答得很好@TedHopp@TedHopp根据您的评论,我有一个问题,您的评论说“头的前一个节点需要是rightTree列表的最后一个节点;该节点是rightTree.left”。使用此逻辑,rightTree的最后一个节点不是rightTree.right吗?假设我们按顺序遍历。
static void convertToSortedList(TreeNode T){
    TreeNode[] r = convertToSortedListHelper(T);
    r[1].next = r[0];
    r[0].prev= r[1];

}
static TreeNode[] convertToSortedListHelper(TreeNode T){        
    TreeNode[] ret = new TreeNode[2];
    if (T == null) return ret;
    if (T.left == null && T.right == null){ 
        ret[0] = T;
        ret[1] = T;
        return ret;
    }       
    TreeNode[] left = TreeNode.convertToSortedListHelper(T.left);
    TreeNode[] right = TreeNode.convertToSortedListHelper(T.right);

    if (left[1] != null) left[1].next = T;
    T.prev = left[1];

    T.next = right[0];
    if (right[0] != null) right[0].prev = T;

    ret[0] = left[0]==null? T:left[0];
    ret[1] = right[1]==null? T:right[1];

    return ret;
}