Suppose I am given a undirected tree and I need to find a path(the only path) between two nodes.
What is the best algorithm to do it.I probably could use a Dijkstra's algorithm but there a probably something better for trees.
C++ example would be helpful but not necessary
Thank you
Assuming each node has a pointer to its parent, then simply back-track up the tree towards the root from each start node. Eventually, the two paths must intersect. Testing for intersection could be as simple as maintaining a std::map of node addresses.
UPDATE
As you've updated your question to specify undirected trees, then the above isn't valid. A simple approach is simply to perform a depth-first traversal starting at Node #1, eventually you'll hit Node #2. This is O(n) in the size of the tree. I'm not sure there's going to be a faster approach than that, assuming a completely general tree.
Breadth-first search and depth-first search are more effective then Dijkstra's algorithm.
Supposing you have
struct Node
{
std::vector<Node *> children;
};
then what could be done is traversing the whole tree starting at root keeping the whole chain during the traversal. If you find e.g. node1 then you save the current chain, if you find node2 then you check for the intersection... in code (UNTESTED):
bool findPath(std::vector<Node *>& current_path, // back() is node being visited
Node *n1, Node *n2, // interesting nodes
std::vector<Node *>& match, // if not empty back() is n1/n2
std::vector<Node *>& result) // where to store the result
{
if (current_path.back() == n1 || current_path.back() == n2)
{
// This is an interesting node...
if (match.size())
{
// Now is easy: current_path/match are paths from root to n1/n2
...
return true;
}
else
{
// This is the first interesting node found
match = current_path;
}
}
for (std::vector<Node *>::iterator i=current_path.back().children.begin(),
e=current_path.back().children.end();
i != e; ++i)
{
current_path.push_back(*i);
if (findPath(current_path, n1, n2, match, result))
return true;
current_path.pop_back(); // *i
}
return false;
}
Related
I have a problem - I need to answer question if given 2 nodes (A, B) is A ancestor of B. I know that the possible solution is to get the time when I enter the node and the time when I leave it. Based on this I can calculate relationship fast. How to get those 'timestamps' using DFS which can not be implemented using recurrence?
I am not expert in algorithms and C++, that is why I am asking.
I found a way to get it. If Your tree in adjacency list then You can go with preprocessing. To each node You need to assign the value when You enter it and when You leave it during DFS, then in constant time You can check it:
if (timeIn[b] >= timeIn[a] && timeIn[b] <= timeOut[a]) {
printf("YES\n");
}
else {
printf("NO\n");
}
where a is possible ancestor of b.
DFS:
// INITIALIZE VARs
int time = 0;
stack<int> stackOfChildNodes;
// put root of tree
stackOfChildNodes.push(first);
// go with DFS
while (!stackOfChildNodes.empty()) {
int current = stackOfChildNodes.top();
stackOfChildNodes.pop();
// if node was not visited (time is not set)
if (timeIn[current] == -1) {
timeIn[current] = time; // node visited
time++; // increase time to go further
stackOfChildNodes.push(current); // include node in processing to leave it in future
int child = children[current];
// add descendants to process them
while (child != 0) {
stackOfChildNodes.push(child);
child = descendants[child];
}
}
// if node was visited, so we gonna leave him now
if (timeIn[current] != -1) {
timeOut[current] = time-1;
}
}
With standard augmented thread indices (ATI -- see reference provided below), that provide for node length data structures, usually arrays, answering the question of whether A is B's ancestor is not possible in constant time.
If you store the pred, thread and depth (that constitute the fundamental data structures in the ATI scheme) of each node, the question of whether A is B's ancestor can be done in O(N) time -- just retrace from B using the pred data structure until you reach the root and check if you encounter A.
However, you can do the above for each node as a preprocessing step and then have a new node length array, ancestor for each node which indicates whether another node is its ancestor or not.
Reference: Bazaara, Jarvis and Sherali page 482.
I am working on implementing a binary search tree. One of the functions required to complete the implementation is a rebalance function.
According to the specifications the function works in the following way:
The rebalance() method should create a balanced tree and thereby reduce skewness to zero. A
balanced tree is one in which the size of the left and right subtrees differ by no more than 1,
throughout the tree (i.e., every subtree is also balanced).
To balance the tree, the rebalance() method should repeatedly move the root value to the smaller
subtree, and move the min/max value from the larger subtree to the root, until the tree is balanced.
It should then recursively balance both subtrees.
So far I have the following code:
struct treeNode {
Type value;
int count;
treeNode* left;
treeNode* right;
};
treeNode* root;
template <class Type>
void bstree<Type>::rebalance(treeNode* sroot){
if (root == NULL) {
throw new underflow_error("tree is empty");
}
while (skewness(sroot) != 0)
{
if (size(sroot->left) < size(sroot->right))
{
sroot->left.insert(sroot->value);
sroot->left.insert(max(sroot->right));
sroot->left.insert(min(sroot->right));
}
else
{
sroot->right.insert(sroot->value);
sroot->left.insert(max(sroot->left));
sroot->left.insert(min(sroot->left));
}
}
rebalance(sroot->left);
rebalance(sroot->right);
}
I can't tell if I have followed the specifications correctly or not. Can I get some insight or pointers as where I may have done things wrong?
You have not followed the specifications correctly. For one thing, your rebalance operation increases the size of the tree. For another, the result is not always a binary search tree.
You must understand how these trees work before you attempt to implement them. There is just no way around that. I advise you to study your text (or wikipedia) and try constructing and balancing some trees with pencil and paper.
I am looking to build my own map class. (Which will behave exactly like the C++ STL) I want to be able to iterate through all the elements in order by key value.
I implemented my map as an unbalanced binary search tree.
So my question is how to do an iterator increment efficiently. One inefficient way is to iterate through every single element in the tree to find the next lowest key. Is there a faster way to do this?
Thank you.
It depends a bit on the implementation details. If the nodes of your unbalanced binary search tree have a "parent" pointer, you could use that to traverse it. Your implementation of ++iterator could look a bit like this:
if (current_node.has_right_child()) {
// We go to the right subtree of the current node and
// take the smallest element of that subtree.
current_node = current_node.right_child();
while (current_node.has_left_child()) {
current_node = current_node.left_child();
}
} else {
// We have to go up. If the current element is the left child of the parent,
// we can just go to the right child of the parent.
// If it is the right child, we have to go further up
while (true) {
if (!current_node.has_parent()) {
// We got up to the root and never found a right child.
// So we are at the end of the iteration.
current_node = NULL;
break;
}
Node* parent = current_node.parent();
bool is_left_child = parent.left_child() == current_node;
current_node = parent;
if (is_left_child) {
// if this was the left child, then the parent is the correct next element.
break;
}
// if this was the right child, we have to go further up
// until we leave this subtree, so we continue iterating.
}
}
If your binary tree does NOT have parent nodes, you could store the parents in the iterator. I.e. you could maintain a vector parents; in which you store the parents of the current node up to the root. If this is still needed, I can provide an implementation, but because you edited my "non parent pointer" version with parent pointers, it seems that you have parent pointers. So I leave it away.
#ifndef __TREE_H
#define __TREE_H
#include <cstdlib>
#include<string>
// structure of a tree node
struct TreeNode{
string str;
TreeNode *parent;
TreeNode *leftChild;
TreeNode *nextSibling;
TreeNode(string str1){
this->str = str1;
this->parent = NULL;
this->leftChild = NULL;
this->nextSibling = NULL;
}
};
class Tree{
TreeNode* root;
int size;
public:
Tree(); //constructor
void insert(string str1); //insert a node
string locate(string str1); //locate a node
TreeNode *ancestor(string str1, string str2); //get lowest common ancestor
};
#endif
this is a class of a generic tree (not a binary tree). What will be the fastest way to implement the locate function? should i go through all the child first and then the siblings or what?
If the tree is unordered there is no algorithm other than brute force testing of all nodes and break out when the element is found (if found). When dealing with trees, recursion is usually the simplest approach. The pseudo algorithm could be something like:
find(current_node,value):
if current_node.value == value
return found
else
if find(current_node.left,value) == found
return found
else if find(current_node.right,value) == found
return found
else
return not_found
Of course, when really implementing this you will need to test for null pointers, and so on. Without any other constraints on the tree, the asymptotic complexity cannot be reduced. You might be able to come with a non-recursive approach or a tail-recursion algorithm (based on the above) that might improve the constant factors, but don't expect a huge improvement there.
If the nodes are ordered, i.e. children are less than this node and siblings are greater than this node, you compare with str and, depending on the result, have this node as a result or search down the children or compare with the siblings
const TreeNode *TreeNode::locate(const string &str1) const
{
int c = str.compare(str1);
if (c == 0)
return this;
if (c > 0) {
if (leftChild)
return leftChild->locate(str1);
return 0;
}
if (nextSibling)
return nextSibling->locate(str1);
return 0;
}
and in Tree
const TreeNode *Tree::locate(const string &str1) const
{
return root->locate(str1);
}
You can try several tree traversal algorithms depending on how your information is distributed among the tree nodes.
BFS, DFS, are some examples.
The C++ STL class std::map implements O(log(n)) look-up using a binary tree. But with trees, it's not immediately obvious how an iterator would work. What does the ++ operator actually mean in a tree structure? Whereas the concept of "next element" has an obvious implementation in an array, for me it's not so obvious in a tree. How would one implement a tree iterator?
For an inorder traversal (probably works for others too), if you have a parent-pointer in your nodes you can do a non-recursive traversal. It should be possible to just store two pointers in your iterator: you need an indication of where you are, and you'll probably (I'm not doing the research now) need something like a "previous" pointer so you can figure out your current movement direction (i.e. do I need to go into the left subtree, or did I just come back from it).
"Previous" will probably be something like "parent", if we've just entered the node; "left" if we're coming back from the left subtree, "right" if we are coming back from the right subtree, and "self" if the last node we returned was our own.
I would like to add my two cents worth as a comment, but since I am not able to I shall have to add an answer. I have been googling and was frustrated because all the answers I found, these excepted, assumed a stack or some other variably-sized data structure. I did find some code. It shows that it can be done without a stack but I found it hard to follow and so decided to attack the problem from first principles.
The first thing to note is that the algorithm is "left-greedy". Thus, when we start at the root we immediately go as far left as possible, since the leftmost node is the one we need first. This means that we never need to consider the left-subtree. It has already been iterated over.
The order of iteration is left subtree, node, right subtree. So if we are positioned at a given node we know that its left subtree and the node itself have been visited and that we should next visit the right subtree, if any, going as far left as possible.
Otherwise, we must go up the tree. if we are going from a left child to its parent then the parent comes next. (Afterwards we will visit its right subtree, as already covered.)
The final case is when we are going from a right child to its parent. The parent has been visited already so we must go up again. In fact we must keep going up until we reach the root or the tree, or find ourselves moving to a parent from its left child. As we have already seen, the parent is the next node in this case. (The root may be indicated by a null pointer, as in my code, or some special sentinel node.)
The following code could easily be adapted for an STL-style iterator
// Go as far left from this node as you can.
// i.e. find the minimum node in this subtree
Node* Leftmost(Node* node)
{
if (node == nullptr)
return nullptr;
while (node->left != nullptr)
node = node->left;
return node;
}
// Start iterating from a root node
Node* First(Node* root)
{
return Leftmost(root);
}
// The iteration is current at node. Return the next node
// in value order.
Node* Next(Node* node)
{
// Make sure that the caller hasn't failed to stop.
assert(node != nullptr);
// If we have a right subtree we must iterate over it,
// starting at its leftmost (minimal) node.
if (node->right != nullptr)
return Leftmost(node->right);
// Otherwise we must go up the tree
Node* parent = node->parent;
if (parent == nullptr)
return nullptr;
// A node comes immediately after its left subtree
if (node == parent->left)
return parent;
// This must be the right subtree!
assert(node == parent->right);
// In which case we need to go up again, looking for a node that is
// its parent's left child.
while (parent != nullptr && node != parent->left)
{
node = parent;
parent = node->parent;
}
// We should be at a left child!
assert(parent == nullptr || node == parent->left);
// And, as we know, a node comes immediately after its left subtree
return parent;
}
Consider the set of all elements in the map that are not less than the current element that are also not the current element. The "next element" is the element from that set of elements that is less than all other elements in that set.
In order to use a map, you must have a key. And that key must implement a "less than" operation. This determines the way the map is formed, such that the find, add, remove, increment, and decrement operations are efficient.
Generally the map internally uses a tree of some kind.
Standard implementation of map iterator operator++ watch in stl_tree.h:
_Self&
operator++() _GLIBCXX_NOEXCEPT
{
_M_node = _Rb_tree_increment(_M_node);
return *this;
}
_Rb_tree_increment implementation is discussed here