How to perform different basic traversals of graphs? - java

I am trying to perform an iterative breadth first traversal, iterative depth first traversal, and recursive depth first traversal of a given graph (using an adjacency matrix).
In its current state, my program outputs various wrong answers.
Here's some examples.
I am expecting
From Node A
DFS (iterative): A B H C D E I F G
DFS (recursive): A B H C D E I F G
BFS (iterative): A B D I H C E F G
but am instead getting
From Node A
DFS (iterative): A I D B H C F G E
DFS (recursive): A B H C F D E I G
BFS (iterative): A B D I H C E F G
I'm unsure if the problem with my program lies within the implementation of the traversals, or my implementation of some other part of the program. To be more specific, I'm not sure if my implementation connectNode or getNeighbors method is what is causing the incorrect output, or if it is my implementation of the traversals.
EDIT: Neighbors are supposed to be chosen in ascending order, if that's important. Perhaps this is part of the problem?
EDIT2: I added the new line of code, thanks to #HylianPikachu's suggestion. I now get full answers, but they are still not in the correct order.
EDIT3: I added the code to make it so the root node is checked as visited for bfs and recursive dfs. I think. I should also note that I was given parts of this code and told to fill in the rest. The use of the stack and queue are what I was told to use, even though there might be better options.
EDIT4: Added what was suggested, and now, the Iterative BFS works and gets the correct result. However, both DSF searches still do not work. I modified the results of the program above, to show this.
import java.util.*;
public class GraphM {
public Node rootNode;
public List<Node> nodes = new ArrayList<Node>(); // nodes in graph
public int[][] adjMatrix; // adjacency Matrix
public void setRootNode(Node n) {
rootNode = n;
}
public Node getRootNode() {
return rootNode;
}
public void addNode(Node n) {
nodes.add(n);
}
// This method connects two nodes
public void connectNode(Node src, Node dst) {
if(adjMatrix == null) {
adjMatrix = new int[nodes.size()][nodes.size()];
}
adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
adjMatrix[nodes.indexOf(dst)][nodes.indexOf(src)] = 1;
}
// Helper method to get one unvisited node from a given node n.
private Node getUnvisitedChildNode(Node n) {
int index = nodes.indexOf(n);
int size = adjMatrix.length;
for (int j = 0; j < size; j++)
if (adjMatrix[index][j] == 1 && ((Node) nodes.get(j)).visited == false)
return nodes.get(j);
return null;
}
// get all neighboring nodes of node n.
public List<Node> getNeighbors(Node n) {
List<Node> neighbors = new ArrayList<Node>();
for(int i = 0; i < nodes.size(); i ++) {
if (adjMatrix[nodes.indexOf(n)][i] == 1) {
neighbors.add(nodes.get(i));
}
Collections.sort(neighbors);
}
return neighbors;
}
// Helper methods for clearing visited property of node
private void reset() {
for (Node n : nodes)
n.visited = false;
}
// Helper methods for printing the node label
private void printNode(Node n) {
System.out.print(n.label + " ");
}
// BFS traversal (iterative version)
public void bfs() {
Queue<Node> queue = new LinkedList<Node>();
queue.add(rootNode);
while(!queue.isEmpty()) {
Node node = queue.poll();
printNode(node);
node.visited = true;
List<Node> neighbors = getNeighbors(node);
for ( int i = 0; i < neighbors.size(); i ++) {
Node n = neighbors.get(i);
if (n != null && n.visited != true) {
queue.add(n);
n.visited = true;
}
}
}
}
// DFS traversal (iterative version)
public void dfs() {
Stack<Node> stack = new Stack<Node>();
stack.add(rootNode);
while(!stack.isEmpty()){
Node node = stack.pop();
if(node.visited != true) {
printNode(node);
node.visited = true;
}
List<Node> neighbors = getNeighbors(node);
for (int i = 0; i < neighbors.size(); i++) {
Node n = neighbors.get(i);
if(n != null && n.visited != true) {
stack.add(n);
}
}
}
}
// DFS traversal (recursive version)
public void dfs(Node n) {
printNode(n);
n.visited = true;
List<Node> neighbors = getNeighbors(n);
for (int i = 0; i < neighbors.size(); i ++) {
Node node = neighbors.get(i);
if(node != null && node.visited != true) {
dfs(node);
}
}
}
// A simple Node class
static class Node implements Comparable<Node> {
public char label;
public boolean visited = false;
public Node(char label) {
this.label = label;
}
public int compareTo(Node node) {
return Character.compare(this.label, node.label);
}
}
// Test everything
public static void main(String[] args) {
Node n0 = new Node('A');
Node n1 = new Node('B');
Node n2 = new Node('C');
Node n3 = new Node('D');
Node n4 = new Node('E');
Node n5 = new Node('F');
Node n6 = new Node('G');
Node n7 = new Node('H');
Node n8 = new Node('I');
// Create the graph (by adding nodes and edges between nodes)
GraphM g = new GraphM();
g.addNode(n0);
g.addNode(n1);
g.addNode(n2);
g.addNode(n3);
g.addNode(n4);
g.addNode(n5);
g.addNode(n6);
g.addNode(n7);
g.addNode(n8);
g.connectNode(n0, n1);
g.connectNode(n0, n3);
g.connectNode(n0, n8);
g.connectNode(n1, n7);
g.connectNode(n2, n7);
g.connectNode(n2, n3);
g.connectNode(n3, n4);
g.connectNode(n4, n8);
g.connectNode(n5, n6);
g.connectNode(n5, n2);
// Perform the DFS and BFS traversal of the graph
for (Node n : g.nodes) {
g.setRootNode(n);
System.out.print("From node ");
g.printNode(n);
System.out.print("\nDFS (iterative): ");
g.dfs();
g.reset();
System.out.print("\nDFS (recursive): ");
g.dfs(g.getRootNode());
g.reset();
System.out.print("\nBFS (iterative): ");
g.bfs();
g.reset();
System.out.println("\n");
}
}
}

