I was wondering if it's possible to print a binary tree in breadth first order while using only O(1) space?
The difficult part is that one have to use additional space to memorize the next level to traverse, and that grows with n.
Since we haven't place any limitation on the time part, maybe there are some inefficient (in terms of time) ways that can achieve this?
Any idea?
The space complexity analysis of the BFS traversal Space complexity is equal to the queue size. We process nodes level by level, so max queue size depends on the level with maximum number of nodes or max-width of binary tree. If maximum width of binary tree is w, then space complexity = O(w).
The space complexity for BFS is O(w) where w is the maximum width of the tree. For DFS, which goes along a single 'branch' all the way down and uses a stack implementation, the height of the tree matters. The space complexity for DFS is O(h) where h is the maximum height of the tree.
This is going to depend on some finer-grained definitions, for example if the edges have back-links. Then it's easy, because you can just follow a back link up the tree. Otherwise I can't think off hand of a way to do it without O(lg number of nodes) space, because you need to remember at least the nodes "above".
Update
Oh wait, of course it can be done in O(1) space with a space time trade. Everywhere you would want to do a back link, you save your place and do BFS, tracking the most recent node, until you find yours. Then back up to the most recently visited node and proceed.
Problem is, that's O(1) space but O(n^2) time.
Another update
Let's assume that we've reached node n_i, and we want to reach the parent of that node, which we'll call wlg n_j. We have identified the distinguished root node n_0.
Modify the breath-first search algorithm so that when it follows a directed edge (n_x,n_y), the efferent or "incoming" node is stored. Thus when you follow (n_x,n_y), you save n_x.
When you start the BFS again from n_0, you are guaranteed (assuming it really is a tree) that at SOME point, you will transition the edge (n_j,n_i). At that point you observe you're back at n_i. You've stored n_j and so you know the reverse edge is (n_i,n_j).
Thus, you get that single backtrack with only two extra cells, one for n_0 and one for the "saved" node. This is O(1)
I'm not so sure of O(n^2) -- it's late and it's been a hard day so I don't want to compose a proof. I'm sure it's O((|N|+|E|)^2) where |N| and |E| are the size of the sets of vertices and edges respectively.
An interesting special case is heaps.
From heapq
docs:
Heaps are binary trees for which every parent node has a value less than or equal to any of its children. This implementation uses arrays for which
heap[k] <= heap[2*k+1]
andheap[k] <= heap[2*k+2]
for all k, counting elements from zero. For the sake of comparison, non-existing elements are considered to be infinite. The interesting property of a heap is that its smallest element is always the root,heap[0]
. [explanation by François Pinard]
How a tree represented in memory (indexes of the array):
0
1 2
3 4 5 6
7 8 9 10 11 12 13 14
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
In this case nodes in the array are already stored in a breadth first order.
for value in the_heap:
print(value)
O(1) in space.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With