Quick sort notes

Keywords: C Algorithm

Code template:

```#include <iostream>

using namespace std;

const int N = 1000010;

int q[N];

void quick_sort(int q[], int l, int r)
{
if (l >= r) return;

int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}

quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}

int main()
{
int n;
scanf("%d", &n);

for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

quick_sort(q, 0, n - 1);

for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);

return 0;
}

Author: yxc
Source: AcWing
```

Quick release formwork (divided by j)
Fast scheduling belongs to divide and conquer algorithm, which has three steps:
1. Sub problem
2. Recursive processing of subproblems
3. Sub problem consolidation

```void quick_sort(int q[], int l, int r)
{
//Termination of recursion
if(l >= r) return;
//Step 1: divide into sub problems
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while(i < j)
{
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}
//Step 2: recursively handle subproblems
quick_sort(q, l, j), quick_sort(q, j + 1, r);
//Step 3: merge subproblems. No operation is required in this step, but the core of merging and sorting is in this step
}```

Problems to be proved
After the while loop ends, Q [L.. J] < = x, Q [J + 1.. R] > = X

Note: Q [L.. J] < = x means Q [l], Q [L + 1]... Q [J-1], all elements of Q [J] are < = X

prove
Loop invariant: Q [L.. I] < = x Q [J.. R] > = x

1. Initialization

Before the start of the cycle, i = l - 1, j = r + 1

Then q[l..i],q[j..r] is null, and the loop invariant is obviously true

2. Keep

Suppose that the loop invariant holds before the start of a cycle, that is, Q [L.. I] < = x, Q [J.. R] > = X

Execution loop body

```  do i++; while(q[i] < x);
Will make q[l..i-1] <= x, q[i] >= x

do j--; while(q[j] > x);
Will make q[j+1..r] >= x, q[j] <= x

if(i < j) swap(q[i], q[j]);
Will make q[l..i] <= x, q[j..r] >= x```

Therefore, after i and j are updated, the loop invariant remains valid until the next loop starts

Note: due to the use of do while loops, i and j must be!!! Self increasing!!!, Make the loop continue, but if the while loop is used (the initialization of i and j changes accordingly), i and j do not increase automatically under special circumstances, the loop will be stuck

For example:

```while(q[i] < x) i++;
while(q[j] > x) j--;
When q[i]and q[j]All for x Time, i and j Will not be updated,cause while Fall into a dead circle```

3. Termination

At the end of the cycle, I > = J

Under normal circumstances, according to the loop invariant, we should feel that the result is obvious

Because I > = J, Q [L.. I] < = x, Q [J.. R] > = x

Therefore, if divided according to j, Q [L.. J] < = x, Q [J + 1.. R] > = x is obvious

However, the last loop is a little special because the if statement of the last loop will not be executed

Because the last round of loop must meet I > = J, otherwise it will not jump out of the while loop, so the if statement must not be executed

Correct analysis:

Because the last round of if statements must not be executed

Therefore, only I > = J and Q [L.. I-1] < = x, Q [i] > = x and Q [J + 1.. R] > = x, Q [J] < = x can be guaranteed

Q [L.. J] < = x can be obtained from Q [L.. I-1] < = x, I > = J (i-1 > = J-1) and Q [J] < = X

Because Q [J + 1.. R] > = x
Therefore, Q [L.. J] < = x, Q [J + 1.. R] > = x, the problem is proved

Conclusion: only at the end of the last cycle, the loop invariant is not established, and the rest of the cycles are established
But in the end, the problem of requirements was solved

Note: at the end of the loop, remember to check whether the array is out of bounds / infinite recursion

Therefore, it is also necessary to prove that the final value range of j is [l..r-1] (that is, there is no infinite recursion in which n is divided into 0 and N). The analysis process is in analysis 5

Boundary condition analysis
analysis
Fast scheduling belongs to divide and conquer algorithm. What I fear most is that n is divided into 0 and N, or n is divided into N and 0, which will cause infinite division

1. When divided by j, x cannot select q[r] (if divided by i, x cannot select q[l])

Suppose x = q[r]

Key sentence quick_sort(q, l, j), quick_sort(q, j + 1, r);

Since the minimum value of j is l, q[j+1..r] does not cause infinite division

But q[l..j] (i.e. quick_sort(q, l, j)) may cause infinite division, because j may be r

For example, if x is selected as q[r], Q [L.. R-1] < x in the array,

Then at the end of this cycle, I = R and j = R will obviously cause infinite division

2.do i++; While (Q [i] < x) and do j--; While (Q [J] > x) cannot use Q [i] < = x and Q [J] > = X

Suppose q[l..r] are all equal

Execute do I + +; while(q[i] <= x); After that, I will increase to r+1

Then continue to execute the Q [i] < = x judgment condition, resulting in the array subscript out of bounds (but this seems not to report an error)

And if the subsequent Q [i] < = x (I > R) condition is also unfortunately true,

It will cause a constant cycle (personal experiment) and memory limit exceeded

3. If (I < J) swap (Q [i], Q [J]) can I < = J be used

You can use if (I < = J) swap (Q [i], Q [J]);

Because when i = j, swap q[i],q[j] has no effect, because it will jump out of the loop soon

4. Can the last sentence be changed to quick_sort(q, l, j-1), quick_sort(q, j, r) is used as a partition (the same is true when i is used as a partition)

No

According to the previous proof, these conclusions can be obtained in the last cycle

J < = I and Q [L.. I-1] < = x, Q [i] > = x and Q [J + 1.. R] > = x, Q [J] < = x

So, Q [L.. J-1] < = x is obviously true,

But quick_ q[j] in sort (Q, J, R) is q[j] < = x, which does not meet the requirements of fast scheduling

Another point, pay attention to quick_sort(q, l, j-1), quick_sort(q, j, r) may cause wireless partition

When x is selected as q[l], infinite partition will be caused, and the error is (MLE),

If it is manually changed to x = q[r], infinite division can be avoided

However, the problem of Q [J] < = x mentioned above cannot be solved, which will cause WA (Wrong Answer)

5. The value range of J is [l..r-1]

prove:

Assuming that the final value of j is r, it means that there is only one cycle (if there are two rounds, j will reduce itself at least twice)

Description Q [R] < = x (because you want to jump out of the do while loop)

Note i > = r (end condition of while loop), i is r or r + 1 (it must not be true)

Note that i increases to r, Q [r] > = x and Q [L.. r-1] < x,

It is concluded that q[r] = x and Q [L.. R-1] < x, but this is in contradiction with x = Q [L + R > > 1]

By the method of counter evidence, J < R is obtained

Assuming that j may be less than l, it indicates Q [L.. R] > x, which is contradictory

The counter evidence method obtains J > = L

Therefore, the value range of j is [l..r-1], which will not cause infinite partition and array out of bounds

Incidentally, i is used as the template for division

```void quick_sort(int q[], int l, int r)
{
if(l >= r) return;

int i = l - 1, j = r + 1, x = q[l + r + 1 >> 1];//Note that it is rounded up, because rounding down may make x take q[l]
while(i < j)
{
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}
quick_sort(q, l, i - 1), quick_sort(q, i, r);//The reason for not dividing q[l..i],q[i+1..r] is the same as that of j in analysis 4
}

```

There are also templates for sorting from large to small (only change the judgment symbols in two places)

```void quick_sort(int q[], int l, int r)
{
if(l >= r) return;

int i = l - 1, j = r + 1, x = q[l + r >> 1];
while(i < j)
{
do i++; while(q[i] > x); // Here and below
do j--; while(q[j] < x); // Change the judgment conditions of this line
if(i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

```

Author: drunk life and dream death