So, we already covered the first part of your question, but I'll restate it here for those who follow. Whenever working with graphs and an adjacency matrix, probably the best way to initialize elements in the array is "both ways."
Instead of just using the following, which would require a specific vertex be listed first in order to find the neighbors:
adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
Use this, which leads to searches that are agnostic of the vertex order:
adjMatrix[nodes.indexOf(src)][nodes.indexOf(dst)] = 1;
adjMatrix[nodes.indexOf(dst)][nodes.indexOf(src)] = 1;
Now, for ordering. You want the vertices to be outputted in order from "least" letter to "greatest" letter. We'll address each one of your data structures individually.
In BFS (iterative), you use a Queue. Queues are "first in, first out." In other words, the element that was least recently added to the Queue will be outputted first whenever you call queue.poll(). Thus, you need to add your nodes from least to greatest.
In DFS (iterative), you use a Stack. Stacks are "last in, first out." In other words, the element that was most recently added to the Stack will be outputted first whenever you call stack.pop(). Thus, you need to add your nodes from greatest to least.
In DFS (recursive), you use a List. Lists have no "in-out" ordering per se, as we can poll them in whatever order we want, but the easiest thing to do would just be to sort the List from least to greatest and output them in order.
With this in mind, we need to introduce protocol for sorting the graph. All three protocols use getNeighbors(), so we'll sort the outputted List immediately after we call that function. Lists can be ordered with the function Collections.sort(List l) from java.utils.Collections, but we first need to modify your nodes class so Java knows how to sort the Nodes. For further reading about the details of what I'm doing, you can look here, but this post is getting way longer than I intended already, so I'm going to just show the code here and let the interested explore the link themselves.
You would first tweak your Node class by implementing Comparable<Node> and adding the compareTo() function.
static class Node implements Comparable<Node>{
public char label;
public boolean visited = false;
public Node(char label) {
this.label = label;
}
#Override
public int compareTo(Node that) {
return Character.compare(this.label, that.label);
}
}
Then, in the cases in which we want to order the List from least to greatest, we can use Collections.sort(neighbors). When we want it from greatest to least, we can use Collections.sort(neighbors, Collections.reverseOrder()). Our final code will look like this:
// BFS traversal (iterative version)
public void bfs() {
Queue<Node> queue = new LinkedList<Node>();
queue.add(rootNode);
while(!queue.isEmpty()) {
Node node = queue.poll();
printNode(node);
node.visited = true;
List<Node> neighbors = getNeighbors(node);
//NEW CODE: Sort our neighbors List!
Collections.sort(neighbors);
for ( int i = 0; i < neighbors.size(); i ++) {
Node n = neighbors.get(i);
if (n != null && n.visited != true) {
queue.add(n);
n.visited = true;
}
}
}
}
// DFS traversal (iterative version)
public void dfs() {
Stack<Node> stack = new Stack<Node>();
stack.add(rootNode);
while(!stack.isEmpty()){
Node node = stack.pop();
if(node.visited != true) {
printNode(node);
node.visited = true;
}
List<Node> neighbors = getNeighbors(node);
//NEW CODE: Sort our neighbors List in reverse order!
Collections.sort(neighbors, Collections.reverseOrder());
for (int i = 0; i < neighbors.size(); i++) {
Node n = neighbors.get(i);
if(n != null && n.visited != true) {
stack.add(n);
}
}
}
}
// DFS traversal (recursive version)
public void dfs(Node n) {
printNode(n);
n.visited = true;
List<Node> neighbors = getNeighbors(n);
//NEW CODE: Sort our neighbors List!
Collections.sort(neighbors);
for (int i = 0; i < neighbors.size(); i ++) {
Node node = neighbors.get(i);
if(node != null && node.visited != true) {
dfs(node);
}
}
}

