Postorder, preorder, inorder traversals, Binary Search Tree [closed] - tree-traversal

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
Consider the following tree structure.
A
/ \
B C
/ \ \
D E F
In what order will the nodes be visited using a postorder, a preorder, and an inorder traversal?

I find that it helps to think of postorder, preorder, and inorder as recursive algorithms.
Postorder traversal
Recursively, this is left, right, self. In other words, do the traversal for the left subtree, then do the traversal for the right subtree, and only then visit the current node. The base case would be when the node has no children.
For this example:
Answer: D, E, B, F, C, A
Explanation:
Start at node A. Evaluate left subtree. At node B. Evaluate left subtree. At node D. No children -> visit D.
Go back to B. Evaluate right subtree. At node E. No children -> visit E.
Go back to B. Visit B.
Go back to A. Evaluate right subtree. At node C. No left subtree, so evaluate right subtree. At node F. No children -> visit F.
Go back to C. Visit C.
Go back to A. Visit A
Preorder traversal
Recursively, this is self, left, right.
See if you can get the answer yourself using the logic of the postorder traversal.
Inorder traversal
Recursively, this is left, self, right.
See if you can get the answer yourself using the logic of the postorder traversal.
If you wish to check your work,
the Preorder traversal would be A, B, D, E, C, F and the Inorder traversal would be D, B, E, A, C, F.

Related

Real World Haskell Chapter 3 excercise: Binary Tree with 1 value constructor - follow up

This question is not a duplicate
A question with the same title already exists, but the answer only partially addressed it, in my opinion, and I am interested also in what it left unaswered.
Foreword
Real World Haskell proposes, in Chapter 3, page 58, the following definition for a binary tree datatype,
data Tree a = Node a (Tree a) (Tree a)
| Empty
deriving (Show)
which provides two constructors (for empty and non-empty Trees).
On the other hand, at page 60, an exercise challenges the reader to define the Tree datatype by using a single constructor.
After several attempts, I came up with the same solution as the one linked above:
data Tree a = Node a (Maybe (Tree a)) (Maybe (Tree a)) deriving(Show)
What is unanswered in the linked question
The drawback of this definition is that it does not allow the instantiation of an empty Tree, although it allows the instantiation of a Tree with empty children through the following syntax:
Node 3 Nothing (Just (Node 2 Nothing Nothing))
I think that there's not much a better solution than the above, if not having a "standalone" empty tree is acceptable and the requirement is to use one constructor only.
Having some comment on the above statement would be nice; however, my main question is how can I define Tree with one constructor such that I can instantiate an empty Tree?
Now that I've written the question, I think that one possible answer is the following, of which I am not at all sure:
If a children being empty or not is encoded in whether it is costructed through Nothing or Just (Node ...), pretty much the same holds for the whole tree (or root node), which can be indeed defined itself as a Nothing or Just (Node ...); this is to say that with only one constructor, Nothing is the way to instanciate an empty tree. (In other words, I'm just starting to think that this question is inherently "ill-formed". Notheless I'll post it, as I think I can learn something your comments/answers.)
Does the above make any sense?
A possible answer
A comment in the original question proposes the following solution
data Tree a = Tree (Maybe (a,Tree a,Tree a))
which (my understanding) allows to instantiate an emtpy tree by Node Nothing, or a non-empty tree by Node (Just (value,child1,child2)).
Here's a hint: you can turn any n-ary constructor into a 1-ary one using a n-tuple type.
For instance, your tree type is isomorphic to the following one:
data Tree a = Node (a, Tree a, Tree a)
| Empty
I think you should now be able to turn this type into one which only involves one constructor.
The answer of #chi alongside his comments says all, it could be done with either as:
data Tree a = T (Either () (a, Tree a, Tree a)) deriving Show
And an example of a tree is:
node1 = T $ Right ("data node 1", node2, node3)
node2 = T $ Left ()
node3 = T $ Right ("data node 3", node2, node2)
$> node1
T (Right ("data node 1",T (Left ()),T (Right ("data node 3",T (Left ()),T (Left ())))))
But everybody already also said, that can be replaced with Maybe, because Either () can be seen as Maybe a

Trees with values on the leaves only

