Super Detailed Talk about Logu P3369 [Template] General Balanced Tree

Posted by kostasls on Sun, 04 Aug 2019 05:01:49 +0200

V1 Strict Disclaimer

This part of the content is abstracted from Baidu Encyclopedia and exempted from liability after declaration.

V2 Pull and spit

Want to experience what despair is?

Try this question and make sure you suspect life.QwQ

(Big man, please skip)

Here is the topic

(Super Details)

1 Conceptual View Details click

1.1 Balanced Binary Tree

Balanced Binary Tree has the following properties:

It is an empty tree or its left and right subtrees have absolute height differences of no more than 1, and both subtrees are balanced binary trees.

The common implementation methods of balanced binary tree are red-black tree, AVL, scapegoat tree, Treap, stretch tree, etc.

Because only Treap is used in arctium, and Treap is not familiar with it, Treap is used.

1.2 Treap

Treap is a binary sorted tree. Its left subtree and right subtree are Treap, respectively.

Unlike a general binary sort tree, Treap records an extra piece of data, the priority.

Treap satisfies the nature of the heap while forming a binary sorting tree with key codes

(Here we assume that the priority of a node is greater than that of its children).

Moreover, Treap has a "BST" property:

  1. The key code of this node is not less than that of any node in its left subtree.

  2. The key code of this node is not greater than that of any node in its right subtree.

2 Process

2.1 Definition

This Treap requires support for the following operations:

  1. Insert x Number

  2. Delete the x number (if there are multiple identical numbers, only one should be deleted)

  3. Query the rank of x numbers (rank is defined as the number of decimal digits less than the current number +1+1).If there are multiple identical numbers, the lowest rank should be output)

  4. Number of queries ranked x

  5. Precursor to x (precursor defined as less than x and maximum number)

  6. Ask for the succession of x (succession defined as a number greater than x and minimum)