I would suggest splitting up your problem into smaller parts.
If you want to write a class for an undirected graph, first do that and test it a bit.
If you want to look if you can implement traversal, make sure your graph works first. You can also use guava, which lets you use MutableGraph (and lots more). Here is how to install it in case you're using IntelliJ and here is how to use graphs from guava.
Also remember to use a debugger to find out were your code goes wrong.

Related

Print out and sort values inside LinkedList

I wrote a recursive backtracking algorithm for the so-called "Coin Change Problem". I store the coin values (int) in a self-written LinkedList ("ll") and each of those LinkedLists is stored inside one master LinkedList ("ll_total"). Now, when I try to print out the LinkedLists inside the master LinkedList, all I get is "LinkedList#1e88b3c". Can somebody tell me how to modify the code, in order to print out the coin values properly?
I would also like the algorithm to chose the LinkedList with the least values stored inside, as it would represent the optimal coin combination for the "coin change problem".
import java.util.Scanner;
public class CoinChange_Backtracking {
static int[] coins = {3, 2, 1};
static int index_coins = 0;
static int counter = 0;
static LinkedList ll = new LinkedList();
static LinkedList ll_total = new LinkedList();
public static void main(String[] args) {
Scanner myInput = new Scanner(System.in);
int amount;
System.out.println("Put in the amount of money: ");
amount = myInput.nextInt();
if (amount < 101) {
//Start recursion and display result.
recursiveFunction(coins, amount, index_coins, ll);
ll_total.show_ll();
} else {
System.out.println("The value must be less than 100!");
}
}
public static LinkedList recursiveFunction(int[] coins, int amount, int index_coins, LinkedList ll) {
//The current coin is being omitted. (If index_coins + 1 is still within range.)
if ((index_coins + 1) < coins.length) {
ll = recursiveFunction(coins, amount, index_coins + 1, ll);
ll_total.insert_ll(ll);
for (int i = 0; i < counter; i++) {
ll.deleteAt(0);
}
counter = 0;
}
//The current coin is not being omitted. (If there is still some change left and value of change isn't lower than value of current coin.)
if (amount != 0) {
if (amount >= coins[index_coins]) {
ll.insert(coins[index_coins]);
counter++;
ll = recursiveFunction(coins, amount - coins[index_coins], index_coins, ll);
}
}
return ll;
}
}
public class LinkedList {
Node head;
public void insert(int data) {
Node node = new Node();
node.data = data;
node.next = null;
if (head == null) {
head = node;
} else {
Node n = head;
while(n.next != null) {
n = n.next;
}
n.next = node;
}
}
public void insert_ll(LinkedList ll) {
Node node = new Node();
node.ll = ll;
node.next = null;
if (head == null) {
head = node;
} else {
Node n = head;
while(n.next != null) {
n = n.next;
}
n.next = node;
}
}
public void deleteAt(int index) {
if(index == 0) {
head = head.next;
} else {
Node n = head;
Node n1 = null;
for (int i = 0; i < index - 1; i++) {
n = n.next;
}
n1 = n.next;
n.next = n1.next;
n1 = null;
}
}
public void show() {
Node node = head;
while(node.next != null) {
System.out.println(node.data);
node = node.next;
}
System.out.println(node.data);
}
public void show_ll() {
Node node = head;
while(node.next != null) {
System.out.println(node.ll);
node = node.next;
}
System.out.println(node.ll);
}
//A toString method I tried to implement. Causes an array error.
/*
public String toString() {
Node n = head.next;
String temp = "";
while (n != null) {
temp = temp + n.data + " ";
n = n.next;
}
return temp;
}
*/
}
public class Node {
int data;
LinkedList ll;
Node next;
}
To answer your question. You are printing the linked list object, see here System.out.println(node.ll);
There are several ways to do it right. One approach is to question why you use Node and LinkedList the way you do ? A node can have a linked list and a linked list can have a node, I believe this is not really what you wanted. Maybe you can make it work, but from a design point of view in my experience it is not good. I find it confusing and it's a great source of bugs.
I try to list some points that caught my eye (or that my IDE had caught for my eyes).
You are not closing the Scanner object. Just close it at the end of the program or use the try-with-resources.
As mentioned before you have linked list that has a node and a node that has a linked list. You are not using that correctly in your program. I recommend to review that approach. It is error prone.
Also simply use the LinkedList of the Java library unless you have a good reason not to. It works fine and offers all you need.
You use many static, global (within the scope of the package) variables. In this case I think you can avoid that. coins does not need to be given as a parameter every time. It should be an immutable object. It is not supposed to change.
...
And I am not sure if it is a backtracking algorithm. It is certainly tree recursive. This just as a side note.
I'd like to propose a solution that looks similar to yours. I'd probably do it differently my way, but then it probably takes time to understand it. I try to adopt your style, which I hope helps. I simplified the program.
In order to print the result, simply write a helper function.
The linked list is an object. You have to make a copy of the list every time you call the recursion in order to work on a dedicated object. Otherwise you modify the same object while recursing different paths.
You can simply use a list of lists. A global list of lists (within package scope), and a list of which you make a copy every time you recurse. When you reach a good base case you add it to the global list. Otherwise just ignore.
import java.util.LinkedList;
import java.util.Scanner;
public class CoinChangeBacktracking {
static final int[] COINS = {3, 2, 1};
static final LinkedList<LinkedList<Integer>> changes = new LinkedList<>();
public static void main(String[] args) {
Scanner myInput = new Scanner(System.in);
int amount;
System.out.println("Put in the amount of money: ");
amount = myInput.nextInt();
if (amount < 101) {
// Start recursion and display result.
recursiveFunction(amount, 0, new LinkedList<>());
print(changes);
} else {
System.out.println("The value must be less than 100!");
}
myInput.close();
}
static void recursiveFunction(int amount, int index,
LinkedList<Integer> list) {
// exact change, so add it to the solution
if (amount == 0) {
changes.add(list);
return;
}
// no exact change possible
if (amount < 0 || index >= COINS.length) {
return;
}
// explore change of amount without current coin
recursiveFunction(amount, index + 1, new LinkedList<>(list));
// consider current coin for change and keep exploring
list.add(COINS[index]);
recursiveFunction(amount - COINS[index], index, new LinkedList<>(list));
}
static void print(LinkedList<LinkedList<Integer>> ll) {
for (LinkedList<Integer> list : ll) {
for (Integer n : list) {
System.out.print(n + ", ");
}
System.out.println();
}
}
}

