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;
}
Related
I am trying to build this n-ary tree having the same structure as an already build one (when creating the new tree to be returned i would like to add the child nodes in the same positions as in the already built one , the built tree is created as follows :
Node A = new Node("","A");
Node B = new Node("","B");
Node C = new Node("","C");
...
Node root = A;
root.children.add(B);
root.children.add(C);
root.children.add(D);
root.children.get(1).children.add(G);
root.children.get(1).children.get(0).children.add(K);
...
The Node Class is like the following :
public class Node {
public String id;
public ArrayList<ArrayList<String>> data;
public Vector<Node> children = new Vector<>();
public void setId(String id) {
this.id = id;
}
public void setData(ArrayList<ArrayList<String>> data) {
this.data = data;
}
public void setChildren(Vector<Node> children) {
this.children = children;
}
public Node(ArrayList<ArrayList<String>> data, String id) {
this.data = data;
this.id = id;
}
public Node(ArrayList<ArrayList<String>> data,String id,Vector<Node> children) {
this.data = data;
this.id = id;
this.children = children;
}
public Node find_parentNode(String childId) {
if (this == null)
return null;
Queue<Node> queue = new LinkedList<>();
// we add start node
queue.add(this);
// iterate while queue not empty
while (!queue.isEmpty()) {
// dequeue and print data
Node next = queue.remove();
for (Node child : next.children) {
if (child.id == childId)
return next;
queue.add(child);
}
}
return null;
}
And finally the main code is the following :
// Create rootOut (the root node to be returned)
Node rootOut = new Node(node.data,node.id,node.children);
queue.add(node);
// iterate while queue not empty
while(!queue.isEmpty()){
// dequeue
Node next = queue.remove();
// we add children nodes if not null after setting an increment var for the children positions
int j =0 ;
for (Node child : next.children) {
// Update children of rootOut (the output Tree)
Node currentNode = rootOut.find_parentNode(child.id);
currentNode.children.get(j).setChildren(child.children);
currentNode.children.get(j).setData(child.data);
currentNode.children.get(j).setId(child.id);
j++;
queue.add(child);
}
}
Basically in the main code, Instead of creating a new tree i override the values of the nodes of the built tree after having copying the old built tree into a new one (through root node rootOut),
Is it a good approach ? otherwise how to create a brand new tree with the same structure (nodes positions) as the built tree ?
Thanks.
To duplicate the structure of an existing tree it's enough to do a depth first traversal, copying each node and adding each children in the same traversal order.
You don't need to find the parent node, that is an expensive search, since the node will be added to the right parent in the previous call of the method.
I cannot test your code, since something is missing (e.g. what is QueryNode?), but it appears to copy only the root node, without actually copying the tree structure.
So this method will recursively duplicate the tree, the only shared resources between the new and the old tree are the data ArraList, where only the reference is copied.
public static Node cloneNode(Node root) {
Node copy=new Node(root.data, root.id);
for (Node c: root.children) {
copy.children.add(cloneNode(c)));
}
return copy;
}
As answer to your last comments, a deep copy of the data is not usual, but if you really want it just replace the first line of the method with these:
ArrayList<ArrayList<String>> copyData=new ArrayList<>();
for (ArrayList<String> l: root.data) {
copyData.add(new ArrayList<String>(l));
}
Node copy=new Node(copyData, root.id);
Some unrelated remarks:
Do not use Vector, use ArrayList instead
In method signature and variable declaration better use the List interface insted of the concrete ArrayList class (e.g. data should be declared as List<List>)
I'm trying to create a hierarchy from flat data. I have the following Node definition:
public class Node {
public String name;
public List<Node> children = new ArrayList<>();
}
Given this data: [Person,Manager,Hourly,New], where the tree should be like:
Person
|--Manager
|--Hourly
|--New
I've tried the following:
public void run()
{
List<List<String>> objects = new ArrayList<>();
String[] str = {"Person","Manager","Hourly","New"};
objects.add(Arrays.asList(str)) ;
String[] str2 = {"Person","Manager","Salary"};
objects.add(Arrays.asList(str2)) ;
String[] str3 = {"Person","Manager","Salary", "New"};
objects.add(Arrays.asList(str3)) ;
// Create a Node with the sequence
myNode = new Node();
createNode(objects.get(0), 0, myNode, myNode);
LOG.debug(myNode.name);
}
And my createNode method is:
public Node createNode(List<String> seq, Integer start, Node parentNode, Node childNode)
{
// do something and return a Node?
}
But conceptually I don't understand how to maintain the structure if Java is return-by-value. What do I add to createNode so that I can add a Manager->Hourly->New hierarchy as a child to Person
You don't need both a Node return type and a Node argument to your method.
Here's one way to do it:
//in run()
myNode = new Node();
myNode.name = "Root";
createNode(objects.get(0), 0, myNode, myNode);
public void createNode(List<String> seq, Integer start, Node parentNode)
{
Node childNode = new Node();
childNode.name = seq[start];
parentNode.children.Add(childNode);
createNode(seq, start+1, childNode);
}
You don't need to return anything from createNode() -- since you have parentNode as a variable, you can add things to its children member. A call to createNode() will recursively add child nodes, following your string array to its end.
Another way to do it is like this:
public Node createNode(List<String> seq, Integer start)
{
if (start >= seq.Length) {
return null;
}
Node node = new Node();
node.name = seq[start];
node.children.Add(createNode(seq, start+1);
return node;
}
In this case, you don't need to pass in node references at all; calling createNode() will generate a new node object, fill its children tree recursively, and return the newly-generated node structure.
As I can see your defination of node is somewhat similar to adjacency list in graph.
In the Target node add the associated node in the list associated with the target node. This is true for each node belonging to all the nodes.
For each object belonging to the objects array (array parameter) in your createNode method, you need to create the Node object.
just pass an String array and the taeget node. Iterate the list and create a node. Add the node in the list.
To avoid duplicates while creating Node add them in an map. Key to the map should be String and value should be Node object. Before creating the object of node just try to get the object from the map, make the object only iff the object is not found in the map(in such a case create and add it to the map). In case object is found un the map, us the samedo not recreate it.
I wrote the following tree class:
public class Tree {
private TreeNode root;
private static class TreeNode {
private Pair<String, Float> data;
private TreeNode leftNode;
private TreeNode rightNode;
private TreeNode( Pair<String, Float> data, TreeNode left, TreeNode right) {
this.data = data;
this.leftNode = left;
this. rightNode = right;
}
}
}
The following input:
"<Hello, 123>"
"<Hi, 1234>"
"<John, 42142>"
"null"
"<Chris, null>"
"<Peter, null>"
"null"
And now, i want to write a function that takes this input as an ArrayList, like this:
ArrayList<Pair<String,Float> input = {"<Hello, 123>", "<Hi, 1234>", "<John, 42142>", "null", "Chris, null", "Peter, null", "null"};
And creates a Tree using the type defined above.
NOTE: if in some position of the array the value is null, it means there should be no node there.
Here's what i've done so far:
public createTree(ArrayList<Pair<String, Float>> treeAsVector) {
int nodes = treeAsVector.size();
root = new TreeNode(treeAsVector.get(0), null,null);
for (int i = 1; i < treeAsVector.size(); i++) {
if(treeAsVector.get(i) == null)
i++;//skips the node
else
//not sure what to do here
}
}
I need help, because i'm not understanding very well how i'm supposed to create the tree, because every TreeNode will require two additional TreeNode's, meaning i always have to see one step ahead...
UPDATE:
The mapping to the tree should be done in levels like this:
TreeNode
(root)
TreeNode TreeNode
2 3
TreeNode TreeNode
4 5
if a value in the ArrayList is null, it is not represented.
public void generateTree(ArrayList<Pair<String , Float>> vector){
//todo holds all nodes that haven't yet had their children assigned
ArrayList<TreeNode> todo = new ArrayList<>();
todo.add(new TreeNode(vector.remove(0) , null , null));
TreeNode root = todo.get(0);
while(!todo.isEmpty() && !vector.isEmpty())
{
TreeNode node = todo.remove(0);
if(node == null)
continue;
//generate the children for the current node
TreeNode left = vector.get(0) == null ? null : new TreeNode(vector.get(0) , null , null);
TreeNode right = vector.get(1) == null ? null : new TreeNode(vector.get(1) , null , null);
vector.remove(0);
vector.remove(0);
node.leftNode = left;
node.rightNode = right;
//left and right haven't yet had their children assigned
//queue them, so that they will be processed as soon as the
//rest of the queue before them has been processed
todo.add(left);
todo.add(right);
}
}
During this process, vector will be emptied!!! This can be avoided by using a counter instead of removing the content. This algorithm should work for unbalanced trees aswell. Basic idea is to add all nodes to a queue. This way all nodes can be processed ordered by their level.
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;
}
when scanning a file for words and using the built-in hashset class from the API, my word count returns 349 (which is what it's supposed to be)
Using my home-made hashset class, I get 235... so something in my add() method must be wrong, but I can't understand what it is.
thanks for any help!
public class HashWordSet implements WordSet {
private int size = 0;
private Node[] buckets = new Node[8];
public Iterator<Word> iterator() {
return new WordIterator();
}
//Add word if not already added
public void add(Word word) {
int key = getBucketNumber(word);
Node node = buckets[key];
while (node != null) {
if (node.value.equals(word))
return;
else
node = node.next;
}
node = new Node(word);
buckets[key] = node;
size++;
if (size == buckets.length) rehash();
}
private int getBucketNumber(Word word) {
int hc = word.hashCode();
if (hc < 0) hc = -hc;
return hc % buckets.length;
}
It seems like you override nodes[key] with the new word [only] instead of appending a new node to the list, so you lose all old data that was already in this node.
It should work fine if there are no elements in there before add() was invoked, but if there are - you will lose some data.
node = new Node(word);
buckets[key] = node;
Though it is hard to be 100% sure about it without the actual implementation of Node.
node = new Node(word);
buckets[key] = node;
If there are any nodes already in the bucket you have just thrown them away. Try something like:
node = new Node(word);
node.next = buckets[key];
buckets[key] = node;