将有序二进制树转换为双循环链接列表

时间:2012-03-05 20:46:46

标签: java binary-tree doubly-linked-list

I have a ordered binary tree:
              4
              |
          |-------|
          2       5
          |
      |-------|
      1       3

叶子指向null。我必须创建一个看起来像

的双重链接列表
1<->2<->3<->4<->5

(显然5应该指向1)

节点类如下:

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

    public Node(int value)
    {
        this.value = value;
        left = null;
        right = null;
    }
}

正如您所见,双重链接列表也是有序(排序)的。

问题:我必须在树中创建链表而不使用任何额外的指针。树的left指针应该是列表的previous指针,树的right指针应该是列表的next指针。

我想到了什么:由于树是一个有序树,因此inorder遍历会给我一个排序列表。但是在进行inorder遍历时,我无法看到,在何处以及如何移动指针以形成双向链表。

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

6 个答案:

答案 0 :(得分:10)

听起来你需要一个方法接受对树的根的Node引用,并返回一个Node引用到循环列表的头部,其中没有新的Node对象已创建。我会以简单的树开始递归地接近这个:

   2
   |
|-----|
1     3

您没有说明树是否已满,因此我们需要允许1和/或3null。以下方法适用于这个简单的树:

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;
}

如果我正确地覆盖了所有链接分配,那么这应该就是您所需要的。

答案 1 :(得分:2)

对列表进行有序遍历,在遇到双列表时将每个列表项添加到双向链表中。完成后,在第一个和最后一个项目之间添加显式链接。

2012年3月6日更新:由于您必须重新使用已有的Node对象,因此在将节点对象放入列表后,您可以遍历列表并重置左侧和Node对象的右指针指向他们的兄弟姐妹。完成后,您可以删除列表并返回第一个节点对象。

答案 2 :(得分:1)

这也应该有效:

NodeLL first = null;
NodeLL last = null;
private void convertToLL(NodeBST root) {
    if (root == null) {
        return;
    }
    NodeLL newNode = new NodeLL(root.data);
    convertToLL(root.left);   
    final NodeLL l = last;
    last = newNode;
    if (l == null)
        first = newNode;
    else {
        l.next = newNode;
        last.prev = l;
    }
    convertToLL(root.right);
}

答案 3 :(得分:1)

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

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;
}

答案 4 :(得分:0)

将以下方法添加到Node

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;
}

编辑通过维护toLinked()返回的列表具有正确格式的不变量,您可以轻松获取由递归调用返回的子列表中的最左侧和最右侧节点子树

答案 5 :(得分:0)

/*  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;
}