I am trying to implement an algorithm that requires a post-order traversal. Here is my graph (taken from here, pg. 8):
When I try to do a postorder traversal of this, the order I get is:
[3, 2, 1, 5, 4, 6]
The problem with this order is that the algorithm won't work in this order. This is the code I am using to get it (pseudocode):
function PostOrder(root, out_list) {
root.visited = true
for child in root.Children {
if not child.visited {
PostOrder(child, out_list)
}
}
out_list.append(root)
}
Is the postorder correct?
Yes, the post order traversal of your algorithm is correct. The expected output is indeed as you provided it.
Your confusion may come from the fact that the graph is not a binary tree, and not even a tree. It is a directed graph.
In general postorder means that you first perform a postorder traversal on the node behind the first outgoing edge, then on the node behind its next outgoing edge, ...etc, and only after all outgoing edges have been traversed, the node itself is output.
Since at node 1 you are not at the end yet, and still can go to 2, and from there to 3, you need to follow those edges before outputting anything. And only then backtrack.
For reference, here is your algorithm implemented in python:
def postorder(root, out_list, children, visited):
visited[root] = True
for child in children[root]:
if not visited[child]:
postorder(child, out_list, children, visited)
out_list.append(root)
children = [
[], # dummy for node 0
[2], # 1
[1,3], # 2
[2], # 3
[2,3], # 4
[1], # 5
[5,4] # 6
]
nodes = []
postorder(6, nodes, children, [False] * len(children))
print(nodes) # [3, 2, 1, 5, 4, 6]
I think you got confused with the postorder traversal of binary trees.
Postorder traversal in graph is different.
Post Ordering in Graphs – If we list the vertices in the order in which they are last visited by DFS traversal then the ordering is called PostOrder.
Assuming your root is node is 6, the order mentioned gives the correct answer.
Checkout the following example on how the post order traversal list is generated:
Pass 1:
List:[]
6 -> 5 -> 1 -> 2 -> 3 (Now Node 3 has no adjacent nodes which are unvisited)
List: [3]
Pass 2:
6 -> 5 -> 1 -> 2
Node 2 has has no adjacent nodes which are unvisited.
List: [3, 2]
Pass 3:
6 -> 5 -> 1
Node 1 has has no adjacent nodes which are unvisited.
List: [3, 2, 1]
Pass 4:
6 -> 5
Node 5 has has no adjacent nodes which are unvisited.
List: [3, 2, 1, 5]
Pass 5:
6 -> 4
Node 4 has has no adjacent nodes which are unvisited.
List: [3, 2, 1, 5, 4]
Pass 6:
Node 6 has has no adjacent nodes which are unvisited.
List: [3, 2, 1, 5, 4, 6]
Important Notes:
As we are using DFS, there can be multiple paths possible depending upon the order of the nodes in the adjacency list.
Possible are the correct orders:
[3, 2, 1, 5, 4, 6]
[1, 3, 2, 4, 5, 6]
[3, 1, 2, 4, 5, 6]
[1, 2, 3, 4, 5, 6]
Related
As we know, Dijkstra finds the shortest path from a single source node to any other node in a given graph. I try to modify the original Dijkstra to find the shortest path between a pair of the source node and destination node. It seems easy that only set a termination condition for terminating the program when the Dijkstra finds the destination node.
However, the "termination condition" I set in my Python codes seems to lead a sub-optimal shortest path rather than the optimal shortest path.
The Dijkstra code is as follows,
def dijkstra(adjList, source, sink):
#define variables
n = len(adjList) #intentionally 1 more than the number of vertices, keep the 0th entry free for convenience
visited = [False]*n
parent = [-1] *n
#distance = [float('inf')]*n
distance = [1e7]*n
heapNodes = [None]*n
heap = FibonacciHeap()
for i in range(1, n):
heapNodes[i] = heap.insert(1e7, i)
distance[source] = 0
heap.decrease_key(heapNodes[source], 0)
while heap.total_nodes:
current = heap.extract_min().value
#print("Current node is: ", current)
visited[current] = True
#early exit
if sink and current == sink:
break
for (neighbor, cost) in adjList[current]:
if not visited[neighbor]:
if distance[current] + cost < distance[neighbor]:
distance[neighbor] = distance[current] + cost
heap.decrease_key(heapNodes[neighbor], distance[neighbor])
if neighbor == sink and current != source: # this is a wrong logic , since the neighbor may not be selected as the next hop.
print("find the sink 1")
printSolution(source, sink, distance,parent)
break
if neighbor == sink:
print("find the sink2")
break
return distance
adjList = [
[],
[[2, 7], [3, 9], [6, 14]],
[[1, 7], [4, 15], [3, 10]],
[[1, 9], [2, 10], [4, 11], [6, 2]],
[[2, 15], [3, 11], [5, 6]],
[[4, 6], [6, 9]],
[[5, 9], [1, 14]]
]
dijkstra(adjList,1,4)
The graph of the adjacency list is as shown:
I want to find the path from node 1 to node 4, there are three paths:
path 1: 1 --> 2 --> 4 cost: 22
path 2: 1 --> 2 --> 3 --> 4 cost: 28
path 3: 1 --> 3 --> 4 cost: 20
path 4: 1 --> 3 --> 6 --> 5 --> 4 cost: 26
path 5: 1 --> 6 --> 3 --> 4 cost: 28
path 6: 1 --> 6 --> 5 --> 4 cost: 29
Originally, Dijkstra will select path 3: 1 --> 3 --> 4 since it has the minimum cost.
But, I modify the termination condition, i.e., when finding the adjacency node of the current node is the destination, the program will be ended. And I get the result of a path between node 1 and node 4. The result is path 1: 1 --> 2 --> 4.
I analyze that, this is because I set the wrong termination condition. The program will be terminated when finding the adjacency node of the current node is the destination, that is wrong but I have no idea that setting a proper termination condition when the destination node is found.Could you please provide some ideas?
The only right place for the termination condition is at the start of the outer loop when you just got the current node from the heap.
It is wrong to do that test when you iterate the neighbors, as you don't have the guarantee that this last edge is part of the shortest path. Just imagine some insane high cost for that last step to the neighbor: never could that be on the shortest path, so don't perform the terminating condition there: there still might be another path to the sink that is cheaper.
I also did not see where you actually populated parent in your code.
I would also not put all nodes on the heap from the start, as heaps are faster when they have fewer elements. You can start with a heap with just 1 node.
Another little optimisation is to use parent also for marking nodes as visited, so you don't actually need both parent and visited.
Finally, I don't know the FibonacciHeap library, so I have just taken heapq, which is a very light heap implementation:
from heapq import heappop, heappush
def dijkstra(adjList, source, sink):
n = len(adjList)
parent = [None]*n
heap = [(0, source, 0)] # No need to push all nodes on the heap at the start
# only add the source to the heap
while heap:
distance, current, came_from = heappop(heap)
if parent[current] is not None: # skip if already visited
continue
parent[current] = came_from # this also marks the node as visited
if sink and current == sink: # only correct place to have terminating condition
# build path
path = [current]
while current != source:
current = parent[current]
path.append(current)
path.reverse()
return distance, path
for (neighbor, cost) in adjList[current]:
if parent[neighbor] is None: # not yet visited
heappush(heap, (distance + cost, neighbor, current))
adjList = [
[],
[[2, 7], [3, 9], [6, 14]],
[[1, 7], [4, 15], [3, 10]],
[[1, 9], [2, 10], [4, 11], [6, 2]],
[[2, 15], [3, 11], [5, 6]],
[[4, 6], [6, 9]],
[[5, 9], [1, 14]]
]
dist, path = dijkstra(adjList,1,4)
print("found shortest path {}, which has a distance of {}".format(path, dist))
You actually have the correct condition for exit in your code that is when current==sink. You cannot impose any other exit condition. The algorithm necessarily needs to run until the destination node is visited because only at this point you can fix the value of the shortest path to the destination. Because of this condition, the complexity of finding the single source single destination shortest path is the same as that of the single source all nodes shortest paths. So your early exit condition is correct and you should remove all the neighbor condition checks.
input -> alphabet -> output (index of a number in alphabet) -> new alphabet (the number moved to the begin of the alphabet):
3 -> [1, 2, 3, 4, 5] -> 3 -> [3, 1, 2, 4, 5]
2 -> [3, 1, 2, 4, 5] -> 3 -> [2, 3, 1, 4, 5]
1 -> [2, 3, 1, 4, 5] -> 3 -> [1, 2, 3, 4, 5]
1 -> [1, 2, 3, 4, 5] -> 1 -> [1, 2, 3, 4, 5]
4 -> [1, 2, 3, 4, 5] -> 4 -> [4, 1, 2, 3, 5]
5 -> [4, 1, 2, 3, 5] -> 5 -> [5, 4, 1, 2, 3]
input: (n - number of numbers in alphabet, m - length of text to be encrypted, the text)
5, 6
3 2 1 1 4 5
Answer: 3 2 1 1 4 5 -> 3 3 3 1 4 5
Is there any data structure or algorithm to make this efficiently, faster than O(n*m)?
I'd be appreciated for any ideas. Thanks.
Use an order statistics tree to store the pairs (1,1)...(n,n), ordered by their first elements.
Look up the translation for a character c by selecting the c-th smallest element of the tree and taking its second element.
Then update the tree by removing the node that you looked up and inserting it back into the tree with the first element of the pair set to -t, where t is the position in the message (or some other steadily decreasing counter).
Lookup, removal and insertion can be done in O(ln n) time worst-case if a self-balanced search tree (e.g. a red-black tree) is used as underlying tree structure for the order statistics tree.
Given that the elements for the initial tree are inserted in order, the tree structure can be build in O(n).
So the whole algorithm will be O(n + m ln n) time, worst-case.
You can further improve this for the case that n is larger than m, by storing only one node for any continuous range of nodes in the tree, but counting it for the purpose of rank in the order statistics tree according to the number of nodes there would normally be.
Starting then from only one actually stored node, when the tree is rearranged, you split the range-representing node into three: one node representing the range before the found value, one representing the range after the found value and one representing the actual value. These three nodes are then inserted back, in case of the range nodes only if they are non-empty and with the first pair element equal to the second and in case of the non-range node, with the negative value as described before. If a node with negative first entry is found, it is not split in this.
The result of this is that the tree will contain at most O(m) nodes, so the algorithm has a worst-time complexity of O(m ln min(n,m)).
Maybe a hashmap with letter/index pairs? I believe that element lookup in a hashmap usually O(1) most of the time, unless you have a lot of collisions (which is unlikely).
Suppose I have the a tree given in the nested list representation, how do I traverse it breadth first? For example, if I'm given
[1, [2, [3, [4, [3, 5]]]], [3, [4, 5, 2]]]
The output would be
[1,2,3,3,4,4,5,2,3,5]
Also, given a flattened representation of the depth-first order like [1,2,3,4,3,5,3,4,5,2], how do I find the indices of the breadth-first order?
Thanks in advance for any help.
Here's the code in Python:
queue = [1, [2, [3, [4, [3, 5]]]], [3, [4, 5, 2]]]
while queue:
firstItem = queue.pop(0)
if type(firstItem) is list:
for item in firstItem:
queue.append(item)
else:
print('Traversed %d' % (firstItem))
The output is:
Traversed 1
Traversed 2
Traversed 3
Traversed 3
Traversed 4
Traversed 5
Traversed 2
Traversed 4
Traversed 3
Traversed 5
After studying my output and what you specified the output should be in your question, I think my output is more correct. More specifically, the left most 3 in the input list and [4, 5, 2] at the end of the input list are on the same "level", and thus should be traversed 3, 4, 5, 2, as shown from the 4th line to the 7th line of my output.
As for your second question, I think you should ask a separate question because it really is a completely different question.
When I am trying to print level Order of BST, this question prompted me.
Here is a
Pre-Order Sequence: 4, 1, 2, 3, 5, 6, 7, 8
In_order Sequence : 1, 2, 3, 4, 5, 6, 7, 8
A level order sequence for a BST with above pre_order and In_order is
[4, 2, 6, 1, 3, 5, 7, 8]
However, for the same Pre-order an In-order sequence this level order sequence seems possible. [4, 1, 5, 2, 6, 3, 7, 8]. I don't know how. I am trying to figure this out.
I am unable to construct BST in paper (drawing) that satisfies all the pre_order, In-order and level order sequences.
If you have in-order traversal together with one of pre/post-order, that is enough to reconstruct a binary tree. Moreover, in case of BST (binary search tree), post-order or pre-order alone is sufficient.
In your case, reconstructing a BST from pre-order 4, 1, 2, 3, 5, 6, 7, 8 gives the following BST:
4
/ \
1 5
\ \
2 6
\ \
3 7
\
8
which gives, again unique, level-order traversal [4,1,5,2,6,3,7,8].
See also:
Reconstructing binary trees from tree traversals
Following combination will generate unique binary tree(which can be BST).
Inorder and Preorder.
Inorder and Postorder.
Inorder and Level-order.
So in your case inorder & pre order are given which will generate unique binary tree which is BST in your case so level order will be unique for that tree.
Pre-Order Sequence: 4, 1, 2, 3, 5, 6, 7, 8
In_order Sequence : 1, 2, 3, 4, 5, 6, 7, 8
SO tree will be
level 0- 4
level 1- 1,5
level 2- 2,6
level 3- 3,7
level 4- 8
Level order is
4,1,5,2,6,3,7,8
in sort there will always unique level order traversal
According to this post on Wikipedia, given a tree with distinct elements, either pre-order or post-order paired with in-order is sufficient to describe the tree uniquely. However, pre-order with post-order leaves some ambiguity in the tree structure.
I am looking for a quick example that would demonstrate this claim.
So given the following tree:
The orderings are:
Pre Order: 1, 2, 4, 3, 5, 7, 8, 6
In Order: 4, 2, 1, 7, 5, 8, 3, 6
Post Order: 4, 2, 7, 8, 5, 6, 3, 1
How can I deserialize this tree using its Pre Order and In Order or Post Order and In Order?
Thanks
So the claim is that given a pre-order and in-order traversal of the tree we can uniquely construct the tree.
In your example
Preorder : 1, 2, 4, 3, 5, 7, 8, 6
In Order : 4, 2, 1, 7, 5, 8, 3, 6
Now the root node in the tree will be the first node in the pre-order traversal. Moreover, all the numbers appearing before a value in the inorder traversal will lie in the left subtree rooted at that node. And similarly all values appearing after a value will lie in the right sub-tree. So continuing in this recursive fashion will deserialize the tree. You can read more about it here http://www.geeksforgeeks.org/construct-tree-from-given-inorder-and-preorder-traversal/
Similarly, you can deserialize the tree given its postorder and inorder traversal. Here the last value appearing in the post order traversal will be the root of the tree. You can read about the deserialization from post-order and in-order from here http://www.programcreek.com/2013/01/construct-binary-tree-from-inorder-and-postorder-traversal/