I'm trying to implement a BFS to find all prerequisites required before a certain course can be taken. My public List<Node> computeAllPrereqs(String courseName) method is where my code is messing up. Can someone look at my method and help me find the issue? I figured the finish node would be none, because my the courses in my graph all lead to none. The graph is not cyclic.
This is the text file that I'm passing into the constructor, the first column is the course and followed by the course are its prerequisites:
CS1 None
CS2 CS1
CS3 CS2
CS4 CS1
CS5 CS3 CS4
CS6 CS2 CS4
.
public class Graph {
private Map<String, Node> graph;
public Graph(String filename) throws FileNotFoundException {
// open the file for scanning
File file = new File(filename);
Scanner in = new Scanner(file);
// create the graph
graph = new HashMap<String, Node>();
// loop over and parse each line in the input file
while (in.hasNextLine()) {
// read and split the line into an array of strings
// where each string is separated by a space.
Node n1, n2;
String line = in.nextLine();
String[] fields = line.split(" ");
for(String name: fields){
if (!graph.containsKey(fields[0]))
{
n1 = new Node(name);
graph.put(name, n1);
}
else{
n2 = new Node(name);
graph.get(fields[0]).addNeighbor(n2);
}
}
}
in.close();
}
public List<Node> computeAllPrereqs(String courseName){
// assumes input check occurs previously
Node startNode;
startNode = graph.get(courseName);
// prime the dispenser (queue) with the starting node
List<Node> dispenser = new LinkedList<Node>();
dispenser.add(startNode);
// construct the predecessors data structure
Map<Node, Node> predecessors = new HashMap<Node,Node>();
// put the starting node in, and just assign itself as predecessor
predecessors.put(startNode, startNode);
// loop until either the finish node is found, or the
// dispenser is empty (no path)
while (!dispenser.isEmpty()) {
Node current = dispenser.remove(0);
if (current == new Node(null)) {
break;
}
// loop over all neighbors of current
for (Node nbr : current.getNeighbors()) {
// process unvisited neighbors
if(!predecessors.containsKey(nbr)) {
predecessors.put(nbr, current);
dispenser.add(nbr);
}
}
}
return constructPath(predecessors, startNode, null);
}
private List<Node> constructPath(Map<Node,Node> predecessors,
Node startNode, Node finishNode) {
// use predecessors to work backwards from finish to start,
// all the while dumping everything into a linked list
List<Node> path = new LinkedList<Node>();
if(predecessors.containsKey(finishNode)) {
Node currNode = finishNode;
while (currNode != startNode) {
path.add(0, currNode);
currNode = predecessors.get(currNode);
}
path.add(0, startNode);
}
return path;
}
}
Here's the node class I used to make the nodes and neighbors in the graph:
public class Node {
/*
* Name associated with this node.
*/
private String name;
/*
* Neighbors of this node are stored as a list (adjacency list).
*/
private List<Node> neighbors;
public Node(String name) {
this.name = name;
this.neighbors = new LinkedList<Node>();
}
public String getName() {
return name;
}
public void addNeighbor(Node n) {
if(!neighbors.contains(n)) {
neighbors.add(n);
}
}
public List<Node> getNeighbors() {
return new LinkedList<Node>(neighbors);
}
#Override
public String toString() {
String result;
result = name + ": ";
for(Node nbr : neighbors) {
result = result + nbr.getName() + ", ";
}
// remove last comma and space, or just spaces in the case of no neighbors
return (result.substring(0, result.length()-2));
}
#Override
public boolean equals(Object other) {
boolean result = false;
if (other instanceof Node) {
Node n = (Node) other;
result = this.name.equals(n.name);
}
return result;
}
#Override
public int hashCode() {
return this.name.hashCode();
}
}
Here's my test class:
public class Prerequisite {
/**
* Main method for the driver program.
*
* #param args the name of the file containing the course and
* prerequisite information
*
* #throws FileNotFoundException if input file not found
*/
public static void main(String[] args) throws FileNotFoundException {
// Check for correct number of arguments
if(args.length != 1) {
String us = "Usage: java Prerequisite <input file>";
System.out.println(us);
return;
}
// create a new graph and load the information
// Graph constructor from lecture notes should
// be modified to handle input specifications
// for this lab.
Graph graph = new Graph(args[0]);
// print out the graph information
System.out.println("Courses and Prerequisites");
System.out.println("=========================");
System.out.println(graph);
// ASSUMPTION: we assume there are no cycles in the graph
// Part I:
// compute how many (and which) courses must be taken before it is
// possible to take any particular course
System.out.println("How many courses must I take "
+ "before a given course?!?!?");
for(String name : graph.getAllCourseNames()) {
List<Node> allPrereqs = graph.computeAllPrereqs(name);
System.out.print(String.valueOf(allPrereqs.size()));
System.out.print(" courses must be taken before " + name + ": ");
for(Node el : allPrereqs) {
System.out.print(el.getName() + " ");
}
System.out.println();
}
}
When I run this test my output is:
0 courses must be taken before CS1:
0 courses must be taken before CS3:
0 courses must be taken before CS2:
0 courses must be taken before CS5:
0 courses must be taken before CS4:
0 courses must be taken before CS6:
it should be:
0 courses must be taken before CS1:
2 courses must be taken before CS3: CS1 CS2
1 courses must be taken before CS2: CS1
4 courses must be taken before CS5: CS1 CS3 CS2 CS4
1 courses must be taken before CS4: CS1
3 courses must be taken before CS6: CS1 CS2 CS4
I know I'm posting a lot of code but I don't want to have to edit in more code in later if it is needed to help fix my bug.
As a note, prerequisites are more effectively determined with depth first search (DFS) such that a topological ordering can be realized.
During graph construction, when linking neighbors, "lookalikes" are being linked rather than existing graph nodes themselves, so the resulting graph is actually unconnected. To resolve that issue, link the actual nodes of the graph to each other.
if (!graph.containsKey(fields[0]))
{
n1 = new Node(name);
graph.put(name, n1);
}
else{
if(graph.containsKey(name)) {
n2 = graph.get(name);
}
else {
n2 = new Node(name);
graph.put(name, n2);
}
graph.get(fields[0]).addNeighbor(n2);
}
An added benefit of the above code snippet is that it adds the terminal "None" node to the graph.
A single line path can't be constructed because a course may have more than one prerequisite. For instance, CS6 depends on both CS2 and CS4. In the constructPath method, the terminal condition would fire after only following one of those paths. So given the current structure of the program, about the best you can achieve is to output the set of prerequisite courses rather than the single line path.
private List<Node> constructPath(Map<Node,Node> predecessors,
Node startNode, Node finishNode) {
/*
// use predecessors to work backwards from finish to start,
// all the while dumping everything into a linked list
List<Node> path = new LinkedList<Node>();
if(predecessors.containsKey(finishNode)) {
Node currNode = finishNode;
while (currNode != startNode) {
path.add(0, currNode);
currNode = predecessors.get(currNode);
}
path.add(0, startNode);
}
*/
Set<Node> prereqs = new HashSet<Node>(predecessors.keySet());
prereqs.remove(graph.get("None"));
return new ArrayList<Node>(prereqs);
}
Given this approach, neither arguments startNode nor finishNode are necessary and the creation of the predecessor map is redundant.
Lastly, a course is not a prerequisite of itself so it's incorrect to assign itself as a predecessor.
// put the starting node in, and just assign itself as predecessor
// predecessors.put(startNode, startNode);
Given these modifications, this was the output
0 courses must be taken before CS1:
1 courses must be taken before CS2: CS1
2 courses must be taken before CS3: CS2 CS1
1 courses must be taken before CS4: CS1
4 courses must be taken before CS5: CS4 CS2 CS3 CS1
3 courses must be taken before CS6: CS4 CS2 CS1
To improve on the code, a TreeSet (http://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html) can be used instead of a HashSet to output the prerequisites in a uniform order. To use TreeSet however, the Node class will have to be augmented to implement Comparable (How to implement the Java comparable interface?).
And again, if outputting the set of prerequisites is not satisfactory, consider using DFS instead to generate a topological ordering (http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/topoSort.htm).
if (current == new Node(null)) {
break;
}
You should have used continue instead of break.
Even if you have encountered null-node, you can have more normal nodes on the queue, which have longer path to the null-node.
You can realize it when you analyze graph starting from CS5
Related
I have an ArrayList of vectors which I am trying to find all possible distinct paths (without repeating the same node). But the ArrayList keeping record of the path seems to be shared and therefore leaking across all paths. My question is, is there an alternative list type that I should be using which prevents this? Thanks.
The available paths are (A->B, B->C, C->D, D->A, A->C)
For a start, I decided to deal with just paths with starting node A.
The output should be:
ABCD
ACD
I've written the code below with additional output to check what is going on as it is not working right. The output for the code below is:
Previous vector: SA
vector history size: 1
Next vector: AB
Previous vector: AB
vector history size: 2
Next vector: BC
Previous vector: BC
vector history size: 3
Next vector: CD
Previous vector: CD
vector history size: 4
Next vector: DA
Looped - ABCDA
ABCDA
ABC
AB
vector history size: 4
Next vector: AC
Looped - AC
AC
As you can see the last 4 lines from the second iteration of the while loop are wrong because the vector history size should be 1 (SA only) and "C" should not have been visited before but somehow the ArrayList for vector history from the first while loop's recursion has leaked over. Is this suppose to happen and what alternatives are there?
public static void main(String[] args) {
ArrayList<vector> vectorList = new ArrayList();
vectorList.add(new vector("A", "B"));
vectorList.add(new vector("B", "C"));
vectorList.add(new vector("C", "D"));
vectorList.add(new vector("D", "A"));
vectorList.add(new vector("A", "C"));
//to record vector history and initialize the start vector
ArrayList<vector> vectorHistory = new ArrayList();
//record the path
String path = "";
//method call
pathFinder(new vector("S", "A"), vectorHistory, vectorList, path);
}
//Recursive method. moves one node forward until there is no more nodes OR the next node is the same as a previously taken node
public static void pathFinder(vector prevVector, ArrayList<vector> vectorHistory, ArrayList<vector> vectorList, String path) {
vectorHistory.add(prevVector);
//add the current node to the path
path = path + prevVector.child;
System.out.println("Previous vector: "+ prevVector.parent+prevVector.child);
// search if there is a next node. looped to search all possible paths
while (vectorList.contains(prevVector)) {
System.out.println("vector history size: "+ vectorHistory.size());
//retrieve the next vector
vector nextVector = vectorList.get(vectorList.indexOf(prevVector));
System.out.println("Next vector: " + nextVector.parent + nextVector.child);
//remove current node so while loop can move to another possible path
vectorList.remove(vectorList.indexOf(prevVector));
//check if the next node has already been visited before
if (vectorHistory.contains(nextVector)) {
path=path+nextVector.child;
System.out.println("Looped - " + path);
} else {
pathFinder(nextVector, vectorHistory, vectorList, path);
}
}
System.out.println(path);
}
/*object vector */
public static class vector {
String parent, child;
public vector(String parent, String child) {
this.parent = parent;
this.child = child;
}
#Override
public boolean equals(Object o) {
vector x = (vector) o;
if (x.parent.equalsIgnoreCase(child)) {
return true;
} else {
return false;
}
}
}
Java is "pass by value" so it passes a copy of the reference of the actual object. But it is a bit strange to understand when using a collection because the copy of the reference that is sent points to the same memory as the original one!
So if you pass a list to a method and you modify the method in the list it modifies the original list.
For example:
method b(List aList){
aList.add(new Object());
}
method c(List aList){
aList=new ArrayList ();
aList.add(new Object());
}
List a=new ArrayList();
b(a); -> it will add an object to a;
c(a); -> it will not add an object to a or modify it in any way
So in your case when you call
pathFinder(nextVector, vectorHistory, vectorList, path); you don't get that "stack" behaviour you expect with recursion because the successor calls of path finder modify the lists for the previous ones.
You can modify your call like that:
pathFinder(nextVector, new ArrayList<>(vectorHistory), new ArrayList<>(vectorList), path);
to avoid that problem but it will lose some additional memory copying the whole list every time ;) and it still won't get the results you want because I guess you have another error in the algorithm.
Your program seems very strange ;) The magic you are doing with vector's equal is not great because you cannot actually compare two equal objects. For example with your code AB is different than AB (which is not the case). So for places you have been you don't need vectors but points. So here is a bit modified program just to illustrate what I mean. It is still far from perfect:
import java.util.ArrayList;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
List<MyVector> vectorList = new ArrayList<MyVector>();
vectorList.add(new MyVector("A", "B"));
vectorList.add(new MyVector("B", "C"));
vectorList.add(new MyVector("C", "D"));
vectorList.add(new MyVector("D", "A"));
vectorList.add(new MyVector("A", "C"));
List<String> pointsHistory=new ArrayList<String>();
//to record points that have been visited
//record the path
String path = "";
//method call
pathFinder(new MyVector(null, "A"), pointsHistory, vectorList, path);
}
//Recursive method. moves one node forward until there is no more nodes OR the next node is the same as a previously taken node
public static void pathFinder(MyVector prevVector, List<String> pointsHistory, List<MyVector> vectorList, String path) {
pointsHistory.add(prevVector.child);
//add the current node to the path
path = path + prevVector.child;
// search if there is a next node. looped to search all possible paths -> no need to do magic with equals
for(MyVector vector:vectorList)
if(vector.parent.equals(prevVector.child)) {
System.out.println("Next vector: " + vector.parent + vector.child);
if (pointsHistory.contains(vector.child)) {
System.out.println("Result " + path); //You get the end result here -> if we have reached a loop
} else {
pointsHistory.add(vector.child);
pathFinder(vector, new ArrayList<>(pointsHistory), vectorList, path);
}
}
}
/*object vector */
public static class MyVector {
String parent, child;
public MyVector(String parent, String child) {
this.parent = parent;
this.child = child;
}
}
}
You will get the results you want like that. See how I copy the visited points here: pathFinder(vector, new ArrayList<>(pointsHistory), vectorList, path); in order for that the work. And please name your classes with a Capital letter.
Hi I have a tree in which I would like to get paths from the initial (root) node to all leaves.
I found several algortithms that list (all) apths betwenn any given two nodes within a graph (for example this SO question:
Graph Algorithm To Find All Connections Between Two Arbitrary Vertices)
For binary tree there also exists an algorithm
http://techieme.in/print-all-paths-in-a-tree/
but I work on a tree with various branching factors.
Is there any better way of achieving what I want to do than traversing the tree once in order to get all leaves and then run the algorithm above for all leaves combined with the initial node?
I was thinking about implementing simple DFS extended by some additional stack containing all nodes alongt he path to a single leaf and then listing all sentences by looping through these stacks.
ArrayList<GrammarNode> discovered = new ArrayList<GrammarNode>();
Stack<GrammarNode> S = new Stack<GrammarNode>();
while (!S.empty()) {
node = S.pop();
if (!discovered.contains(node)) {
discovered.add(node);
System.out.println(node.getWord.getSpelling().trim());
for (GrammarArc arc : node.getSuccessors()) {
S.push(arc.getGrammarNode());
}
}
}
UPDATE:
The problem of this is that one has alyways go back to the root in order to generate full sentences. So I guess the question is: How to remember the node which was already fully visited (this means where all children nodes were already explored)?
Printing all paths from the root to every leaf would mean to print the entire tree so I'd just use a simple DFS and do the following for each node:
add it to the list/stack
if the node has children, repeat for the children
if the node is a leaf, print the list/stack
pop the node from the list/stack
Example:
A
/ \
B E
/ \ / \
C D F G
The first steps would look like this:
put A on the list -> {A}
put B on the list -> {A,B}
put C on the list -> {A,B,C}
since C is a leaf, print the list (A,B,C)
remove C from the list -> {A,B}
put D on the list -> {A,B,D}
since D is a leaf, print the list (A,B,D)
...
if you know that the graph is indeed a tree (there is only one path to each node), them yes, a simple DFS would be more efficient (at least from a memory usage point of view). Otherwise, you can also use the iterative deepening DFS.
So here's a sample approach. Note that you need an extra visited field in your node structure:
public class TreeNodeExtra {
int val;
TreeNodeExtra left;
TreeNodeExtra right;
boolean visited;
TreeNodeExtra (int v) {
val = v;
visited = false;
}
}
private ArrayList<ArrayList<TreeNodeExtra>> all_path_from_root_to_leaf(TreeNodeExtra root) {
Stack<TreeNodeExtra> st = new Stack<>();
ArrayList<ArrayList<TreeNodeExtra>> res = new ArrayList<>();
st.push(root);
root.visited = true;
while (!st.isEmpty()) {
TreeNodeExtra top = st.peek();
if (top.left != null && !top.left.visited) {
st.push(top.left);
top.left.visited = true;
}
// if left node is null
else {
if (top.right == null && top.left == null) {
// we have a leaf
ArrayList<TreeNodeExtra> tmpList = new ArrayList<>();
for (TreeNodeExtra t : st) {
tmpList.add(t);
}
res.add(tmpList);
st.pop();
}
else if (top.right != null && !top.right.visited) {
st.push(top.right);
top.right.visited = true;
}
else {
st.pop();
}
}
}
return res;
}
A slight modification of DFS (which includes back-tracking) prints all the paths from a given source. In the below example the graph is represented in adjacency list format.
public void mDFS(ArrayList<node> v,int ind,ArrayList<Boolean> visit,ArrayList<node> tillNow){
visit.set(ind,true);
node n = v.get(ind);
int len = n.adj.size();
tillNow.add(n);
int count = 0;
for(node tmp: n.adj){
if( !visit.get(tmp.id) ){
count++;
tmp.pre = ind;
mDFS(v,tmp.id,visit,tillNow); // id gives index of node in v
}
}
if(count == 0){
for(node tmp: tillNow){
System.out.print((tmp.id + 1) + " - ");
}System.out.print("\n");
}
visit.set(ind,false);
tillNow.remove(tillNow.size() - 1);
return;
}
I am writing a program that accepts names (strings) from a user using node data structures, displays the names, then selectively deletes the names. (I want the input to be in a array). When the names are entered using a for loop and displayed the the program will ask me what I want to delete or what name to remove from the array.
This is my class:
public class Node
{
Node next;
String data;
public Node (String data)
{
this.data = data;
}
}
What the program should do:
Lets say I enter 5 names in a for loop.
Alex, George, Fryon, Storm, Hilbert.
Then when I entered it it will display:
Alex.
George
Fryon
Storm
Hilbert
Then it will ask what name should I delete?
(Alex)
George
Fryon
Storm
Hilbert
But I cant even enter them.
String[] contestant = new String [MAX];
head = null;
for (int i = 0; i <= 5; i++)
{
System.out.println ("Enter a name:");
name [0] = stdin.readLine ();
node = new Node (name [i]);
node.next = head;
head = node;
}
And Im confused if nodes or linked lists relate to arrays. I was hoping to store it as a "node array", use a for loop to display it "like an array" and then "delete it using sorting like an array". But its honestly difficult.
Write the following:
String name;
Node current = null;
while(true)
{
System.out.println ("Enter a name:");
name = stdin.readLine ();
if( name.equals("exit") ) break;
if( current == null ) {
current = new Node(name);
}
else {
current.next = new Node(name);
current = current.next;
}
}
Wow. I amaze myself.
A array is a different data structure then a linked list. Ive been programming with arrays for so long that I didn't realize there were more efficient ones like circular linked lists.
.
This question should be rather easy for any Java developer. I swear I looked it up after spending ~2 hours on it, but I can't really understand what's wrong with this code.
Basically, I am implementing Karger's minimum cuts algorithm. It requires me to keep merging nodes in a graph and then compute the number of crossing edges at the end (an int value). This algorithm must be repeated n times, always from the starting graph. My problem is that I am unable to create a deep copy of my Graph object, and I can't find the mistake.
I have cropped the code to just show the problem and no more, but I am still unable to figure out what's wrong. Here the code is.
Class Node:
public class Node {
public Integer Data;
public Node() {
Data = 0;
}
public Node(Node rhs) {
Data = rhs.Data.intValue();
}
public Node(Integer rhs) {
Data = rhs.intValue();
}
public void setNode(Integer rhs) {
Data = rhs;
}
Class Graph:
public class Graph {
public ArrayList<ArrayList<Node>> AdjList;
public ArrayList<Node> NodeSet; // This contains all the nodes
public Graph() {
AdjList = new ArrayList<ArrayList<Node>>();
NodeSet = new ArrayList<Node>();
}
public Graph(Graph G) {
AdjList = new ArrayList<ArrayList<Node>>();
for (ArrayList<Node> L : G.AdjList) {
ArrayList<Node> Lcopy = new ArrayList<Node>();
for (Node N : L) {
Node copy = new Node(N);
Lcopy.add(copy);
}
AdjList.add(L);
}
}
public void addNewAdjList(ArrayList<Node> NodeAdjList) {
// Input is the adjacency list of a new node
// The first element in the NodeAdjList is the node itself, the rest is the adj nodes
AdjList.add(NodeAdjList);
}
public static void printAdjList(ArrayList<Node> Adjlist) {
Node start = Adjlist.get(0);
System.out.print(start.Data + " : ");
for (int j=1; j < Adjlist.size(); ++j) {
System.out.print(Adjlist.get(j).Data + ", ");
}
System.out.print("\n");
}
Main:
public class Main {
/**
* #param args
*/
public static void main(String[] args) {
Node Five = new Node(5);
Node Seven = new Node(7);
Node One = new Node(1);
Graph G = new Graph();
ArrayList<Node> L = new ArrayList<Node>();
L.add(Five);
L.add(Seven);
L.add(One);
G.addNewAdjList(L);
Graph R = new Graph(G);
R.AdjList.get(0).get(1).setNode(19); // Gets node #1 in the first adj list, i.e. 7
Graph.printAdjList(G.AdjList.get(0));
Graph.printAdjList(R.AdjList.get(0));
}
}
Output:
5 : 19, 1,
5 : 19, 1,
This kind of puzzles me to be honest. I understand that Java is pass by value only, but objects are always represented by their reference. As far as I understand, my copy constructor for G should always make a deep copy: I am moving through every adjacency list and then I am making a deep copy of the Node. I don't understand why invoking .setNode() on the copied object modifies also the original object (that has a different reference).
Previous answers like 1 seem to go the same direction I am going, what am I missing here? :S
Your error is here:
ArrayList<Node> Lcopy = new ArrayList<Node>();
for (Node N : L) {
Node copy = new Node(N);
Lcopy.add(copy);
}
AdjList.add(L);
You created a copy of L (called Lcopy) but then you added the original L to your cloned graph. To fix it the last line should be this:
AdjList.add(Lcopy);
Note: If you have used a sensible name for your variable instead of L this error would probably never have happened!
I have a List with some tables from a database where each row contains a parent field refering to another row. Like this
title, parent
A, null
B, A
C, A
D, C
E, B
F, null
Here the A and F are root nodes, B and C is child to A, D is child to C and E is child to B in turn.
What is the best way to produce a tree structure from this list?
One way is to recurse over the list finding the root (the title without no parents) then for each root again loop over the list and attach the roots nodes. Then for those nodes again loop over the full list to attach any children of their own.
Example:
private Node getNode(SomeData d) {
List<SomeData> tmp = getChildren(d);
if (tmp == null OR empty) return new Node(d);
Node n = new Node(d);
for (SomeData m : tmp) {
n.addChild(getNode(m)); // recurse
}
return n;
}
private List<SomeData> getChildren(SomeData parent) {
List<SomeData> tmp = new ArrayList<SomeData>();
for (SomeData candidateChild : myBigFlatDataList.values()) {
if (parent.equals(candidateChild)) {
tmp.add(candidateChild);
}
}
return tmp;
}
Is there a better way to do this?
This is a pretty good way, but it is more naive than it has to be.
Another route takes just linear time. Is there something about a SomeData that uniquely identifies it? I would assume so; this could be SomeData itself implementing equals() and hashCode() properly.
Lets say there is a method int SomeData.getID(). Then we can keep Nodes we've previously seen in a HashMap.
Map<Integer, Node> visitedNodes = new HashMap...
Then we just read forward through the rows:
for ( SomeData data : ... ) {
SomeData parent = data.getParent();
Node<SomeData> parentNode = getOrCreateNode(parent);
Node<SomeData> childNode = getOrCreateNode(data);
parentNode.addChild(childNode);
}
private Node<SomeData> getOrCreateNode(SomeData data) {
Node<SomeData> node = visitedNodes.get(data.getID());
if ( node == null ) {
node = new Node<SomeData>(data);
visitedNodes.put(data.getID(), node);
}
return node;
}
Re-reading the entire file (or worse querying the database) for every node is rather expensive. I would rather you build the tree as you read the list. Here's my 2 cents
Let Nodes be a set of Nodes (initially an empty set).
Let RootNodes be a set of all Root Nodes (initially an empty set).
For every pair of nodes (N1,N2):
For each N in (N1,N2) if N not in Nodes, create N and insert into Nodes.
If N2 == null, also insert N2 into RootNodes (additionally you could also delete it from Nodes)
Mark N2.child = N1.
If you follow this, at the end of the iteration over the list you should have:
RootNodes = {A,F}
Nodes = {B,C,D,E}
A.child = B
A.child = C
C.child = D
B.child = E
Hope this helps.
You can build your tree all at once. You can do a first pass over the table to build all of the nodes (build a hashtable from name to Node), then do another pass where you can add parent-child relationships between two Nodes (add parent pointer to child and add child to list of children in the parent).
Since you get the data from a DB you can sort the rows according to the parent attribute. Then you wouldn't need to iterate over the whole list everytime you search for the children of a node.
EDIT:
When the list is sorted you can stop iterating over the list when you found all children you were looking for. For example when you have the root "A" and you start searching for its children in this list:
B, A
C, A
E, B <- when you reach "B" you can assume that there are no
D, C other nodes which are children of "A" and stop the iteration
List<User> list = new ArrayList<User>();
User blankNode;
class User{
String userid;
User child;
public User() {
//blankNode
}
public User(String userid) {
this.userid = userid;
}
#Override
public int hashCode(){
return userid.hashCode();
}
}
public void addUser(User parent,String userid){
if(null == userid)return;
User child = new User(userid);
parent.child = child;
list.add(child);
}
public void removeUser(User child){
if(null == child)return;
list.remove(child);
}
/* move the rank to up - assume
* secParent - assign to new child
*/
public void boubbleUp(User secParent, User oldParent, User child){
if(null == child || null == secParent)return;
secParent.child = child;
oldParent.child = null;
}
public List<User> getTopUser(int num){
if(num <1)return null;
Map<Integer, List<User>> map = new HashMap<Integer, List<User>>();
for(User usr : list){
int count =0;
User temp = usr.child;
while(null != temp){
count++;temp=temp.child;
}
if(map.get(count)== null){
List<User> sameNoOfChildren = new ArrayList<User>() ;
sameNoOfChildren.add(usr);
map.put(count, sameNoOfChildren);
}else{
map.get(count).add(usr);
}
}
Integer[] arr = (Integer[]) map.keySet().toArray();
Arrays.sort(arr);
List<User> result = new ArrayList<User>();
for(int i = arr.length-1; i <=arr.length-num; i-- ){
result.addAll(map.get(i));
}
return result;
}