One, stretching the tree
This article describes an improved data structure for a two-fork lookup tree – the stretching tree (splay trees). Its main feature is not to ensure that the tree is always balanced, but the various operations of the split time complexity is O (log n), thus, from the level of complexity, binary search tree is also a balanced binary tree. In addition, the spatial requirements and programming complexity of the stretching tree are much smaller than other tree-like data structures (such as red-black trees, AVL trees, etc.).
The starting point of the stretching tree is this: Taking into account the principle of locality (the content that has just been accessed may still be accessed the next time, the number of lookups may be accessed the next time), in order to make the whole search time smaller, those nodes with high frequency are often located near the root. In this way, it is easy to think of the following scenario: Every time the node is searched, the tree is reconstructed, the node being searched is moved to the root, and the self-tuning two-fork search tree is the stretching tree. Each time the stretching tree is manipulated, it rotates the accessed node to the root location by means of a rotation.
In order to rotate the currently accessed node to the root, we usually rotate the node from the bottom up until the node becomes the root of the tree. The neat thing about "rotation" is that all the basic operations are still O (log n), without disrupting the data size relationship in the sequence (meaning that the middle order traversal results are all ordered).
In the AVL tree I know there are 4 types of rotation (actually two) RR type, LL type, RL type, LR type, if it is not clear how the AVL tree rotation can refer to the Balanced Binary tree (AVL) diagram and implementation, the individual feel is very clear, here we still have a schematic way to explain the operation of the stretching tree, In the rotation of the stretching tree I followed the part of the AVL tree name, of course, this is not very rigorous, mainly to understand the principle, as to how to call this is a personal habit. First, the structure definition of the stretching tree is given:
typedef struct SPLAYNODE *tree;
typedef int ElementType;
struct Splaynode
{
tree parent;//parent node of the node, convenient operation
ElementType Val;//Node value
Tree lchild;
Tree Rchild;
Splaynode (int val=0)//default constructor
{
parent=null;
Lchild=rchild=null;
this->val=val;
}
};
Second, the rotation of the stretching tree operation
What is a single R type, in the figure above, we look for the element is 9, its parent node is 7, and 7 is the root node, the lookup node is the parent of the right child, and the 9 into the root nodes only one time left to rotate (about 9 ascension layer), so we call a single R type, After a left rotation after the node 9 instead of the original root node 7, become a new root node (note here because the diagram is simple, 9 eventually become the root node, in the tree complex situation, generally will not become a root node once, but will certainly become the root of the atomic tree, which is the program said in the current subtree in the new root). For the sake of easier back, the single left-handed code is posted here, which can be used to compare diagrams and code analysis for easy comprehension
Tanzoo operation
//parameters: root, Rotation node (center of rotation)
//return: The new root tree
left_single_rotate (tree &root,tree node) in the current subtree
{
if (node==null)
return NULL;
Tree parent=node->parent; Its parent
node Tree grandparent=parent->parent;//Its grandfather knot
parent->rchild=node->lchild;//Set its parent to the right child
if (node->lchild)//If there is a left child, update the parent node information for the left child of node node
node->lchild->parent=parent;
node->lchild=parent; Update the left child information of node node
parent->parent=node;//Update the original parent node information
node->parent=grandparent;
if (grandparent)//Update grandfather child node information
{
if (grandparent->lchild==parent)
grandparent->lchild=node;
else
grandparent->rchild=node;
}
else//There is no grandparent node, the original parent node is root, then node is the root
root=node after rotation;
return node;
}
(2) Single L type
The single L type and the single R type are symmetric, that is, finding node 3 is the left subtree of its parent node, and its parent is the root node, so that after the right rotation, 3 is the root node.
Single right-hand operation
//parameter: root, rotation node (center of rotation)
//return: New root in current subtree tree
right_single_rotate (tree &root,tree node)
{
if (node==null)
return NULL;
Tree parent,grandparent;
parent=node->parent;
grandparent=parent->parent;
parent->lchild=node->rchild;
if (node->rchild)
node->rchild->parent=parent;
node->rchild=parent;
parent->parent=node;
node->parent=grandparent;
if (grandparent)
{
if (grandparent->lchild==parent)
grandparent->lchild=node;
else
grandparent->rchild=node;
}
else
Root=node;
return node;
}
(3) RR type
The so-called RR type, simple point is two times R, two left rotation, this situation is to find nodes have parent node, but also have grandfather nodes, and three in the same right, this is RR type, in order to this case, first the parent node of the lookup node is rotated once, that is, to raise a layer, and then to find the node again rotation So the lookup node is to the root node, all the left rotation, but the rotation of the object is not the same.
Two single left-hand operation
//parameter: root, and finally becomes subtree node of the node
void left_double_rotate (Tree &root,tree node)
{
Left_single_ Rotate (root,node->parent);
Left_single_rotate (Root,node);
}
(4) LL type
Two single right-handed operation
//parameter: root, and finally becomes subtree node of the node
void right_double_rotate (Tree &root,tree node)
{
Right_single_ Rotate (root,node->parent); First raise its parent node
right_single_rotate (Root,node); Finally promote yourself
}
The LL and RR types are symmetrical, after a double-turn result as above, but this is the end. Recall that the rotation of the stretching tree is intended to be done, not to push the lookup node to the root, yes, but now this is not the root of the node 9, but this is not the single r that we talked about before. So again to the left can be, that is, the following look:
(5) RL Type
Double spin operation (RL type), similar to the AVL tree
//parameter: root, will eventually become subtree node of node
void Rl_rotate (Tree&root,tree node)
{
Right_single_ Rotate (Root,node); First right after left
left_single_rotate (Root,node);
}
This is the same as the RL in the AVL tree, and when the rotation is complete, it takes one step left:
OK, it's in place.
(6) LR type
Double-spin operation (LR type), similar to the AVL tree
//parameter: root, will eventually become subtree node of the node
void lr_rotate (Tree &root,tree node)
{
Left_single_ Rotate (Root,node); First left
right_single_rotate (root,node);//After Right
}
OK, to here to extend the tree of several cases introduced, how so many rotating way, in fact, can not do so, this is just my study when I summed up, and online also dozen not the same, the main is their own understanding of the rotation of the way, it is good, as for the name of these have little impact, I am here to divide them into these kinds of ways, mainly in order to encapsulate into a function, convenient for my call, so the logic is clearer, I understand the later can be based on their own understanding to organize the code.
Third, stretching the operation of the tree stretching tree operation and the AVL tree is nothing more than the search, plug, delete, we introduce them separately below. (1) First look at the search function search:
Find function with adjustment function
//Parameters: Root node, Val
//return required to find: TRUE or false
bool Search (Tree &root,elementtype val)
{
Tree parent=null;
Tree *temp=null;
Temp=search_val (root,val, parent);
if (*temp && *temp!=root)
{
splaytree (root,*temp);
return true;
}
return false;
}
Find function inside there is another specific lookup function, we first regardless of it, first comb the logic, we first through the internal lookup function, find the value of val node, find the return node to temp, if the search succeeds, and the current node is not the root node, then we will make the tree adjustment, Push the node temp to the root, or exit directly, this is the function of search, simple and clear.
Specific lookup function
//parameter: root, need to find Val, parent node pointer
//SUCCESS: Returns its node
//failure: Returns its reference for easy insertion behind the
tree *search_val (Tree & Root,elementtype val,tree &parent)
{
if (root==null)
return &root;
if (root->val>val)
return Search_val (root->lchild,val,parent=root);
else if (root->val<val)
return Search_val (root->rchild,val,parent=root);
Return &root;
}
Here we need to introduce the internal lookup function, because this is a common interface, which will be used later, the lookup function, if the lookup succeeds returns a reference to the node, otherwise it returns the reference to the insertion place, that is, a child of its last parent, The parent is the parents of the lookup success or failure node, and also the reference type. OK, this is our lookup function, there is no enhancement of its search function, but it is convenient for us to insert and delete work behind.
(2) Insert
Insert function
//parameter: root, Val
//return required to insert: TRUE or false
bool Insert (tree &root,elementtype val)
{
tree * Temp=null;
Tree Parent=null;
First, if successful, you do not need to insert it, otherwise it returns a reference to that node.
Temp=search_val (root,val,parent);
if (*temp==null)//need to insert data
{
Tree node=new Splaynode (val);
*temp=node; Because it is a reference type, it is straightforward to assign a value, simplifying a lot.
node->parent=parent;//Set parent node.
return true;
}
return false;
}
can see this insertion function is also very short, pay attention to observe, there is something familiar to us, yes is the above-mentioned internal lookup function, here to insert the node, we first to find, if the lookup is not inserted, otherwise return the insertion address of the reference, so we directly let *temp=node, It completes the insertion work, simplifies a lot of work, and then sets the parent node information to insert successfully.
(3) Stretching when we look for a Val, we need to stretch the tree, and here's our stretching function.
Splay adjustment Operation
void Splaytree (Tree &root,tree node)
{while
(Root->lchild!=node && root-> Rchild!=node && Root!=node)//The current node is not the root, or the left or right child of its root, the rotation operation up
(root, node) according to the situation;
if (Root->lchild==node)//The current node is the root of the left child, simply perform a single right-click
root=right_single_rotate (Root, node);
else if (root->rchild==node)//Current node is the root of the right child, just one Tanzoo
root=left_single_rotate (Root, node);
}
It can be seen that there is a up function, outside of this function, there is a separate if judgment structure, the two if is to judge the special case, that is, we just need to make a single-spin can be promoted to the root node of the situation, this is very simple, combined with a picture can be seen. OK, look at our up function.
Depending on the situation, choose a different rotation method
void up (tree &root,tree node)
{
tree parent,grandparent;
int i,j;
parent=node->parent;
grandparent=parent->parent;
I=grandparent->lchild==parent? -1:1;
J=parent->lchild==node? -1:1;
LL-type
right_double_rotate (root, node) in the IF (I==-1 && j==-1)//avl tree;
The LR-type
lr_rotate (root, node) in the else if (i==-1 && j==1)//avl tree;
The RL-type
rl_rotate (root, node) in the else if (i==1 && j==-1)//avl tree;
else //AVL rr-type
left_double_rotate (root, node) in the tree;
}
Up as the name implies is upward, that is, the lookup node upward push, in this function we determine the type of rotation, is the ll type, or RR type, or LR type, or RL type, and then call the rotation function we have shown earlier. Just rotate the function best to combine the diagram and then look at the code, so it's easy to understand and don't just look at the code.
Here we find and insert, as well as the stretching process we have shown, here is important a function is to find the function, there are several ways of rotation.
(4) Delete
Delete operation void Remove (tree &root,elementtype val) {tree parent=null;
Tree *temp;
Tree *replace;
Tree Replace2; Temp=search_val (root,val, parent); First find operation if (*temp)//If you find {if (*temp!=root)//Determine whether the root node, not the root node, you need to adjust to the root node Splaytree (root, *te
MP); To delete the root node or the root node, first check if there is an alternative element if (root->rchild) {//There is an alternative element replace=find_min (r Oot->rchild); Find replacement element root->val= (*replace)->val;
Replace if ((*replace)->lchild==null)//left dial hand tree is empty {replace2=*replace; *replace= (*replace)->rchild;
Re-connect its right child delete replace2;
} else if ((*replace)->rchild==null)//Right subtree is empty {replace2=*replace; *replace= (*replace)->lchild;
Re-connect its left child delete replace2; }} else {//no alternative element, the root straightMove to the left dial hand tree, regardless of whether the left subtree is empty can be processed replace2=root;
root=root->lchild;
Delete Replace2; }
}
}
In the deletion function, we first make a search, find the failure to exit, after the successful search, we will push it to the root node, and then use our BST Delete method, find alternative elements, so complex as simple, but here the unified use of reference method, it is much simpler. Here are the functions that are familiar to us looking for alternative elements:
Operation minimum node//return for the current subtree
: the reference to its minimum node tree
*find_min (tree &root)
{
if (root->lchild)
return Find_ Min (root->lchild);
Return &root;
}
OK, here our extension tree is introduced, here we can see the function of our stretching tree and the function of the AVL tree is very different, in the AVL tree, we use the reference (part), but also can be set by the return value, plus rusty, write a bit messy, here the extension tree, I use the same reference method, can return the value without return value, this can simplify a lot of operations, and the extension tree is simpler than the AVL tree, different judgment balance factor, so it is easier to write.
Here's our total code:
#include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std;
typedef struct SPLAYNODE *tree;
typedef int ElementType;
struct Splaynode {tree parent;//parent node of the node, convenient operation ElementType Val;//node value Tree lchild;
Tree Rchild;
Splaynode (int val=0)//default constructor {Parent=null;
Lchild=rchild=null;
this->val=val;
}
};
BOOL Search (Tree &,elementtype);
Tree *search_val (tree&,elementtype,tree&);
BOOL Insert (Tree &,elementtype);
Tree left_single_rotate (Tree&,tree);
Tree right_single_rotate (tree &,tree);
void Lr_rotate (Tree&,tree);
void Rl_rotate (Tree&,tree);
void Right_double_rotate (Tree&,tree);
void Left_double_rotate (Tree&,tree);
void Splaytree (Tree &,tree);
void up (Tree &,tree);
Tree *find_min (tree &);
void Remove (Tree &,elementtype); Find function with adjustment function//Parameters: root node, Val//return required to find: TRUE or false bool Search (tree &root,elementtype val) {Tree Parent=null;
Tree *temp=null;
Temp=search_val (root,val, parent);
if (*temp && *temp!=root) {splaytree (root,*temp);
return true;
} return false; }//Specific lookup function//parameter: root, need to find Val, parent node pointer//SUCCESS: Return its node//failure: Returns its reference, convenient for subsequent insert operation Tree *search_val (tree &root,elementtype val,tree
&parent) {if (root==null) return &root;
if (Root->val>val) return Search_val (Root->lchild,val,parent=root);
else if (root->val<val) return Search_val (Root->rchild,val,parent=root);
Return &root;
}//Insert function//parameter: root, need to insert Val//return: TRUE or false bool Insert (tree &root,elementtype val) {tree *temp=null;
Tree Parent=null;
First, if successful, you do not need to insert it, otherwise it returns a reference to that node.
Temp=search_val (root,val,parent);
if (*temp==null)//need to insert data {Tree node=new Splaynode (val); *temp=node;
Because it is a reference type, it is straightforward to assign a value, simplifying a lot. node->parent=parent;
Sets the parent node.
return true;
} return false; }//Tanzoo operation//Parameters: root, SpinTurn node (rotation center)//return: The New root tree left_single_rotate (tree &root,tree node) {if (node==null) return NULL in the current subtree; Tree parent=node->parent; Its parent node, the Tree grandparent=parent->parent;. His grandfather knot Point parent->rchild=node->lchild;
Set the parent node's right child if (node->lchild)//If there is a left child, update the parent node information of the left child of the node node node->lchild->parent=parent; node->lchild=parent; Update the left Child information parent->parent=node; node node
Update the original parent node information node->parent=grandparent; if (grandparent)//Update grandfather child node information {if (grandparent->lchild==parent) Grandparent->lchi
Ld=node;
else grandparent->rchild=node;
} else//There is no grandparent node, then the original parent node is root, then node is the root root=node after rotation;
return node;
}//Single right-handed operation//parameters: root, Rotation node (center of rotation)//return: New root in current subtree tree right_single_rotate (tree &root,tree node) {if (node==null)
return NULL;
Tree parent,grandparent;
parent=node->parent;
grandparent=parent->parent; parent->lchild=node->rchild;
if (node->rchild) node->rchild->parent=parent;
node->rchild=parent;
parent->parent=node;
node->parent=grandparent;
if (grandparent) {if (grandparent->lchild==parent) grandparent->lchild=node;
else grandparent->rchild=node;
} else Root=node;
return node; }//double-spin operation (LR type), similar to//parameter of the AVL tree: root, will eventually become subtree node of the node void lr_rotate (Tree &root,tree node) {left_single_rotate (Root,node); /First left right_single_rotate (root,node);//After Right}//double-spin operation (RL type), similar to//parameter in AVL tree: root, finally will become subtree node of the node void Rl_rotate (Tree&root,tree
node) {right_single_rotate (Root,node);//First right rear left left_single_rotate (Root,node);} Two single right-handed operation//parameter: root, and finally becomes subtree node of the node void right_double_rotate (Tree &root,tree node) {right_single_rotate (root,node-> ;p arent); First raise its parent node right_single_rotate (root,node);
finally promote yourself}//Two single left-handed operation//parameters: root, and finally will become subtree node of the node void left_double_rotate (Tree &root,tree node) { Left_single_rotate (root,node->parent);
Left_single_rotate (Root,node); }//splay adjustment operation void Splaytree (Tree &root,tree node) {while (Root->lchild!=node && Root->rchild!=node
&& Root!=node)//The current node is not the root, or the left or right child of its root, the rotation operation up (root, node) according to the situation;
if (Root->lchild==node)//The current node is the root of the left child, simply perform a single right-click Root=right_single_rotate (Root, node);
else if (root->rchild==node)//Current node is the root of the right child, only one Tanzoo root=left_single_rotate (root, node) is required;
}//Depending on the situation, choose a different rotation method void up (tree &root,tree node) {tree parent,grandparent;
int i,j;
parent=node->parent;
grandparent=parent->parent; I=grandparent->lchild==parent?
-1:1;
J=parent->lchild==node? -1:1;
ll-type right_double_rotate (root, node) in the IF (I==-1 && j==-1)//avl tree;
The LR-type lr_rotate (root, node) in the else if (i==-1 && j==1)//avl tree;
The RL-type rl_rotate (root, node) in the else if (i==1 && j==-1)//avl tree; else//RR-type left_double_rotate (root, node) in the AVL tree; }//manipulate the minimum node of the current subtree//return: The reference to its minimum node tree *find_min (tree &root) {if (Root->lchild) return Find_min (root->
Lchild);
Return &root;
}//delete operation void Remove (tree &root,elementtype val) {tree parent=null;
Tree *temp;
Tree *replace;
Tree Replace2; Temp=search_val (root,val, parent); First find operation if (*temp)//If you find {if (*temp!=root)//Determine whether the root node, not the root node, you need to adjust to the root node Splaytree (root, *te
MP); The modulation root node, or the original root node, is deleted, first to see if there is an alternative element if (root->rchild) {//There is an alternative element replace=find_min (r Oot->rchild); Find replacement element root->val= (*replace)->val;
Replace if ((*replace)->lchild==null)//left dial hand tree is empty {replace2=*replace; *replace= (*replace)->rchild;
Re-connect its right child delete replace2;
} else if ((*replace)->rchild==null)//right subtree is empty {replace2=*replace; *replace= (*replace)->lchild;
Re-connect its left child delete replace2;
}} else {//No alternative elements, the root is moved directly to the left dial hand tree, and the replace2=root can be handled regardless of whether the left subtree is empty or not;
root=root->lchild;
Delete Replace2;
}}}//Pre-order void preorder (Tree root) {if (root==null) return;
printf ("%d", root->val);
Preorder (Root->lchild);
Preorder (Root->rchild);
}//middle order void inorder (Tree root) {if (root==null) return;
Inorder (Root->lchild);
printf ("%d", root->val);
Inorder (Root->rchild);
} int main () {Tree root=null;
Insert (root, 11);
Insert (root, 7);
Insert (root, 18);
Insert (root, 3);
Insert (root, 9);
Insert (root, 16);
Insert (root, 26);
Insert (root, 14);
Insert (root, 15);
Search (root,14);
printf ("Find 14:\n");
printf ("Pre-order:");
Preorder (root);
printf ("\ n");printf ("middle order:");
Inorder (root);
printf ("\ n");
Remove (root,16);
Remove (root,26);
Remove (root,11);
Remove (root,16);
printf ("Delete 16:\n");
printf ("Pre-order:");
Preorder (root);
printf ("\ n");
printf ("middle order:");
Inorder (root);
printf ("\ n");
return 0;
}
Procedures I have repeatedly tested, there is no problem, of course, there are still bugs, whether it is the code or the process of thinking, if there are problems, but also hope that you feel free. Thank.
A diagram of a test data in the main function:
Test results:
Finally, perhaps the extension tree is relatively brief, because with the AVL tree has a lot of acquaintance with the part, you can refer to: Balanced binary tree AVL diagram and implementation
Delete node in binary search tree reference: Two-fork search tree
If you have doubts about pointers, look at the following: A tentative study of C language pointers