I'm using Morris inorder traversal to traverse (iterate over all nodes of) a binary tree. The relevant code (inspired by the C code here) looks like this:
#include <stdio.h> /* printf(). */
#include <stdlib.h> /* NULL. */
struct node {
struct node *left, *right;
int data;
};
void traverse(struct node *root) {
struct node *current = root;
struct node *pre;
while (current != NULL) {
if (current->left == NULL) goto process_node;
/* Find the inorder predecessor of current */
pre = current->left;
while (pre->right != NULL && pre->right != current) {
pre = pre->right;
}
if (pre->right == NULL) {
/* Make current the right child of its inorder predecessor */
pre->right = current; /* This breaks the tree temporarily. */
current = current->left;
} else {
/* Revert the changes made in the 'if' part to restore the
* original tree i.e., fix the right child of predecessor.
*/
pre->right = NULL;
process_node:
printf("%d ", current->data);
if (current->data < 0) {
/* !!! Add cleanup code here. */
return; /* Stop the traversal. */
}
current = current->right;
}
}
}
This code compiles and works correctly if there are no nodes with negative values. For nodes with negative values, I want to stop the traversal right after the first negative value. However, at that point some of the ->right pointers are broken (see the code line with this breaks in it), and I need to add cleanup code (to the code line with !!! in it) to restore these broken pointers, and make the tree valid again.
My question: What should this cleanup code be?
Please note that I still want to keep the Morris inorder traversal algoritm, because the program runs in a resource-constrained system with a small fixed stack and no dynamic memory available. So replacing the Morris algorithm with another traversal algorithm which doesn't need cleanup, but it uses more memory (e.g. recursive traversal or a traversal with a manually managed path stack) is not a valid answer to my question.
There's likely a better solution to this, but here's something I hacked together. It works by having a new exit variable, which gets flagged when a negative value is read. It then continues running as usual until after the functions's already existing code to revert the changes is ran, at which point it will early exit.
The one flaw of this implementation is that the program will still attempt to process another node before this happens, if the negative value node is not a leaf. Wrapping most of the process_node label's code in an if (!exit) so that it doesn't get processed when it's trying to exit fixes this.
void traverse(struct node *root) {
struct node *current = root;
struct node *pre;
int exit = 0;
while (current != NULL) {
if (current->left == NULL) goto process_node;
/* Find the inorder predecessor of current */
pre = current->left;
while (pre->right != NULL && pre->right != current) {
pre = pre->right;
}
if (pre->right == NULL) {
/* Make current the right child of its inorder predecessor */
pre->right = current; /* This breaks the tree temporarily. */
current = current->left;
} else {
/* Revert the changes made in the 'if' part to restore the
* original tree i.e., fix the right child of predecessor.
*/
pre->right = NULL;
if (exit) return;
process_node:
if (!exit){
printf("%d ", current->data);
if (current->data < 0) {
/* !!! Add cleanup code here. */
exit = 1;
}
}
current = current->right;
}
}
}
Related
Morris inoder tree traversal is an inorder traversal of a binary search tree which uses only O(1) memory (and no recursion), but temporarily modifies (and then restores) some of the ->right pointers of the tree.
Example C code (inspired by the C code here):
#include <stdio.h> /* printf(). */
#include <stdlib.h> /* NULL. */
struct node {
struct node *left, *right;
int data;
};
void traverse(struct node *root) {
struct node *current = root;
struct node *pre;
while (current != NULL) {
if (current->left == NULL) goto process_node;
/* Find the inorder predecessor of current */
pre = current->left;
while (pre->right != NULL && pre->right != current) {
pre = pre->right;
}
if (pre->right == NULL) {
/* Make current the right child of its inorder predecessor */
pre->right = current; /* This breaks the tree temporarily. */
current = current->left;
} else {
/* Revert the changes made in the 'if' part to restore the
* original tree i.e., fix the right child of predecessor.
*/
pre->right = NULL;
process_node:
printf("%d ", current->data);
/* find(root, current->data + 2); */
/* find(root, current->data - 2); */
current = current->right;
}
}
}
However, because of the temporary modifications, additional binary tree value lookups don't work. For example, if we uncomment the find(...) calls here, then the following naïve find implementation will fall to an infinite loop if called during the traversal:
int datacmp(int a, int b) { /* Increasing order: -1 means less (a < b). */
return a < b ? -1 : a > b;
}
struct node *find(struct node *root, int data) {
struct node *explore = root;
int c;
while (explore != NULL) {
c = datacmp(data, explore->data);
if (c == 0) {
return explore;
} else if (c < 0) {
explore = explore->left;
} else {
explore = explore->right;
}
}
return NULL;
}
Is there an implementation of find which works even during the Morris inorder traversal?
This implementation of find works outside and during Morris inorder traversal:
int datacmp(int a, int b) { /* Increasing order: -1 means less (a < b). */
return a < b ? -1 : a > b;
}
struct node *find(struct node *root, int data) {
struct node *explore = root;
struct node *milestone = NULL;
int c;
while (explore != NULL) {
c = datacmp(data, explore->data);
if (c == 0) {
return explore;
} else if (c < 0) {
milestone = explore;
explore = explore->left;
} else {
explore = explore->right;
/* Stop on circular path created by a Morris inorder traversal. */
if (explore == milestone) break;
}
}
return NULL;
}
This implementation of binary search tree lookup is still O(depth), but it detects and stops on circular paths created by a pending Morris inorder traversal. It works like this: Such a circular path starts with a node (let's call it milestone), then the path moves left, then the path moves right at least once, reaching the milestone node again. The implementation above records the current node as milestone before each left move, and it checks for reaching the milestone node after each right move.
void MorrisTraversal(tNode* root)
{
tNode *current, *pre;
if (root == NULL)
return;
current = root;
while (current != NULL)
if (current->left == NULL) {
current = current->right;
}
else {
pre = current->left;
while (pre->right != NULL
&& pre->right != current)
pre = pre->right;
if (pre->right == NULL) {
pre->right = current;
current = current->left;
}
else {
pre->right = NULL;
current = current->right;
}
}
}
}
I use this function to travers the bst, i also have a function (delete) that takes a node as parameter and delets it from the tree.
I'd like for example to remove from the tree all nodes whose value is 4, is it possible to do so using the functions mentioned above?
Like traversing the tree one time and removing all the nodes with a certain property.
I hope my problem it's clear...
Thanks in advance!
It is possible, but you will need to modify your Traversal function.
Currently MorrisTraversal traverses the tree, but it doesn't do anything to any node. You could modify MorrisTraversal to call a function on the current node (probably on the line after while (current != NULL).
I would first try putting a simple print function/line there that prints the value of the current node and run it to verify your tree traversal works as expected.
Then you could change the print function to modify the node in some way.
If you do delete the node, you will have to be careful to clean up the tree. Eg. if you have the following tree Where A is the root node:
A-B-C
|
D-E
|
F
What happens if you delete B? Will you move C, A. or D into B's place?
Good luck!
I have created a pathfinding algorithm in C that uses a map.txt file with a maze written in 0s for walls and 1s for paths. Currently the path finding algorithm I'm using is a DFS with a stack to keep track of nodes but I'm trying to change it to a queue to utilize BFS so that I will be certain that the shortest path is always found. But when I try to print out the new path in BFS it seems like the rest of the path gets deleted somewhere but I can't find where.
Here is the path-finding algorithm in question:
struct node *BFS(struct graph* graph, int vertex, int endVertex, struct node* path_stack) {
struct node* temp = graph->adjlist[vertex]; //This is the first neighbor to the vertex node
struct node* temp2 = graph->adjlist[endVertex];// We use this temp to check if there exist a path at all (if the end node exist and has neighbours)
graph->visited[vertex] = 1; // sets the self as visited
static int done = 0;
if(temp2 == NULL || temp == NULL)
{
printf("No Path can be found");
return path_stack;
}
// if we havent arrived yet
while (temp != NULL) { // checks if the current neighbor exists
if(done == 1)
{
break;
return path_stack;
}
if(temp->vertex == endVertex) // Check for when we have gotten to the goal
{
printStack(path_stack);
done = 1;
break;
}
path_stack = push(temp, path_stack); // we push the first neighbor to the stack
if (graph->visited[temp->vertex] == 0) { //if the neighbor is not visited we run the algorithm again and now with the neighbors value as the vertex
BFS(graph, temp->vertex, endVertex,path_stack);
}
else
{
if(graph->visited[temp->vertex] == 1)
{
path_stack = pop(path_stack);
}
temp = temp->next; // we go the next neighbor to the current node
}
}
return path_stack;
}
Here is my push code that has been changed to push last like a queue:
struct node* push(struct node* Node, struct node* head)// Push has now been altered to push like a queue, so we can use bfs instead of dfs
{
struct node* temp = CreateNode(Node->vertex,Node->Y,Node->X); // We use a queue to keep track of visited nodes
temp->next = NULL;
if(head == NULL)
head = temp;
else
{
head->next = temp;
head = temp;
}
return head;
}
and here is my function that prints the path but now it only prints the last node in the path and all the others are gone.
void printStack(struct node* head)//Function that prints all the nodes in the stack
{
struct node* tmp = head;
//struct node* tmp2 = NULL;
printf("(The Start)->");
while(tmp != NULL)
{
//tmp2 = push_path(tmp, tmp2);
printf("(%d,%d)->", tmp->X, tmp->Y);
tmp = tmp->next;
}
/* while(tmp2 != NULL)
{
printf("(%d,%d)->", tmp2->X, tmp2->Y);
tmp2 = tmp2->next;
}
*/
printf("(The End)\n");
}
The code for printing the path has also been changed a little because when using DFS the path would be printed reversed so the code that has been commented out was for when I needed to reverse it back but now it's not necessary to use it. Any help with why the path can't be printed is very much appreciated, I'm still quite new to C programming and path finding algorithms.
so I have some questions on how to do correct memory management.
Basically my question is, what happens when for example I take this code(shown below). Does this need to be freed to prevent memory leaks?
void traverseLeftRight(struct node *head){
current = head;
if(current == NULL){
printf("There are no nodes within the list\n");
}
while(1){
if(current != NULL){
printf("left to right output: %d\n", current -> value);
current = current -> next;
}else{
break;
}
}
}
Also, would free(current) and current = NULL break the list if i were do it within the break section of the list. Also, would something like this just break the pointing variable and not effect the node that it corresponds to?
void traverseLeftRight(struct node *head){
current = head;
if(current == NULL){
printf("There are no nodes within the list\n");
}
while(1){
if(current != NULL){
printf("left to right output: %d\n", current -> value);
current = current -> next;
}else{
free(current);
current = NULL;
break;
}
}
}
I think you are confused about memory management.
In the examples you've shown, there's no need for any freeing, because nothing has (as far as we can see) been allocated.
If you allocate memory with malloc(3) or friends, then you will typically need to free it later, exactly once. In general, failing to free something leaks memory (ie, the memory is still allocated, but you're not pointing to it any more, so can't use it), and freeing more than once is an error (in the sense that this opens the possibility of two bits of the code both believing they've been allocated exclusive use of the same bit of memory). Using a bit of memory (ie, dereferencing a pointer) after it's been freed is the classic ‘use-after-free’ error. Each of these results in somewhat hard-to-find bugs (but valgrind is your friend).
The call to free in your second would (unfortunately) not cause an error, because free doesn't report such errors.
You don't need to free memory if it's in use all the way to the end of the program – that's (in effect) freed automatically when the program finishes.
Almost the only case where you'd call free within a function like this is if you were writing a function to walk along a linked list (or something like that), freeing as it went. A traverse of a list (as your function name suggests) would not be expected to result in the list being freed.
here is your first code, corrected, so an empty list does not enter the 'while()' loop
void traverseLeftRight(struct node *head)
{
current = head;
if(current == NULL)
{ // then list empty
printf("There are no nodes within the list\n");
}
while( current != NULL )
{ // then, another node to print
printf("left to right output: %d\n", current -> value);
current = current->next; // step to next node in list
}
}
here is your second code, with comments
while(1)
{
if(current != NULL)
{ // then, not at end of list
printf("left to right output: %d\n", current -> value);
current = current->next; // step to next entry in list
}
else
{ // else, current == NULL ,, I.E. past end of list
free(current); // nothing will happen because Current == NULL
current = NULL; // already NULL, so nothing will change
break; // exit the 'while()' loop
}
}
proposed code that free's the linked list as it traverses it.
Note that the 'head' needs to be updated as the list is 'free'd
// note the 'pointer to pointer parameter, so 'head' can be modified
void traverseLeftRight(struct node **head)
{
if( *head == NULL)
{ // then, list is empty
printf("There are no nodes within the list\n");
}
while( *head != NULL)
{ // while another node to process
struct node *temp = *head; // remember current node pointer
printf("left to right output: %d\n", current -> value);
*head = (*head)->next; // step to next node
free( temp ); // eliminate current node
}
}
Hello stackoverflowers,
i am facing a problem with my function in C, i want to create a function that give me the min and max value in BST.
The problem is when i use this function it returns the same value for min and max:
void Find_Min_Max(node *bt,int* maxint,int* minint)
{
node *tmp = bt;
if( bt == NULL)
{
*maxint = 0; // Only if the tree contains nothing at all
*minint = 0; // Only if the tree contains nothing at all
}
if( bt->left)
return Find_Min_Max(bt->left,&(*maxint),&(*minint));
*minint = bt->data;
if( tmp->right)
return Find_Min_Max(tmp->right,&(*maxint),&(*minint));
*maxint = tmp->data;
}
But when i use it to give me just one result max/min, i delete this part of code, everything work perfectly:
if( tmp->right)
return Find_Min_Max(tmp->right,&(*maxint),&(*minint));
*maxint = tmp->data;
Any idea how this will work?.
Thank you in advance.
It's not really easy / intuitive to recursively compute max and min at the same time in the same function. I would even say it's not possible, because those are two completely different traversals.
You should have a function to get the minimum, a function to get the maximum, and call each of them inside Find_Min_Max.
This would be a possible approach:
int find_min(node *n) {
if (n == NULL) {
return 0;
}
while (n->left != NULL) {
n = n->left;
}
return n->data;
}
find_max is similar, but traverses right links only:
int find_max(node *n) {
if (n == NULL) {
return 0;
}
while (n->right != NULL) {
n = n->right;
}
return n->data;
}
Then, find_min_max() is easy to code:
void find_min_max(node *bt, int *min, int *right) {
*min = find_min(bt);
*max = find_max(bt);
}
find_min() and find_max() could be recursive, but the iterative approach has the desirable property of using constant memory (and consequently avoids stack overflows).
To find the minimum value in a BST, you follow the chain of left children from the root until you reach a node with no left child. That node contains the minimum value (even if it does have a right child).
The algorithm to find the maximum is exactly the mirror image: follow the chain of right children until you reach a node with no right child. That node contains the maximum value.
It does not make sense to try to perform both traversals at the same time, because they follow completely different paths. If you want a single function to discover both the minimum and the maximum value, then it doesn't make much sense for that function itself to be recursive. It could, however, wrap calls to two separate recursive functions, one to find the minimum, and the other to find the maximum.
Finding min and max value in a BST are very easy. Please check both code snippet below I explain how these codes work.
public int minValueInBST(Node node){
if (node == null) throw new IllegalStateException();
Node current = node;
while (current.leftChild != null) {
current = node.leftChild;
}
return current.value;
}
To find the minimum value in BST we have to find the leftmost leaf node because that node contains the minimum value. So at first, we check the root node is null or not if null we throw IllegalStateException otherwise we find the left node, at last, we return the left node value.
public int maxValueInBST(Node node){
if (node == null) throw new IllegalStateException();
Node current = node;
while (current.rightChild != null) {
current = node.rightChild;
}
return current.value;
}
To find the maximum value in BST we have to find the rightmost leaf node because that node contains the maximum value. So at first, we check the root node is null or not if null we throw IllegalStateException otherwise we find the right node, at last, we return the right node value.
// try this
tree_node *min(tree_node *root)
{
if (!root)
{
printf("Tree is empty");
exit(1);
}
tree_node *ret_val;
if (root->left == NULL)
{
ret_val = root;
}
else
{
ret_val = min(root->left);
}
return ret_val;
}
tree_node *max(tree_node *root)
{
if (!root)
{
printf("Tree is empty");
exit(1);
}
tree_node *ret_val;
if (root->right == NULL)
{
ret_val = root;
}
else
{
ret_val = max(root->right);
}
return ret_val;
}
complete code