Given a tree, find the common subtrees and replace the common subtrees and compact the tree.
e.g.
1
/ \
2 3
/ | /\
4 5 4 5
should be converted to
1
/ \
2 3
/ | /\
4 5 | |
^ ^ | |
|__|___| |
|_____|
this was asked in my interview. The approach i shared was not optimal O(n^2), i would be grateful if someone could help in solutioning or redirect me to a similar problem. I couldn't find any. Thenks!
edit- more complex eg:
1
/ \
2 3
/ | /\
4 5 2 7
/\
4 5
whole subtree rooted at 2 should be replaced.
1
/ \
2 <--3
/ | \
4 5 7
You can do this in a single DFS traversal using a hash map from (value, left_pointer, right_pointer) -> node to collapse repeated occurrences of the subtree.
As you leave each node in your DFS, you just look it up in the map. If a matching node already exists, then replace it with the pre-existing one. Otherwise, add it to the map.
This takes O(n) time, because you are comparing the actual pointers to the left + right subtrees, instead of traversing the trees to compare them. The pointer comparison gives the same result, because the left and right subtrees have already been canonicalized.
Firstly, we need to store the node values that appear in a hash table. If the tree already exists, we can iterate the tree and if a node value is already in the set of nodes and delete the branches of that node. Otherwise, store the values in a hash map and each time, when a new node is made, check if the value appears in the map.
Related
Given a k-ary tree, i want to convert it into a min-heap with minimum number of changes. Change is defined as relabelling a node.
one solution i have found is that, i can try a dp solution of changing a nodes value or not changing. But its going to be exponential in time complexity ?
Any ideas, (preferable with optimality proofs).
Example : Say the tree is, 1-3, 3-2, 1-4, 4-5. where 1 is root. Then i can relabel node 3 to 1 or 2, that is in 1 change it becomes a min-heap.
If all you want to do is make sure that the tree satisfies the heap property (the key stored in each node is less than or equal to the keys stored in the node's children), then you should be able to use something like the build-heap algorithm, which operates in O(n).
Consider this tree:
8
-------------
| | |
15 6 19
/ \ | / | \
7 3 5 12 9 22
Now, working from the bottom up, you push each node down the tree as far as it can go. That is, if the node is larger than any of its children, you replace it with the smallest of its children, and you do so until you reach the leaf level, if necessary.
For example, you look at the node valued 15. It's larger than its smallest child, so you swap it, making the subtree:
3
/ \
7 15
Also, 6 swaps places with 5, and 19 swaps places with 9, giving you this tree:
8
-------------
| | |
3 5 9
/ \ | / | \
7 15 6 12 19 22
Note that at the next to leaf level, each node is smaller than its smallest child.
Now, the root. Since the rule is to swap the node with its smallest child, you swap 8 with 3, giving:
3
-------------
| | |
8 5 9
/ \ | / | \
7 15 6 12 19 22
But you're not done because 8 is greater than 7. You swap 8 with 7, and you get this tree, which meets your conditions:
3
-------------
| | |
7 5 9
/ \ | / | \
8 15 6 12 19 22
If the tree is balanced, the entire procedure has complexity O(n). If the tree is severely unbalanced, the complexity is O(n^2). There is a way to guarantee O(n), regardless of the tree's initial order, but it requires changing the shape of the tree.
I won't claim that the algorithm guarantees the "minimal number of changes" for any given tree. I can prove, however, that with a balanced tree the algorithm is O(n). See https://stackoverflow.com/a/9755805/56778, which explains it for binary heap. The explanation also applies to d-ary heap.
The idea I have heard about is finding the Lowest Common ancestor (LCA) of these 2 nodes using the binary lifting method. To know more about it:
https://www.topcoder.com/community/data-science/data-science-tutorials/range-minimum-query-and-lowest-common-ancestor/#Lowest%20Common%20Ancestor%20(LCA)
But I don't know where in that algorithm I can store the weight information. Any ideas??
Construct a tree for LCA as follows. In the weighted input tree, find the heaviest edge, delete it, and construct two (output) trees recursively, one for each remaining component of the input. Make these output trees the children of a newly created root. (The base case is to turn a single vertex into a single vertex.)
Say we have an unrooted weighted tree:
1 5 4
A-----B-----C-----D
| |
|2 |3
| |
E F
The rooted tree that we prepare for LCA is:
5
/ \
/ \
/ \
2 4
/ \ / \
1 E D 3
/ \ / \
A B C F
If I delete node x and then node y or delete y and the x, after this deleted I will stay with the same binary search tree?
I tried a few examples and I think that's true.
But how can i prove this?
It's false. Consider the tree
4
/ \
1 5
\
2
\
3 ,
from which 4 and 5 are deleted in some order. If the order is 5 then 4, the result is
1
\
2
\
3 .
If the order is 4 then 5, the result could be
3
/
1
\
2 ,
assuming that, when we would delete a node with two children, we instead replace its value by that of its in-order predecessor and delete the predecessor. (I'm assuming also the standard deletion procedure for zero- and one-child nodes.)
Although I found this example by hand, I often turn to computer assistance.
The question may look very simple, and probably the answer is too, but I always get confused in the tree questions.
Ok so I want to make a tree something like:
3 level 0
/ \
4 5 level 1 ..
/ \ / \
6 7 8
/ \ / \ / \
9 10 11 12
What are such trees called? Sorry, I'm a beginner..
Function can pass an array[] of ints, or function can take input till N = 3 (denoting level 3 with 10 nodes). Also can you give solution in C/C++/Java.
Given your requirements are only for traversal, I would simply implement this using an array a, containing each level as a contiguous sub-array. Level i then occurs in entries L(i-1) up to but not including L(i), where L(n) = n*(n+1)/2. In particular, the jth value on the ith level is in a[L(i-1)+j].
As long as you always keep track of i and j, you can now easily navigate through your pyramid.
I am reading the Pairing heap.
It is quite simple, the only tricky part is the delete_min operation.
The only non-trivial fundamental operation is the deletion of the
minimum element from the heap. The standard strategy first merges the
subheaps in pairs (this is the step that gave this datastructure its
name) from left to right and then merges the resulting list of heaps
from right to left:
I don't think I need copy/paste the code here, as it is in the wiki link.
My questions are
why they do this two pass merging?
Why they first merge pairs? not directly merge them all?
also why after merging pairs, merge specifically from right to left?
With pairing heap, adding an item to the heap is an O(1) operation because all it does is add the node either as the new root (if it's smaller than the current root), or as the first child of the current root. So if you created a pairing heap and added the numbers 0 through 9 to it, in order, you would end up with:
0
|
-----------------
| | | | | | | | |
9 8 7 6 5 4 3 2 1
If you then do a delete-min, you then have to look at each child to determine the minimum item and build the new heap. If you use the naive left to right combining method, you end up with this tree:
1
|
---------------
| | | | | | | |
9 8 7 6 5 4 3 2
And the next time you do a delete-min you have to look at the 8 remaining children, etc. Using this technique, creating and then removing all items from the heap would be an O(n^2) operation.
The two-pass method of combining in pairs and then combining the pairs results in a much more efficient structure. Consider the first case. After deleting the minimum item, we're left with the nine children. They're combined in pairs from left to right to produce:
8 6 4 2 1
/ / / /
9 7 5 3
Then we combine the the pairs right to left. In steps:
8 6 4 1
/ / / /
9 7 5 2
/
3
8 6 1
/ / / \
9 7 2 4
/ /
3 5
8 1
/ |
9 ---------
6 4 2
/ / /
7 5 3
1
|
----------
8 6 4 2
/ / / /
9 7 5 3
Now, the next time we call delete-min, there are only four nodes to check, and the next time after that there will only be two. Using the two-pass combining method reduces the number of nodes at the child level by at least half. The arrangement I showed is the worst case. If the items were in ascending order, the first delete-min operation would result in a tree with only two child nodes below the root.
This is a particularly good example of the amortized complexity of pairing heap. insert is O(1), but the first delete-min after a bunch of insert operations is O(n), where n is the number of items that were inserted since the last delete-min. The beauty of the two-pass combining rule is that it quickly reorganizes the heap to reduce that O(n) complexity.
With this combining rule, the amortized complexity of delete-min is O(log n). With the strict left-to-right rule, it's O(n).