Level-order tree traversal - java

I am trying to write a method that will take an IntTree as a parameter and return a Queue with the values of the IntTree in level order. To clarify: an IntTree is a tree with an integer value as its root, and has an IntTree as its left and right subtrees.
A note on some methods:
value(t) - returns the integer value at the root of the tree
left(t) - returns the left IntTree subtree //
right(t) - returns the right sub-tree
Here's the code I have so far. I'm rather new to programming, so I'm sorry if it's not very elegant.
public static QueueList levelOrder(IntTree t) {
//returns a QueueList with the values in level order
Object val;
QueueList q = new QueueList(); //temp queueList
QueueList theta = new QueueList(); //the QueueList which will eventually be printed
if (isEmpty(t)) {
return theta; //possible problem here
} else {
IntTree tL = left(t); //using for possible updating. probably won't work
IntTree tR = right(t);
q.enqueue(value(t)); //enqueue the root of the tree
while (!q.isEmpty()) {
val = q.dequeue(); //pop off the top element for printing
theta.enqueue(val); // put the element in the queue to be printed
if (tL != null) {
q.enqueue(value(tL)); //if the left isn't null, enqueue the lefts
tL = left(tL); //this will only branch left
}
if (tR != null) { //if the right isn't null, enqueue the rights
q.enqueue(value(tR));
tR = right(tR); //this will only branch right
}
}
}
return theta; //returns a queue with values in order
}
I wrote the tL and tR variables because if I wrote something like "if (left(t) != null)", I would end up with infinite recursion, since 't' was never updated. The problem with this code is that 'tL' will only branch left and 'tR' will only branch right. So after one level below the root, some values will never be stored. I hope this was clear, and any help is greatly appreciated. Thank you.

Instead of implementing the fringe as a queue of values, implement it as a queue of IntTrees (nodes). That will simplify your logic tremendously.
while (!q.isEmpty()) {
IntTree node = q.dequeue(); //pop off the top element
theta.enqueue(value(node)); // put the element in the queue to be printed
//enqueue the children
IntTree left = left(node);
if ( left != null ) {
q.enqueue(left);
}
//...similar for right
}
When you have this you don't have to maintain the tL and tR pointers in parallel, which I imagine is a flawed approach anyhow.
Links
Breadth-First Traversal (Wikipedia)

Related

AVL Search Tree having problem with either insert or balance or rotations

