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.
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 have a method that finds a node in a linked list with a certain id and adds items to the arraylist of that node.
ArrayList <String> elems;
public Place addElemToLst(String id, String elem) {
// if no nodes create new node
if (head == null) {
Node node = new Node(id);
node.elems.add(item);
head = unit;
} else if (head != null) {
Node curr = head;
while (curr.next != null && !curr.next.id.equals(id)) {
curr = curr.next;
}
// if there is a id match
if (curr.id.equals(id)) {
curr.elems.add(item);
}
// add new Node
else { // the error is in this section
Node node = new Node(id);
node.elems.add(elem);
curr.next = node;
}
}
return this;
}
The problem is when I call addElemToLst() on an id of lets say "item1" multiple times and keep adding elements to the arraylist, the arraylist will only keep the last item entered into the arraylist. Essential, the arraylist is always a size of 1 because previous entries keep getting replaced. Why is this and what is the error? I have isolated the error to the comment in the code.
Thanks
You are not checking the first element/head for the correct ID
while (curr.next != null && !curr.next.id.equals(id))
You can try something like this for the second half:
else {
Node curr = head;
Node prev = null;
while(curr != null){
if(curr.id == id){
curr.elems.add(elem);
return this;
}
prev = curr;
curr = curr.next;
}
Node node = new Node(id);
node.elems.add(elem);
prev.next = node;
}
}
Your approach to finding a node with a matching ID will return a few different false negatives, which will in turn cause existing nodes to be overwritten. The issue is that you're using a sliding window to inspect nodes and determine a match, but your criteria for sliding doesn't line up with your criteria for determining whether to append to an existing node or create a new node.
Here are a few examples to illustrate:
_ = null
x = node
o = node with matching ID
[ ] = sliding window (left side is `curr`, right side is `curr.next`)
x -> o -> _
[ ]
Since the matching node is at the end of the list, you should append
`elem` to it. However, your code checks `curr.id` (instead of `curr.next.id`),
incorrectly concludes that no matching node is found, and overwrites `curr.next`.
o -> x -> _
[ ]
[ ]
Here, the matching node is at the head of the list, but your code
skips it and incorrectly creates a new node at the end of the list.
o -> x -> x -> _
[ ]
[ ]
[ ]
Same as above, a new node is incorrectly inserted at the end of
the list since you skipped the matching node at the head of the list.
x -> o -> x -> _
[ ]
Since you're checking `curr.id` (instead of `curr.next.id`), your code
incorrectly concludes that a new node needs to be created, overwriting
the existing (matching) node and truncating the tail of the list.
Linked lists are tough to reason about, and combining while conditions makes it even more difficult to grok. Here is a fixed implementation that (IMO) is easier to reason about:
public Place addElemToLst(String id, String elem) {
// if the list is empty, create a new node
if (head == null) {
Node node = new Node(id);
node.elems.add(elem);
head = node;
return this;
}
Node curr = head;
while (curr.next != null) {
// if the matching node appears inside the list,
// append the element and return
if (curr.id.equals(id)) {
curr.elems.add(elem);
return this;
}
curr = curr.next;
}
// if the last node in the list is a match, use it
if (curr.id.equals(id)) {
curr.elems.add(elem);
// now that you've exhausted all nodes, create a new one
} else {
Node node = new Node(id);
node.elems.add(elem);
curr.next = node;
}
return this;
}
I have been trying to implement a singly linked list in java with generics and I tried to implement a method "headInsert()" to insert a new Node and make it the new head of the list.
I am trying to achieve this by creating a new Node and swapping it with the head.
However, I have an error while I am trying to parameterize the new Node.
Here is the code I have written and I would appreciate any sort of help I could get.
Thank you in advance.
package datastructures;
public class LinkedList<E> {
private Node head;
private Node tail;
private static class Node<E> {
E data;
Node next;
Node (E data) {
this.data = data;
}
}
//Insert at the beginning of a LinkedList
public void headInsert(Node n) {
if(head == null) {
head = n;
}
else {
Node temp = head;
Node n1 = new Node(n1.data); //error here
n1.next=head;
head=n1;
}
}
The error occurs because n1 was not created yet when you try to access n1.data, it should be:
Node n1 = new Node(n.data);
That said, you don't really need to create a new node to place in the head. You can implement headInsert() like this:
public void headInsert(Node n) {
n.next = this.head;
this.head = n;
}
Suppose you have an empty list (head is null) and you want to add node A, A.next will point to null, and head will point to A resulting in the list A->null
Now suppose you have a non-empty list, like A->B->C, and you want to insert node Z, Z.next will point to A, and head will point to Z so you will get Z->A->B->C.
I'm trying to compress all the ancestors of a given node by having them point to the root of the parameter node passed to
private E compressToRoot (E e) throws IllegalArgumentException;
For example, in the image above, if I did compressToRoot(D) then D would point directly to A and C would point directly to A. If there were other nodes between the parameter and the root then they would all point to A.
All of the labels and arrows are stored in two separate maps:
private Map<E,E> parentMap = new HashMap<E,E>(); //labels + arrows
I can complete this method by (1) holding all the nodes between D and the root in a set. (2) having all elements of the set point to (make parent) the root (3) return the root.
However, I'm stuck on how I can traverse this map to get to the root. So, for the method I would do something along the lines of
private E compressToRoot (E e) throws IllegalArgumentException {
Set<E> collectLables = new HashSet<E>();
E root = null;
//get root.
for (E cycle : parentMap.keys()) {
while (parentMap.get(e) != e)
e = parentMap.get(e);
if (parentMap.get(e) == e)
root = cycle;
}
//collect all labels from parameter to root.
for (E element : parentMap.keys()) {
while (parentMap.get(e) != root) {
collectLables.add(element);
}
}
}
But I'm not sure how I can cycle through the parents of the given node all the way to the root.
Recursive is pretty, but favor an iterative method if the length of a longest path to the root may get large. You don't want to run out of stack.
private E compressToRoot(E node) {
if (parentMap.get(node) != node)
parentMap.set(node, compressToRoot(node));
return parentMap.get(node);
}
private E compressToRoot(E cursor) {
E node;
ArrayList<E> nodes = new ArrayList<E>();
while ((node = parentMap.get(cursor)) != cursor) {
nodes.add(cursor);
cursor = node;
}
for (node : nodes)
parentMap.set(node, cursor);
return cursor;
}
I'm stuck on how I can traverse this map to get to the root.
It appears that your definition of the "root" is the only node which points to itself. It's not particularly efficient, but you could just look for a such element:
E findRoot(Map<E, E> parentMap) {
for (Map.Entry<E, E> entry: parentMap.entrySet() {
if (entry.key().equals(entry.value()) {
return entry;
}
}
// parentMap is empty, or the graph is corrupted
// handle this edge case however you want
return null;
}
A slight potential improvement to rambo coder's answer above, depending on the efficiency of the Map. You can do it without using the extra memory and overhead for the ArrayList by just traversing the path twice:
private E compressToRoot(E cursor) {
E cursor2 = cursor;
E node = parentMap.get(cursor);
while (node != cursor) {
cursor = node;
node = parentMap.get(node);
}
// Now cursor (and node) are the root.
node = parentMap.get(cursor2);
while (node != cursor2) {
parentMap.set(cursor2, cursor);
cursor2 = node;
node = parentMap.get(node);
}
return cursor;
}
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;
}