Iterating over AVL tree in O(1) space without recursion - c

I have an AVL Tree. Each node looks like this:
typedef struct {
Node *parent; // the parent node
Node *left; // the node left of this node
Node *right; // the node right of this node
int height; // the height of this node
void *value; // payload
} Node;
Is it possible to iterate over an AVL tree with these nodes in O(1) space, without recursion, if yes, how?
If not, a solution with sub-O(n) space or iff really necessary O(N) space is appreciated as well.
With iterating over the tree I mean I want to visit each node once, and if possible in order (from the mostleft to the mostright node).

If you store the last node you have visited, you can derive the next node to visit in an iterator.
If the last node was your parent, go down the left subtree.
If the last node was your left subtree, go down the right subtree.
If the last node was your right subtree, go to your parent.
This algorithm gives you a traversal in O(1) for the tree. You need to flesh it out a little for the leaves and decide what kind of iterator (pre/in/post-order) you want to decide where the iterator should and wait for incrementation.

It is possible to get the next in-order node given a pointer to some node, as long as you keep parent pointers. This can be used to iterate the tree, starting with the leftmost node. From my implementation of AVL tree:
BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n)
{
if (n->link[1]) {
n = n->link[1];
while (n->link[0]) {
n = n->link[0];
}
} else {
while (n->parent && n == n->parent->link[1]) {
n = n->parent;
}
n = n->parent;
}
return n;
}
To get the leftmost node:
BAVLNode * BAVL_GetFirst (const BAVL *o)
{
if (!o->root) {
return NULL;
}
BAVLNode *n = o->root;
while (n->link[0]) {
n = n->link[0];
}
return n;
}
Here, node->link[0] and node->link[1] are the left and right child of the node, respectively, and node->parent is the pointer to the parent node (or NULL for root).
A single GetNext() operation has O(logn) time complexity. However, when used to iterate the entire tree, you get O(n) amortized time complexity.

"Datastructures and their algorithms" by Harry Lewis and Larry Denenberg describe link inversion traversal for constant space traversal of a binary tree. For this you do not need parent pointer at each node. The traversal uses the existing pointers in the tree to store path for back tracking. 2-3 additional node references are needed. Plus a bit on each node to keep track of traversal direction (up or down) as we move down. In my implementation of this algorithms from the book, profiling shows that this traversal has far less memory / processor time. An implementation in java (c would be faster i guess) is here.

Related

Delete function binary search tree BST with parent passing the tree pointer as argument

