I am new to C and I am learning functions and pointers. I have to print Binary search tree in the requisite format below in the t_print method and I would be really grateful some could guide me how to go about it.
I have this code till now:
typedef struct tree {
void* data;
struct tree* left;
struct tree* right;
} Tree;
/*set the data on the tree node */
void t_set_data(Tree* t, void* data) {
t->data = data;}
/*let l be the left node of tree *t */
void t_set_left(Tree* t, Tree* l){
t->left = l;}
/*let r be the left node of tree *t */
void t_set_right(Tree* t, Tree* r){
t->right = r;}
/* simply return left node of the tree */
Tree* t_left(Tree* t){
return t-> left;}
/* simply return right node of the tree */
Tree* t_right(Tree* t){
return t-> right;}
/* simply return the data of the tree */
void* t_data(Tree* t){
return t->data;}
/* make the node of the tree and allocate space for it*/
Tree* t_make(){
Tree *t = (Tree*)malloc(sizeof(tree));
t->left=NULL;
t->right = NULL;
t-> data = NULL;
return t;
}
/*
print the whole tree in the following format
Root is printed with zero trailing spaces to the left
Every node/subtree is printed with one additional space
Below is an example for a tree with depth 2:
Root
<space>Left-Child
<space><space>Left-Left-Child
<space><space>Left-Right-Child
<space>Right-Child
.... and so on ...
Use (void(* p))(function pointer) to print.
*/
void t_print( Tree* t ,int space, void(* p)(void*) ){
}
It depends on the order in which you want the data printed, but for a BST, 'in order' is appropriate (as opposed to pre-order or post-order).
To print a tree 'in order', the function does:
If the current node is not null
print the left sub-tree in order
print the current node
print the right sub-tree in order
The 'print the xxx sub-tree in order' operations are recursive calls to the 'print in order' function with the left or right pointer.
The algorithm for pre-order is:
If the current node is not null
print the current node
print the left sub-tree in pre-order
print the right sub-tree in pre-order
The algorithm for post-order is:
If the current node is not null
print the left sub-tree in post-order
print the right sub-tree in post-order
print the current node
And it really is that simple.
Well, nearly that simple...
If you want to bracket the data or otherwise make it possible to identify the tree structure, you have to work a little harder. You also typically end up with a cover function that adds a prefix (tag identifying the output) and a suffix (maybe just a newline); this is not part of the recursive printing, usually. But the core of the algorithm is as simple as described.
Related
Write function in C that for given binary (ordered) tree returns pointer to node that is the closest to the root and divisible by 3.
Here is what I have tried:
Node* find_closest(Node* root) {
if(root == NULL)
return NULL;
if(root->number % 3 == 0)
return root;
if(root->left != NULL)
return find_closest(root->left);
if(root->right != NULL)
return find_closest(root->right);
}
But this doesn't seem to be working. Can someone please help me with this problem?
There are two basic ways to search a binary tree. DFS (depth-first search), and BFS (breadth-first search). DFS searches a tree by going as deep as it can in one direction, only going back up and trying other routes when it finds a dead-end. BFS searches the entire first layer of the tree, then the entire second layer of the tree and so on until it finds what it's looking for.
DFS is very easy to implement with recursion, and appears to be what you were trying to do with your code, but based on the problem you're trying to solve, BFS is a more appropriate algorithm to use, however it is a bit harder to implement because it involves a queue.
Here's an example implementation of BFS that should serve your purposes:
struct nodeQueue{ //This struct lets us store Node pointers in a queue
Node *node;
struct nodeQueue *next;
};
Node* find_closest(Node *root){
if(!root)
return NULL; //Just in case
//Head and tail let us manage the queue of nodes that need to be searched next
struct nodeQueue *head = malloc(sizeof(struct nodeQueue));
struct nodeQueue *tail = head;
*head = (struct nodeQueue){.node = root,.next = NULL}; //root is first in line
while(head){ //As long as there are nodes to check
Node *check = head->node; //Let's pull the next node out of the queue
if(check->number % 3 == 0){ //It is divisible by three so we're done
while(head){ //Free queue to prevent memory leak
struct nodeQueue *hold = head->next;
free(head);
head = hold;
}
return check; //return the node we found
}
//...otherwise
//We need to add the left and right nodes to the queue so they can wait their turn
if(check->left){ //If there is a node to the left put it at the end of the queue
tail->next = malloc(sizeof(struct nodeQueue));
tail = tail->next;
*tail = (struct nodeQueue){.node = check->left,.next = NULL};
}
if(check->right){ //If there is a node to the right put it at the end of the queue
tail->next = malloc(sizeof(struct nodeQueue));
tail = tail->next;
*tail = (struct nodeQueue){.node = check->right,.next = NULL};
}
struct nodeQueue *hold = head->next;
free(head);
head = hold; //Remove the node we just checked so the loop starts with the node next in line
} //If this loop breaks it means none of the nodes' numbers are divisible by three
return NULL;
}
Ensure stdlib.h is included for malloc() and free() and you must be using at least C99 for compound literals to work.
Algorithmically, using a breadth-first search instead of a depth-first search is the most natural solution for finding the node closest to the root (in terms of depth in the tree) that meets a particular criterion. That means testing all the nodes at each depth before testing any deeper nodes, which allows you to test the smallest number of nodes. Read up on breadth-first search if you want to consider this.
Alternatively, you can do it with a depth-first search such as you are presently using, but to do so, you need to track and report back additional data. Specifically, the depth at which the returned result was found, or an equivalent. For example, consider this:
/*
* Finds and returns one of the nodes among those whose number is evenly
* divisible by three and that share a minimum distance from the specified
* root, or returns null if the tree does not contain any nodes with numbers
* divisible by three
*
* root: a pointer to the root node of the tree
* result_depth: a pointer to a `size_t` wherein the depth of the result node
* should be written
*/
Node *find_closest_helper(Node *root, size_t *result_depth);
That can be implemented as a recursive depth-first search, with the result_depth argument used to pass the information between calls that is necessary to favor the nodes closer to the root. The "_helper" part of the function name is meant to convey that you can preserve the function signature presented in the question by making that function a (non-recursive) wrapper around this one.
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 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.
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 have two questions about binary search trees - one about the code I am writing and the other more about theory. First of all, the code that I wrote below works fine except for when I try to display the case where the BST is actually empty; it gives me a segmentation fault when I would like it to print out an error message. I feel like I may have gotten my pointers mixed up at some point and so that is giving me the error. Here is my code:
#include <stdio.h>
#include <stdlib.h>
struct Node {
char *word;
struct Node *left;
struct Node *right;
};
/* Function that creates a new node as a leaf; that is, its */
/* left and right children are NULL. */
/* Require: node -> word != NULL */
struct Node * createNode (char *word) {
struct Node *item = malloc(sizeof(struct Node));
item -> word = word;
item -> left = NULL;
item -> right = NULL;
return item;
}
/* Recursive function that inserts a node into proper position by */
/* searching through tree. */
struct Node * insertNode (struct Node *root, char *word) {
// If tree is empty, node becomes root
if(root == NULL)
return createNode(word);
else {
if(strcmp(word, root -> word) < 0) {
root -> left = insertNode(root -> left, word);
return root;
} else if(strcmp(word, root -> word) > 0) {
root -> right = insertNode(root -> right, word);
return root;
} else if(strcmp(word, root -> word) == 0)
printf("Word is already present in the tree.");
}
}
/* Function to display Binary Search Tree via inorder traversal. */
/* -- prints entire left subtree, then root, then right subtree */
void display (struct Node *root) {
if(root -> word == NULL)
printf("Tree is empty.");
if(root -> left != NULL)
display(root -> left);
printf("%s\n", root -> word);
if(root -> right != NULL)
display(root -> right);
}
void main () {
struct Node root;
struct Node *rootP = &root;
root = createNode("
}
The second question involves populating the binary tree. I want to use a small dictionary which will of course be in alphabetical order. If I feed those words into the binary tree starting with, say, "aardvark," won't the tree end up incredibly skewed as all subsequent words will come after the first alphabetically and thus always be right children? I'm afraid that I will end up with a tree that is incredibly off balance! Is there some method I can use to shuffle the tree around as I am populating it?
Thank you for taking the time to read this!
In your display function, you first need to test whether root == null before testing whether root -> word == null. That should fix the seg fault.
Regarding the theory question: the answer is that yes, the tree will end up incredibly skewed. That's what balanced binary trees are all about.
if(root -> word == NULL)
printf("Tree is empty.");
Your problem lies here. What happens if root itself is null? Double check that pointer before de-referencing it.
Yes, if you insert items in sorted order (or relatively sorted), you'll get a skewed tree. Look into algorithms on rotations for nodes in balanced binary trees.
Regarding your second question, others have already mentioned looking into balancing your binary tree. However, as an alternative, if your input is already known to be sorted, then it is more appropriate instead to use a linear array with a binary search to find items of interest, rather than a binary tree. A binary search of a sorted array has the same time complexity as a search in a balanced binary tree.