How do I parallelise this DFS?

I have a binary tree where each node has the value of 0 or 1, and each path from the root to leaf node represents a binary string of a certain length. The aim of the program is to find all possible binary String (i.e. all possible paths from root to leaf). Now I want to parallelise it, so that it can use multiple cores. I assume I need to somehow split up the workload on the branch nodes, but I have no idea where to begin. I am looking at the ForkJoin functionality, but I have no idea how to split up the work and then combine it.
public class Tree{
Node root;
int levels;
Tree(int v){
root = new Node(v);
levels = 1;
}
Tree(){
root = null;
levels = 0;
}
public static void main(String[] args){
Tree tree = new Tree(0);
populate(tree, tree.root, tree.levels);
tree.printPaths(tree.root);
}
public static void populate(Tree t, Node n, int levels){
levels++;
if(levels >6){
n.left = null;
n.right = null;
}
else{
t.levels = levels;
n.left = new Node(0);
n.right = new Node(1);
populate(t, n.left, levels);
populate(t, n.right, levels);
}
}
void printPaths(Node node)
{
int path[] = new int[1000];
printPathsRecur(node, path, 0);
}
void printPathsRecur(Node node, int path[], int pathLen)
{
if (node == null)
return;
/* append this node to the path array */
path[pathLen] = node.value;
pathLen++;
/* it's a leaf, so print the path that led to here */
if (node.left == null && node.right == null)
printArray(path, pathLen);
else
{
/* otherwise try both subtrees */
printPathsRecur(node.left, path, pathLen);
printPathsRecur(node.right, path, pathLen);
}
}
/* Utility function that prints out an array on a line. */
void printArray(int ints[], int len)
{
int i;
for (i = 0; i < len; i++)
{
System.out.print(ints[i] + " ");
}
System.out.println("");
}
}
You can use Thread Pools to efficiently split the load between separate threads.
One possible approach:
ExecutorService service = Executors.newFixedThreadPool(8);
Runnable recursiveRunnable = new Runnable() {
#Override
public void run() {
//your recursive code goes here (for every new branch you have a runnable (recommended to have a custom class implementing Runnable))
}
};
service.execute(recursiveRunnable);
However, this approach is no longer a deep first search, since you list the sub-branches for your position before searching through the first one. In my understanding, DFS is a strictly linear approach and is therefore not entirely parallelizable (feel free to correct me in the comments though).