I am having a very difficult time understanding what my book is wanting me to learn. I am currently re-vising some algorithms and data structure material I studied quite some years ago. One chapter is about Binary Search Tree and their application, etc. The book talks about a BST (binary search tree) as a tree with nodes that has the following qualities: int data, node left, node right, node parent. I understand this and I have implemented a search function.
Now to the issue: How do I create a delete function when the arguments I have are N* delete(Tree* tree, N* delete_node);?
There are lots of recursive functions but they rely on the fact that we recursively pass a pointer of a node and a key value. This is not what the exercise in the book is looking for. An example of a method that I have tried and works is: source code for C Program for Insertion and Deletion in Binary Search Tree. Two other great sources that fails to meet these criteria are codezclub and geeksforgeeks. They solve the "delete" function but their function have other parameters in their function. My book specifies
A modifying operation that, given a pointer delete_node to an
element in the BST, tree, deletes delete_node from the BST tree and returns its pointer.
Function declaration: N* delete(Tree* tree, N* delete_node);
Anybody got any ideas? Also minimally reproducible examples are given in the sources above with the caviat that the structs are missing "parent" node. Below are my definitions for the tree and the node:
typedef struct N {
int data;
struct N* left;
struct N* right;
struct N* parent;
} N;
typedef struct Tree {
N* root;
} Tree;
You can use CLRS as a reference for understanding Binary search tree data structures and operations on it. Let's implement delete procedure first and then explain necessary parts.
We need 2 procedures, SUCCESSOR and transplant other than delete itself to make it complete. required procedures are implement before delete.
In order to move subtrees around within the binary search tree, we define a
subroutine transplant, which replaces one subtree as a child of its parent with
another subtree. When transplant replaces the subtree rooted at node u with
the subtree rooted at node v, node u’s parent becomes node v’s parent, and u’s
parent ends up having v as its appropriate child.
void transplant(Tree** root, Tree* u, Tree* v) {
if(u == NULL)
return;
else if(u->parent == NULL)
*root = v;
else if(u->parent->left == u)
u->parent->left = v;
else
u->parent->right = v;
if(v != NULL)
v->parent = u->parent;
}
We need to implement Tree* SUCCESSOR(Tree* T) procedure which returns the node where node->key is the next larger element greater than T->key, if it exists. otherwise, returns NULL. We need SUCCESSOR when delete_node has 2 children. It'll be replaced with the SUCCESSOR(delete_node).
Tree* SUCCESSOR(Tree* T) {
if(T == NULL)
return T; // Since T==NULL, it's equivalent to return NULL
else if(T->left != NULL) {
while(T->left != NULL)
T=T->left;
return T;
}
else {
while(T->parent != NULL && T->parent->right == T)
T = T->parent;
return T->parent;
}
}
We can all needed to implement delete procedure.
Tree* delete(Tree* root, Tree* delete_node) {
if(delete_node == NULL)
return root;
if(delete_node->left== NULL)
transplant(&root, delete_node, delete_node->right);
else if(delete_node->right == NULL)
transplant(&root, delete_node, delete_node->left);
else {
Tree* succ = SUCCESSOR(delete_node);
if(delete_node->right != succ) {
transplant(&root, succ , succ ->right);
succ->right = delete_node->right;
succ->right->parent = succ;
}
transplant(&root, delete_node, succ);
succ->left = delete_node->left;
succ->left->parent = succ;
}
return root;
}
Here's how delete procedure works:
The procedure for deleting a given node delete_node from a binary search tree root takes as arguments pointers to root and delete_node. It organizes its cases a bit differently from the three mentioned above.
If delete_node has no left child (first if statement do this part), then we replace delete_node by its right child, which may or may not be NULL. When delete_node's right child is NULL, this case deals with the situation in which delete_node has no children. When delete_node's right child is non-NULL, this case handles the situation in which delete_node has just one child, which is its right child.
If delete_node has just one child, which is its left child (else if part handles this case), then we replace delete_node by its left child.
Otherwise, delete_node has both a left and a right child. We find delete_node’s successor succ, which lies in delete_node’s right subtree and has no left child (If it has a left, child, then it's not the smallest number greater than delete_node->key. We get contradiction). We want to splice succ out of its current location and have it replace delete_node in the tree.
If succ is delete_node's right child, then we replace delete_node by succ.
Otherwise, succ lies within delete_node’s right subtree but is not delete_node's right child. In this case, we first replace succ by its own right child, and then we replace delete_node by succ.
Since both quotes are taken fromCLRS, I encourage you to review it in case of having problem in understanding why it works.
Of course you can do deletion without recursive function.
typedef struct N {
int data;
struct N* left;
struct N* right;
struct N* parent;
} N;
typedef struct Tree {
N* root;
} Tree;
N* delete(Tree* tree, N* delete_node);
Lets start from this definition. Well, have to say the delete function is uncommon, given the fact that in most cases we are using:
void delete(Tree* tree, int element);
Because we don't know whether the element exists or not (it need to be determined by contains() or find() function when we execute the deletion), and we don't need any return value most likely.
Anyway lets put it your way.
Deleting from BST is complicated and you should proceed with three basic situation:
The node you want to delete is leaf, i.e. it has no child;
The node has one child, either left or right;
The node have both right and left child.
These situations are different in processing, and for simplicity we will omit some implementation details and safety checks, but don't forget them in production code.
In situation 1, you simply free the memory, and change the pointer in its parent to NULL:
if (delete_node->parent->data < delete_node->data)
delete_node->parent->right = NULL; // WARNING: corner case
else
delete_node->parent->left= NULL; // WARNING: corner case
free(delete_node);
Situation 2 is pretty like Situation 1; instead of NULL, we need to change the pointer in parent node to its only child, then we free the node. The logic is similar and we will skip the code.
Situation 3 is a little more complicated. Generally we have two ways and both of them works; either we replace the node's data with largest (in BST we can say "rightest") data of its left subtree, or we replace the node's data with smallest/leftest data of its right subtree. After the replacing, we remove that node instead. Let's examine the second approach, i.e. replace the node's data with smallest data of its right subtree. The first approach has very similar logic.
For this approach, we need this function implemented:
N* findMin(N* root_of_subtree);
It will take root_of_subtree as input (search will start from this node), and returns the pointer to the desired, smallest node.
Assume we have found the desired smallest node in the right subtree of delete_node:
N* smallestNodeOfRightSubtree = findMin(delete_node->right);
delete_node->data = smallestNodeOfRightSubtree->data;
smallestNodeOfRightSubtree->parent->left = smallestNodeOfRightSubtree->right; // WARNING, corner case
free(smallestNodeOfRightSubtree);
We directly changed parent->left here, since in general case (corner case 2 explains what is not general), that node has two charateristics:
It has no left child.
It must be on the left subtree of its parents.
But, it is not always general, we still have some corner cases need to cover:
What if delete_node is root node? It has no parent, and code in Situation 1 will be undefined behavior.
What if the smallest node of the right subtree of delete_node is delete_node->right, i.e. the right subtree only have 1 node. In this case, we cannot change smallestNodeOfRightSubtree->parent->left; instead, we should change parent->right.
THAT'S IT. Now you know the logics, and some corner cases. GL & HF.

spliting a binary search tree in half in O(h) time complexity

I am practicing binary search trees and i have to answer a problem:
A tree struct is given as
struct tree{
int key;
int lcnt;
struct tree *lc;
struct tree *rc;
};
where lcnt is an integer holding the number of the nodes at the left subtree of each node. The problem is to split the tree in half updating every time the lcnt with the valid value. The split algorith must take O(h) time where h is the tree's hight. I found the solution down below and it works for the most trees. But consider now this tree
170
/
45
\
30
the result will be: tree1: 170, tree2: 45.
I have no idea how to fix it because if i try something like "dont split if the node is a leaf" or something then i have problems with other trees. The split function takes the parameter root which is the root of the primary tree, an integer which is the trees lenght/2 and it returns the 2 new trees. The one with return and the other by reference using a third parameter double pointer tree. I am also using updt function and some calculations to update the lcnt at every split.
the code is here:
struct tree* split(struct tree *root, struct tree **new_tree, int collect){
struct tree *new_root_1, *new_root_2, *link1=NULL, *link2=NULL;
struct tree *current=root, *prev=NULL, *temp=NULL;
if(!root)
return NULL; //empty tree
int collected=0, created_root1=0, created_root2=0;
int decrease;
while(current!=NULL && collected<collect){
if(collected+current->lcnt+1<=collect){
// there is space for the left subtree so take it all and move to the right
collected=collected+current->lcnt+1; //update the number of the collected nodes
if(!created_root1){
//create the root for the one tree
created_root1=1;
new_root_1=current;
link1=current;
}else{
link1->rc=current;
link1=current;
}
if(!created_root2 && collect==collected)
//in case the tree must be splited in half
new_root_2=current->rc;
prev=current;
current=current->rc;
//break the node link
prev->rc=NULL;
}else{
// there is no space for the left subtree so traverse it until it becomes small enough
if(!created_root2){
//create the root for the second tree
created_root2=1;
new_root_2=current;
link2=current;
}else{
link2->lc=current;
// at every link at left the count_total_tasks will help to update the lcnt of the
parent node
temp=new_root_2;
while(temp!=NULL){
temp->lcnt=count_total_tasks(temp->lc);
temp=temp->lc;
}
link2=current;
}
prev=current;
current=current->lc;
//break the node link
prev->lc=NULL;
//update the lcnt
decrease=prev->lcnt;
updt(new_root_2, decrease);
}
}
*new_tree=new_root_2;
return new_root_1;
}
And this is the updt function:
void updt(struct tree* root, int decrease){
struct tree *temp;
temp=root;
while(temp!=NULL){
temp->lcnt=temp->lcnt-decrease;
temp=temp->lc;
}
}
Your test case,
170
/
45
\
30
is not a valid binary search tree.

How to convert Doubly linked list to Binary tree

struct cnode
{
int info;
struct cnode *next;
struct cnode *previous;
};
typedef struct cnode cnode;
pre-made DOUBLY LINKED LIST: 1<->2<->3<->4<->5<->6<->7
So I'm trying to make a recursive function that grabs the mid of the doubly linked list (root = 4) and convert it into a the remaining into a binary tree. I'm still new to recursion so an explanation along with code would be GREATLY appreciated!
EX. 4
/ \
2 6
/ \ / \
1 3 5 7
This is the code I have thus far (which isn't much due to difficulties with recursion)
void *convert(cnode *head){
if(head == NULL)
return;
int count = 0;
cnode *tempHead = head;
while(tempHead != NULL){
count++;
tempHead = tempHead->next;
}
int move = (count/2) + (count%2);
int i;
for(i=1; i<move; i++){
head = head->next;
}
}
Pretty much just sets the head pointer to the mid info (4)
I think I understand; you're making a balanced binary tree from cnodes with the previous and next pointers being reused for the left and right sub-trees.
... so that's your algorithm.
Find the middle node of the binary tree (which you've already done).
Turn the left half into a binary tree. The left half is the original head, with the last element (middle->previous) now having a next pointer of NULL.
Link this left half to middle->previous (hijacked as the left sub-tree).
Turn the right half into a binary tree; this is headed by middle->next. Make it the new value of middle->next.
You have to keep the original head as the pointer to the left sub-tree.
You'll want your routine to return the binary tree's root, so the previous call can link it into the level above.
You still have to pick a termination condition, such as the head pointer being NULL.
Does that get you moving to a solution?
I hope this code will help you. call the DLL2BT method with head of the doubly linked list which return the root node of the tree created.
class box
{
int data;
box left=null,right=null;
box(int a)
{
data=a;
}
}
public static box DLL2BT(box head)// head = linked list head
{
if(head!=null)
{
box node=null;
try
{
node = findMid(head);
node.left.right=null;
node.left=DLL2BT(head);
node.right.left=null;
node.right=DLL2BT(node.right);
}
catch( Exception e){ }
return node;
}
return null;
}
public static box findMid(box head)
{
box slow=head,fast=head.right;
try
{
while(fast!=null)
{
slow=slow.right;
fast=fast.right.right;
}
}
catch(Exception e){ }
return slow;
}
Firstly, You are trying to convert DLL to binary tree assuming the DLL is given as in-order of the binary tree you have to make. Note that there isn't a unique tree you can make from only inorder traversal.Even if you have to make BST, you can't make it with only inorder traversal. What I actually think you are trying to do is to convert it into a balanced BST. Even though, it will also not be unique.
Here's the algorithm..
1) Get the Middle of the linked list and make it root.
2) Recursively do same for left half and right half.
  a) Get the middle of left half and make it left child of the root
