Let us assume that we have a tree consisting on N nodes. The task is to find all longest unique paths in the tree. For example, if the tree looks like following:
Then there are three longest unique paths in the tree: 1 - 2 - 3 - 4 - 5, 6 - 2 - 3 - 4 - 5 and 1 - 2 - 6.
I want to programmatically find and store all such paths for a given tree.
One way to do it would be to compute paths between each pair of node in the tree and then reject the paths which are contained in any other path. However, I am looking for an efficient way to do it. My questions are as follows:
Is it possible to compute this information in less than O(N^2)? I have not been able to think of a solution which would be faster than O(N^2).
If yes, could you be kind enough to guide me towards the solution.
The reason why I want to try it out is because I am trying to solve this problem: KNODES
An algorithm with a time complexity below O(N^2) may only exist, if every solution for a tree with N nodes can be encoded in less than O(N^2) space.
Suppose a complete binary tree with n leaves (N=n log n). The solution to the problem will contain a path for every set of 2 leaves. That means, the solution will have O(n^2) elements. So for this case we can encode the solution as the 2-element sets of leaves.
Now consider a nearly complete binary tree with m leaves, which was created by only removing arbitrary leaves from a complete binary tree with n leaves. When comparing the solution of this tree to that of the complete binary tree, both will share a possibly empty set of paths. In fact for every subset of paths of a solution of a complete binary tree, there will exist at least one binary tree with m leaves as mentioned above, that contains every solution of such a subset. (We intentionally ignore the fact that a tree with m leaves may have some more paths in the solution where at least some of the path ends are not leaves of the complete binary tree.)
Only that part of the solution for a binary tree with m leaves will be encoded by a number with (n^2)/2 bits. The index of a bit in this number represents an element in the upper right half of a matrix with n columns and rows.
For n=4 this would be:
x012
xx34
xxx5
The bit at index i will be set if the undirected path row(i),column(i) is contained in the solution.
As we have already statet that a solution for a tree with m leaves may contain any subset of the solution to the complete binary tree with n>=m leaves, every binary number with (n^2)/2 bits will represent a solution for a tree with m leaves.
Now encoding every possible number with (n^2)/2 bits with less than (n^2)/2 is not possible. So we have shown that solutions at least require O(n^2) space to be represented. Using N=n log n from above we yield a space requirement of at least O(N^2).
Therefore there doens't exist an algorithm with time complexity less than O(N^2)
As far as I could understand, you have a tree without a selected root. Your admissible paths are the paths that do not allow to visit tree nodes twice (you are not allowed to return back). And you need to find all such admissible paths that are not subpaths of any admissible path.
So if I understood right, then if a node has only one edge, than the admissible path either start or stop at this node. If tree is connected, then you can get from any node to any node by one admissible path.
So you select all nodes with one edge, call it S. Then select one of S and walk the whole tree saving the paths to the ends (path, not the walk order). Then you do this with every other item in S and remove duplicated paths (they can be in reverse order: like starting from 1: 1 - 2 - 6 and starting from 6: 6 - 2 - 1).
So here you have to visit all the nodes in the tree as much times as you have leafs in the tree. So complexity depends on the branching factor (in the worst case it is O(n^2). There are some optimizations that can reduce the amount of operations, like you don't have to walk the tree from the last of S.
In this picture the longest paths are {1, 2, 3, 4}, {1, 2, 3, 5}, {1, 2, 3, 6}, {1, 2, 3, 7}, {1, 2, 3, 8}, {1, 2, 3, 9}, {1, 2, 3, 10}
For tree like this storing all longest paths will cost you O(N2)
Let's take the tree which looks like the star with n nodes and n-1 edges.
Then you have got C(n-1, 2) unique longest paths.
So the lower limit of complexity can't be less than O(n^2).
Suppose you have got Binary Tree like on your picture.
class Node(object):
def __init__(self, key):
self.key = key
self.right = None
self.left = None
self.parent = None
def append_left(self, node):
self.left = node
node.parent = self
def append_right(self, node):
self.right = node
node.parent = self
root = Node(3)
root.append_left(Node(2))
root.append_right(Node(4))
root.left.append_left(Node(1))
root.left.append_right(Node(6))
root.right.append_right(Node(5))
And we need to get all paths between all leaves. So in your tree they are:
1, 2, 6
6, 2, 3, 4, 5
1, 2, 3, 4, 5
You can do this in (edit: not linear, quadratic) time.
def leaf_paths(node, paths = [], stacks = {}, visited = set()):
visited.add(node)
if node.left is None and node.right is None:
for leaf, path in stacks.iteritems():
path.append(node)
paths.append(path[:])
stacks[node] = [node]
else:
for leaf, path in stacks.iteritems():
if len(path) > 1 and path[-2] == node:
path.pop()
else:
path.append(node)
if node.left and node.left not in visited:
leaf_paths(node.left, paths, stacks, visited)
elif node.right and node.right not in visited:
leaf_paths(node.right, paths, stacks, visited)
elif node.parent:
leaf_paths(node.parent, paths, stacks, visited)
return paths
for path in leaf_paths(root):
print [n.key for n in path]
An output for your tree will be:
[1, 2, 6]
[6, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
The idea is to track all visited leaves while traversing a tree. And to keep stack of paths for each leaf. So here is memory/performance tradeoff.
Draw the tree. Let v be a vertex and p its parent. The length of the longest path including v but not p = (height of left subtree of v) + (height of right subtree of v).
The maximum over all v is the longest path in the graph. You can calculate this in O(n):
First calculate all the intermediate heights. Start at the leaves and work up: (height below v) = 1 + max(height below left child, height below right child)
Then calculate the sum (height of left subtree of v) + (height of right subtree of v) for each vertex v, and take the maximum. This is the length of longest path in the graph.
Related
This is similar to a practice question I am trying to solve. You have been given an undirected tree with an array of values. A value val[i] is associated with the ith node. In a single operation, two nodes can be selected, and their values can be decremented by 1 at a cost equal to the distance between the two nodes, i.e., the number of edges in the simple path between them. It is possible to select the same node for the operation and decrease its value by 2 at the cost of 0.
t_from = [1, 1, 3, 5], t_to = [2, 3, 4, 5] val = [3, 2, 4, 2, 5]
The optimal strategy is to choose nodes (1, 1), (2, 2), (3, 3), (3, 3), (4,4), (5,5) and (5,5) again to get the costs [1, 0, 0, 0, 1]. Now the nodes (1,5) can be chosen to decrease by 1 at the cost of 2. So the final answer would be 2.
Can anyone tell me what can I do? I used bfs to find the pairs of odd-weighed nodes and added the distance between them but it does not work.
Here is what you could do:
Set all node values to modulo 2, so you have only 1s and 0s.
Create a hash map where you have the node as key and the distance travelled as value.
Add all nodes with a value of 1 which are at the deepest level to the map with a distance travelled of 0.
Add all nodes with a value of 1 which are 1 level above the current one to a new hash map with a distance travelled of 0.
For all entries in the old map add the node's parent to the new hash map and increase the distance travelled by one. Whenever there is a collision (a node is already in the hash map) remove the node in the map instead of adding the colliding one and add the distances travelled of both colliding nodes + 1 to the total costs (unless you increase distance travelled before checking collision, then you don't need to do +1).
Repeat steps 4. and 5. until all 1s are consumed. This has to be latest at the root node if the number of nodes with a value of 1 is even, otherwise there is no solution. Total costs should now be the solution. Like this no node is travelling farther than it needs to (well not exactly, there could be a closer node, but then another node would have to travel the same distance more than what we would save).
Credit to #maraca for the approach!
counting the steps from the leaves essentially makes this algorithm a modified topological BFS.
Rough steps are that:
remove leaves that are even (or "0" after mod 2)
if leaf is odd -> totalcost += 1
if leaf is odd, invert parent (1 -> 0, or 0 -> 1)
check whether parent is a leaf
def getMinCost(val, t_nodes, t_from, t_to):
for i in range(len(val)):
val[i] = val[i]%2
adj_list = [set() for i in range(t_nodes)]
for i in range(len(t_from)):
adj_list[t_from[i]-1].add(t_to[i]-1)
adj_list[t_to[i]-1].add(t_from[i]-1)
leaves = [i for i in range(t_nodes) if len(adj_list[i]) == 1]
remaining = t_nodes
cost = 0
while leaves and remaining > 2:
remaining -= len(leaves)
newLeaves = []
for leaf in leaves:
#each leaf only has one parent so pop() any from adj_list
parent = adj_list[leaf].pop()
#remove leaf from parent to check if parent becomes a leaf
adj_list[parent].remove(leaf)
if val[leaf] == 1: #odd
cost += 1
# parnt shd change fm 0 to 1 and vice versa
val[parent] = 1 - val[parent]
if len(adj_list[parent]) == 1:
newLeaves.append(parent)
leaves = newLeaves
# check if remaining two leaves are odd
if leaves and val[leaves[0]] == 1:
cost += 1
return cost
This is Problem 3 in my practice section ...
Key: It's a tree rather than a graph !!!
just take it as Linked List problems in LeetCode, solve it from leaf to root.
I am not sure whether this is an appropriate question to post on this platform.
The problem statement is present here:
https://www.interviewbit.com/problems/permutation-swaps/
Other than that, here's a short summary of this graph-based question.
Problem statement with an example:
let N = 4
given permutation A for numbers 1 to 4,
A = [1, 3, 2, 4]
and given,
B = [1, 4, 2, 3]
other than that we are given pairs of positions,
C = [[2, 4]]
We have to find out, whether we can convert permutation A to permutation B if only numbers at pairs of positions(good pairs) mentioned in C can be swapped.
Here, in the above example for pair (2, 4) in C, the number at position 2 in A is = 3 and the number at position 4 is 4. if we, swap the numbers at both these positions, we get [1, 4, 2, 3] which is equal to B. Hence, it is possible.
Your really need to ask an actual question!
Here is a simple algorithm to strt your thinking about what you want.
Add A to L, a list of reachable permutations
Loop P over permutations in L
Loop S over C
Apply C to P, giving R
If R == B
**DONE** TRUE
If R not in L
Add R to L
**DONE** FALSE
Algorithm:
Create a graph whose nodes are the positions 1..n, and with an edge between two positions if and only if you can swap them.
For every position where you have an unwanted number, if the number you want can't be reached through the graph, then no permutation can work and you should return 0.
If every position has its wanted number reachable, then return 1.
But why is it sufficient to have every wanted number reachable?
To start, construct a maximal spanning forest in the graph according to the following pseudo-code:
foreach node in 1..10:
if node not in any tree:
construct maximal tree by breadth-first search
if tree has > 1 element:
add tree to list of trees in our forest
And now we construct our permutation as follows:
while the forest is non-empty:
pick a tree in the forest:
find a leaf in the tree:
With a chain of swaps, get the right value into that node
remove the leaf from the tree
if the tree has only 1 element left, remove the tree from the forest
I will omit the proof that this always produces a permutation that gets to the desired end result. But it does.
I know how to find the minimum numbers of nodes in an AVL tree of height h (which includes external nodes) with the formula n(h) = n(h-1) + n(h-2) + 1 but I was wondering if there was a formula to just find the minimum internal nodes only of an AVL tree with height h.
So for n(3) = 4, if we're only counting internal nodes. n(4) = 7, if we're only counting internal nodes. I can draw it out and count the internal nodes but when you get to bigger AVL trees it's a mess.
I can't seem to find anything on this and trying to find a pattern with consistent answers has only led to hours of frustration. Thanks in advance.
Yep, there’s a nice way to calculate this. Let’s begin with the two simplest AVL trees, which have order 0 and order 1:
* *
|
*
This first tree has no internal nodes, and the second has one internal node. This gives us our base cases for a recurrence relation:
I(0) = 0
I(1) = 1
From here, we notice that the way to get the fewest internal nodes in an AVL tree of order n+2 is to pick two trees of order n and n+1 as children (minimizing the number of nodes) that have the fewest internal nodes possible. The resulting tree will have a number of internal nodes equal to the number of internal nodes in the two subtrees, plus one for the new root. This means that
I(n+2) = I(n) + I(n+1) + 1.
Applying this recurrence gives us the sequence
0, 1, 2, 4, 7, 12, 20, etc.
And hey - have we seen this before somewhere? We have! Adding one to each term gives us
1, 2, 3, 5, 8, 13, 21, etc.
which is the Fibonacci sequence, shifted down two positions! So our hypothesis is that
I(n) = F(n+2) - 1
You can prove that this is the case by induction on n.
Here’s a different way to arrive at this result. Imagine you take an AVL tree of height n and remove all the leaves. You’re now left with an AVL tree of height n-1 (prove this!), and all of the remaining nodes in this tree are the internal nodes of the original tree. The smallest possible number of nodes in an AVL tree of height n is F(n+2)-1, matching our result.
Hi all i have algorithmic problem and i struggle with finding optimal solution. I have tree which i want to traverse. Nodes of the tree consist of value and a rank of node (value as well as rank can be random number).
What i want to do is traverse tree and for each node i want to sum values from all descendant nodes except descendants with lower rank and all nodes under them (irrespective of rank). Tree does not have any special properties as every node can have <0, Integer.MAX_VALUE> of children. There are no rules applied on relations between parent and children regarding rank or value.
My naive solution is to recursively traverse subtree for each node and stop recursion as soon as i find node with lower rank, summing values on my way back to root of subtree. However this feels to me like suboptimal solution (in worst case - that is each node has only one descendant - its basically linked list and ranks are sorted ascending to the root this solution would be O(n^2)).
It is possible to have sums for all nodes settled after one traversal?
Edit:
Solution, slightly better as my naive approach could be for every node visited propagate its value recursively back to the root while keeping minimum rank of visited nodes (during back to root traversal). Then adding value only to nodes that have lower than minimal rank.
Edit from my phone: since we only ever inspect the value of tree roots, we can use a disjoint sets structure with path compression only instead of a dynamic tree. Don't bother updating non-roots.
Here's an O(n log n)-time algorithm with dynamic trees. (I know. They're a pain to implement. I try not to include them in answers.)
Sort the nodes from greatest to least rank and initialize a totally disconnected dynamic tree with their values. For each node in order, issue dynamic tree operations to
Report its current value (O(log n) amortized, output this value eventually), and
If it's not the root, add its value to each of its ancestors' values (O(log n) amortized) and then link it to its parent (also O(log n) amortized).
The effect of Step 2 is that each node's dynamic value is the sum of its descendants' (relative to the present tree) original values.
Edit
Not correct answer, does not solve the problem asked by the OP (cf comment)
Old answer (before edit)
You can see that this problem (like most problem on trees) can be solved with a recursive approach. That is because the sum value of a node depends only on the sum values and respective ranks of its children.
Here is a pseudo-code describing a solution.
get_sum(my_node, result_arr):
my_sum = 0
for child in my_node.children():
get_sum(child, result_arr) // we compute the sum value of the children
if rank(child) >= rank(my_node): // if child node is big enough add its sum
my_sum += result_arr[child]
result_arr[my_node] = my_sum // store the result somewhere
This is a BFS based algorithm, which should run in O(n) with n the number of nodes in your tree. To get the values for all the nodes, call this recursive function on the root node of your tree.
I suggest you a postfixed DFS. For every node, keep the reference of its predecessor.
the sum_by_rank for a leaf is an empty dict;
the sum_by_rank for any node is the dict ranks of all subnodes -> values of all subodes. If two or more subnodes have the same rank, just add their values.
The postfixed DFS allows you to compute the sums from bottom to up.
Here's some Python 3.7 program to play with (the code is probably not optimized):
from dataclasses import dataclass
from typing import List, Dict
#dataclass
class Node:
value: int
rank: int
sum_by_rank: Dict[int, int]
children: List[object]
tree = Node(5, 0, {}, [
Node(4,2, {}, [Node(3,1, {}, [])]),
Node(7,2, {}, [Node(11,1, {}, [Node(7,8, {}, [])])]),
Node(8,4, {}, [Node(3,3, {}, []), Node(9,5, {}, []), Node(4,2, {}, [])]),
])
def dfs(node, previous=None):
for child in node.children:
dfs(child, node)
node.sum_by_rank[child.rank] = node.sum_by_rank.get(child.rank, 0) + child.value # add this children rank -> value
# add the subtree rank -> value
for r,v in child.sum_by_rank.items():
node.sum_by_rank[r] = node.sum_by_rank.get(r, 0)+v
dfs(tree)
print (tree)
# Node(value=5, rank=0, sum_by_rank={2: 15, 1: 14, 8: 7, 4: 8, 3: 3, 5: 9}, children=[Node(value=4, rank=2, sum_by_rank={1: 3}, children=[Node(value=3, rank=1, sum_by_rank={}, children=[])]), Node(value=7, rank=2, sum_by_rank={1: 11, 8: 7}, children=[Node(value=11, rank=1, sum_by_rank={8: 7}, children=[Node(value=7, rank=8, sum_by_rank={}, children=[])])]), Node(value=8, rank=4, sum_by_rank={3: 3, 5: 9, 2: 4}, children=[Node(value=3, rank=3, sum_by_rank={}, children=[]), Node(value=9, rank=5, sum_by_rank={}, children=[]), Node(value=4, rank=2, sum_by_rank={}, children=[])])])
Hence, to get the sum of a node, just add the values is associated with the ranks greater or equal to the node rank. In Python:
sum(value for rank, value in node.sum_by_rank.items() where rank >= node.rank)
Let a, b, c be nodes. Assume that a is an ancestor or b, and b is an ancestor of c.
Observe: if b.rank > c.rank and a.rank > b.rank then a.rank > c.rank.
This leads us to the conclusion that the sum_by_rank of a is equal to the sum of sum_by_rank(b) + b.value for every b direct child of a, having a rank lower than a.
That suggests the following recursion:
ComputeRank(v)
if v is null
return
let sum = 0
foreach child in v.children
ComputeRank(child)
if child.rank <= v.rank
sum += child.sumByRank + child.value
v.sumByRank = sum
By the end of the algorithm each node will have it's sumByRank as you required (if I understood correctly).
Observe that for each node n in the input tree, the algorithm will visit n exactly once, and query it once again while visiting it's predecessor. This is a constant number of times, meaning the algorithm will take O(N) time.
Hope it helps :)
The algorithm is about to visit k leave nodes and return back to root node in minimum steps!!
Think it is as A person standing at Point 2 and he/she wants to visit k leaves of the tree in minimum steps and back to Point 2.
For k=3 path can be like
2-> "3" ->2->1-> "0" ->1-> "5" ->1->2 (here 3,0,5 are leaves)
So we visited 3 leves..
Answer: 8
This is yet another bottom-up dynamic program on a rooted tree.
For each subtree, we compute the cost of visiting at least j leaves for j in 0…k, as a list. If the subtree is a single leaf, then this list is [0, 0, ∞, ∞, …, ∞], where ∞ denotes infinity, representing an infeasible leaf quantity. Otherwise, we compute the list for each child, increase all of the entries except the first in each list by 2, and then reduce by convolution. To convolve two lists A and B is to compute [min {A[i] + B[j-i]: i in 0…j}: j in 0…k]. Return the k entry for the root.
This is O(n k^2)-time.