Getting all children on same level of binary tree

I want to display all children on the same level of tree. So if I've got a tree like this:
A
B C D
E F G H I J
for example, level 3 would return E, F, G, H, I and J nodes. I have a method inside TreeNode class, that returns all children of given node, so I thought about doing something like this:
static Collection<ITreeNode<IProduct>> getOnLevel(ITree<IProduct> tree, int level)
{
Collection<ITreeNode<IProduct>> temp;
int i;
Iterator<ITreeNode<IProduct>> iterator = tree.getRoot().getChildren().iterator();
for(i=0; i<=(level); i++)
{
while(iterator.hasNext())
{
ITreeNode<IProduct> elem = iterator.next();
if(i == (level))
{
temp = elem.getChildren();
return temp;
}
}
}
return tree.getRoot().getChildren();
}
but then I realized that I just iterate through first level children, so I probably have to do this somehow recursively?
Thanks in advance, Amar!
You could either do it recursively or with iterations, it's up to you.
I find recursive solution slightly easier to read. It would look like this:
static Collection<ITreeNode<IProduct>> getOnLevel(
ITree<IProduct> tree
, int desiredLevel
) {
List<ITreeNode<IProduct>> result = new ArrayList<>();
findOneLevel(tree.getRoot(), desiredLevel, 0, result);
return result;
}
static void findOnLevel(
ITreeNode<IProduct> node
, int desiredLevel
, int currentLevel
, List<ITreeNode<IProduct>> result
) {
if (currentLevel == desiredLevel) {
result.add(node);
return;
}
Iterator<ITreeNode<IProduct>> iterator = node.getChildren().iterator();
while(iterator.hasNext()) {
findOnLevel(iterator.next(), desiredLevel, currentLevel+1, result);
}
}
The approach is very simplistic: top-level method makes a list in which to store the result, and calls the recursive findOnLevel. Recursive method checks if we have reached the desired level, and adds the current node to the result if we did. Otherwise, we go through all children of the current node in a recursive invocation, passing currentLevel+1 for the new current level.

Lowest Common Ancestor in a Binary tree

