How to traverse a level-oriented structure? - java

I have a linear data structure where every node has a level. The parent node has a level of 1, a node that is child of the parent has a level 2, a node that is child of child has node of 3, another parent node would have a level of 1. e.g. below
<node level=1> //parent node
<node level=2> //child of encountered parent node
<node level=2> //child of encountered parent node
<node level=3> //child of encountered child node
<node level=2> //child of encountered parent node
<node level=1> //parent node
<node level=1> //parent node
<node level=2> //child of encountered parent node
<node level=3> //child of encountered child node
<node level=4> //child of encountered child node
<node level=1> //parent node
Essentially I am trying to build a List. (Open to other suggestions), such that each element in the list is a parent node, each parent node will have list of child nodes, each child node can have list of child nodes etc. Each of the element is a node and all properties are same.
I have tried code by keeping track of the current level but than I am not sure how to properly add a child node, that has child node, that has child node, back to the parent node of the first child node. I feel this might be handled best by recursion but I am have never been able to truly implement recursion in an orderly fashion.

You don't have to think about all the nesting. You only have to think about where you are (as in, what level was the previous entry in the list of nodes) and where the next entry goes.
In this case, the solution lies in the way the tree is read in. Notice in your list of nodes, which is the tree input source, that right after a parent node, the next node is a child. If the node after some node isn't a child of that node (i.e. if its level isn't one level lower), it is the child of one of the previous node's ancestors.
So:
If the level on line n is equal to one plus the level on line n-1, line n holds a child of line n-1.
Otherwise, go up the tree from the node for line n-1 until you find one with a level one less than the level on line n. That ancestor node is the parent of node on line n.
You also don't have to do it recursively.
currLevel = 0;
currNode = root;
do {
node = read();
if (somethingRead()) {
// If this one is one level below the last one, it goes in as a child and we're done
if (node.level == currNode.level + 1) {
currNode.addChild(node);
currNode = node;
} else {
// Otherwise this one has to be at a level above this node's child, so back up
while (node.level >= currNode.level) {
currNode = currNode.parent(); // check for root left out here ...
}
if (node.level == currNode.level + 1) {
currNode.addChild(node);
currNode = node;
} else {
// handle illegal condition in list
}
}
}
} while (moreNodesToRead());
Response to Your Solution
Your solution reflects your reluctance to use a fake node as the root of the tree. That's a design choice one can make. Here is my version of it.
I am a little concerned about how you handle incoming data that is fouled up
A node is presented that is more than one level beyond the one before it.
The first node presented isn't at level 1.
A node has a level of 0 or less (no checks for that below)
Also, I suggest you allow currNode to be null when the current node should be the root. Theoretically it could happen in the while loop that backs up the current node but notice that, at that point in the code, you already know the new node has a level above 1 so currNode should never back up beyond the level 1 nodes. It is reasonable to have it generate a NPE if that assumption is wrong.
I suggest these changes:
Node currNode = null;
List<Root> roots = new ArrayList<Root>();
do {
Node node = generateNode(nodesList.next());
if (node.getLevel() == 1) { //implies root level node
roots.add(node);
currNode = node;
} else if (currNode == null) {
// ... handle misformed input ... first node isn't level 1, ignore it
} else if (node.getLevel() == currNode.getLevel() + 1) {
currNode.childrenList.addChild(node);
node.setParent(currNode);
currNode = node;
} else {
Node savedCurrNode = currNode;
while (node.getLevel() <= currNode.getLevel()) {
currNode = currNode.getParent();
}
if (node.getLevel() == currNode.getLevel() + 1) {
currNode.childrenList.addChild(node);
node.setParent(currNode);
currNode = node;
} else {
// ... handle misformed input ... node level > last node level + 1, ignore it
currNode = savedCurrNode;
}
} while (hasMoreNodes(nodesList));
Printing
I rearranged it a bit and changed some names and responsibilities (listed in comments). Just to belabor a point from above, if the root was just a node you wouldn't need the 'printRoots()' method at all. Just call 'printChildren()' on the fake root node with level set to 0. But it would print one extra line at the top.
(It always makes it easier to read if you indent the output.)
Warning: Not tested.
/** Print all the root nodes, each with any children it may have */
private void printRoots(List<Node> roots) {
for (int j = 0; j < roots.size(); j++) {
Node node = roots.get(j);
printContents(node, 1);
}
}
/** Print one node and any children it might have */
private void printContents(Node node, int level) {
for (int i=1 ; i < level ; ++i) {
print(" ");
}
print(node.toString());
println();
printChildren(node, level+1);
}
/** Print each child of the supplied node. Notice how this is just like getting the
* list of children and calling 'printRoots()'
*//
private void printChildren(Node node, int level) {
for (int i = 0; i < node.getChildrenList().size(); i++) {
Node childNode = node.getChildrenList().get(i);
printContents(childNode, level);
}
}

Related

OutOfMemoryError when trying to add elements from a B+Tree to an ArrayList

I'm attempting to traverse through a B+ Tree and add the elements from the leaves into an ArrayList with the following code:
public void toArrayList(Node node){
Node currentNode = node;
if(currentNode instanceof InnerNode){
InnerNode inner = (InnerNode) currentNode;
int i = 0;
int temp = inner.children.length;
while(i < temp){
currentNode = inner.children[i];
toArrayList(currentNode);
i++;
}
}
if(currentNode instanceof LeafNode){
LeafNode leaf = (LeafNode) currentNode;
int j = 0;
int temp = leaf.values.length;
while(j < temp){
if(leaf.values[j] != null) {
retArray.add(leaf.values[j]);
}
j++;
}
}
}
What it does is it checks if the node is an instance of an Inner Node or a Leaf Node. If it is an Inner Node it recursively calls the function with each of its children. If it is a Leaf Node then it will add the values into the ArrayList. However while running this fucntion I end up getting a java.lang.OutOfMemoryError.
Is there a way to make my code more efficient or should I look to take a different approach to this method?
Unless your tree structure has a few million elements in it, what's going on here is that some InnerCode contains itself, and thus this code keeps going forever, or, at least, until your retArray fills up.
Also note that this code will add a leaf node twice if it's the last child of an inner node. I strongly suggest you don't overwrite locals like this. Also, for loops are just more compact here (note that making this code smaller is not going to make any difference at all for memory issues; OOMError refers to the heap, and locals don't live there).

What does this code do specifically? And how can I change it?

public void deleteAfter(Node prev){
prev.setNext(prev.next().next());
}
This method deletes the new node after the given prev node. Could someone explain to me what this code does specifically step by step and how I can change it so that it won't error if prev is the last node on the list.
#param prev - The node before the place where it will be inserted.
prev.next().next() gets the node after the next node (from the one given). That's then passed to the prev.setNext method, making it the next node. That essentially removes the node in between prev and the next next node.
Basically, it takes prev -> next_1 -> next_2 ... and turns it into prev -> next_2 ...
If prev is the last node in the list, then next() should return null (I assume). If that's the case, you can do a null check to avoid an error.
public void deleteAfter(Node prev){
if(prev.next() != null) {
prev.setNext(prev.next().next());
}
}
The code sets a new next such that if you had a -> b -> c, now you'll have a -> c.
To avoid the error, you can check if next returns null, or maybe invoke hasNext if such a method exists on Node.
if(prev.next() != null){
...
}
It unlinks the link between the node before the node to be deleted and then links the node before the node to be deleted to the node after the node to be deleted.
You have some Node, somewhere in a list. I've highlighted it.
... - node - node - (prev) - node - node - ...
The first call, is to set the "prev"'s next link prev.setNext(...) but what is that next going to be set to? It will be set to "next's next" or (staring with prev)
... - node - node - (prev) - node - node - ...
The prev.next() would be
... - node - node - prev - (node) - node - ...
And the prev.next().next() would be
... - node - node - prev - node - (node) - ...
So, after the setting operation, your arrangement would look like
... - node - node - prev node--node - ...
\------/
Note that the prev's new "next node" was previously prev's next, next node.
If you ever ask yourself "What is this code doing????", there is an excellent chance that the code can be written in a more descriptive way. For example:
public void deleteNodeAfter(Node index) {
// Borrowed from Bill the Lizard's answer
if (index.next() != null) {
index.setNext(index.next().next());
}
}
To answer you directly: This code deletes a node from a linked list.

Delete from Binary Search Tree in Java

I can't find what is wrong with my deletion algorithm. When I run my delete method on the root of the BST, it will replace the root with the value of the minimum of the right subtree, but it is not deleting the node thereafter.
public void delete(BinaryTreeNode node, int x){
if (node==null)
return;
else if (x<node.getKey())
delete(node.getLeftChild(),x);
else if (x>node.getKey())
delete(node.getRightChild(),x);
else{
if (node.getLeftChild()==null)
node = node.getRightChild();
else if (node.getRightChild()==null)
node = node.getLeftChild();
else{
BinaryTreeNode temp = findMin(node.getRightChild());
System.out.println(temp.getKey() + " " + node.getKey());
node.setKey(temp.getKey());
System.out.println(temp.getKey() + " " + node.getKey());
delete(node.getRightChild(), node.getKey());
}
}
}
and my findMin() method:
public BinaryTreeNode findMin(BinaryTreeNode node){
if (node.getLeftChild()==null)
return node;
else
return findMin(node.getLeftChild());
}
For a BST containing all integers 1-9 with a root of 4, my output for inorderPrint() yields
1,2,3,5,5,6,7,8,9
instead of
1,2,3,5,6,7,8,9
Update your deleted-node's parent's pointer with the correct node. You need to get rid of any traces of the deleted node. Have a temporary node to keep track of the parent node, so this is easier to do once you find the node to delete.
When you are setting node = node.getLeftChild(); (or symetrically node = node.getRightChild(); you are changing the value of the local variable node, and not the reference to this node from the father.
You are missing something like:node.father.left = ....(or node.father.right = ...).
You should change the values in the tree itself, and not only the references you hold in the method.

Adding an element a binary search tree

Trying to add an element to BST. I have an idea of how to do it, but my implementation is destructive, and the original root is not preserved (so the tree basically becomes useless). The tree is based on lists, and this method is based on recursion. My real problem is preserving the original root. I'm using generics.
So far what I have:
public void addElement(E elem, Node<E> root) {
Create node with a value of elem, call it newNode
Case 1: Tree is empty
root = newNode();
return; //End of method.
Otherwise, keep searching the tree (by comparing the value of out node a with the root of the tree.
if (!root.hasLeft() && !root.hasRight) { //if the root in question has no children
if (elem < rootValue) { //Set the element as the left element
root.setLeft(newNode);
}
else { //Set the element as the right element.
root.setRight(newNode);
}
}
else {
if (E < root.getElem()) {
//This is where the value of our node is compared to the value of the root, which we passed in.
//(I know that we can't use the < and > operators with generics, but assume it works).
root = root.getLeft() //Left node is new root
addElement(elem, root); //Call the method again
}
else {
root = root.getRight(); //Right node is new root
addElement(elem, root) //Call method again
}
}
}
Forgive me if this is a duplicate/vague question, this is my first post on SO, and I'm kind of noob.
if (!root.hasLeft() && !root.hasRight) {
This logic is wrong. You're only considering "setting" the left child, if you have neither a left nor right child. This change should do it:
void addElement(elem, root)
{
if (elem < root.value) {
if(!root.hasLeft())
root.setLeft(newNode);
else
addElement(elem, root.getLeft());
}
else {
if(!root.hasRight())
root.setRight(newNode);
else
addElement(elem, root.getRight());
}
}
You should not be changing root of the class, just passing it into the next method call. This should preserve root.
By the way, I assume you have rootValue = root.value somewhere or somethign similar?

Remove the minimum value in a binary search tree

I understand the algorithms but I am not sure how to put it into actual codes. Please help! And also please explain in details. I really want to understand this besides just copying down the answer. ;)
Here are my codes:
public boolean getLeftChild(){
Node insertNode = root;
while(insertNode!=null){
insertNode = insertNode.left;
}
return true;
}
public Boolean removeMin(){
Node insertNode = root;
Node parentNode =root;
if (insertNode.left ==null){
insertNode.right = parentNode;
insertNode = null;
}else if (getLeftChild() ==true && insertNode.right != null){
insertNode.left = null;
}else{
parentNode.left = insertNode.right;
}
return true;
}
First things first: For trees I highly recommend recursion.
Just one example:
getSmallestNode(Node node){
if(node.left != null){
return getSmallestNode(node.left)
}
return node;
}
For the deletion, there can be two cases if you want do delete the smallest (and therefore the "most left leaf" child) of a binary tree.
Case 1: The leaf has no child nodes, in that case just set the according entry in the parent to null (mostLeftChild.getParent().left = null)
Case 2: The leaf has a right child node (there can't be a left child node because that means there would be a smaller node and your currently selected node isn't the smallest) in that case you replace the current left node with the smallest node of the right subtree mostLeftChild.getParent().left = getSmallestFromSubtree(mostLeftChild.right)
So now to make that into code, it could look something like this (No guarantee that it really works)
public Node deleteSmallest(Node node){
// haven't reached leaf yet
if(node.left != null{
return deleteSmallest(node.left)
}
// case 1, no child nodes
if(node.right == null){
node.getParent().left = null;
} else { // case 2, right child node
node.getParent().left = deleteSmallest(node.right)
}
return node;
}
And you would call it with deleteSmallest(root)

Categories

Resources