created in step 1.
  b) Get the middle of right half and make it right child of the
root created in step 1.
Time complexity: O(nLogn) where n is the number of nodes in Linked List.
Although,it can be solved in O(n) if you insert nodes in BST in the same order as the appear in Doubly Linked List.

Adding a new branch to a tree then adding the new leaf to the right place in a linked list in an efficient way

Suppose we have a binary tree and a linked list that holds the data that appears in all of the leafs.
For example, the following tree's list would be: 9,2,3 (order matter, left to right)
Now we add a new node somewhere, thus creating a new branch, like so:
Is there an efficient way to add this new leaf to list such that the list will keep the order of the leafs from left to right? i.e the new list is supposed to be 9,1,2,3.
Anything I come up with, in the worst case scenario, is the same as making a completely new list, i.e, traversing the whole tree.
Like traversing the tree in LDR and looking for the new leaf while keeping the info of the last leaf, but it could go through all or most of the tree in the worst case scenario.
BTW the list and tree are arbitrarily defined like so:
typedef struct listNode {
int data;
struct listNode* next;
} ListNode;
typedef struct treeNode {
int data;
struct treeNode* parent; //prev node
struct treeNode* left;
struct treeNode* right;
} TreeNode;
Yes there is, but it will also require adding some more data to treeNode, a pointer to the list node (if such exist).
Now, the idea is once you have found where to add the new node (v), and let's say it is a son of some node u.
You need to find the previous leaf. It can be done in O(h) by going up on the tree, until you have found a node x that has a left son. Go to the left son, and keep traversing on it, in the following order:
if the current node has right son:
go to right son, repeat
else if the current node has left son:
go to left son, repeat
else:
found previous leaf, let it be l
Now, you have the new node v, and the previous leaf l.
All you have to do now is to put v's node after l's node:
v.node = createNode();
v.node.next = l.node.next;
l.node.next = v.node;
Complexity of this algorithm is O(h), where h is the height of the tree.
Note: Take care of the simple edge case where v is the first node in the linked list.

