I was asked to write the iterative version, but I wrote the recursive version i.e.
void inorderTraverse(BinaryTree root)
{
if(root==NULL)
printf("%d",root->id);
else
{
inorderTraverse(root->left);
printf("%d",root->id);
inorderTraverse(root->right);
}
}
I'm not looking for the code, I want to understand how this can be done. Had it been just the last recursive call, I would have done
void inorderTraverse(BinaryTree root)
{
while(root!=NULL)
{
printf("%d",root->id);
root=root->right;
}
}
But how do I convert to an iterative program when there are two recursive calls?
Here are the type definitions.
struct element{
struct element* parent;
int id;
char* name;
struct element* left;
struct element* right;
};
typedef element* BinaryTree;
This is what I thought of, am I on the right track?
temp=root;
while(1)
{
while(temp!=NULL)
{
push(s,temp);
temp=temp->left;
continue;
}
temp=pop(s);
if(temp==NULL)
return;
printf("%d\t",temp->data);
temp=temp->right;
}
The problem you're seeing is that you need to "remember" the last place you were iterating at.
When doing recursion, the program internally uses "the stack" to remember where to go back to.
But when doing iteration, it doesn't.
Although... does that give you an idea?
I can't think of a really elegant way to do this iteratively off-hand.
One possibility might be using a 'mark algorithm', where you start out with all nodes 'unmarked' and 'mark' nodes as they're handled. The markers can be added to the object model or kept in a seperate entity.
Pseudocode:
for (BinaryTree currentNode = leftmostNode(root); currentNode != null; currentNode = nextNode(currentNode)):
print currentNode;
currentNode.seen = true;
sub nextNode(BinaryTree node):
if (!node.left.seen):
return leftmostNode(node.left)
else if (!node.seen)
return node
else if (!node.right.seen)
return leftmostNode(node.right)
else
return nextUnseenParent(node)
sub leftmostNode(BinaryTree node):
while (node.left != null)
node = node.left
return node;
sub nextUnseenParent(BinaryTree node):
while (node.parent.seen)
node = node.parent
return node.parent
I take it for granted, that iterating down from the parent nodes to the left nodes is not a problem. The problem is to know what to do when going up from one node to the parent: should you take the right child node or should you go up one more parent?
The following trick will help you:
Before going upwards remember the current node. Then go upwards. Now you can compare: Have you been in the left node: Then take the right node. Otherwise go up one more parent node.
You need only one reference/pointer for this.
There is a general way of converting recursive traversal to iterator by using a lazy iterator which concatenates multiple iterator suppliers (lambda expression which returns an iterator). See my Converting Recursive Traversal to Iterator.
Related
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.
I have a function that creates a binary tree, and for each node in the tree, I need to add a node to a separate linked list that points to the node in the binary tree.
My function to create the binary tree:
typedef struct myTree _node;
void INSERT(_node *(*tree), _node *item) {
if (!(*tree)) {
*tree = item;
return;
}
if (item->val < (*tree)->val) {
INSERT(&(*tree)->left, item);
}
else if (item->val > (*tree)->val) {
INSERT(&(*tree)->right);
}
}
My main function:
int main(void) {
int i;
int *balanced;
_node *current, *root;
root = NULL;
for (i = 0; i < size; i++) {
current = (_node *)malloc(sizeof(_node));
current->left = current->right = NULL;
current->val = balanced[i];
INSERT(&root, current);
}
return 0;
}
I've left out parts of my main function for simplicity.
The idea is that I want to print out the contents of the tree in pre, in, and post order, as well as traverse the linked list and print the value of the node in the tree that each linked list node points to.
I'm only a few months into learning C, so I am not terribly advanced.
Much like your insert function is recursive on the tree, walking the tree is recursive as well. There are two ways to do this: the specific way and the generic way. Let's see both.
The specific way just prints values as it encounters them. It solves this specific problem: printing the values. If you have to do tree walks to do more than one thing, you'd have to copy the code, which is generally a bad thing.
On the other hand, the code is much simpler and easier to follow. Let's look at the in-order case (you can do the other two by yourself; they are very similar):
void print_in_order (const struct myTree * tree) {
// if we're in a null node, do nothing
if (!tree) return;
// otherwise, do the left subtree, then the current node, then the right subtree
// non-existent subtrees will be handled by the above check, so don't check twice
print_in_order(tree -> left);
printf("%d\n", tree -> val);
print_in_order(tree -> right);
}
The generic way, on the other hand, is a better approach if your program is doing tree walks for all sorts of purposes. The idea is that you encapsulate the actual task to be done at each node (in this case, printing it) in a separate function:
void print_node (const struct myTree * node) {
printf("%d\n", node -> val);
}
And then you write a function that takes this function as an argument and calls it on each node, in the corresponding order. Let's do it for in-order:
void apply_in_order (const struct myTree * tree,
void (* callback)(const struct myTree *)) {
// this is the same as before...
if (!tree) return;
apply_in_order(tree -> left, callback);
// ...except that, instead of doing a specific thing, we call the callback on each node
callback(tree);
apply_in_order(tree -> right, callback);
}
Now, you just call this function as apply_in_order(tree, print_node); and you get the same behavior as above. But the next time you need to write a function that walks a tree, you only need the per-node thing; the rest is already done.
I'd just like to note I already saw this post before asking my question: C How to "draw" a Binary Tree to the console
Let's say I have the following tree. If my print function were to print only the numbers (in order traversal), I would have the following printed out: 1,3,4,6,7,8,10,13,14.
What would be the best approach to draw the tree like something below considering the tree gets printed in that order?
I feel that if 8 got printed first followed by 3,10 etc.. it would be easier but since it is in-order traversal 1 is getting printed first which would be the first print statement at the top.
I did this about 2 years ago for some coursework...
I created a node struct that contained its own data and 2 nodes, one left and one right, it looked like this (I couldn't find the final code, that might have used shared pointers):
struct node
{
int data;
node *left;
node *right;
};
I then created my tree by adding more nodes to it using recursion like so:
void insert(node **tree, int value)
{
if (*tree == nullptr)
{
*tree = new node;
(*tree)->data = value;
(*tree)->left = nullptr;
(*tree)->right = nullptr;
}
else if (value < (*tree)->data)
{
insert(&((*tree)->left), value);//memory location of the pointer to the node of the node
}
else if (value > (*tree)->data)
{
insert(&((*tree)->right), value);
}
else
return;
}
Side note: Looking back, I never accounted for adding a node with the same value as an existing node if that's even possible.
I assume you will be doing something similar. Now for the bit that answers your question, printing it out, also using recursion.
void inorder(node *tree)
{
if (!(tree == nullptr))
{
inorder((tree)->left);
cout << (tree->data) << endl;//Prints on new lines, you could comma separate them if you really wanted.
inorder((tree)->right);
}
}
Lastly, you'll want to clean up your tree after you've used it so you'll need to delete it... recursively.
To be honest it's been a while and this recursion thing is still a little confusing to me so I have probably forgotten something, but the theory's there!
Edit, headers used: <iostream> and <memory>, also this was c++ not c but they're very similar.
What manipulations are we doing in insert/delete that we aren't in mirror?
By mirror I'm referring to switching the left and right child of every single node in the tree.
The reason I'm asking is that when it comes to an insert, if you don't save the root returned by the function, it leads to errors. The same is not true of mirror. I assumed mirror would have the same requirement (returning the root node), but that is not the case. Why is that?
Here's my code if it's relevant (the insert is implemented for a BST):
Insert:
Node *insert(Node *root,int val)
{
Node *newNode=NULL;
if(root==NULL)
{
newNode=(Node*) malloc(sizeof(Node));
newNode->value=val;
newNode->left=NULL;
newNode->right=NULL;
return newNode;
}
if(root->value>val)
{
root->left=insert(root->left,val);
}
else
{
root->right=insert(root->right,val);
}
return root;
}
Mirror:
void mirror(Node *root)
{
Node *temp_left;
if(root==NULL)
return;
temp_left=root->left;
root->left=root->right;
root->right=temp_left;
mirror(root->left);
mirror(root->right);
}
Notice the line:
root->left=insert(root->left,val);
Here you are assigning the result of insert() to root->left. You need insert() to return a node* pointer here, or else we would have no idea where malloc placed the new node in memory, and so you wouldn't be able to add the node to the tree.
On the other hand, mirror only traverses the tree recursively and swaps some already-existing pointers. You need never assign the result mirror() to a variable.
I am trying to figure out an algorithm to delete from the middle of a linked list..
My idea is to traverse the list, find the node right before the node I want to delete, call it Nprev, and set Nprev to Nnext where Nnext is after the node to delete Ndelete.
So Nprev -> Ndelte -> Nnext.
My problem is that I cannot figure out how to traverse this list to find the node before the one I want.
I've been doing this with seg faults because I assign pointers out of range I assume.
Its a very messy algorithm that I have, with many if else statements..
Is there an easier way to do this?
Basically I need to go through the list, apply a function to each node to test if
it is true or false. If false I delete the node.
Deleting first and last is not as hard but middle stumped me.
Please let me know if there are some general ways to solve this problem. I've
been scouring the internet and found nothing I need.
I used this: http://www.cs.bu.edu/teaching/c/linked-list/delete/
but the algorithm before step 4 only deletes the first node in my list
and doesn't do any more.
How can I modify this?
They also give a recursive example but I don't understand it and am intimidated by it.
First you need to find the middle node.
Well take 3 pointers fast, slow, prev
with fast moving with twice the speed of slow and prev storing the address of the node previous of slow.
i.e.
*slow=&head,*fast=&head,prev=Null
traverse the list and when fast=NULL
slow will point to the middle node if number of elements are odd and prev will store the address of node previous of the mid node.
so simply
prev->next=slow->next.
Here an example of something I use to search and remove by index:
Given this struct: (Can also be adapted to other self referencing structs)
struct node
{
S s;
int num;
char string[10];
struct node *ptr;
};
typedef struct node NODE;
Use this to remove an item from somewhere in the "middle" of the list (by index)
int remove_by_index(NODE **head, int n) /// tested, works
{
int i = 0;
int retval = -1;
NODE * current = *head;
NODE * temp_node = NULL;
if (n == 0) {
return pop(head);
}
for (int i = 0; i < n-1; i++) {
if (current->ptr == NULL) {
return -1;
}
current = current->ptr;
}
temp_node = current->ptr;
retval = temp_node->num;
current->ptr = temp_node->ptr;
free(temp_node);
return retval;
}