
The Great Tree-List Recursion Problem (2000) - happy-go-lucky
http://cslibrary.stanford.edu/109/TreeListRecursion.html
======
cbarrick
Isn't this just in-order traversal...

    
    
        class Linked:
            def __init__(self, data, left=None, right=None):
                self.data = data
                self.left = left
                self.right = right
        
            def tree_to_list(self):
                if self.left is not None:
                    first, left = self.left.tree_to_list()
                    left.right = self
                    self.left = left
                else:
                    first = self
        
                if self.right is not None:
                    right, last = self.right.tree_to_list()
                    right.left = self
                    self.right = right
                else:
                    last = self
        
                first.left = last
                last.right = first
        
                return first, last

------
atilimcetin
This is one of the problems in 'Elements of Programming Interviews' book.

-

Convert a sorted doubly linked list into a BST

C++:
[http://elementsofprogramminginterviews.com/solutions/cpp/sor...](http://elementsofprogramminginterviews.com/solutions/cpp/sorted_list_to_bst.cc)

Java:
[http://elementsofprogramminginterviews.com/solutions/java/So...](http://elementsofprogramminginterviews.com/solutions/java/SortedListToBST.java)

Python:
[http://elementsofprogramminginterviews.com/solutions/python/...](http://elementsofprogramminginterviews.com/solutions/python/sorted_list_to_bst.py)

-

Convert a BST to a sorted doubly linked list

C++:
[http://elementsofprogramminginterviews.com/solutions/cpp/bst...](http://elementsofprogramminginterviews.com/solutions/cpp/bst_to_sorted_doubly_list.cc)

Java:
[http://elementsofprogramminginterviews.com/solutions/java/BS...](http://elementsofprogramminginterviews.com/solutions/java/BSTToSortedDoublyList.java)

Python:
[http://elementsofprogramminginterviews.com/solutions/python/...](http://elementsofprogramminginterviews.com/solutions/python/bst_to_sorted_doubly_list.py)

------
pklausler
The reversed problem is more interesting: given a doubly-linked sorted list
(and its length), convert it to a balanced binary search tree.

~~~
cbarrick
That's the DSW algorithm.

[https://en.wikipedia.org/wiki/Day-Stout-
Warren_algorithm](https://en.wikipedia.org/wiki/Day-Stout-Warren_algorithm)

~~~
nine_k
Pretty trivial, since you can easily find the middle of a doubly-linked list
_and step back_. Then you simply do "find middle, use it as root, recursively
build two subtrees".

With a singly-linked list, it's much harder; I don't know (without googling)
if a more efficient than "build and rebalance as you go" algorithm exists.

~~~
tzs
You could convert a singly-linked list to a doubly-linked list in O(n), and
then apply your favorite doubly-linked solution.

You could even do this in place without increasing the size of each node at
the cost of doubling the size of variables that point to nodes and slightly
lowing down stepping through the list. You do this by storing the next and
previous pointers for each node in the same location, xor'ed together.

That doubles the size of variables that point to nodes and slows down stepping
because you have to turn them into structs that store both a pointer to the
node and a pointer to an adjacent node and update both of these pointers as
you move through the list.

~~~
jandrese
If you're willing to burn O(n) memory on it you could just create an array of
pointers to the nodes in the list and then use that to build the balanced
tree. It's probably faster than iterating over the sublist every time to find
the center.

This assumes of course that the list is sorted. If not, you should probably
sort the list first to avoid driving yourself insane.

If memory is too tight to make temporary storage and the list is too big to
sort and you want a perfectly balanced tree then you've got an interesting
problem on your hands.

~~~
pklausler
You can sort a linked list in O(nlog(n)) time with O(log(n)) space, and then
convert it to a balanced binary tree in O(nlog(n)) time and O(log(n)) space.

EDIT: demonstration:

    
    
      sortList [] = []
      sortList [x] = [x]
      sortList xs = merge (sortList as) (sortList bs)
        where
          (as, bs) = split xs
    
      split [] = ([], [])
      split [x] = ([x], [])
      split (x:y:rest) = let (xs, ys) = split rest in (x:xs, y:ys)
    
      merge xs [] = xs
      merge [] ys = ys
      merge (x:xs') ys@(y:_) | x <= y = x : merge xs' ys
      merge xs (y:ys) = y : merge xs ys
    
      data Tree a = Empty | Node a (Tree a) (Tree a) deriving(Show)
    
      balancedTree xs = mktree (length xs) xs
        where
          mktree 0 [] = Empty
          mktree n xs = Node x (mktree n' less) (mktree (n - 1 - n') more)
            where
              n' = (n - 1) `div` 2
              (less, (x:more)) = splitAt n' xs
    
      listToBalancedTree :: Ord a => [a] -> Tree a
      listToBalancedTree = balancedTree . sortList

------
kazinator
Here is a C library I wrote in 1999 which can convert, in-place, a container
between Red-Black, AVL and linked list:

[http://www.kylheku.com/~kaz/austin.html](http://www.kylheku.com/~kaz/austin.html)

------
zmonx
The declarative programming language Prolog is very suitable for reasoning
about trees, lists and recursive structures in general.

For example, here is a Prolog definition of the _tree_ from the article:

    
    
        tree(node(4,
                  node(2,
                       node(1,nil,nil),
                       node(3,nil,nil)),
                  node(5,nil,nil))).
    

I am using terms of the form nil/0 and node/3 to represent the tree.

Let us start by converting the tree to a _list_. As requested, we shall
consider the _in-order_ traversal of the tree elements, described as a list.
For such situations, Prolog DCG notation is very convenient, since it easily
allows us to describe a _list_ in relation to other terms (in this case, a
tree):

    
    
        tree_to_list(nil) --> [].
        tree_to_list(node(E,Left,Right)) -->
                tree_to_list(Left),
                [E],
                tree_to_list(Right).
    

Here is a sample query and its result:

    
    
        ?- tree(T), phrase(tree_to_list(T), Ls).
        T = node(4, ...),
        Ls = [1, 2, 3, 4, 5].
    

OK, so we now have the in-order traversal. Of course, lists are already
implicitly "linked" in Prolog in one direction: Given a list, we can readily
reason about the first element and the remaining elements. The task requires
us to do more, i.e., reason about the reverse direction as well.

One way to express this is to use _rational trees_ , also known as _cyclic
terms_ in Prolog. For example, here is a case of cyclic term:

    
    
        ?- append([a,b,c], Ls, Ls).
        Ls = [a, b, c|Ls].
    

Here, Ls represents the circular list [a,b,c,a,b,c,a,...].

We can use this feature to our advantage as follows:

    
    
        list_with_backlinks([], []).
        list_with_backlinks([L|Ls0], [L-Prev|Ls]) :-
                append([L|Ls], Cs, Cs),
                foldl(with_backlink, Ls0, Ls, Cs, Prev).
    
        with_backlink(E, E-Cs0, Cs0, Cs) :- Cs0 = [_|Cs].
    

Sample query and answer:

    
    
        ?- list_with_backlinks([a,b,c], Cs).
        Cs = [a-[_S2|_S1], b-_S1, _S2], % where
            _S1 = [a, b-_S1, _S2|_S1],
            _S2 = c-[b-_S1, _S2|_S1] .
    

Thus, as desired, we can generate a doubly-linked list from the original tree:

    
    
        ?- tree(T), phrase(tree_to_list(T), Ls0),
           list_with_backlinks(Ls0, Ls).
        T = node(4, ...),
        Ls0 = [1, 2, 3, 4, 5],
        Ls = [1-[_S4|_S1], 2-_S1, _S2, _S3, _S4], % where
            _S1 = [1, 2-_S1, _S2, _S3, _S4|_S1],
            _S2 = 3-[2-_S1, _S2, _S3, _S4|_S1],
            _S3 = 4-[_S2, _S3, _S4|_S1],
            _S4 = 5-[_S3, _S4|_S1] .
    

Of course, this does not completely solve the task, which requires in-place
modifications of the original tree. Pure Prolog lacks destructive
modifications, so we cannot fully translate the code to Prolog. On the plus
side, _no pointers at all_ were necessary, and the recursions are pretty
straight-forward.

------
llimllib
Neat!

Also the C answer is missing a brace at the end of treeToList and that makes
me a little crazy

------
amelius
Not really a useful problem, considering that it will be impossible to solve
if the tree would be properly balanced.

~~~
mbrumlow
I don't agree. The tree being balanced of not has not bearing on the
solvability of the problem.

Even a naive approach would work balanced or unbalanced and that would be
simply to pop the lowest value in the tree and construct your list until you
run out of nodes.

The trick is to do it in O(n) time and use recursion.

------
brianberns
In a time where we're finally realizing the benefits of functional programming
(i.e. no side effects), a problem like this really seems like a callback to a
more barbaric era. Like writing a state machine using gotos.

EDIT: Never expected this to devolve into an argument about the merits of
gotos! I thought we were all past that, but apparently not.

~~~
nialo
Asking honestly because I would like to know: What is wrong with writing a
state machine using gotos? Assuming that one is solving a problem that
definitely needs a state machine for some reason.

~~~
brianberns
Gotos are generally considered a bad idea in high-level programming.

[http://www.u.arizona.edu/~rubinson/copyright_violations/Go_T...](http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html)

~~~
CoolGuySteve
This is lazy thinking, the gotos described in that essay can jump anywhere in
the program while modern gotos are limited to the function scope and have
various warnings about initialization order and whatnot.

All the arguments it makes no longer apply but people still quote the snappy
headline.

That essay does make an excellent argument against programming with many tiny
functions though, as those do cause the execution cursor to jump all over the
source code document.

