pick subset of nodes from a tree - algorithm

Given a tree with n nodes each having some value in it.Find a subset of nodes such that the nodes picked in the subset has a maximum value and each node picked in the subset has even number of children.Here is a recursive solution that I thought of
func recurce(node)
if(node is null) return 0
exclude_current_node = sum of recurce of all node's children
//satisfy even number of child property
if(odd number of childeren's)
include_current_node = sum of recurce of all node's children except child with lowest value+value of node
else
include_current_node = sum of recurce of all node's children+value of node
return max(exclude_current_node,include_node)
Is this solution correct?
I can cache the result using dynamic programming to improve runtime.

Related

Sum node values towards the root node in a rooted tree

Problem
I have a directed rooted tree where every node has some initial value. I would like every node's new value to equal the sum of both it and its descendants' initial values. I envisage this process as starting at leaf nodes and propagating values up the tree towards the root node.
By the end of the process, the root node's value should be the sum of all nodes; every other node has a lesser value accordingly; every leaf node has the value it started with (since there are no children to sum into it).
What is the best/most elegant way to perform this process on a given graph?
Data Structure
Just to clarify the directed rooted tree: it is a restrict form of a directed acyclic graph, where each node has at most 1 parent and there is a sole designated root node which doesn't have a parent.
In my implementation each node has a reference to its parent node (null for the root node) and an array of references to its child nodes (empty for leaf nodes).
Illustration
Initial tree and node values
Modified tree with updated node values
Failed attempt
My simple initial attempt starts with the leaf nodes, and for each one traverses the tree upwards, adding the running sum into its parent nodes until a bifurcating parent node is reached -- such a node is added as a candidate to begin with on the next iteration.
The problem this has is that certain bifurcating nodes can be summed into their own parent more than once, for example, if the paths from its own children to the relevant leaf nodes have a varying number of further bifurcating nodes.
List<Node> nodes = getLeaves();
HashSet<Node> nextNodes = new HashSet<>();
while (!nodes.isEmpty()) {
for (Node node : nodes) {
Node current = node;
while (current.degree < 2) { // terminate when reach bifurcating parent node
current.parent.value += current.value;
current = current.parent;
}
if (current.parent != null) { // skip root node
nextNodes.add(current.parent); // add bifurcating parent node for next iteration
}
}
nodes = new ArrayList<Node>(nextNodes);
nextNodes.clear();
}

Calculate the number of nodes on either side of an edge in a tree

A tree here means an acyclic undirected graph with n nodes and n-1 edges. For each edge in the tree, calculate the number of nodes on either side of it. If on removing the edge, you get two trees having a and b number of nodes, then I want to find those values a and b for all edges in the tree (ideally in O(n) time).
Intuitively I feel a multisource BFS starting from all the "leaf" nodes would yield an answer, but I'm not able to translate it into code.
For extra credit, provide an algorithm that works in any general graph.
Run a depth-first search (or a breadth-first search if you like it more) from any node.
That node will be called the root node, and all edges will be traversed only in the direction from the root node.
For each node, we calculate the number of nodes in its rooted subtree.
When a node is visited for the first time, we set this number to 1.
When the subtree of a child is fully visited, we add the size of its subtree to the parent.
After this, we know the number of nodes on one side of each edge.
The number on the other side is just the total minus the number we found.
(The extra credit version of your question involves finding bridges in the graph on top of this as a non-trivial part, and thus deserves to be asked as a separate question if you are really interested.)
Consider the following tree:
1
/ \
2 3
/ \ | \
5 6 7 8
If we cut the edge between node 1 and 2, The tree will surely split into two tree because there is only one unique edge between two nodes according to tree property:
1
\
3
| \
7 8
and
2
/ \
5 6
So, now a is the number of nodes rooted at 1 and b is number of nodes rooted at 2.
> Run one DFS considering any node as root.
> During DFS, for each node x, calculate nodes[x] and parent[x] where
nodes [x] = k means number of nodes of sub-tree rooted at x is k
parent[x] = y means y is parent of x.
> For any edge between node x and y where parent[x] = y:
a := nodes[root] - nodes[x]
b := nodes[x]
Time and space complexity both O(n).
Note that n=b-a+1. Due to this, you don't need to count both sides of the edge. This greatly simplifies things. A normal recursion over the nodes starting from the root is enough. Since your tree is undirected you don't really have a "root", just pick one of the leaves.
What you want to do is to "go down" the tree until you reach the bottom. Then you count backwards from there. The leaf returns 1, and each recursive step sums the return values for each edge and then increment by 1.
Here is the Java code. Function countEdges() takes in the adjacency list of the tree as an argument also current node and the parent node of the current node(here parent node means that current node was introduced by parent node in this DFS).
Here edge[][] stores the number of nodes on one side of the edge[i][j], obviously the number of nodes on the other side will be equal to (total nodes - edge[i][j]).
int edge[][];
int countEdges(ArrayList<Integer> adj[], int cur, int par) {
// If current nodes is leaf node and is not the node provided by the calling function then return 1
if(adj[cur].size() == 1 && par != 0) return 1;
int count = 1;
// count the number of nodes recursively for each neighbor of current node.
for(int neighbor: adj[cur]) {
if(neighbor == par) continue;
count += countEdges(adj, neighbor, cur);
}
// while returning from recursion assign the result obtained in the edge[][] matrix.
return edge[par][cur] = count;
}
Since we are visiting each node only once in the DFS time complexity should be O(V).

Algorithms-how to find a subset in weighted binary tree that maximises weight and no pair of nodes have parent-child relationship

So i am on my algorithms and complexity homework and there is an exercise that's a pain. i have a binary tree with weight labels(integers) on each node and an integer k and i have to find a subset of the nodes containing at most k nodes , that maximizes the sum of weights and no pair of nodes in the subset have parent-child relationship . I am supposed to utilize dynamic programming to solve that .
So as a first notion i was thinking of checking the sum of all the subsets with cardinality 2 ( that have the required property ) , and scale to cardinality k using the subproblems to construct the solution . i have problems all the way though . Any thoughts ?
Here is a dynamic programming solution:
The state is (node, set_size, is_taken). The value is the maximum sum of a set such that all nodes in this set belong to a subtree rooted in a node, its size is set_size and is_taken indicates whether the node itself belongs to this set or not.
The base case is as follows: for all leaves, f(leaf, 0, false) = 0(it means that this leaf is not taken) and f(leaf, 1, true) = the weight of this leaf (it means that this leaf is taken).
We can compute the values of f for inner nodes in the following manner: first, we compute it for a node's children, and then we merge the results(either taking this node or not).
The answer is the maximum of max(f(root, k', false), f(root, k', true)) among all k' <= k.
This problem is similar to the well-known maximum independent set problem.
Hint: You will have to construct a 2-D matrix where
DP[i][j] = Maximum node weight till node i taking at-most j nodes.
DP[i][j] ~ Max(DP[parent(i)][j], DP[grandparent(i)][j-1] + Wi)
Fill up the matrix by conducting a DFS on the tree starting from the root.
Note:
The above recurrence relation is just an idea to get you started. The exact relation shall have to be worked out taking into account the DFS and order of traversal of nodes.
You can define it as
T(v, parent_chosen)
T is the maximum subset sum for the subtree rooted at v whether v's parent was chosen or not.
T(null, _) = 0
#if v's parent was chosen only check for children
T(v, true) = T(left(v), false)+T(right(v), false)
#if v's parent wasn't chosen, try both options (choosing and not choosing)
T(v, false) = max(
T(left(v), true)+T(right(v), true)+weight(v),
T(left(v), false)+T(right(v), false)
)
The solution is given by:
T(root, false)
This solution is O(n).

Select a Node at Random from Unbalanced Binary Tree

One of my friends had the following interview question, and neither of us are quite sure what the correct answer is. Does anyone have an idea about how to approach this?
Given an unbalanced binary tree, describe an algorithm to select a node at random such that each node has an equal probability of being selected.
You can do it with a single pass of the tree. The algorithm is the same as with a list.
When you see the first item in the tree, you set it as the selected item.
When you see the second item, you pick a random number in the range (0,2]. If it's 1, then the new item becomes the selected item. Otherwise you skip that item.
For each node you see, you increase the count, and with probability 1/count, you select it. So at the 101st node, you pick a random number in the range (0,101]. If it's 100, that node is the new selected node.
When you're done traversing the tree, return the selected node. The operation is O(n) in time, with n being the number of nodes in the tree, and O(1) in space. No preprocessing required.
We can do this recursively in one parse by selecting the random node while parsing the tree and counting the number of nodes in left and right sub tree. At every step in recursion, we return the number of nodes at the root and a random node selected uniformly randomly from nodes in sub tree rooted at root.
Let's say number of nodes in left sub tree is n_l and number of nodes in right sub tree is n_r. Also, randomly selected node from left and right subtree be R_l and R_r respectively. Then, select a uniform random number in [0,1] and select R_l with probability n_l/(n_l+n_r+1) or select root with probability 1/(n_l+n_r+1) or select R_r with probability n_r/(n_l+n_r+1).
Note
If you're only doing a single query, and you don't already have a count at each node, the best time complexity you can get is O(n), so the depth-first-search approach would be the best one.
For repeated queries, the best option depends on the given constraints
(the fastest per-query approach is using a supplementary array).
Supplementary array
O(n) space, O(n) preprocessing, O(1) insert / remove, O(1) query
Have a supplementary array containing all the nodes.
Also have each node store its own index (so you can remove it from the array in O(1) - the way to do this would be to swap it with the last element in the array, update the index of the node that was at the last index appropriately and decrease the size of the array (removing the last element).
To get a random node, simply generate a random index in the array.
Per-node count
Modified tree (O(n) space), N/A (or O(n)) preprocessing, O(depth) insert / remove, O(depth) query
Let each node contain the number of elements in its subtree.
When generating a random node, go left or right based on the value of a random number generated and the counts of the left or right subtrees.
// note that subtreeCount = leftCount + rightCount + 1
val = getRandomNumber(subtreeCount)
if val = 0
return this node
else if val <= leftCount
go left
else
go right
Depth-first-search
O(depth) space, O(1) preprocessing, O(1) insert / remove, O(n) query
Count the number of nodes in the tree (if you don't already have the count).
Generate a random number between 0 and the number of nodes.
Simply do a depth-first-search through the tree and stop when you've processed the desired number of nodes.
This presumes a node doesn't have a parent member - having this will make this O(1) space.
I implemented #jim-mischel's algorithm in C# and it works great:
private void SelectRandomNode(ref int count, Node curNode, ref Node selectedNode)
{
foreach( var childNode in curNode.Children )
{
++count;
if( random.Next(count) == count - 1 )
selectedNode = childNode;
SelectRandomNode(ref count, childNode, ref selectedNode);
}
}
Call it like this:
var count = 1;
Node selected = root;
SelectRandomNode(ref count, root, ref selected);

Generating uniformly random curious binary trees

A binary tree of N nodes is 'curious' if it is a binary tree whose node values are 1, 2, ..,N and which satisfy the property that
Each internal node of the tree has exactly one descendant which is greater than it.
Every number in 1,2, ..., N appears in the tree exactly once.
Example of a curious binary tree
4
/ \
5 2
/ \
1 3
Can you give an algorithm to generate a uniformly random curious binary tree of n nodes, which runs in O(n) guaranteed time?
Assume you only have access to a random number generator which can give you a (uniformly distributed) random number in the range [1, k] for any 1 <= k <= n. Assume the generator runs in O(1).
I would like to see an O(nlogn) time solution too.
Please follow the usual definition of labelled binary trees being distinct, to consider distinct curious binary trees.
There is a bijection between "curious" binary trees and standard heaps. Namely, given a heap, recursively (starting from the top) swap each internal node with its largest child. And, as I learned in StackOverflow not long ago, a heap is equivalent to a permutation of 1,2,...,N. So you should make a random permutation and turn it into a heap; or recursively make the heap in the same way that you would have made a random permutation. After that you can convert the heap to a "curious tree".
Aha, I think I've got how to create a random heap in O(N) time. (after which, use approach in Greg Kuperberg's answer to transform into "curious" binary tree.)
edit 2: Rough pseudocode for making a random min-heap directly. Max-heap is identical except the values inserted into the heap are in reverse numerical order.
struct Node {
Node left, right;
Object key;
constructor newNode() {
N = new Node;
N.left = N.right = null;
N.key = null;
}
}
function create-random-heap(RandomNumberGenerator rng, int N)
{
Node heap = Node.newNode();
// Creates a heap with an "incomplete" node containing a null, and having
// both child nodes as null.
List incompleteHeapNodes = [heap];
// use a vector/array type list to keep track of incomplete heap nodes.
for k = 1:N
{
// loop invariant: incompleteHeapNodes has k members. Order is unimportant.
int m = rng.getRandomNumber(k);
// create a random number between 0 and k-1
Node node = incompleteHeapNodes.get(m);
// pick a random node from the incomplete list,
// make it a complete node with key k.
// It is ok to do so since all of its parent nodes
// have values less than k.
node.left = Node.newNode();
node.right = Node.newNode();
node.key = k;
// Now remove this node from incompleteHeapNodes
// and add its children. (replace node with node.left,
// append node.right)
incompleteHeapNodes.set(m, node.left);
incompleteHeapNodes.append(node.right);
// All operations in this loop take O(1) time.
}
return prune-null-nodes(heap);
}
// get rid of all the incomplete nodes.
function prune-null-nodes(heap)
{
if (heap == null || heap.key == null)
return null;
heap.left = prune-null-nodes(heap.left);
heap.right = prune-null-nodes(heap.right);
}

Resources