How to traverse through a binary tree with parent node pointer (without stack, without recursion)
Recently I have some research coding works, and I’ve met a big problem.
I’m patching a compiler, and to make the change as little as possible, I decided to write the whole patch in a function.
I thought it would be easy for me to achieve this, but I found it’s FAR MORE harder than I ever thought.
I’ve got to create a custom AST, so I can’t use the original implementation of tree, but to write a custom one.
So I have to write a tree implementation.
First of all, the definition of struct, quite easy:
struct Node { Node* lchild; Node* rchild; Node* parent; union { size_t pos; uint8_t ins; }; };
(As we needed to do the iteration without function, we cannot use the recursive way, and I doesn’t wanted the tree to be changed, so I choose to include a parent node pointer.)
Second, how to create a empty node elegantly:
struct Node emptyNode = { 0 }; struct Node* nodehead = new Node(emptyNode);
Use the copy construction ;)
Third, create a full binary tree with specified degree:(in a iterative way)
int curLevel = 1; int c = 1; while (true) { if (curLevel < TREE_DEGREE && !curnode->lchild) { curLevel++; curnode->lchild = new Node(emptyNode); curnode->lchild->parent = curnode; curnode = curnode->lchild; curnode->pos = c++; } else { if (!curnode->parent->rchild) { curnode->parent->rchild = new Node(emptyNode); curnode->parent->rchild->parent = curnode->parent; curnode = curnode->parent->rchild; curnode->pos = c++; } else { curLevel--; curnode = curnode->parent; } } if (curLevel == 1) { break; } }
(A little long, but still easy enough)
Fourth, delete selected a leaf node:
int selected = rand() % (remainingLeafNode - 1); int curLayer = 1; Node* curnode = nodehead; bool deleteFail = 0; while (true) { if (curLayer < TREE_DEGREE - 1) { if (selected >= pow(2, TREE_DEGREE - curLayer - 1)) { curnode = curnode->rchild; selected -= pow(2, TREE_DEGREE - curLayer - 1); } else { curnode = curnode->lchild; } } else { if (selected == 0) { if (!curnode->lchild) deleteFail = true; else curnode->lchild = NULL; } else { if (!curnode->rchild) deleteFail = true; else curnode->rchild = NULL; } break; } curLayer++; }
(Every node is virtually mapped into a full binary tree which its leaf nodes are numbered from left to right)
Finally, we have to traverse through the tree: (Here I’ll only show you the last-root order traversing method)
You may thought I’ll show you with this one:
while (true) { while (curnode->lchild) { curnode = curnode->lchild; } if (!curnode->rchild) // leaf node here visit(curnode); else { // no left child, but have a right child curnode = curnode->rchild; continue; } // a leaf node if (curnode->parent->lchild == curnode) { // a leaf left node finished, go to its sibiling if (curnode->parent->rchild) { // has a right sibiling, redoing the process above curnode = curnode->parent->rchild; continue; } } else { // a leaf right node while (curnode->parent && curnode == curnode->parent->rchild) {// way up until it's a left node or root node curnode = curnode->parent; visit(curnode); } if (curnode == nodehead) { break; } if (curnode->parent->rchild) curnode = curnode->parent->rchild; } }
But that one has a fatal shortage, the visit code is called in two location, that means I would have to use a function, or to copy the code twice times.
OFC, neither of them satisfied me. Using a function is against my prerequsition, and copy code twice is ugly and not robust
So Instead, I changed the code structure and moved the while loop, introducing two flags, and finally we can have only one visit function here :)
while (true) { while (curnode->lchild) { curnode = curnode->lchild; } if (curnode->rchild) { // no left child, but have a right child curnode = curnode->rchild; continue; } bool doingRightNodeBacktrack = false; bool hasRightNodeBacktrack = false; while (true){ if (hasRightNodeBacktrack && !doingRightNodeBacktrack) { break; } if (doingRightNodeBacktrack || !curnode->rchild) { // short-circuit if doingRightNodeBacktrack is true // leaf node here visit(curnode); } // a leaf node if (curnode->parent && curnode->parent->lchild == curnode) { // a leaf left node finished, go to its sibiling if (curnode->parent->rchild) { // has a right sibiling, redoing the process above curnode = curnode->parent->rchild; break;// break into the outer while loop } } else { // a leaf right node if (curnode->parent && curnode == curnode->parent->rchild) {// way up until it's a left node or root node curnode = curnode->parent; doingRightNodeBacktrack = true; hasRightNodeBacktrack = true; } else { doingRightNodeBacktrack = false; } } } if (curnode == nodehead) { break; } if (hasRightNodeBacktrack) { if (curnode->parent->rchild) curnode = curnode->parent->rchild; } }
That’s all~ Have a nice day~ :)