Create Balanced Binary Search Tree from Sorted linked list

后端 未结 11 1741
囚心锁ツ
囚心锁ツ 2020-12-07 16:48

What\'s the best way to create a balanced binary search tree from a sorted singly linked list?

相关标签:
11条回答
  • 2020-12-07 17:41

    This is a python implementation:

    def sll_to_bbst(sll, start, end):
        """Build a balanced binary search tree from sorted linked list.
    
        This assumes that you have a class BinarySearchTree, with properties
        'l_child' and 'r_child'.
    
        Params:
            sll: sorted linked list, any data structure with 'popleft()' method,
                which removes and returns the leftmost element of the list. The
                easiest thing to do is to use 'collections.deque' for the sorted
                list.
            start: int, start index, on initial call set to 0
            end: int, on initial call should be set to len(sll)
    
        Returns:
            A balanced instance of BinarySearchTree
    
        This is a python implementation of solution found here: 
        http://leetcode.com/2010/11/convert-sorted-list-to-balanced-binary.html
    
        """
    
        if start >= end:
            return None
    
        middle = (start + end) // 2
        l_child = sll_to_bbst(sll, start, middle)
        root = BinarySearchTree(sll.popleft())
        root.l_child = l_child
        root.r_child = sll_to_bbst(sll, middle+1, end)
    
        return root
    
    0 讨论(0)
  • 2020-12-07 17:44

    Similar to @Stuart Golodetz and @Jake Kurzer the important thing is that the list is already sorted.

    In @Stuart's answer, the array he presented is the backing data structure for the BST. The find operation for example would just need to perform index array calculations to traverse the tree. Growing the array and removing elements would be the trickier part, so I'd prefer a vector or other constant time lookup data structure.

    @Jake's answer also uses this fact but unfortunately requires you to traverse the list to find each time to do a get(index) operation. But requires no additional memory usage.

    Unless it was specifically mentioned by the interviewer that they wanted an object structure representation of the tree, I would use @Stuart's answer.

    In a question like this you'd be given extra points for discussing the tradeoffs and all the options that you have.

    0 讨论(0)
  • 2020-12-07 17:47

    A slightly improved implementation from @1337c0d3r in my blog.

    // create a balanced BST using @len elements starting from @head & move @head forward by @len
    TreeNode *sortedListToBSTHelper(ListNode *&head, int len) {
        if (0 == len)   return NULL;
    
        auto left = sortedListToBSTHelper(head, len / 2);
        auto root = new TreeNode(head->val);
        root->left = left;
        head = head->next;
        root->right = sortedListToBSTHelper(head, (len - 1) / 2);
        return root;
    }
    
    TreeNode *sortedListToBST(ListNode *head) {
        int n = length(head);
        return sortedListToBSTHelper(head, n);
    }
    
    0 讨论(0)
  • 2020-12-07 17:50

    Trick question!

    The best way is to use the STL, and advantage yourself of the fact that the sorted associative container ADT, of which set is an implementation, demands insertion of sorted ranges have amortized linear time. Any passable set of core data structures for any language should offer a similar guarantee. For a real answer, see the quite clever solutions others have provided.


    What's that? I should offer something useful?

    Hum...

    How about this?

    The smallest possible meaningful tree in a balanced binary tree is 3 nodes. A parent, and two children. The very first instance of such a tree is the first three elements. Child-parent-Child. Let's now imagine this as a single node. Okay, well, we no longer have a tree. But we know that the shape we want is Child-parent-Child.
    Done for a moment with our imaginings, we want to keep a pointer to the parent in that initial triumvirate. But it's singly linked!
    We'll want to have four pointers, which I'll call A, B, C, and D. So, we move A to 1, set B equal to A and advance it one. Set C equal to B, and advance it two. The node under B already points to its right-child-to-be. We build our initial tree. We leave B at the parent of Tree one. C is sitting at the node that will have our two minimal trees as children. Set A equal to C, and advance it one. Set D equal to A, and advance it one. We can now build our next minimal tree. D points to the root of that tree, B points to the root of the other, and C points to the... the new root from which we will hang our two minimal trees.

    How about some pictures?

    [A][B][-][C]  
    

    With our image of a minimal tree as a node...

    [B = Tree][C][A][D][-]
    

    And then

    [Tree A][C][Tree B]
    

    Except we have a problem. The node two after D is our next root.

    [B = Tree A][C][A][D][-][Roooooot?!]  
    

    It would be a lot easier on us if we could simply maintain a pointer to it instead of to it and C. Turns out, since we know it will point to C, we can go ahead and start constructing the node in the binary tree that will hold it, and as part of this we can enter C into it as a left-node. How can we do this elegantly?

    Set the pointer of the Node under C to the node Under B.
    It's cheating in every sense of the word, but by using this trick, we free up B.
    Alternatively, you can be sane, and actually start building out the node structure. After all, you really can't reuse the nodes from the SLL, they're probably POD structs.

    So now...

    [TreeA]<-[C][A][D][-][B]  
    [TreeA]<-[C]->[TreeB][B] 
    

    And... Wait a sec. We can use this same trick to free up C, if we just let ourselves think of it as a single node instead of a tree. Because after all, it really is just a single node.

    [TreeC]<-[B][A][D][-][C]  
    

    We can further generalize our tricks.

    [TreeC]<-[B][TreeD]<-[C][-]<-[D][-][A]    
    [TreeC]<-[B][TreeD]<-[C]->[TreeE][A]  
    [TreeC]<-[B]->[TreeF][A]  
    [TreeG]<-[A][B][C][-][D]
    [TreeG]<-[A][-]<-[C][-][D]  
    [TreeG]<-[A][TreeH]<-[D][B][C][-]  
    [TreeG]<-[A][TreeH]<-[D][-]<-[C][-][B]  
    [TreeG]<-[A][TreeJ]<-[B][-]<-[C][-][D]  
    [TreeG]<-[A][TreeJ]<-[B][TreeK]<-[D][-]<-[C][-]      
    [TreeG]<-[A][TreeJ]<-[B][TreeK]<-[D][-]<-[C][-]  
    

    We are missing a critical step!

    [TreeG]<-[A]->([TreeJ]<-[B]->([TreeK]<-[D][-]<-[C][-]))  
    

    Becomes :

    [TreeG]<-[A]->[TreeL->([TreeK]<-[D][-]<-[C][-])][B]    
    [TreeG]<-[A]->[TreeL->([TreeK]<-[D]->[TreeM])][B]  
    [TreeG]<-[A]->[TreeL->[TreeN]][B]  
    [TreeG]<-[A]->[TreeO][B]  
    [TreeP]<-[B]  
    

    Obviously, the algorithm can be cleaned up considerably, but I thought it would be interesting to demonstrate how one can optimize as you go by iteratively designing your algorithm. I think this kind of process is what a good employer should be looking for more than anything.

    The trick, basically, is that each time we reach the next midpoint, which we know is a parent-to-be, we know that its left subtree is already finished. The other trick is that we are done with a node once it has two children and something pointing to it, even if all of the sub-trees aren't finished. Using this, we can get what I am pretty sure is a linear time solution, as each element is touched only 4 times at most. The problem is that this relies on being given a list that will form a truly balanced binary search tree. There are, in other words, some hidden constraints that may make this solution either much harder to apply, or impossible. For example, if you have an odd number of elements, or if there are a lot of non-unique values, this starts to produce a fairly silly tree.

    Considerations:

    • Render the element unique.
    • Insert a dummy element at the end if the number of nodes is odd.
    • Sing longingly for a more naive implementation.
    • Use a deque to keep the roots of completed subtrees and the midpoints in, instead of mucking around with my second trick.
    0 讨论(0)
  • 2020-12-07 17:51

    If you know how many nodes are in the linked list, you can do it like this:

    // Gives path to subtree being built.  If branch[N] is false, branch
    // less from the node at depth N, if true branch greater.
    bool branch[max depth];
    
    // If rem[N] is true, then for the current subtree at depth N, it's
    // greater subtree has one more node than it's less subtree.
    bool rem[max depth];
    
    // Depth of root node of current subtree.
    unsigned depth = 0;
    
    // Number of nodes in current subtree.
    unsigned num_sub = Number of nodes in linked list;
    
    // The algorithm relies on a stack of nodes whose less subtree has
    // been built, but whose right subtree has not yet been built.  The
    // stack is implemented as linked list.  The nodes are linked
    // together by having the "greater" handle of a node set to the
    // next node in the list.  "less_parent" is the handle of the first
    // node in the list.
    Node *less_parent = nullptr;
    
    // h is root of current subtree, child is one of its children.
    Node *h, *child;
    
    Node *p = head of the sorted linked list of nodes;
    
    LOOP // loop unconditionally
    
        LOOP WHILE (num_sub > 2)
            // Subtract one for root of subtree.
            num_sub = num_sub - 1;
    
            rem[depth] = !!(num_sub & 1); // true if num_sub is an odd number
            branch[depth] = false;
            depth = depth + 1;
            num_sub = num_sub / 2;
        END LOOP
    
        IF (num_sub == 2)
            // Build a subtree with two nodes, slanting to greater.
            // I arbitrarily chose to always have the extra node in the
            // greater subtree when there is an odd number of nodes to
            // split between the two subtrees.
    
            h = p;
            p = the node after p in the linked list;
            child = p;
            p = the node after p in the linked list;
            make h and p into a two-element AVL tree;
        ELSE  // num_sub == 1
    
            // Build a subtree with one node.
    
            h = p;
            p = the next node in the linked list;
            make h into a leaf node;
        END IF
    
        LOOP WHILE (depth > 0)
            depth = depth - 1;
            IF (not branch[depth])
                // We've completed a less subtree, exit while loop.
                EXIT LOOP;
            END IF
    
            // We've completed a greater subtree, so attach it to
            // its parent (that is less than it).  We pop the parent
            // off the stack of less parents.
            child = h;
            h = less_parent;
            less_parent = h->greater_child;
            h->greater_child = child;
            num_sub = 2 * (num_sub - rem[depth]) + rem[depth] + 1;
            IF (num_sub & (num_sub - 1))
              // num_sub is not a power of 2
              h->balance_factor = 0;
            ELSE
              // num_sub is a power of 2
              h->balance_factor = 1;
            END IF
        END LOOP
    
        IF (num_sub == number of node in original linked list)
            // We've completed the full tree, exit outer unconditional loop
            EXIT LOOP;
        END IF
    
        // The subtree we've completed is the less subtree of the
        // next node in the sequence.
    
        child = h;
        h = p;
        p = the next node in the linked list;
        h->less_child = child;
    
        // Put h onto the stack of less parents.
        h->greater_child = less_parent;
        less_parent = h;
    
        // Proceed to creating greater than subtree of h.
        branch[depth] = true;
        num_sub = num_sub + rem[depth];
        depth = depth + 1;
    
    END LOOP
    
    // h now points to the root of the completed AVL tree.
    

    For an encoding of this in C++, see the build member function (currently at line 361) in https://github.com/wkaras/C-plus-plus-intrusive-container-templates/blob/master/avl_tree.h . It's actually more general, a template using any forward iterator rather than specifically a linked list.

    0 讨论(0)
提交回复
热议问题