So we need to define the following structure variables (the reason I'll explain later)

1 struct Treap
2 {
3     int l,r,val,data,num,size;
4 }t[100005];

 

l: Left Child

r: Right child

val: The key code of the node

data: Priority of the node

num: How many nodes (counting themselves) are equal to the current node's val ue, i.e. $"number of copies"$

size: how many descendants of a node (to count in itself)

Get a new point

  1. Depth++;
  2. Assignment;
  3. Randomly assign priority;
  4. Number of copies is 1 (counts itself)
  5. Size is 1 (counts in itself)
1 int Get_New(int val)
2 {
3     ++law;
4     t[law].val=val;
5     t[law].data=rand();
6     t[law].num=1;
7     t[law].size=1;
8     return law;
9 }
 

Find Size

Size = Size of the left child + Size of the right child + number of copies of itself.

1 void Update(int &p)
2 {
3     t[p].size=t[t[p].l].size+t[t[p].r].size+t[p].num;
4 }

 

Build Trees

  1. Insert a point of negative infinity size (root node)

  2. Insert a point of positive infinite size (the right node of the root node)

The purpose is to prevent the balance tree from degenerating into chains

1 void Building()
2 {
3     Get_New(INT_MIN);
4     Get_New(INT_MAX);
5     root=1,t[1].r=2;
6     Update(root);
7 }

 

2.2 Insert Elements

2.2.1 Introduction

As shown in the figure above, there is a balanced binary tree with black numbers inside the circle representing the key codes of the nodes.

The number of red around the circle represents the priority of the nodes (assuming that the nature of the root heap has not been maintained).

In this case, insert the number 4 into the balanced binary tree.

2.2.2 Inserted Rules

Why did 4 become the right son of 3?

In this case, the "BST" property of binary lookup tree is used.(1.21.2 explained, not redundant)

In the data, 4 is greater than 2, so 4 cannot be the son of 1.Again 4 < 5, 4 therefore cannot be the son of 6.

Of all leaf nodes, only 3 satisfied.And because 4 > 3, 4 became the right son of 3.

Furthermore, each inserted node has and only has a suitable location.

2.3 Left and Right Rotation

2.3.1 Reason for rotation

Clearly, assigning the priority of the balance tree Treap with the random number function rand() does not guarantee the nature of the small root heap, that is, node A may have a higher priority number than its child nodes.

In order to maintain the nature of the small root heap, Treap must be rotated left and right.

2.3.2 Rule of Rotation

So when should I turn left and when should I turn right?

Let's study the figure above first.In the figure, 3 is rotated to the left, and 4 replaces the original position of 3 after rotation.

The answer is clear.Because the priority of 3 (i.e. 30) is larger than that of 4 (i.e. 15), it does not have the nature of a small root heap, so it is left-handed.

Generally speaking, this summarizes when to rotate left and when to rotate right:

A's right child has a higher priority than A's left-handed

A's left child has a higher priority than A's right hand

2.3.3 Rotation Process

2.3.3.1 Left Rotation

  1. The left child of A becomes the right child of the left child of A;

  2. A The value of the left child's right child becomes the value of A;

  3. The value of A becomes the value of the left child of A.

2.3.3.2 Right Hand

  1. The right child of A becomes the left child of the right child of A;

  2. A The value of the right child's left child becomes the value of A;

  3. The value of A becomes the value of the right child of A;

2.3.3.3 Summary

Obviously, left-handed and right-handed operations are the exact opposite.

Taken together 2.2 and 2.3, we can get the code for the rotation and insertion operations

 1 void Zig(int &p)    //Right Hand
 2 {
 3     int q=t[p].l;
 4     t[p].l=t[q].r;
 5     t[q].r=p;
 6     p=q;
 7     Update(t[p].r); //Get Right Child's Size
 8     Update(p);  //Update because location was updated p Of Size
 9 }
10 void Zag(int &p)    //Left-handed
11 {
12     int q=t[p].r;
13     t[p].r=t[q].l;
14     t[q].l=p;
15     p=q;
16     Update(t[p].l); //Get Right Child's Size
17     Update(p);  //Ditto
18 }
19 void Insert(int &p,int val)
20 {
21     if(p==0)    //Node does not exist and has found its proper location
22     {
23         p=Get_New(val);
24         return ;
25     }
26     if(val==t[p].val)
27     {
28         t[p].num++; //Node already exists
29         Update(p);  //Of its own Size
30         return ;
31     }
32     if(val<t[p].val)    //The key code of the current node is too large
33     {
34         Insert(t[p].l,val); //Find the right place to the left
35         if(t[p].data<t[t[p].l].data)
36             Zig(p); //rotate
37     }
38     else
39     {
40         Insert(t[p].r,val); //Find the right place
41         if(t[p].data<t[t[p].r].data)
42             Zag(p); //rotate
43     }
44     Update(p);  //To update p Of Size
45 }

 

2.4 Delete

This operation is the operation in the original topic 22

Deleting a node is not that easy for a tree.

If the leaf node needs to be deleted, then it is more convenient, just delete the node.

If the node you want to delete now has children, it is more troublesome and needs to be dealt with.

Of course, like insertion, deletion is done recursively.

2.4.1 Process

Then there are the following processes:

  1. If the node corresponding to the key to be deleted does not exist, it is returned directly.

  2. If the node is found

    1. If the number of copies of this node is more than 1, subtract it from the number of copies

    2. If the node has a child node, that is, it is not a leaf node: if the priority of the left node is greater than that of the right node, or if there is no right node, turn right.Because right-hand rotation allows the left node to replace itself (see 2.32.3).Then delete the value of the right node of the left node (that is, the right node after right-handing).If the opposite is true, do the opposite.

    3. Then the node is a leaf node and is deleted directly.

  3. Not found

    1. If the current key is larger than the one you are looking for, look to the left.

    2. If the current key is smaller than the one you are looking for, look to the right.

2.4.2 Code

Code is derived from the above process:

 1 void Remove(int &p,int val)
 2 {
 3     if(p==0) return ;
 4     if(val==t[p].val)
 5     {
 6         if(t[p].num>1)
 7         {
 8             t[p].num--;
 9             Update(p);
10             return ;
11         }
12         if(t[p].l||t[p].r)
13         {
14             if(t[p].r==0||t[t[p].l].data>t[t[p].r].data)
15             {
16                 Zig(p);
17                 Remove(t[p].r,val);
18             }
19             else
20             {
21                 Zag(p);
22                 Remove(t[p].l,val);
23             }
24             Update(p);
25         }
26         else p=0;
27         return ;
28     }

 

2.5 Ranking by Worth

Obviously, this kind of operation is to find rankings.

Therefore, this operation is based on a query.

2.5.1 Process

  1. Return directly if the node does not exist

  2. If found, return Size+1 of the left node

Why is that so?

Because all nodes of the left subtree of the node have smaller key codes than themselves, the largest subtree of the left node is its Size

So at this point, Size comes into play.

Also, because each node's key code of the right subtree of the node is larger than that of the node, it is not related to the rank of the node.

The Size of the left node is the number of left subtree nodes including the left node, which is also smaller than the key code of the node.

Just like no other number in a list is larger than him, and it ranks one, so add one.

  1. If found, then the same routine:

  2. If the current key is larger than the one you are looking for, look to the left.

  3. If the current key is smaller than the one you are looking for, look to the right.

2.5.2 Code:

 1 int Sortt(int &p,int val)
 2 {
 3     if(p==0)
 4         return 0;
 5     if(val==t[p].val)
 6         return t[t[p].l].size+1;
 7     if(val<t[p].val)
 8         return Sortt(t[p].l,val);
 9     return Sortt(t[p].r,val)+t[t[p].l].size+t[p].num;
10 }

 

2.6 Value from Rank

This operation, like 2.52.5, is query-based, but the query is not ranking, but the value of the node.

2.6.1 Process

Then, the process is also very good.

  1. If the node does not exist, return the maximum value directly (note, not 0)

  2. If the maximum number of subtree nodes of the left node is larger than the rank to be queried, look to the left.

Similarly, explain why:

It is known that the maximum number of subtree nodes of the left node is the number of nodes with small key codes of the current node, that is, its rank -1.

If the rank it is querying is lower than the rank it is currently, the key code of the current node is larger than the actual answer, so it queries the left node.

  1. If the sum of the maximum number of subtree nodes of the left node and the number of copies of the current node is larger than the rank to be queried, return your own key code.

Because if the rank it is querying is lower than it is now, you have already executed the second ifif statement, which is not going to happen.

So the rank you want to query is larger than the rank you have now, and the number of copies you have is smaller than the rank you have now.

So if ranking counts the number of replicas, the ranking to be queried is one of the rankings of all replicas of the current node.

So the node corresponding to this ranking is the current node or its copy.

Also, because the keys of the current node and its copy are equal, the keys of the current node are returned.

2.6.2 Process

 1 int Value(int &p,int rank)
 2 {
 3     if(p==0)
 4         return INT_MAX;
 5     if(t[t[p].l].size>=rank)
 6         return Value(t[p].l,rank);
 7     if(t[t[p].l].size+t[p].num>=rank)
 8         return t[p].val;
 9     return Value(t[p].r,rank-t[t[p].l].size-t[p].num);
10 }

 

2.7 Seeking Forward

To be honest, it is the best understanding to seek precursors and successors.

Precursor: Defined as a number less than x and largest.

Generally speaking, the nearest x is smaller than it.

Not difficult to obtain, its main feature is smaller than x.

So:

2.7.1 Process

  1. Find the left node of the current node first.

  2. As long as there is a right node, look to the right.

Process 1 satisfies a smaller property than x, while process 2 satisfies the largest number.

2.7.2 Code

 1 int Pre(int val)
 2 {
 3     int pre=1;
 4     int p=root;
 5     while(p)
 6     {
 7         if(val==t[p].val)
 8         {
 9             if(t[p].l>0)
10             {
11                 p=t[p].l;
12                 while(t[p].r>0)
13                     p=t[p].r;
14                 pre=p;
15             }
16             break;
17         }
18         if(t[p].val<val&&t[p].val>t[pre].val) pre=p;
19         if(val<t[p].val)
20             p=t[p].l;
21         else p=t[p].r;
22     }
23     return t[pre].val;
24 }

 

2.8 Succession

2.8.1 Process

Contrary to seeking precursors.

  1. Find the right node of the current node first.

  2. As long as there is a left node, look to the left.

Process 1 meets larger properties than x, while process 2 meets the minimum number.

2.8.2 Code

int Suf(int val)
{
    int suf=2;
    int p=root;
    while(p)
    {
        if(val==t[p].val)
        {
            if(t[p].r>0)
            {
                p=t[p].r;
                while(t[p].l>0)
                    p=t[p].l;
                suf=p;
            }
            break;
        }
        if(t[p].val>val&&t[p].val<t[suf].val)
            suf=p;
        if(val<t[p].val)
            p=t[p].l;
        else p=t[p].r;
    }
    return t[suf].val;
}

 

2.9 main function

Reference Title Meaning:

int main()
{
    std::ios::sync_with_stdio(false);   //Input and output optimization
    Building();
    cin>>m;
    while(m--)
    {
        int op,x;
        cin>>op>>x;
        if(op==1)
        {
            Insert(root,x);
            continue;
        }
        if(op==2)
        {
            Remove(root,x);
            continue;
        }
        if(op==3)
        {
            cout<<Sortt(root,x)-1<<endl;
            continue;
        }
        if(op==4)
        {
            cout<<Value(root,x+1)<<endl;
            continue;
        }
        if(op==5)
        {
            cout<<Pre(x)<<endl;
            continue;
        }
        if(op==6)
        {
            cout<<Suf(x)<<endl;
            continue;
        }
    }
    return 0;
}

 

3 Code complete code:

I've explained it in detail, so it's clean and uncommented

  1 #include<bits/stdc++.h>
  2 #pragma GCC optimize(3) //Manual ozone suction
  3 using namespace std;
  4 struct Treap
  5 {
  6     int l,r,val,data,num,size;
  7 }t[100005];
  8 int law,root,m;
  9 int Get_New(int val)
 10 {
 11     ++law;
 12     t[law].val=val;
 13     t[law].data=rand();
 14     t[law].num=1;
 15     t[law].size=1;
 16     return law;
 17 }
 18 void Update(int &p)
 19 {
 20     t[p].size=t[t[p].l].size+t[t[p].r].size+t[p].num;
 21 }
 22 void Building()
 23 {
 24     Get_New(INT_MIN);
 25     Get_New(INT_MAX);
 26     root=1,t[1].r=2;
 27     Update(root);
 28 }
 29 void Zig(int &p)
 30 {
 31     int q=t[p].l;
 32     t[p].l=t[q].r;
 33     t[q].r=p;
 34     p=q;
 35     Update(t[p].r);
 36     Update(p);
 37 }
 38 void Zag(int &p)
 39 {
 40     int q=t[p].r;
 41     t[p].r=t[q].l;
 42     t[q].l=p;
 43     p=q;
 44     Update(t[p].l);
 45     Update(p);
 46 }
 47 void Insert(int &p,int val)
 48 {
 49     if(p==0)
 50     {
 51         p=Get_New(val);
 52         return ;
 53     }
 54     if(val==t[p].val)
 55     {
 56         t[p].num++;
 57         Update(p);
 58         return ;
 59     }
 60     if(val<t[p].val)
 61     {
 62         Insert(t[p].l,val);
 63         if(t[p].data<t[t[p].l].data)
 64             Zig(p);
 65     }
 66     else
 67     {
 68         Insert(t[p].r,val);
 69         if(t[p].data<t[t[p].r].data)
 70             Zag(p);
 71     }
 72     Update(p);
 73 }
 74 void Remove(int &p,int val)
 75 {
 76     if(p==0) return ;
 77     if(val==t[p].val)
 78     {
 79         if(t[p].num>1)
 80         {
 81             t[p].num--;
 82             Update(p);
 83             return ;
 84         }
 85         if(t[p].l||t[p].r)
 86         {
 87             if(t[p].r==0||t[t[p].l].data>t[t[p].r].data)
 88             {
 89                 Zig(p);
 90                 Remove(t[p].r,val);
 91             }
 92             else
 93             {
 94                 Zag(p);
 95                 Remove(t[p].l,val);
 96             }
 97             Update(p);
 98         }
 99         else p=0;
100         return ;
101     }
102     if(val<t[p].val)
103         Remove(t[p].l,val);
104     else Remove(t[p].r,val);
105     Update(p);
106 }
107 int Sortt(int &p,int val)
108 {
109     if(p==0)
110         return 0;
111     if(val==t[p].val)
112         return t[t[p].l].size+1;
113     if(val<t[p].val)
114         return Sortt(t[p].l,val);
115     return Sortt(t[p].r,val)+t[t[p].l].size+t[p].num;
116 }
117 int Value(int &p,int rank)
118 {
119     if(p==0)
120         return INT_MAX;
121     if(t[t[p].l].size>=rank)
122         return Value(t[p].l,rank);
123     if(t[t[p].l].size+t[p].num>=rank)
124         return t[p].val;
125     return Value(t[p].r,rank-t[t[p].l].size-t[p].num);
126 }
127 int Pre(int val)
128 {
129     int pre=1;
130     int p=root;
131     while(p)
132     {
133         if(val==t[p].val)
134         {
135             if(t[p].l>0)
136             {
137                 p=t[p].l;
138                 while(t[p].r>0)
139                     p=t[p].r;
140                 pre=p;
141             }
142             break;
143         }
144         if(t[p].val<val&&t[p].val>t[pre].val) pre=p;
145         if(val<t[p].val)
146             p=t[p].l;
147         else p=t[p].r;
148     }
149     return t[pre].val;
150 }
151 int Suf(int val)
152 {
153     int suf=2;
154     int p=root;
155     while(p)
156     {
157         if(val==t[p].val)
158         {
159             if(t[p].r>0)
160             {
161                 p=t[p].r;
162                 while(t[p].l>0)
163                     p=t[p].l;
164                 suf=p;
165             }
166             break;
167         }
168         if(t[p].val>val&&t[p].val<t[suf].val)
169             suf=p;
170         if(val<t[p].val)
171             p=t[p].l;
172         else p=t[p].r;
173     }
174     return t[suf].val;
175 }
176 int main()
177 {
178     std::ios::sync_with_stdio(false);
179     Building();
180     cin>>m;
181     while(m--)
182     {
183         int op,x;
184         cin>>op>>x;
185         if(op==1)
186         {
187             Insert(root,x);
188             continue;
189         }
190         if(op==2)
191         {
192             Remove(root,x);
193             continue;
194         }
195         if(op==3)
196         {
197             cout<<Sortt(root,x)-1<<endl;
198             continue;
199         }
200         if(op==4)
201         {
202             cout<<Value(root,x+1)<<endl;
203             continue;
204         }
205         if(op==5)
206         {
207             cout<<Pre(x)<<endl;
208             continue;
209         }
210         if(op==6)
211         {
212             cout<<Suf(x)<<endl;
213             continue;
214         }
215     }
216     return 0;
217 }

 

Four people summary

Click to see my bumpy AC Road

Final AC Record

I hope you can get a lot from this blog. It took me more than a month to edit this blog.

If there are any deficiencies, consult you!!

Topics: PHP less iOS