Following is my implementation of lowest common ancestor for a Binary Search tree. I have two question:
1) The time complexity is O(n) and space complexity is O(n) worse case but O(logn) average case for both time and space if BST is balanced. is that correct?
2) How can i extend my solution to binary trees and not just binary search trees.
Hope to hear from you soon.
//The function findOrQueue is to enqueue all elements upto target node to a queue
public void findOrQueue(Node target, Node top, LLQueue q) {
int cmp = target.getData().compareTo(top.getData());
if(cmp == 0) {
q.enqueue(top);
return ;
}
else if (cmp < 0) {
q.enqueue(top);
findOrQueue(target, top.getLeftChild(),q);
}
else {
q.enqueue(top);
findOrQueue(target, top.getRightChild(),q);
}
}
public Node LCA(Node n1, Node n2) throws QueueEmptyException {
LLQueue q1 = new LLQueue();
LLQueue q2 = new LLQueue();
findOrQueue(n1,getRoot(),q1);
findOrQueue(n2,getRoot(),q2);
Node t = null;
while (!q1.isEmpty() && !q2.isEmpty()) {
Node t1 = (Node)q1.dequeue();
Node t2 = (Node)q2.dequeue();
if(t1.getData() != t2.getData()) {
return t;
}
else t = t1;
}
if(q1.isEmpty() && q2.isEmpty())
return null;
else
return t;
}
The key to extending your solution to a general binary tree seems to lie in finding the path connecting the root and a target node. Once obtaining this path, you could easily modify your LCA function to find the lowest common ancestor.
The following crude implementation makes use of LinkedBlockingQueue in package java.util.concurrent.* and Stack in java.util.* - however, any other ordinary queue and stack would also do the trick. The code assumes that the target nodes exists in the tree.
public static void findPath2(Node target,Node top, Stack<Node> path){
LinkedBlockingQueue<Node> q1 = new LinkedBlockingQueue<Node>(), q2 = new LinkedBlockingQueue<Node>(),
q3 = new LinkedBlockingQueue<Node>();
Node n = top;
Node parrent = null;
while(n.getData().compareTo(target.getData())!= 0){
parrent = n;
if(n.right != null){
q2.add(n.right);
q3.add(parrent);
}
if(n.left != null){
q2.add(n.left);
q3.add(parrent);
}
n = q2.remove();
q1.add(n);
}
Stack<Node> s3 = new Stack<Node>(), s2 = new Stack<Node>();
while(!q3.isEmpty()){
s3.push(q3.remove());
}
for(int i = 1; i <= q2.size(); i++)
s3.pop();
while(!s3.isEmpty())
s2.push(s3.pop());
while(!q1.isEmpty())
s3.push(q1.remove());
LinkedBlockingQueue<Node> temp = new LinkedBlockingQueue<Node>();
while(!s2.isEmpty())
temp.add(s2.pop());
while(!temp.isEmpty())
s2.push(temp.remove());
path.push(s3.pop());
path.push(s2.pop());
while(!s3.isEmpty()){
n = s3.peek();
while(n.getData().compareTo(path.peek().getData()) != 0 && !s3.isEmpty()){
s3.pop();
s2.pop();
if(!s3.isEmpty())
n = s3.peek();
}
if(!s3.isEmpty()){
s3.pop();
path.push(s2.pop());
}
}
}

Lowest Common Ancestor of a Binary Tree