Binary-like tree in C - recursion

Recursion is soo confusing for me...
below are the structs that are used to create the binary-like tree:
struct parent {
char *name;
Child *children;
};
typedef struct parent Parent;
struct child {
struct Parent *Pptr;
struct child *next;
};
typedef struct child Child;
I want to iterate through the entire tree and on each child/parent (basically every node) call a function called birthdaygift().
Below is what I've tried so far, but I don't think it works.
tree(Parent *parent) {
Child* child = parent->children;
birthdaygift(parent);
if (child == NULL) {
return;
}
while (child->next != NULL) {
Parent * recurseparent = child->Pptr;
tree(recurseparent);
}
birthdaygift(parent);
}
It'd be really helpful if someone can give me some pointers?
Your data structure looks very, very odd, it looks like you have the child nodes pointing back to the parents and are somehow maintaining the list of children in the child node as a linked list.... I'll give my answer assuming you are intending to work with a binary tree.
Now, assuming that is the case, wouldn't going up to the parent from one child and then going up from the next child take you to the same node? Typically, binary trees are parsed by starting from the top and going down.
A traditional binary tree has pointers from the parent (for this example, *left and *right), and you would traverse the entire tree by using either a Depth First Search algorithm (basically, you keep going left recursively until you run out of nodes, and then you go right), in pseudocode
function gift_node(*node) {
birthday_gift(node);
if (node->left != NULL) gift_node(node->left);
if (node->right != NULL) gift_node(node->right);
}
Now, if you were to watch the process parsing this binary tree, you would see it start at the top and keep following the left node. It would then backtrack, process the right node and it's subordinate nodes and keep going until it has visited every node.
The struct you declared is not a tree, it is similar to a doubly-linked list.
The struct for a binary tree node would be:
struct BST_Node {
Value value;
BST_Node *right;
BST_Node *left;
};
For traversing a tree, you want to visit every single node. One way to do this is to visit all the nodes to the left recursively, then the node itself, then all the nodes to the right recursively (this is known as in-order traversal, but this isn't important for this situation).
If you want to call birthdaygift() on each node:
void traverse(Node *n) {
if (n->left)
traverse(n->left);
birthdaygift(n);
if (n->right)
traverse(n->right);
}
A discussion of other tree traversal algorithms can be found here.

Resources