Binary Tree in C Language
I have learned the importance of Binary Trees before, but I have never summarized them. examples found on the Internet are either a lot of theory, then pseudo-code implementation, or complex code, there is no explanation; in the end, I still rely on FQ to find some good articles. For the reference address, I will post the Problem to you in the See Also section. Suppose we want to generate a binary tree like; Solution obviously, the data we need to save on the node has only one integer; struct binary_tree {int Data; // data area // TODO}; so in the struct, our code should be similar to the above; through observation, we also find that each node points to both the left and right sides (except the last leaf node). Therefore, we need to let it have two pointer domains; you may think of a statement similar to the following: struct binary_tree {int data; // Data area void * left; void * right ;}; the above definition format seems Yes, but the type does not seem to be what we want. For example, when left points to the child node on the left, the child node should also be a node containing data fields; therefore, we need to define another struct with the same structure as it itself; struct binary_tree {int data; // Data area struct binary_tree * left; struct binary_tree * right;}; so, we will define it like this; obviously, this is a recursive definition; if we want to instantiate a node, we can: struct binary_tree * tree; obviously, we need to define an instance to write such a long type name, which is really uncomfortable; so we can; typedef struct binary_tree node; node * tree; okay! So far our data domains have been defined! Your current Code should look like the following;
struct binary_tree { int data ; // Data area binary_tree * left; binary_tree * right;}; typedef struct binary_tree node;
Next, we need to insert the data to the corresponding location. We hope that the data on the left branch of the tree is always smaller than that on the right branch of the tree;
void insert(node ** tree, int val) { node * temp = NULL; if(!(*tree)) { //TODO return ; } if (val < (*tree)->data) { //TODO }else if (val > (*tree)->data) { //TODO }}
Therefore, our code will be written as above; the first if statement determines whether the tree node exists; if it does not exist, we should generate a node and add it to the tree; the second if-else determines whether the given data to be saved is greater than or less than the current node. if it is smaller than, there is a left branch. Greater than the existing right branch;
if(!(*tree)) { temp = (node *)malloc(sizeof(node)); temp->left = temp->right = NULL; temp->data = val; *tree = temp; return ; }
After analyzing the code snippet above, we found that temp serves as a temporary variable just like its name; malloc allocates memory, and then initializes the Left and Right pointer fields of the node to be empty, and the data fields to be val; finally, * tree = temp installs the node on the tree and returns the upper level. For existing tree nodes, we need to expand to the left and right sides. Therefore, our code will be like this; if (val <(* tree)-> data) {insert (& (* tree)-> left, val);} else if (val> (* tree) -> data) {insert (& (* tree)-> right, val);} from the code, we can see that only data smaller than or greater than two directions are operated; you may consider the case where it is equal. Note that data uniqueness is required here. It is similar to a set in mathematics and there will be no duplicates; this feature is very helpful for writing the word statistics program in the future. All the code of this function is as follows:
void insert(node ** tree, int val) { node * temp = NULL; if(!(*tree)) { temp = (node *)malloc(sizeof(node)); temp->left = temp->right = NULL; temp->data = val; *tree = temp; return ; } if (val < (*tree)->data) { insert(&(*tree)->left,val); }else if (val > (*tree)->data) { insert(&(*tree)->right,val); }}
After the node is created, note that we use malloc to create the node. Therefore, we allocate the memory in the heap, so we need to manually release the node. Obviously, we need to use the free function; therefore, the function for releasing nodes should be like this. void deltree (node * tree) {if (tree) {free (tree) ;}} does not seem to be a problem! But observe carefully and we find that releasing free is just to release the root node; just like pulling out the peanuts; we just cut the leaves with scissors; I have never thought that it is impossible to dig all the peanuts along the root; therefore, we need to do this;
void deltree(node * tree) { if(tree) { deltree(tree->left); deltree(tree->right); free(tree); }}
In this way, we can find the root on the left and continue to find the root on the left. If we cannot find the root on the right, we can find the root on the right. If we cannot find the root on the right, we can execute the free release node and return to the upper level! There are also functions to build the tree, and there are ways to "cut" it! Next we will show you how to present our trees. There are three types of tree traversal: PRE, middle, and back; void print_preorder (node * tree) {if (tree) {// TODO} first, we need to determine whether the tree is empty. If it is empty, we do not need to check any data in it;
void print_preorder(node * tree) { if(tree) { printf("%d\n",tree->data); print_preorder(tree->left); print_preorder(tree->right); }}void print_inorder(node * tree) { if(tree) { print_inorder(tree->left); printf("%d\n",tree->data); print_inorder(tree->right); }}void print_postorder(node * tree) { if(tree) { print_postorder(tree->left); print_postorder(tree->right); printf("%d\n",tree->data); }}
X similarly, we write the middle and back orders;
void print_preorder(node * tree) { if(tree) { printf("%d\n",tree->data); print_preorder(tree->left); print_preorder(tree->right); }}void print_inorder(node * tree) { if(tree) { print_inorder(tree->left); printf("%d\n",tree->data); print_inorder(tree->right); }}void print_postorder(node * tree) { if(tree) { print_postorder(tree->left); print_postorder(tree->right); printf("%d\n",tree->data); }}
Okay! All the functions are available. We should write test functions;
int main(void){ node * root; node * tmp; //int i; root = NULL; /* Inserting nodes into tree */ insert(&root,9); insert(&root,4); insert(&root,15); insert(&root,6); insert(&root,12); insert(&root,17); insert(&root,2); printf("Pre Order Display\n"); print_preorder(root); printf("In Order Display\n"); print_inorder(root); printf("Post Order Display\n"); print_postorder(root); /* Deleting all nodes of tree */ deltree(root);}
The running result is as follows:
I have learned the importance of Binary Trees before, but I have never summarized them. examples found on the Internet are either a lot of theory, then pseudo-code implementation, or complex code, there is no explanation; in the end, I still rely on FQ to find some good articles. For the reference address, I will post the Problem to you in the See Also section. Suppose we want to generate a binary tree like; Solution obviously, the data we need to save on the node has only one integer; struct binary_tree {int Data; // data area // TODO}; so in the struct, our code should be similar to the above; through observation, we also find that each node points to both the left and right sides (except the last leaf node). Therefore, we need to let it have two pointer domains; you may think of a statement similar to the following: struct binary_tree {int data; // Data area void * left; void * right ;}; the above definition format seems to be correct, but the type is as follows: This is not what we want. For example, when left points to the child node on the left, the child node should also be a node that contains data domains; therefore, we need to define another structure that is the same as it itself; struct binary_tree {int data; // Data area struct binary_tree * left; struct binary_tree * right;}; so we will define it like this; obviously, this is a recursive definition; if we want to instantiate a node, we can: struct binary_tree * tree; obviously we need to define an instance to write such a long type name, which is really uncomfortable; so we can; typedef struct binary_tree node; node * tree; okay! So far our data domains have been defined! Your current Code should look like the following; copy the code struct binary_tree {int data; // Data area binary_tree * left; binary_tree * right ;}; typedef struct binary_tree node; to copy the code, we need to insert the data to the corresponding location. We hope that the data in the left branch of the tree is always smaller than that in the right branch of the tree. We do not explain why; copy the code void insert (node ** tree, int val) {node * temp = NULL; if (! (* Tree) {// TODO return;} if (val <(* tree)-> data) {// TODO} else if (val> (* tree) -> data) {// TODO} copies the code. Therefore, the code is written as above. The first if statement determines whether the tree node exists. if the tree node does not exist, we should generate a node and add it to the tree. The second if-else determines whether the given data to be saved is greater than or less than the current node, the left branch exists. If (! (* Tree) {temp = (node *) malloc (sizeof (node); temp-> left = temp-> right = NULL; temp-> data = val; * tree = temp; return;} copy the code to analyze the code snippet above. We found that temp serves as a temporary variable just like its name. malloc allocates memory, and then initializes the pointer fields left and right of the node to be empty, and the data field is val. Finally, * tree = temp installs the node on the tree and returns the upper level. For existing tree nodes, we need to expand to the left and right sides; therefore, our code will be like this; if (val <(* tree)-> data) {insert (& (* tree)-> left, val );} else if (val> (* tree)-> data) {insert (& (* tree)-> right, val);} can be seen from the code, only Perform operations on the data. You may consider the case if it is equal. Note that data uniqueness is required here. It is similar to a set in mathematics and there will be no duplicates; this feature is very helpful for writing the word statistics program in the future. All the code of this function is as follows: copy the code void insert (node ** tree, int val) {node * temp = NULL; if (! (* Tree) {temp = (node *) malloc (sizeof (node); temp-> left = temp-> right = NULL; temp-> data = val; * tree = temp; return;} if (val <(* tree)-> data) {insert (& (* tree)-> left, val );} else if (val> (* tree)-> data) {insert (& (* tree)-> right, val) ;}} the copied code node has been created, note that we create with malloc. Therefore, we allocate the memory in the heap, so we need to release it manually. It is obvious that we need to use the free function to correspond to it; therefore, the function for releasing nodes should be like this. void deltree (node * tree) {if (tree) {free (tree) ;}} does not seem to be a problem! But observe carefully and we find that releasing free is just to release the root node; just like pulling out the peanuts; we just cut the leaves with scissors; I have never thought that it is impossible to dig all the peanuts along the root; therefore, we need to do this; copy the code void deltree (node * tree) {if (tree) {deltree (tree-> left); deltree (tree-> right); free (tree) ;}} copy the code so that we can find the root on the left and continue searching on the left; if you cannot find the node, you can find it on the right. If you cannot find it, you can execute the free release node and return to the upper level. Okay! There are also functions to build the tree, and there are ways to "cut" it! Next we will show you how to present our trees. There are three types of tree traversal: PRE, middle, and back; void print_preorder (node * tree) {if (tree) {// TODO} first, we need to determine whether the tree is empty. If it is empty, we do not need to check the data in it. Copy the code void print_preorder (node * tree) {if (tree) {printf ("% d \ n", tree-> data); print_preorder (tree-> left); print_preorder (tree-> right );}} copy the code in the same order. Copy the code void print_preorder (node * tree) {if (tree) {printf ("% d \ n ", tree-> data); print_preorder (tree-> left); print_preorder (tre E-> right) ;}} void print_inorder (node * tree) {if (tree) {print_inorder (tree-> left); printf ("% d \ n ", tree-> data); print_inorder (tree-> right) ;}} void print_postorder (node * tree) {if (tree) {print_postorder (tree-> left ); print_postorder (tree-> right); printf ("% d \ n", tree-> data) ;}} copy the code! All functions are available. We should write the test function. Copy the code int main (void) {node * root; node * tmp; // int I; root = NULL; /* Inserting nodes into tree */insert (& root, 9); insert (& root, 4); insert (& root, 15); insert (& root, 6 ); insert (& root, 12); insert (& root, 17); insert (& root, 2); printf ("Pre Order Display \ n"); print_preorder (root ); printf ("In Order Display \ n"); print_inorder (root); printf ("Post Order Display \ n"); print_postorder (root );/ * Deleting all nodes of tree */deltree (root);} copy the code execution result as follows: copy the code Pre Order Display9426151217In Order Display2469121517Post Order display%1217159 copy the code Discussion. Then this example seems too simple! It does not have a function to query the tree or to measure the height of the tree. However, it is concise to make it easier to understand! It's so concise that we don't even know why we need to make the data into a tree structure. Why isn't the linear data still able to solve our problems? You have asked yourself this question. I used to ask myself some questions before. To help everyone understand the necessity of data with a tree structure, we want to count the frequency of the C language keywords in the code; what will we do ?? (I will explain this in another article)
X Discussion, then this example seems too simple! It does not have a function to query the tree or to measure the height of the tree. However, it is concise to make it easier to understand! It's so concise that we don't even know why we need to make the data into a tree structure. Why isn't the linear data still able to solve our problems? You have asked yourself this question. I used to ask myself some questions before. To help everyone understand the necessity of data with a tree structure, we want to count the frequency of the C language keywords in the code; what will we do ?? (I will explain this in another article)