The previous sections describe the knowledge of static lookup tables, starting with this section to introduce another lookup table-dynamic lookup tables.
When a lookup operation is found in a dynamic lookup table, it can be deleted if the lookup succeeds, and if the lookup fails, the keyword is not in the table and can be inserted into the table.
Dynamic lookup tables are represented in a variety of ways, and this section describes an implementation method that uses a tree structure to represent a dynamic lookup table-a two-fork sort tree (also known as a binary lookup tree).
What is a two-fork sort tree?
Binary sort tree is either an empty binary tree or has the following characteristics:
- Binary sort tree, if its root node has left dial hand tree, then the value of all nodes on the left subtree is smaller than the value of the root node;
- Binary sort tree, if its root node has a right subtree, then the value of all nodes on the right subtree is the value of the root node;
- The left and right subtree of the binary sort tree are also required to be two-fork sort trees;
For example, Figure 1 is a binary sort tree:
Figure 12 Fork Sort tree Find keywords using two-fork sorting tree
When looking for a keyword in a binary sort tree, the lookup process is similar to the suboptimal two fork tree, where the binary sort tree is not empty and the root node of the tree is compared by the first lookup value, and there are 3 different results:
- If equal, find success;
- If the comparison result is a large keyword value for the root node, then the keyword may exist in its left subtree;
- If the comparison result is a small keyword value for the root node, the keyword may exist in its right subtree;
The implementation function is: (using recursive method)
Bitree Searchbst (Bitree T, KeyType key)
{ // if T is null in the recursive procedure, the result is found, and null is returned, or if the lookup succeeds, a pointer to that keyword is returned if(! T | | Key==t->data)
{ returnT; }
Else if(key<t->data)
{ // recursively traverse its left child returnSearchbst (t->lchild, key); }
Else
{// recursively traverse its right child returnSearchbst (t->rchild, key); }}
Insert a keyword in a binary sort tree
The binary sort tree itself is a representation of a dynamic lookup table, sometimes inserting or deleting elements from a table during a lookup, and when a data element needs to be inserted because the lookup fails, the insertion position of the data element must be at the leaf node of the binary sort tree, and must be the left child or right child to find the last node visited at the time of the failure.
For example, in Figure 1 in the two-fork sort tree to find the operation of the keyword 1, when the keyword 3 is found in the leaf node, the table is determined that there is no such keyword, the keyword 1 is inserted in the position of the keyword 3 left child.
Therefore, the binary sorting tree indicates that the dynamic lookup table does the insertion operation, only need to change the above code a little bit to implement, the implementation code is:
BOOL Searchbst (Bitree T, KeyType key, Bitree F, Bitree *p)
{ // If the T pointer is empty, the lookup fails, the P pointer points to the last leaf node in the lookup process, and returns the information that the lookup failed if(!T)
{ *p =F; return false; } Else if(key==t->data)
{
If equal, the P pointer points to the keyword and returns the Find success information*p =T; return true; } //if the key value is smaller than the value of the T root node, look for its left subtree, and conversely, find its right subtree Else if(key<t->data)
{ returnSearchbst (t->lchild, Key, T, p); }
Else
{returnSearchbst (t->rchild, Key, T, p); }}
// Insert FunctionBOOL Insertbst (bitree T, Elemtype e)
{Bitree P=NULL; // If the lookup is unsuccessful, insert action is required if(! Searchbst (T, E,null, &p))
{ // Initialize Insert nodeBitree s = (bitree)malloc(sizeof(Bitree)); S->data =e; S->lchild = S->rchild =NULL; //if P is null, the two-fork sorting tree is an empty tree, at which point the inserted node is the root node of the entire tree if(!2)=s; } //if P is not NULL, then p points to the last leaf node that failed to find, just by comparing the values of P and E to determine if S is P's left child or right child Else if(e<p->data)
{P->lchild =s; }Else{p->rchild =S }
return true; }
//If the lookup succeeds, no insert operation is required, insert fails return false;}
By using a two-fork sort tree to find and insert a dynamic lookup table, you can get an ordered sequence of all the keywords while traversing the binary sort tree in the middle order.
For example, if the original binary sort tree is an empty tree, when searching for a dynamic lookup table {3,5,7,2,1}
and inserting an operation, you can construct a two-fork sort tree containing all the keywords in the table, as in procedure 2:
Figure 22 Fork Sort Tree Insert procedure
With the continuous find and insert operations, the final build of the two-fork sort Tree 2 (5) is shown. When using the middle order traversal algorithm to traverse the binary sort tree, the resulting sequence is: 1 2 3 5 7
, ordered sequence.
An unordered sequence can be transformed into an ordered sequence by constructing a binary sort tree.
Delete a keyword in a binary sort tree
During a lookup, if you delete a data element in a dynamic lookup table that is represented by a two-fork sort tree, you need to delete the node successfully while still making the tree a two-fork sort tree.
If you want to delete the node p, then for the binary sort tree, you need to do different operations according to the node P location, there are 3 possible:
1, node p is the leaf node, at this time only need to delete the node, and modify its parent node pointer can be;
2, node p only Zuozi or only the right sub-tree, at this time only to its left subtree or right subtree directly into the node p parents node of the left subtree can be;
3. There are two kinds of processing methods at this time: the node P and the subtree
1) The left subtree of the node p is the left subtree of its parent node, and the right subtree of the node p is the right subtree of its own direct precursor node, and 3 is shown;
Figure 32 Deleting a node in the fork sorting tree (1)
2) the direct precursor (or direct successor) of node p is used instead of node p, and the direct precursor (or direct successor) of the binary sort tree is deleted. 4 for the use of direct precursor instead of node P:
Figure 42 Deleting a node in the fork Sorting tree (2)
In Figure 4, in the left image in the middle sequence traversal, the resulting node P direct precursor node is the node s, so directly with the node s cover node p, because the node s and left children, according to the 2nd rule, directly into the parent node of the right child.
Specific implementation code: (Can run)
#include <stdio.h>#include<stdlib.h>#defineTRUE 1#defineFALSE 0#defineElemtype int#defineKeyType int
/*Two fork Sorting tree node structure definition*/typedefstructbitnode{intdata; structBitnode *lchild, *Rchild;} Bitnode,*Bitree;//Two-fork sorting tree Lookup algorithmintSearchbst (Bitree T, KeyType key, Bitree F, Bitree *p)
{ //If the T pointer is empty, the lookup fails, the P pointer points to the last leaf node in the lookup process, and returns the information that the lookup failed if(!T)
{ *p =F; returnFALSE; } //if equal, the P pointer points to the keyword and returns the Find success information Else if(Key = = T->data)
{ *p =u; returnTRUE; } //if the key value is smaller than the value of the T root node, look for its left subtree, and conversely, find its right subtree Else if(Key < T->data)
{ returnSearchbst (t->lchild, Key, T, p); }
Else
{returnSearchbst (t->rchild, Key, T, p); }}
intInsertbst (Bitree *T, Elemtype e)
{Bitree P=NULL; //If the lookup is unsuccessful, insert action is required if(! Searchbst ((*t), E, NULL, &p))
{ //Initialize Insert nodeBitree s = (bitree)malloc(sizeof(Bitree)); S->data =e; S->lchild = S->rchild =NULL; //if P is null, the two-fork sorting tree is an empty tree, at which point the inserted node is the root node of the entire tree if(!p)
{ *t =s; } //if P is not NULL, then p points to the last leaf node that failed to find, just by comparing the values of P and E to determine if S is P's left child or right child Else if(E < p->Data
{P->lchild =s; }
Else
{p->rchild =s; } returnTRUE; } //If the lookup succeeds, no insert operation is required, insert fails returnFALSE;}
// Delete a functionintDelete (Bitree *p) {Bitree Q, S; // in case 1, the node p itself is a leaf node, which can be deleted directly . if(! (*p)->lchild &&! (*p)rchild)
{ *p =NULL; } Else if(! (*p)->lchild)
{
//left dial hand tree is empty, just use node P's right subtree node instead of node p;Q = *p; *p = (*p)Rchild; Free(q); } Else if(! (*p)->rchild)
{
//The right sub-tree is empty, just use node P's Zogen node instead of node p;Q = *p; *p = (*p)->lchild;//This is not the pointer *p points to the left dial hand tree, but instead assigns the address of the node stored by the left subtree to the pointer variable p. Free(q); } Else
{
//the left and right subtrees are not empty, with a 2nd wayQ = *p; S= (*p)Lchild; // traverse to find the direct precursor of node P while(s->rchild) {Q=s; S= s->Rchild; } // directly change the value of the node P(*p)->data = s->data; //to determine if the left subtree of the node P has a right subtree, it is divided into two kinds of discussion . if(Q! = *p)
{Q->rchild = s->lchild;// If so, then delete the direct precursor node and change the left child node of the precursor to the child node of the Q -point node.}
Else
{Q->lchild = s->lchild;// Otherwise, move the left subtree directly up } Free(s); }
returnTRUE;}
intDeletebst (Bitree *t,intkey) { if( ! (*t))
{
//there is no data element with the keyword equal to key returnFALSE; } Else { if(Key = = (*t),data)
{Delete (T); returnTRUE; } Else if(Key < (*T)data)
{ // use a recursive approach returnDeletebst (& (*t),lchild, key); } ElsereturnDeletebst (& (*t),rchild, key); }}
voidOrder (Bitree T)// Middle Sequence output{ if(T = =NULL)
{ return ; } order (T-lchild); printf ("%d", t->data); Order (T-rchild);}
intMain () {inti; inta[5] = {3,4,2,5,9}; Bitree T=NULL; for(i =0; I <5; i++ )
{Insertbst (&T, a[i]); } printf ("Middle sequence Traversal binary sort tree: \ n"); Order (T); printf ("\ n"); printf ("after deleting 3, the middle sequence traverses the binary sort tree: \ n"); Deletebst (&t,3); Order (T);} Run Result: Middle sequence traversal binary sort tree:2 3 4 5 9after deleting 3, the middle sequence traverses the binary sort tree:2 4 5 9
Summarize
The time complexity of finding operations in a lookup table using a two-fork sort tree is related to the structure of the two-fork tree itself. Even though the data elements in the lookup table are exactly the same, the two-fork sort tree that is constructed differs greatly from the order of arrangement.
For example: Lookup tables {45,24,53,12,37,93}
and tables are {12,24,37,45,53,93}
built with the two-fork sort tree shown:
Figure 5 Two-fork sort tree with different constructs
The process of using a two-fork sort tree to implement a dynamic find operation is actually the process of starting from the root node of the two-fork sorting tree to finding the element nodes, so the time complexity is related to the depth (number of levels) of the tree in which the element is found.
In order to compensate for the factors that affect the efficiency of the algorithm when the binary sort tree is constructed, it is necessary to "balance" the two-fork sort tree so that it becomes a balanced binary tree.
A balanced binary tree is another way to implement a dynamic lookup table, and the next section focuses on this.
Data Structure 53: Two fork Sort tree (binary search tree)