This is a popular interview question and the only article I can find on the topic is one from TopCoder. Unfortunately for me, it looks overly complicated from an interview answer's perspective.
Isn't there a simpler way of doing this other than plotting the path to both nodes and deducing the ancestor? (This is a popular answer, but there's a variation of the interview question asking for a constant space answer).
A simplistic (but much less involved version) could simply be (.NET guy here Java a bit rusty, so please excuse the syntax, but I think you won't have to adjust too much). This is what I threw together.
class Program
{
static void Main(string[] args)
{
Node node1 = new Node { Number = 1 };
Node node2 = new Node { Number = 2, Parent = node1 };
Node node3 = new Node { Number = 3, Parent = node1 };
Node node4 = new Node { Number = 4, Parent = node1 };
Node node5 = new Node { Number = 5, Parent = node3 };
Node node6 = new Node { Number = 6, Parent = node3 };
Node node7 = new Node { Number = 7, Parent = node3 };
Node node8 = new Node { Number = 8, Parent = node6 };
Node node9 = new Node { Number = 9, Parent = node6 };
Node node10 = new Node { Number = 10, Parent = node7 };
Node node11 = new Node { Number = 11, Parent = node7 };
Node node12 = new Node { Number = 12, Parent = node10 };
Node node13 = new Node { Number = 13, Parent = node10 };
Node commonAncestor = FindLowestCommonAncestor(node9, node12);
Console.WriteLine(commonAncestor.Number);
Console.ReadLine();
}
public class Node
{
public int Number { get; set; }
public Node Parent { get; set; }
public int CalculateNodeHeight()
{
return CalculateNodeHeight(this);
}
private int CalculateNodeHeight(Node node)
{
if (node.Parent == null)
{
return 1;
}
return CalculateNodeHeight(node.Parent) + 1;
}
}
public static Node FindLowestCommonAncestor(Node node1, Node node2)
{
int nodeLevel1 = node1.CalculateNodeHeight();
int nodeLevel2 = node2.CalculateNodeHeight();
while (nodeLevel1 > 0 && nodeLevel2 > 0)
{
if (nodeLevel1 > nodeLevel2)
{
node1 = node1.Parent;
nodeLevel1--;
}
else if (nodeLevel2 > nodeLevel1)
{
node2 = node2.Parent;
nodeLevel2--;
}
else
{
if (node1 == node2)
{
return node1;
}
node1 = node1.Parent;
node2 = node2.Parent;
nodeLevel1--;
nodeLevel2--;
}
}
return null;
}
}
Constant space answer: (although not necessarily efficient).
Have a function findItemInPath(int index, int searchId, Node root)
then iterate from 0 .. depth of tree, finding the 0-th item, 1-th item etc. in both search paths.
When you find i such that the function returns the same result for both, but not for i+1,
then the i-th item in the path is the lowest common ancestor.
The main reason why the article's solutions are more complicated is that it is dealing with a two-stage problem- preprocessing and then queries- while from your question it sounds like you're only doing one query so preprocessing doesn't make sense. It's also dealing with arbitrary trees rather than binary trees.
The best answer will certainly depend on details about the tree. For many kinds of trees, the time complexity is going to be O(h) where h is the tree's height. If you've got pointers to parent nodes, then the easy "constant-space" answer is, as in Mirko's solution, to find both nodes' height and compare ancestors of the same height. Note that this works for any tree with parent links, binary or no. We can improve on Mirko's solution by making the height function iterative and by separating the "get to the same depth" loops from the main loop:
int height(Node n){
int h=-1;
while(n!=null){h++;n=n.parent;}
return h;
}
Node LCA(Node n1, Node n2){
int discrepancy=height(n1)-height(n2);
while(discrepancy>0) {n1=n1.parent;discrepancy--;}
while(discrepancy<0) {n2=n2.parent;discrepancy++;}
while(n1!=n2){n1=n1.parent();n2=n2.parent();}
return n1;
}
The quotation marks around "constant-space" are because in general we need O(log(h)) space to store the heights and the difference between them (say, 3 BigIntegers). But if you're dealing with trees with heights too large to stuff in a long, you likely have other problems to worry about that are more pressing than storing a couple nodes' heights.
If you have a BST, then you can easily take a common ancestor (usu. starting with root) and check its children to see whether either of them is a common ancestor:
Node LCA(Node n1, Node n2, Node CA){
while(true){
if(n1.val<CA.val & n2.val<CA.val) CA=CA.left;
else if (n1.val>CA.val & n2.val>CA.val) CA=CA.right;
else return CA;
}
}
As Philip JF mentioned, this same idea can be used in any tree for a constant-space algorithm, but for a general tree doing it this way will be really slow since figuring out repeatedly whether CA.left or CA.right is a common ancestor will repeat a lot of work, so you'd normally prefer to use more space to save some time. The main way to make that tradeoff would be basically the algorithm you've mentioned (storing the path from root).
It matters what kind of tree you are using. You can always tell if a node is the ancestor of another node in constant space, and the top node is always a common ancestor, so getting the Lowest Common Ancestor in constant space just requires iterating your way down. On a binary search tree this is pretty easy to do fast, but it will work on any tree.
Many different trade offs are relevant for this problem, and the type of tree matters. The problem tends is much easier if you have pointers to parent nodes, and not just to children (Mirko's code uses this)
See also:
http://en.wikipedia.org/wiki/Lowest_common_ancestor
The obvious solution, that uses log(n) space, (n is the number of nodes) is the algorithm you mentioned. Here's an implementation. In the worst case it takes O(n) time (imagine that one of the node you are searching common ancestor for includes the last node).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class Node
{
private static int counter = 0;
private Node left = null;
private Node right = null;
public int id = counter++;
static Node constructTreeAux(int depth)
{
if (depth == 0)
return null;
Node newNode = new Node();
newNode.left = constructTree(depth - 1);
newNode.right = constructTree(depth - 1);
return newNode;
}
public static Node constructTree(int depth)
{
if (depth == 0)
return null;
Node root = new Node();
root.left = constructTreeAux(depth - 1);
root.right = constructTreeAux(depth - 1);
return root;
}
private List<Node> findPathAux(List<Node> pathSoFar, int searchId)
{
if (this.id == searchId)
{
if (pathSoFar == null)
pathSoFar = new List<Node>();
pathSoFar.Add(this);
return pathSoFar;
}
if (left != null)
{
List<Node> result = left.findPathAux(null, searchId);
if (result != null)
{
result.Add(this);
return result;
}
}
if (right != null)
{
List<Node> result = right.findPathAux(null, searchId);
if (result != null)
{
result.Add(this);
return result;
}
}
return null;
}
public static void printPath(List<Node> path)
{
if (path == null)
{
Console.Out.WriteLine(" empty path ");
return;
}
Console.Out.Write("[");
for (int i = 0; i < path.Count; i++)
Console.Out.Write(path[i] + " ");
Console.Out.WriteLine("]");
}
public override string ToString()
{
return id.ToString();
}
/// <summary>
/// Returns null if no common ancestor, the lowest common ancestor otherwise.
/// </summary>
public Node findCommonAncestor(int id1, int id2)
{
List<Node> path1 = findPathAux(null, id1);
if (path1 == null)
return null;
path1 = path1.Reverse<Node>().ToList<Node>();
List<Node> path2 = findPathAux(null, id2);
if (path2 == null)
return null;
path2 = path2.Reverse<Node>().ToList<Node>();
Node commonAncestor = this;
int n = path1.Count < path2.Count? path1.Count : path2.Count;
printPath(path1);
printPath(path2);
for (int i = 0; i < n; i++)
{
if (path1[i].id == path2[i].id)
commonAncestor = path1[i];
else
return commonAncestor;
}
return commonAncestor;
}
private void printTreeAux(int depth)
{
for (int i = 0; i < depth; i++)
Console.Write(" ");
Console.WriteLine(id);
if (left != null)
left.printTreeAux(depth + 1);
if (right != null)
right.printTreeAux(depth + 1);
}
public void printTree()
{
printTreeAux(0);
}
public static void testAux(out Node root, out Node commonAncestor, out int id1, out int id2)
{
Random gen = new Random();
int startid = counter;
root = constructTree(5);
int endid = counter;
int offset = gen.Next(endid - startid);
id1 = startid + offset;
offset = gen.Next(endid - startid);
id2 = startid + offset;
commonAncestor = root.findCommonAncestor(id1, id2);
}
public static void test1()
{
Node root = null, commonAncestor = null;
int id1 = 0, id2 = 0;
testAux(out root, out commonAncestor, out id1, out id2);
root.printTree();
commonAncestor = root.findCommonAncestor(id1, id2);
if (commonAncestor == null)
Console.WriteLine("Couldn't find common ancestor for " + id1 + " and " + id2);
else
Console.WriteLine("Common ancestor for " + id1 + " and " + id2 + " is " + commonAncestor.id);
}
}
}
The bottom up approach described here is an O(n) time, O(1) space approach:
http://www.leetcode.com/2011/07/lowest-common-ancestor-of-a-binary-tree-part-i.html
Node *LCA(Node *root, Node *p, Node *q) {
if (!root) return NULL;
if (root == p || root == q) return root;
Node *L = LCA(root->left, p, q);
Node *R = LCA(root->right, p, q);
if (L && R) return root; // if p and q are on both sides
return L ? L : R; // either one of p,q is on one side OR p,q is not in L&R subtrees
}

Categories

Resources