I am creating my own implementation of an AVL search tree. I have done a basic binary search tree and had no problems with it at all. I am using my basic binary tree code as a starting point for the AVL tree. When I test it, it comes up with a Null Pointer Exception. Now, I do understand what that means, and in the past I have been able locate and fix this sort of problem. However, I can't seem to determine exactly where the problem is, or how to fix it. I feel I could be overlooking something pretty simple. As sometimes we are too close to our own projects.
I believe I have narrowed down the problem to just a few possible sections of code that is causing the problem. I have included that code here. If anyone feels they need to see more code, please let me know and I will post more. I have narrowed down the problem so it seems to only appear when inserting. Since the insert method also calls the balance method, which in turn calls the rotation methods, the problem is quite possibly in one of those areas. I also feel that the problem is related to either the balance or rotation methods, as the insert method is almost the same I am using on the basic binary search tree, which works fine. I feel that I should be able to find where this is coming from, just getting tired and frustrated, so I thought it would be time to ask for help.
Here are the related methods, if you need to see more, let me know.
// Insert item into tree
// Duplicates are ignored
// Pre: Accept item x, that represents item to insert
public void insert(E x) {
root = insert(x, root);
}
// Internal method to insert item into tree
// Pre: Accept item x, that represent item to insert
// Pre: Accept AVL Node t, that represents roots of the subtree
// Post: Return AVL Node, representing new root of tree
private AvlNode<E> insert(E x, AvlNode<E> t) {
if (t == null) {
return new AvlNode<>(x, null, null);
}
int compareResult = x.compareTo(t.item);
if (compareResult < 0) {
t.leftChild = insert(x, t.leftChild);
} else if (compareResult > 0) {
t.rightChild = insert(x, t.rightChild);
} else {
; // Duplicate do nothing
}
return balance(t);
}
// Balances the tree
// Assume t is either balanced or within one of being balanced
// Pre: Accept AVL Node t, representing roots of tree
// Post: Return AVL Node
private AvlNode<E> balance(AvlNode<E> t) {
if (t == null) {
return t;
}
if (height(t.leftChild) - height(t.rightChild) > IMBALANCE_ALLOWED) {
if (height(t.leftChild.leftChild) >= height(t.leftChild.rightChild)) {
t = rotateWithLefChild(t);
} else {
t = doubleWithLeftChild(t);
}
} else {
if (height(t.rightChild) - height(t.leftChild) > IMBALANCE_ALLOWED) {
if (height(t.rightChild.rightChild) >= height(t.rightChild.leftChild)) {
t = rotateWithRightChild(t);
}
} else {
t = doubleWithRightChild(t);
}
}
t.height = Math.max(height(t.leftChild), height(t.rightChild)) + 1;
return t;
}
// Rotate tree node with left child
// Single rotation
// Updates height
// Pre: accept AVL Node
// Post: Return AVL Node, this node represents new root
private AvlNode<E> rotateWithLefChild(AvlNode<E> k2) {
AvlNode<E> k1 = k2.leftChild;
k2.leftChild = k1.rightChild;
k1.rightChild = k2;
k2.height = Math.max(height(k2.leftChild), height(k2.rightChild)) + 1;
k1.height = Math.max(height(k1.leftChild), k2.height) + 1;
return k1;
}
// Rotate tree node with right child
// Single rotation
// Updates height
// Pre: Accept AVL Node
// Post: Return AVL Node, this node represents new root
private AvlNode<E> rotateWithRightChild(AvlNode<E> k1) {
AvlNode<E> k2 = k1.rightChild;
k1.rightChild = k2.leftChild;
k2.leftChild = k1;
k1.height = Math.max(height(k1.leftChild), height(k1.rightChild)) + 1;
k2.height = Math.max(height(k2.rightChild), k1.height) + 1;
return k2;
}
// Double rotate tree node, first left child with its right child, then node k3 with new left child
// Double rotation
// Updates height
// Pre: Accept AVL Node
// Post: Return AVL Node, this node represents new root
private AvlNode<E> doubleWithLeftChild(AvlNode<E> k3) {
k3.leftChild = rotateWithRightChild(k3.leftChild);
return rotateWithLefChild(k3);
}
// Double rotate tree node, first right child with its left child then node k1with new right child
// Double rotation
// Updates height
// Pre: Accept AVL Node
// Post: Return AVL Node, this node represents new root
private AvlNode<E> doubleWithRightChild(AvlNode<E> k1) {
k1.rightChild = rotateWithLefChild(k1.rightChild);
return rotateWithRightChild(k1);
}
During my debugging process, my compiler showed that the following lines could be where the problem is:
In the public insert method:
root = insert(x, root);
In the private insert method:
return balance(t);
In the balance method:
t = doubleWithRightChild(t);
In the rotateWithLeftChild method:
k2.leftChild = k1.rightChild;
And finally the doubleWithRightChild method:
k1.rightChild = rotateWithLefChild(k1.rightChild);
These are the lines marked by the compiler (not that, that always shows where the problem is).
After further debugging I seemed to have located the place that throws the exception. It appears to happen as soon as the code enters the private insert method:
private AvlNode<E> insert(E x, AvlNode<E> t) {
if (t == null) {
return new AvlNode<>(x, null, null);
}
It seems to be right here that the exception gets thrown. So now I am thinking it could be my constructor or something. However, in my basic binary tree, the constructor is exactly the same, and there is no problem with that code. My constructor is pretty basic, and like I said the exact same as with my properly functioning basic binary tree.
// Constructor
public MyAVLTree() {
root = null;
}
So any thoughts or suggestions. Thank you for your time and assistance.
After doing a bit more testing, I have discovered that this exception is only thrown when one more one value is inserted. If their is only one value added, then there is no error.
So, now I am thinking it has something to do with how I am linking nodes?
OK, I have figured out what the problem was. I had some of my brackets {} screwed up and in the wrong places in the balance method. For those who are interested, this is how it should be.
// Balances the tree
// Assume t is either balanced or within one of being balanced
// Pre: Accept AVL Node t, representing roots of tree
// Post: Return AVL Node
private AvlNode<E> balance(AvlNode<E> t) {
if (t == null) {
return t;
}
if (height(t.leftChild) - height(t.rightChild) > IMBALANCE_ALLOWED) {
if (height(t.leftChild.leftChild) >= height(t.leftChild.rightChild)) {
t = rotateWithLefChild(t);
}
else {
t = doubleWithLeftChild(t);
}
}
else {
if (height(t.rightChild) - height(t.leftChild) > IMBALANCE_ALLOWED) {
if (height(t.rightChild.rightChild) >= height(t.rightChild.leftChild)) {
t = rotateWithRightChild(t);
}
else {
t = doubleWithRightChild(t);
}
}
}
t.height = Math.max(height(t.leftChild), height(t.rightChild)) + 1;
return t;
}
Thank you for your time

Breadth-first tree

I seem to be having issues structuring a breadth-first tree.
In the code below, I have a node that is inserted through a loop in another class.
The structure of the tree is supposed to be as so:
A
/ \
B C
/\ /\
D E F G
Now for the code:
My code structures the left side correctly, whereas the right side adds the left side as well. I understand where in the code this happens, but is there a way to prevent this from happening?
public Node familyTree;
public void breadthFirst(Node newNode){
familyTree = breadthFirst(familyTree,newNode);
}
public Node breadthFirst(Node T, Node newNode){
if(T == null){
T = newNode;
return T;
}
if(T.left == null){
newNode.height = T.height + 1;
T.left = newNode;
return T;
}
else if(T.right == null){
newNode.height = T.height + 1;
T.right = newNode;
return T;
}
else{
T.left = breadthFirst(T.left, newNode);
T.right = breadthFirst(T.right, newNode); <-- this is the corporate
}
return T;
}
if you are using recursive, definitely the implementation is a "depth-first-search", for breadth-first-search, you use a queue or a FIFO data structure
pseudo-code
public Node breadthFirst(Node T, Node searchNode){
Queue queue = new Queue();
queue.queue(T);
while (!queue.isEmpty()) {
Node curNode = queue.dequeue();
if (curNode == null) continue;
if (curNode.value().equals(searchNode.value()) {
return curNode;
}
queue.queue(curNode.left);
queue.queue(curNode.right);
}
return null; //or throw exception not found
}
I think a breadth-first tree resemble a complete binary tree, so you can employ Array to store it rather than link list. And about complete binary tree if the parent number is n then the left number=2*n+1 right=2*n+2.
for example: use array nodes[the amount of node]
and the 0th Node is A(number begin zero)
when the number n of Node is even like C(n=2) then nodes[(n-2)/2].right = nth node
else odd like B then nodes[(n-1)/2].left = nth node
What you are missing is using the height of the left and right node to determine which side the new node should be a child of when you reach the else statement. Currently, you're adding it to both sides regardless of where the node should be placed.
As an aside, it looks like you might be keeping track of depth of the tree in height attribute, rather than the height. This stackoverflow post does a good job of explaining the difference.

Remove recursively from a binary search tree

This is homework; please don't just give me code
I have two methods: remove(T data) and removeRec(Node<T> node, T data).
In its current state, it seems my code only removes the root node of the BST.
#Override
public T remove(T data) {
if (data == null) {
throw new IllegalArgumentException("Data is null");
}
if (root == null) {
throw new java.util.NoSuchElementException("BST is empty");
} else {
size--;
BSTNode<T> dummy = new BSTNode<T>(null);
return removeRec(root, data, dummy).getData(); //This is probably wrong too
}
}
/**
* Helper method to recursively search for, and remove the BSTNode with
* the given data in it
* #param node is the node we're currently at
* #param data is the data we're looking for
* #param temp I have no idea why
* #return node that was removed
*/
private BSTNode<T> removeRec(BSTNode<T> node, T data, BSTNode<T> temp) {
if (compare(data, node.getData()) < 0) {
temp.setLeft(removeRec(node.getLeft(), data, temp));
} else if (compare(data, node.getData()) > 0) {
temp.setRight(removeRec(node.getRight(), data, temp));
} else if (node.getLeft() != null && node.getRight() != null) {
temp.setData(findMin(node.getRight()).getData());
temp.setRight(removeRec(node.getRight(), data, temp));
} else {
if (node.getLeft() != null) {
temp = node.getLeft();
} else {
temp = node.getRight();
}
}
return temp;
}
private int compare(T a, T b) {
return a.compareTo(b);
}
My instructor has told me (as a hint) that I should see what passing in a third argument into the method, in this case, BSTNode<T> temp. I don't understand how that helps though, or how to utilize it. I don't see how using a third argument helps; and I can't find anything online as to why you'd do this either.
There are three main possibilities when you try to remove data from your Binary Search Tree:
data is less than the current node value: Call remove on the left subtree or throw a NoSuchElementException if it is null.
data is greater than the current node value: Call remove on the right subtree or throw a NoSuchElementException if it is null.
data is equal to the current node value.
1 and 2 are pretty straightforward, but 3 has four more cases to consider:
3.1. current node is a leaf: Both left and right subtrees are null. Just replace the reference to the current node in its parent by null.
3.2. current node has only the left child: You need to make the parent of the current node point to the left subtree, thus removing the current point. To do this, you can implement a function that will check if the current point was on the left or right subtree of the parent and replace it accordingly. Calling it would look like this:
replaceNodeInParent(node, node.getLeft(), parent);
3.3. current node has only the right child: Similar to 3.4, but using getRight() instead of getLeft().
3.4. current node has both the left and right children: You should maintain the property of the BST that all nodes on the left are less than the current node and all nodes on the right are greater than the current node. To do so, you should find the smallest value on the right, copy it to the current node, and delete it from the right subtree. Something like this:
BSTNode<T> successor = findMin(node.getRight());
node.setData(successor.getData());
removeRec(node.getRight(), successor.getData(), node);
It looks like your BSTNode doesn't hold a reference to the parent node. If so, I believe that's what the third argument for removeRec should be. You will need a reference to the parent every time you replace the current node, so you can set the parent left or right subtree as needed.
For further reading, you can check this article on Binary Search Trees from Wikipedia.

With Binary Trees, I want to add the next node in consecutive order, my algorithm isn't quite working however

For instance, if I had
A
/ \
B C
/
D
I would want the next addition to be:
A
/ \
B C
/ \
D E
But I'm having a lot of trouble detecting where the next spot for the item to input will be. I have the following code:
public static BinaryTree<String> addToTree(BinaryTree<String> tree, String name) {
if (tree.getLeft() == null) {
BinaryTree<String> newTree = new BinaryTree<String>();
newTree.makeRoot(name);
tree.attachLeft(newTree);
}
else if (tree.getRight() == null) {
BinaryTree<String> newTree = new BinaryTree<String>();
newTree.makeRoot(name);
tree.attachRight(newTree);
}
// Both are non-null
else {
if (tree.getLeft().getLeft() == null || tree.getLeft().getRight() == null) {
tree.attachLeft(addToTree(tree.getLeft(), name));
}
else if (tree.getRight().getLeft() == null || tree.getRight().getRight() == null) {
tree.attachRight(addToTree(tree.getRight(), name));
}
}
return tree;
}
But it will only work for up to a three level tree. If I try to add the fourth, it no longer adds any.
How do I implement it so it will figure out where the next item is null, and then add it there?
I also thought of having a checkNullity() method, wherein I'd take a tree, and check if its children were null, but I was also having trouble figuring out how to get the children's children. I wanted to find where it was null and then add it there.
Could anyone offer some input?
You can modify breadth first traversal to accomplish this I think. When you pop up the items from the queue, check if any of the children is empty. The first empty child slot is the place you want to add to.
addNode(root, newNode)
q = empty queue
q.enqueue(root)
while not q.empty do
node := q.dequeue()
if node.left == null
//create new node as nodes left child
return
q.enqueue(node.left)
if node.right == null
//create new node as nodes right child
return
q.enqueue(node.right)
Since, you want to insert the element in the order from left to right and starting from the same level. I would suggest you to look in to Breath First Search. I have provided an basic implementation.
public void insert(child, root){
if (root == null){
root = child
}
Node iter = root
Myqueue q = new Myqueue(); //Implementation of the Java Queue Interface
while (iter!=null){
//Check: If the left node exists, enque in the que
if(iter.is_left()){
q.insert(iter.left)
}
else{
iter.left = child
iter = null
}
//Similary for the right
if(iter.is_right()){
q.insert(iter.right)
}
else{
iter.right = child
iter = null
}
if (iter != null){
iter = q.poll() //Retreiving the head of the queue
}
}
}
You could enumerate all nodes while adding them to the tree. If you want to add the n-th node to the tree, it'll be a child of the n/2-th node: left if n%2 == 0 and right if n%2 == 1.
This certainly creates the tree you are asking for although I am still not sure it is what you want:
public class BinaryTree<T> {
T root = null;
BinaryTree<T> left = null;
BinaryTree<T> right = null;
public BinaryTree<T> getLeft() {
return left;
}
public BinaryTree<T> getRight() {
return right;
}
public void makeRoot(T root) {
this.root = root;
}
public void attachLeft(BinaryTree<T> tree) {
left = tree;
}
public void attachRight(BinaryTree<T> tree) {
right = tree;
}
public static BinaryTree<String> addToTree(BinaryTree<String> tree, String name) {
if (tree.getLeft() == null) {
BinaryTree<String> newTree = new BinaryTree<String>();
newTree.makeRoot(name);
tree.attachLeft(newTree);
} else if (tree.getRight() == null) {
BinaryTree<String> newTree = new BinaryTree<String>();
newTree.makeRoot(name);
tree.attachRight(newTree);
} else {
addToTree(tree.getLeft(), name);
}
return tree;
}
public static void main(String[] args) {
try {
BinaryTree<String> tree = new BinaryTree<String>();
String add = "ABCDEFG";
tree.makeRoot(add.substring(0, 1));
for (int i = 1; i < add.length(); i++) {
addToTree(tree, add.substring(i, i + 1));
}
System.out.println("Done");
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Added
I have clearly misunderstood the question. Perhaps an example will help.
If I added one character at a time (as strings) from the following string what would you expect?
"ABCDEFG"
A
/ \
B C
/ \ | \
D E F G?
or something else.
What would you then expect from
"ADEFGBC"
A
/ \
D E
/ \ | \
F G B C
or
A
/ \
B C
/ \ | \
D E F G
or something else?
Either is possible but I cannot see any value in either case.
In order to add an element to a proper place in a binary tree, you have to go from the root at at each node answer the following question: Should I descend to the left or to the right subtree? This is what your problem boils down to - how to make this decision at each node.
You start is OK. If the node has no left subtree, then the newly added leaf should be its left child. And if the node has a left subtree but no right subtree, then the newly added leaf should be its right child.
But how to decide if the node has both subtrees? For this you'll need to keep some sort of information at the nodes that you can use to decide. One possibility is to keep at each node the total size of its subtree. Then if both subtrees have the same size, it means both are perfectly balanced and so you add to the left. Otherwise, if the left subtree has size 2^n-1 it means that it's balanced (and the right one is not) so you add to the right. If not, add to the left.
However, you can do much simpler than that. Since your trees always keep this structure, you can represent a tree as an ArrayList. The root node is at index 0 and for a node at index n its children are at indexes _2*n+1_ and _2*n+2_. This is just how binary heaps are implemented. This way, you'll get O(1) time complexity for adding a new node - simply append it at the end of the list. (However, if you need some classical tree operations like rotations, this implementation won't work.)

How do I iterate over Binary Tree?

Right now I have
private static void iterateall(BinaryTree foo) {
if(foo!= null){
System.out.println(foo.node);
iterateall(foo.left);
iterateall(foo.right);
}
}
Can you change it to Iteration instead of a recursion?
What you're looking for is a successor algorithm.
Here's how it can be defined:
First rule: The first node in the tree is the leftmost node in the tree.
Next rule: The successor of a node is:
Next-R rule: If it has a right subtree, the leftmost node in the right subtree.
Next-U rule: Otherwise, traverse up the tree
If you make a right turn (i.e. this node was a left child), then that parent node is the successor
If you make a left turn (i.e. this node was a right child), continue going up.
If you can't go up anymore, then there's no successor
As you can see, for this to work, you need a parent node pointer.
Example:
First rule: The first node in the tree is the leftmost node in the tree: (1)
Next-U rule: Since (1) has no right subtree, we go up to (3). This is a right turn, so (3) is next.
Next-R rule: Since (3) has a right subtree, the leftmost node in that subtree is next: (4).
Next-U rule: Since (4) has no right subtree, we go up to (6). This is a right turn, so next is (6).
Next-R rule: Since (6) has a right subtree, the leftmost node in that subtree is next: (7).
Next-U rule: Since (7) has no right subtree, we go up to (6). This is a left turn, so we continue going up to (3). This is a left turn, so we continue going up to (8). This is a right turn, so next is (8).
Next-R rule: Since (8) has a right subtree, the leftmost node in that subtree is next: (10).
Next-R rule: Since (10) has a right subtree, the leftmost node in that subtree is next: (13).
Next-U rule: Since (13) has no right subtree, we go up to (14). This is a right turn, so next is (14).
Next-U rule: Since (14) has no right subtree, we go up to (10). This is a left turn, so we continue going up to (8). This is a left turn, so we want to continue going up, but since (8) has no parent, we've reached the end. (14) has no successor.
Pseudocode
Node getLeftMost(Node n)
WHILE (n.leftChild != NULL)
n = n.leftChild
RETURN n
Node getFirst(Tree t)
IF (t.root == NULL) RETURN NULL
ELSE
RETURN getLeftMost(t.root);
Node getNext(Node n)
IF (n.rightChild != NULL)
RETURN getLeftMost(n.rightChild)
ELSE
WHILE (n.parent != NULL AND n == n.parent.rightChild)
n = n.parent;
RETURN n.parent;
PROCEDURE iterateOver(Tree t)
Node n = getFirst(t);
WHILE n != NULL
visit(n)
n = getNext(n)
Java code
Here's a simple implementation of the above algorithm:
public class SuccessorIteration {
static class Node {
final Node left;
final Node right;
final int key;
Node parent;
Node(int key, Node left, Node right) {
this.key = key;
this.left = left;
this.right = right;
if (left != null) left.parent = this;
if (right != null) right.parent = this;
}
Node getLeftMost() {
Node n = this;
while (n.left != null) {
n = n.left;
}
return n;
}
Node getNext() {
if (right != null) {
return right.getLeftMost();
} else {
Node n = this;
while (n.parent != null && n == n.parent.right) {
n = n.parent;
}
return n.parent;
}
}
}
}
Then you can have a test harness like this:
static Node C(int key, Node left, Node right) {
return new Node(key, left, right);
}
static Node X(int key) { return C(key, null, null); }
static Node L(int key, Node left) { return C(key, left, null); }
static Node R(int key, Node right) { return C(key, null, right); }
public static void main(String[] args) {
Node n =
C(8,
C(3,
X(1),
C(6,
X(4),
X(7)
)
),
R(10,
L(14,
X(13)
)
)
);
Node current = n.getLeftMost();
while (current != null) {
System.out.print(current.key + " ");
current = current.getNext();
}
}
This prints:
1 3 4 6 7 8 10 13 14
See also
Complete Java listing and output on ideone.com
Can you change it to Iteration instead of a recursion?
You can, using an explicit stack. Pseudocode:
private static void iterateall(BinaryTree foo) {
Stack<BinaryTree> nodes = new Stack<BinaryTree>();
nodes.push(foo);
while (!nodes.isEmpty()) {
BinaryTree node = nodes.pop();
if (node == null)
continue;
System.out.println(node.node);
nodes.push(node.right);
nodes.push(node.left);
}
}
But this isn’t really superior to the recursive code (except for the missing base condition in your code).
Sure, you have two general algorithms, depth first search and breadth first search.
If order of traversal is not important to you, go for breadth first, it's easier to implement for iteration. You're algorithm should look something like this.
LinkedList queue = new LinkedList();
queue.add(root);
while (!queue.isEmpty()){
Object element = queue.remove();
queue.add(element.left);
queue.add(element.right);
// Do your processing with element;
}
As with every recursion, you can use additional data structure - i.e. the stack.
A sketch of the solution:
private static void visitall(BinaryTree foo) {
Stack<BinaryTree> iterationStack = new Stack<BinaryTree>();
iterationStack.push(foo);
while (!iterationStack.isEmpty()) {
BinaryTree current = iterationStack.pop();
System.out.println(current.node);
current.push(current.right); // NOTE! The right one comes first
current.push(current.left);
}
}
I had a tree (not binary) and eventually solved it with this very simple algorithm. The other solutions used left and right that were not relevant or even implemented in the examples.
My structure was: nodes with each parent containing list of children, and each child containing a pointer back to the parent. Pretty common...
After a bunch of refactoring, I came up with the following example using Kotlin. It should be trivial to convert to your language of choice.
Helper Functions
First, the node must provide 2 simple functions. This will vary depending on your Node class' implementation:
leftMost - This is the first child node. If that node has children, it's first child, etc. If no children, return this.
fun leftMost(): Node {
if (children.isEmpty()) {
return this
}
var n = this
while (n.children.isNotEmpty()) {
n = n.children[0]
}
return n
}
nextSibling - The next sibling of this node, or NULL
fun nextSibling(): Node? {
if (parent == null) return null
val siblingIndex = parent.children.indexOf(this) + 1
return if (siblingIndex < parent.children.size) {
parent.children[siblingIndex]
} else {
null
}
}
The Iteration
The iteration starts with the leftMost of the root.
Then inspect the next sibling.
If NOT NULL the sibling's leftMostChild
If NULL, the parent, and if the parent is NULL, we are done.
That's it.
Here is a Kotlin iterator function.
fun iterator(): Iterator<Node> {
var next: Node? = this.leftMost()
return object : Iterator<Node> {
override fun hasNext(): Boolean {
return next != null
}
override fun next(): Node {
val ret = next ?: throw NoSuchElementException()
next = ret.nextSibling()?.leftMost() ?: ret.parent
return ret
}
}
}
Here is the same next() function, but without the Kotlin shorthand for dealing with NULL values, for those that are not hip to the syntax.
fun next(): Node {
val ret = next
if (ret == null) throw NoSuchElementException()
val nextSibling = ret.nextSibling()
if (nextSibling != null) {
next = nextSibling.leftMost()
}
else {
next = ret.parent
}
return ret
}
Yes, you can change it to iteration instead of a recursion, but then it gets much more complicated, since you need to have some way to remember where to go back from the current node. In the recursive case, the Java call stack handles that, but in an iterative solution you need to build your own stack, or perhaps store back pointers in the nodes.

Categories