Binary Tree common operation algorithm set, explanation and precautions

Source: Internet
Author: User
Tags cdata
Binary Tree is a common data structure. Binary Trees are often used in programs. However, the language you use does not necessarily provide binary tree data types, we can implement a binary tree data type by ourselves. It is as convenient as using other defined types as needed. The following are some algorithms and explanations I have written (based on the C language). I hope this will be helpful for readers to write a binary tree data type. 0. The four basic laws of recursion are implemented by Recursive Algorithms in binary trees, and the code is concise and easy to understand. However, writing a good recursive algorithm is not easy, so I think it is necessary to introduce some Recursive Implementation Rules to you before you begin to explain these algorithms. The code in this article is based on the following rules (at least I think so ). 1) baseline. There must always be some benchmark situations that can be solved without recursion. 2) Continuous promotion. For situations that require recursion, each recursive call must push the solution to a situation close to the benchmark. 3) design rules. Assume that all recursive calls can be performed. 4) synthetic benefit rules. When solving the same instance of a problem, do not perform repetitive work in different recursive calls. 1. Data Storage Structure and definition
# Define true 1 # define false 0 // define your own data type typedef char datatype; typedef int bool; typedef struct binode {datatype CDATA; // used to store real data struct binode * lchild; // point to left child struct binode * rchild; // point to right child} binode, * bitree;
2. Implementation of basic operations 1) traversal of Binary Trees is the basis of other operations. Many operations of Binary Trees are based on traversal, having mastered traversal is helpful for understanding and implementing other algorithms, let's take a look at the Traversal Algorithms. In binary trees, there are three types of algorithms based on the access root order, that is, first traverse (first access the root, then access the left subtree first, then access the right subtree first), and then traverse in the middle (first visit the left subtree in the middle order, visit the root, finally, access the right subtree in the middle order) and traverse in the back order (access the left subtree sequentially, then access the right subtree in the later order, and finally access the root ), another method is hierarchical traversal (with Queue). Their implementation is as follows:
Bool preordertraverse (bitree BT, bool (* visit) (binode *) {// traverses Binary Trees sequentially and calls visit once for each node, and only once // implement some operation on the node. If visit fails, the traversal fails if (BT! = NULL) {If (* visit) (BT) // access the root node {If (preordertraverse (BT-> lchild, visit )) // first access the left subtree if (preordertraverse (BT-> rchild, visit) // first access the right subtree return true; return false;} else return true ;} // describool inordertraverse (bitree BT, bool (* visit) (binode *) {// traverses the binary tree in the middle order and calls visit once for each node, and only once // implement some operation on the node. If visit fails, the traversal fails if (BT! = NULL) {If (inordertraverse (BT-> lchild, visit) // The left subtree for central access {If (* visit) (BT )) // access the root node if (inordertraverse (BT-> rchild, visit) // return true; return false;} else return true ;} // describool postordertraverse (bitree BT, bool (* visit) (binode *) {// traverses the binary tree in descending order and calls visit once for each node, and only once // implement some operation on the node. If visit fails, the traversal fails if (BT! = NULL) {If (postordertraverse (BT-> lchild, visit) // access the left subtree in a descending order {If (postordertraverse (BT-> rchild, visit )) // access the right subtree in the descending order if (* visit) (BT) // access the root node return true; return false ;}} else return true ;} // define bool levelordertraverse (bitree BT, bool (* visit) (binode *) {// traverses Binary Trees hierarchically and calls visit once for each node, and only once // implement some operation on the node. If visit fails, the traversal fails. // use an array to simulate a loop queue if (bt = NUL L) return true; const int ncapicity = 300; bitree dt [ncapicity]; int nfront = 0, nrear = 1; dt [0] = Bt; // int nsize = 1; while (nsize! = 0) // The queue is not empty. {If (dt [nfront]-> lchild) {// The left subtree is not empty, left subtree into the team dt [nrear] = dt [nfront]-> lchild; ++ nrear; ++ nsize;} If (dt [nfront]-> rchild) {// The right subtree is not empty. The right subtree is queued for dt [nrear] = dt [nfront]-> rchild; ++ nrear; ++ nsize ;} // access the element of the queue header and leave the queue if (! (* Visit) (dt [nfront]) return false; ++ nfront; -- nsize; If (nsize> ncapicity) return false; If (nrear = ncapicity) nrear = 0; If (nfront = ncapicity) nfront = 0;} return true ;}
Note: From the code above, we can see that if the node access is removed from the function, the traversal code in the first, middle, and back order is exactly the same. It can be seen that the traversal of these three sequences only differs in the order of the access root. 2) destroy the tree with Bt as the root node
Bitree destorybitree (bitree BT) {// release all Tree nodes and leave the pointer to the root of the tree empty. // only the back order is available. Otherwise, one (Middle Order) is required) or two (pre-order) temporary variables // to save Bt-> lchild and BT-> rchild if (BT) {destorybitree (BT-> lchild ); destorybitree (BT-> rchild); free (BT);} return NULL ;}
Note: I think it is better to destroy objects in sequence after the destruction operation, because it is the most intuitive method, because if the destruction is first executed, two variables are required to save the left child (BT-> lchild) and right child (BT-> rchild) of Bt, because the root is destroyed first, that is, after free (BT, you cannot use BT to directly reference its left or right child, that is, you cannot use the following statement: destorybitree (BT-> lchild); destorybitree (BT-> rchild );. In the same way, a variable is required to save the right subtree of BT. In addition, this algorithm can be used to destroy any sub-tree of the entire tree or tree, as long as BT is the root pointer of the tree to be deleted. 3) Find the node with the node value C in the binary tree.
Bitree findnode (bitree BT, datatype c) {// returns the pointer to the node where the value of Bt in the binary tree is C. // If C does not exist in BT, null if (! BT) return NULL; else if (BT-> CDATA = C) // locate the corresponding node and return its pointer return Bt; bitree bn = NULL; BN = findnode (BT-> lchild, c); // search for if (BN = NULL) in its left subtree // not found, search for bn = findnode (BT-> rchild, c); Return Bn;} in the right subtree ;}
Note: The search operation can be any of the first, middle, and back-order searches. Here, the first-order searches are used. In addition, if your language supports the reference type, it is more efficient to define the function as bitree findnode (bitree BT, const datatype & C) because the C language does not have a reference type, therefore, it can only be written as above. 4) Calculate the depth of a binary tree with Bt as the root node
Int bitreedepth (bitree BT) {// evaluate the depth of the tree // according to the binary tree depth definition, the depth of the binary tree should be the maximum value of the left and right subtree depth plus 1, // because the root node is also regarded as Layer 1. If (bt = NULL) // if the tree is empty, return-1 return-1; else {int nldepth = bitreedepth (BT-> lchild ); // evaluate the depth of the left Tree int nrdepth = bitreedepth (BT-> rchild); // evaluate the depth of the right tree if (nldepth> = nrdepth) {return nldepth + 1 ;} else {return nrdepth + 1 ;}}}
Note: In some books, the depth of the empty tree is 0, and the depth of the binary tree with only one node is 1, but here I use the depth of the empty tree as-1, the depth of a binary tree with only one node is 0. 5) Calculate the parent node of a node in a binary tree
Bitree getparent (bitree BT, datatype c) {// obtain the parent node of the node with the value of C. // If C is the root node or does not exist in the tree, null if (! BT | Bt-> CDATA = C) return NULL; If (BT-> lchild & BT-> lchild-> CDATA = C) | (BT-> rchild & BT-> rchild-> CDATA = C) return Bt; bitree parent = NULL; parent = getparent (BT-> lchild, c); If (parent = NULL) parent = getparent (BT-> rchild, c); Return parent ;}
Note: before determining the value of the Left or Right child, you must first determine whether the Left or Right child is empty. For example, if the left subtree of Bt is empty, the expression Bt-> lchild-> CDATA produces exceptions. Therefore, you must check whether the child is empty before determining the condition. In addition, if the function returns NULL, there are two possibilities: one is the root node of the tree (the root node has no parent node), and the other is that the node does not exist in the tree. Therefore, if the returned value is null during the application, you must determine whether the node with the value of C is the root node. If it is not the root node, it indicates that no value in the tree bt is the c node. Similar to searching, if your language supports reference types, the function definition will be bitree getparent (bitree BT, const datatype & C) more efficient. 6) Find the maximum and minimum values in the binary tree.
Bitree maxnode (bitree BT) {// returns the maximum value of the node in the binary tree BT if (bt = NULL) // returns NULL return NULL; binode * Pmax = Bt; // binode * TMP = maxnode (BT-> lchild) is used as the current maximum node by default. // find the maximum node of the Left subtree if (TMP! = NULL) {// The left subtree exists, and the maximum node of the Left subtree is greater than the current maximum node if (TMP-> CDATA> Pmax-> CDATA) Pmax = TMP ;} TMP = maxnode (BT-> rchild); // find the maximum node of the right subtree if (TMP! = NULL) {// The right subtree exists, and the maximum node of the right subtree is greater than the current maximum node if (TMP-> CDATA> Pmax-> CDATA) Pmax = TMP ;} return Pmax ;}
Note: The algorithm IDEA for finding the smallest node is the same. It is not provided here. This algorithm mainly focuses on whether the left or right subtree exists to avoid program exceptions due to memory access errors. If the left-side Pair does not exist, it returns NULL according to the code, so it cannot be referenced, that is, it cannot use statements such as TMP-> CDATA. 7) calculate the number of leaf nodes and non-leaf nodes in a binary tree.
Int leavescount (bitree BT) {// returns the number of leaf nodes in the binary tree BT if (bt = NULL) return 0; // bt is an empty tree, returns 0 int ncount = 0; If (! (BT-> lchild | Bt-> rchild) ++ ncount; // BT indicates the leaf node, add 1 else {// Add the leaf node ncount + = leavescount (BT-> lchild) on the left subtree ); // Add the leaf node ncount + = leavescount (BT-> rchild);} return ncount;} // use int notleavescount (bitree BT) {// returns the number of non-leaf nodes in the binary tree BT if (bt = NULL | (! (BT-> lchild | Bt-> rchild) return 0; // If bt is an empty tree or a leaf node, 0 else {int ncount = 1 is returned; // at this time, the root node is also a non-leaf node // accumulate the number of non-leaf nodes on the left subtree ncount + = notleavescount (BT-> lchild ); // accumulate the number of non-leaf nodes on the right subtree ncount + = notleavescount (BT-> rchild); Return ncount ;}}
Note: expression :! (BT-> lchild | Bt-> rchild) determines whether a node is a leaf node. If it is a leaf node, the value is true. Otherwise, the value is false. 3. Supplement 1) in all algorithms, the passing of tree parameters is the pointer to the root node of the required tree. The interfaces are unified, easy to use, and error-free. 2) The datatype can be redefined to completely reuse these algorithms. 3) these operations are basic operations in a binary tree. You can use these operations to combine more functions and operations. These functions have passed simple tests and no running errors have been found.

If any algorithm error is found, please point it out!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.