A few years ago, during a C# course I learned to write a binary tree that looked more or less like this:
data Tree a = Branch a (Tree a) (Tree a) | Leaf
I saw the benefit of it, it had its values on the branches, which allowed for quick and easy lookup and insertion of values, because it would encounter a value on the root of each branch all the way down until it hit a leaf, that held no value.
Ever since I started learning Haskell, however; I've seen numerous examples of trees that are defined like this:
data Tree a = Branch (Tree a) (Tree a) | Leaf a
That definition puzzles me. I can't see the usefulness of having data on the elements that don't branch, because it would end up leading to a tree that looks like this:
Which to me, seems like a poorly designed alternative to a List. It also makes me question the lookup time of it, since it can't asses which branch to go down to find the value it's looking for; but rather needs to go through every node to find what it's looking for.
So, can anyone shed some light on why the second version (value on leaves) is so much more prevalent in Haskell than the first version?
I think this depends on what you're trying to model and how you're trying to model it.
A tree where the internal nodes store values and the leaves are just leaves is essentially a standard binary tree (tree each leaf as NULL and you basically have an imperative-style binary tree). If the values are stored in sorted order, you now have a binary search tree. There are many specific advantages to storing data this way, most of which transfer directly over from imperative settings.
Trees where the leaves store the data and the internal nodes are just for structure do have their advantages. For example, red/black trees support two powerful operations called split and join that have advantages in some circumstances. split takes as input a key, then destructively modifies the tree to produce two trees, one of which contains all keys less than the specified input key and one containing the remaining keys. join is, in a sense, the opposite: it takes in two trees where one tree's values are all less than the other tree's values, then fuses them together into a single tree. These operations are particularly difficult to implement on most red/black trees, but are much simpler if all the data is stored in the leaves only rather than in the internal nodes. This paper detailing an imperative implementation of red/black trees mentions that some older implementations of red/black trees used this approach for this very reason.
As another potential advantage of storing keys in the leaves, suppose that you want to implement the concatenate operation, which joins two lists together. If you don't have data in the leaves, this is as simple as
concat first second = Branch first second
This works because no data is stored in those nodes. If the data is stored in the leaves, you need to somehow move a key from one of the leaves up to the new concatenation node, which takes more time and is trickier to work with.
Finally, in some cases, you might want to store the data in the leaves because the leaves are fundamentally different from internal nodes. Consider a parse tree, for example, where the leaves store specific terminals from the parse and the internal nodes store all the nonterminals in the production. In this case, there really are two different types of nodes, so it doesn't make sense to store arbitrary data in the internal nodes.
Hope this helps!
You described a tree with data at the leaves as "a poorly designed alternative to a List."
I agree that this could be used as an alternative to a list, but it's not necessarily poorly designed! Consider the data type
data Tree t = Leaf t | Branch (Tree t) (Tree t)
You can define cons and snoc (append to end of list) operations -
cons :: t -> Tree t -> Tree t
cons t (Leaf s) = Branch (Leaf t) (Leaf s)
cons t (Branch l r) = Branch (cons t l) r
snoc :: Tree t -> t -> Tree t
snoc (Leaf s) t = Branch (Leaf s) (Leaf t)
snoc (Branch l r) t = Branch l (snoc r t)
These run (for roughly balanced lists) in O(log n) time where n is the length of the list. This contrasts with the standard linked list, which has O(1) cons and O(n) snoc operations. You can also define a constant-time append (as in templatetypedef's answer)
append :: Tree t -> Tree t -> Tree t
append l r = Branch l r
which is O(1) for two lists of any size, whereas the standard list is O(n) where n is the length of the left argument.
In practice you would want to define slightly smarter versions of these functions which attempt to keep the tree balanced. To do this it is often useful to have some additional information at the branches, which could be done by having multiple kinds of branch (as in a red-black tree which has "red" and "black" nodes) or explicitly include additional data at the branches, as in
data Tree b a = Leaf a | Branch b (Tree b a) (Tree b a)
For example, you can support an O(1) size operation by storing the total number of elements in both subtrees in the nodes. All of your operations on the tree become slightly more complicated since you need to correctly persist the information about subtree sizes -- in effect the work of computing the size of the tree is amortized over all the operations that construct the tree (and cleverly persisted, so that minimal work is done whenever you need to reconstruct a size later).
More is better worse more. I'll explain just a couple basic considerations to show why your intuition fails. The general idea, though, is that different data structures need different things.
Empty leaf nodes can actually be a space (and therefore time) problem in some contexts. If a node is represented by a bit of information and two pointers to its children, you'll end up with two null pointers per node whose children are both leaves. That's two machine words per leaf node, which can add up to quite a bit of space. Some structures avoid this by ensuring that each leaf holds at least one piece of information to justify its existence. In some cases (such as ropes), each leaf may have a fairly large and dense payload.
Making internal nodes bigger (by storing information in them) makes it more expensive to modify the tree. Changing a leaf in a balanced tree typically forces you to allocate replacements for O(log n) internal nodes. If each of those is larger, you've just allocated more space and spent extra time to copy more words. The extra size of the internal nodes also means that you can fit less of the tree structure into the CPU cache.

What about arrows?

Reading through various tutorials about Haskell's various category-themed classes, we find things like Monoid, Functor, Monad and so on - all of which have dozens of instances. But for some reason, when we reach Arrow, there are only two instances: functions and monads. In both cases, using the Arrow instance is less powerful and more difficult than just using the underlying thing directly.
Does anybody have any interesting examples of arrows? I'm sure there must be some, but I've never come across any writing about them...
I like to think of Arrows as composable directed acyclic graphs. For example, an arrow of type:
SomeArrow (a, b, c) (d, e, f)
... you can think of as a graph that has three incoming edges of type a, b, and c and three outgoing edges of type d, e, and f.
Using this interpretation, the category composition operations for Arrows are like horizontal concatenation for graphs, connecting their edges together:
(.) :: SomeArrow b c -> SomeArrow a b -> Some Arrow a c
... where a, b, and c may be themselves tuples. Similarly, id is just the identity graph that forwards all incoming edges to outgoing edges:
id :: SomeArrow a a
The other key operation is (***) which is like vertical concatenation of graphs:
(***) :: Arrow a b -> Arrow c d -> Arrow (a, c) (b, d)
You can think of that as putting two graphs side-by-side, combining their input edges and output edges.
So Arrow commonly arise when working with typed directed acyclic graphs. However, the reason you usually don't see them that often is because most people mentally associate graphs with untyped and high-performance data structures.
HXT, a library which is used for parsing XML, is a very good example for the usage of arrows (have a look how often the word Arrow occurs in the module names of this package!). You shall have a look on the great tutorial: http://adit.io/posts/2012-04-14-working_with_HTML_in_haskell.html
But it is also good to have the arrow concept for functions. For example the following code
((+1) &&& (*2)) 3 -- result is (4,6)
just works, because (->) is an instance of the arrow class (The operator &&& is defined in Control.Arrow).
Thanks to the arrow syntax you have also a great tool to write complex computations in Haskell (it works as well for functions, monads and XML filters in HXT).

What are the names used in computer science for some of the following tree data types?

Sometimes I get myself using different types of trees in Haskell and I don't know what they are called or where to get more information on algorithms using them or class instances for them, or even some pre-existing code or library on hackage.
Examples:
Binary trees where the labels are on the leaves or the branches:
data BinTree1 a = Leaf |
Branch {label :: a, leftChild :: BinTree1 a, rightChild :: BinTree1 a}
data BinTree2 a = Leaf {label :: a} |
Branch {leftChild :: BinTree2 a, rightChild :: BinTree2 a}
Similarly trees with the labels for each children node or a general label for all their children:
data Tree1 a = Branch {label :: a, children :: [Tree1 a]}
data Tree2 a = Branch {labelledChildren :: [(a, Tree2 a)]}
Sometimes I start using Tree2 and somehow on the course of developing it gets refactored into Tree1, which seems simpler to deal with, but I never gave a lot of thought about it. Is there some kind of duality here?
Also, if you can post some other different kinds of trees that you think are useful, please do.
In summary: everything you can tell me about those trees will be useful! :)
Thanks.
EDIT:
Clarification: this is not homework. It's just that I usually end up using those data types and creating instances (Functor, Monad, etc...) and maybe if I new their names I would find libraries with stuff implemented and more theoretical information on them.
Usually when a library on Hackage have Tree in the name, it implements BinTree2 or some version of a non-binary tree with labels only on the leaves, so it seems to me that maybe Tree2 and BinTree2 have some other name or identifier.
Also I feel that there may be some kind of duality or isomorphism, or a way of turning code that uses Tree1 into code that uses Tree2 with some transformation. Is there? May be it's just an impression.
The names I've heard:
BinTree1 is a binary tree
BinTree2 don't know a name but you can use such a tree to represent a prefix-free code like huffman coding for example
Tree1 is a Rose tree
Tree2 is isomoprhic to [Tree1] (a forest of Tree1) or another way to view it is a Tree1 without a label for the root.
A binary tree that only has labels in the leaves (BinTree2) is usually used for hash maps, because the tree structure itself doesn't offer any information other than the binary position of the leaves.
So, if you have 4 values with the following hash codes:
...000001 A
...000010 B
...000011 C
...000010 D
... you might store them in a binary tree (an implicit patricia trie) like so:
+ <- Bit #1 (least significant bit) of hash code
/ \ 0 = left, 1 = right
/ \
[B, D] + <- Bit #2
/ \
/ \
[A] [C]
We see that since the hash codes of B and D "start" with 0, they are stored in the left root child. They have exactly the same hash codes, so no more forks are necessary. The hash codes of A and C both "start" with 1, so another fork is necessary. A has bit 2 as 0, so it goes to the left, and C with 1 goes to the right.
This hash table implementation is kind of bad, because hashes might have to be recomputed when certain elements are inserted, but no matter.
BinTree1 is just an ordinary binary tree, and is used for fast order-based sets. Nothing more to say about it, really.
The only difference between Tree1 and Tree2 is that Tree2 can't have root node labels. This means that if used as a prefix tree, it cannot contain the empty string. It has very limited use, and I haven't seen anything like it in practice. Tree1, however, obviously has an use as a non-binary prefix tree, as I said.

If two binary search tree is given how do i check if one is sub tree of other

If two binary search tree is given how do i check if one is sub tree of other. Is the same algorithm that checks if one tree is the sub tree of other true in this case ?
Algo from here Find whether a tree is a subtree of other
Traverse T1. If the current node is equal to the root node of T2, traverse both of the trees (T2 and the current subtree of T1) at the same time. Compare the current node. If they are always equal, T2 is a subtree of T1
Kindly suggest
Yes, a binary tree is a particular kind of tree, so because this algorithm works for any tree, it will certainly work for a binary tree.

Resources