Fundamentals of Data Structure and Algorithm (Wang Zhuo) (36): Quick Sort of Exchange Sorting [Phase 3: Digging Deep to Solve Problems] Essence! Essence! Essence! ! ! Say important things three times

Table of contents

Review:

Specific issues:

Operating core:

Note:

Operation breakdown:

Operation implementation:

Question (1): Make if/else judgments of different times

Problem (2): Passing the condition as "element smaller than the sentinel" doesn't work

Question (3):

When using [greater than or equal to] as the loop condition (while) loop, the operating principle of the program (framework)

at first

later we found out

Did you think we were finally going to end here?

however did not

the biggest issue:

At the beginning (before), we thought:

And later we found out that it's actually not that simple:

real problem:

Concrete the problem in the program flow:

(1):

(2):

The complete process of concretization:

(1):

(2):

It is not confirmed to find the next element with a blank space before proceeding to the next step (change pointer operation)

Instead, as long as the next element is compared, the element is exchanged, or the position pointer is moved, the next step is performed

Modify the final result as follows:

We only change the pointer operation after we are sure to find an element larger/smaller than the sentinel and make sure to insert the space

However, the previous program will perform the operation of changing the pointer regardless of whether it is found or exchanged.

In fact, the result we want to modify is to let the program determine that the operation of inserting spaces is performed before proceeding to the next step (next round)

standard answer:


Review:

Specific issues:

For the problem of operating logic mechanism, let's take the example on the PPT as an example:

Convert each specific operation into a table and display as follows: 

the first few steps operate Low points to High points to
step 1 49 Change Sentinel 1 8
step 2

49' do not move, high-- (the first if / else judgment)

1 7
step 3 49 does not move, low++ (the second if / else judgment) 2 7


Here comes the third step, and obviously something goes wrong:

There is no element in the space yet, your algorithm starts to overtake him and jump to the next space, why?

The repetition is relatively harmless, but the low moves, dangerous! ! ! (going wrong)

Clearly here, as we can see, the problem lies in:

After the first processed element is placed in the sentinel, we compare the sentinel with the element itself

That is, comparing yourself and missing this space;

Instead of using this space to fit the first element smaller than the sentinel


Operating core:

So what we need to do is:

From the beginning of the program, monitor every step of the program's operation to ensure that:

We put [the space left by the first element] into [the first element smaller than the sentinel]


Note:

Monitor:

To be more precise, our so-called "surveillance" here refers to rewriting

Start from the beginning of the program until [the space left by the first element] is loaded into [the first element smaller than the sentinel]

During this process, all the operating procedures of the whole process are rewritten by hand step by step.


Operation breakdown:

And we're going to (at the beginning) handwrite:

Start from the beginning of the program until [the space left by the first element] is loaded into [the first element smaller than the sentinel]

The operation of the whole process is nothing more than:

From the beginning of the program, (always) compare the high pointer: (compare the last bit first)

  1. If the [high pointer points to the element] is smaller than the sentinel, put the element in the front space
  2. If not less than (>=):
  • This element continues to be placed behind
  • 【high pointer】Continue to search forward and compare the size of the previous element with the sentinel

Operation implementation:

Question (1): Make if/else judgments of different times

And here, if we write the program or follow/like the previous section, simply use the if/else judgment statement every time, then

Our list for different order:

Undoubtedly, different times of if/else judgments are required each time

(Who knows how far behind the first element smaller than the sentinel is)

This is no doubt impossible


Problem (2): Passing the condition as "element smaller than the sentinel" doesn't work

Since our purpose is to find the first element from the back to the front that is less than the sentinel

Directly find this "first element smaller than the sentinel" itself

If the setting condition is: directly search through [judgment condition is "element smaller than the sentinel"]

Unless the last element is smaller than the sentinel, it is impossible to find it directly

If there are elements greater than or equal to the sentinel, we can't cross it at all

if (low < high && L.r[high].key >= L.r[0].key)
{
    if (low < high && L.r[high].key >= L.r[0].key)
    {
        ...//无数个:
//【if (low < high && L.r[high].key >= L.r[0].key){}     else  把元素放前面空格里面】语句
//根本写不完,实现不了,死循环
    }
    else  把元素放前面空格里面
}
else  把元素放前面空格里面

Therefore, we can only set the judgment loop condition to be not less than (>=) sentinel


Question (3):

When using [greater than or equal to] as the loop condition (while) loop, the operating principle of the program (framework)


at first

When we write algorithms, we take it for granted that

When using [greater than or equal to] as the loop condition (while) loop, the strategy is to take the next best thing

Make sure to find [the previous element less than (sentinel element)];

Then find the element by (+1) the previous element of the element smaller than the sentinel for operation


later we found out

No, the execution process written above is the result we take for granted

In fact, when the loop condition is changed to [greater than or equal to] sentinel, the logic of program operation is:

If greater than or equal to: always high--;

until we find the first element smaller than the sentinel

When the program exits the loop, high already points to the element we need to point to when exchanging

Directly found the "first element smaller than the sentinel" itself

instead of the previous element

So make changes: 

int 遍历(SqList &L, int low, int high)
{
    L.r[0] = L.r[low];
    while (low < high && L.r[high].key >= L.r[0].key)
        high--;
    L.r[low] = L.r[high];


    while (low < high)
    {
        if (L.r[high].key < L.r[0].key)
        {
            L.r[low] = L.r[high];
            low++;
        }
        else
            high--;
        if (L.r[0].key < L.r[low].key)
        {
            L.r[high] = L.r[low];
            high--;
        }
        else
            low++;
    }
    L.r[low] = L.r[high] = L.r[0];
    return low;
}

void QuickSort(SqList& L, int low, int high)
{
    int pivot = 遍历(L, low, high);
    QuickSort(L, low, pivot-1);
    QuickSort(L, pivot + 1, high);
}

int main()
{
    SqList L;
    cin >> L.length;
    cin >> L.r->key;

    QuickSort(L, 1, L.length);
}

Did you think we were finally going to end here?

however did not


the biggest issue:

Then, we continued to dig deeper and found that the problem with this program is not that simple:

At the beginning (before), we thought:

The program appears only because at the beginning, due to the first element being stored in the sentinel:

The element itself compares the sentinel, that is, compares itself and misses the space;

The error caused by the phenomenon , the important thing is:

We thought this was a special case, the whole program only had a problem at the beginning

And later we found out that it's actually not that simple:

The problem that the program has already appeared at the beginning (cannot be sorted correctly) also exists in the subsequent (sequential) program:

real problem:

If the space before/after has not been filled, we start to move the pointer before/after

Then this blank will never be filled again, so (from now on), we can never go back and find this blank

Then the following procedures are all messed up, causing big problems


Concrete the problem in the program flow:

(Procedure) The real (emerging) problem is:

Not sure yet:

(1):

The element pointed to by the high pointer] has been moved (filled) to the previous space

We start to move the low pointer to point to an element behind the space

or

(2):

[The element pointed to by the low pointer] has been moved (filled) to the space behind

We start to move the high pointer to point to the previous element of the space


The complete process of concretization:


(1):

Not sure yet:

The element pointed to by the high pointer] has been moved (filled) to the previous space

We start to move the low pointer to point to an element behind the space


When starting a (new round) comparison at the beginning (from the second if/else statement)

If the situation we encounter is: the element pointed to by the high pointer is not smaller than the sentinel

(Lr[high].key < Lr[0].key) is not established, so execute the else statement: high--;

Execute the second if/else statement:

Everything is fine here, and then something goes wrong in the next step:

According to our manual operation process, the next step of the program should have continued to execute "high--;"

But in Project 1, according to the operation flow of the program of Project 1, the next step is:

  • If the first element pointed to by low is greater than the sentinel: Lr[high] = Lr[low]; high--;

    Put the high pointer element directly into the space. In this case, unless the penultimate element is just smaller than the sentinel, it is also a wrong insertion


  • If the first element pointed to by low is less than or equal to the sentinel, low++;

    Move the low pointer directly, and no more spaces can be found


(2):

Not sure yet:

[The element pointed to by the low pointer] has been moved (filled) to the space behind

We start to move the high pointer to point to the previous element of the space


When starting a (new round) comparison at the beginning (from the second if/else statement)

If the situation we encounter is: the element pointed to by the low pointer is not larger than the sentinel
 
(Lr[0].key < Lr[low].key) is not true, so execute the else statement: low++;

Re-execute the first if/else statement


If the first element pointed to by high is smaller than the sentinel: Lr[low] = Lr[high];low++;

Directly put the low pointer element into the space. In this case, unless the second element is just larger than the sentinel, it is also a wrong insertion

If the first element pointed to by high is greater than or equal to the sentinel, high--;

Move the high pointer directly, and no more spaces can be found


In the final analysis, the core of the program problem is the operation performed by the program:

It is not confirmed to find the next element with a blank space before proceeding to the next step (change pointer operation)

Instead, as long as the next element is compared, the element is exchanged, or the position pointer is moved, the next step is performed

Modify the final result as follows:

#include<iostream>
using namespace std;

#define MAXSIZE 20  //记录最大个数
typedef int KeyType;  //关键字类型

typedef int InfoType;

//定义每个记录(数据元素)的结构
struct RecType
    //Record Type:每条记录的类型
{
    KeyType key;  //关键字
    InfoType otherinfo;  //其他数据项
};

struct SqList
    //顺序表(的)结构
{
    RecType r[MAXSIZE + 1];
    //类型为【记录类型】的数组
    //r[0]一般做哨兵或缓冲区
    int length;  //顺序表长度
};

int 遍历(SqList& L, int low, int high)
{
    L.r[0] = L.r[low];
    while (low < high)
    {
        //从后往前遍历,指向小于等于哨兵的元素:退出循环、插入空格
        while (low < high && L.r[high].key > L.r[0].key)         
            high--;
        L.r[low] = L.r[high];

        //继续,从前往后遍历,指向大于等于哨兵的元素:退出循环、插入空格
        while (low < high && L.r[0].key < L.r[low].key)
            low++;
        L.r[high] = L.r[low];
    }
    L.r[low] = L.r[high] = L.r[0];
    return low;
}

void QuickSort(SqList& L, int low, int high)
{
    int pivot = 遍历(L, low, high);
    QuickSort(L, low, pivot - 1);
    QuickSort(L, pivot + 1, high);
}

int main()
{
    SqList L;
    cin >> L.length;
    cin >> L.r->key;

    QuickSort(L, 1, L.length);
}

The difference from the program we wrote earlier is:

We only change the pointer operation after we are sure to find an element larger/smaller than the sentinel and make sure to insert the space

However, the previous program will perform the operation of changing the pointer regardless of whether it is found or exchanged.

In fact, the result we want to modify is to let the program determine that the operation of inserting spaces is performed before proceeding to the next step (next round)


Of course, there is still a difference between this modification and the standard answer, but not much:

standard answer:

int Partition(SqList& L, int low, int high)
{
    L.r[0] = L.r[low];
    KeyType pivotkey = L.r[low].key;
    while (low < high) 
    {
        while (low < high && L.r[high].key >= pivotkey)
            high--;  
        L.r[low] = L.r[high];

        while (low < high && L.r[low].key < pivotkey)
            low++;
        L.r[high] = L.r[low];
    }
    L.r[low] = L.r[0];
    return low;
}

void QuickSort(SqList& L, int low, int high) {
    if (low < high)
    {
        int pivotloc = Partition(L, low, high);  //将L一份为二
        QuickSort(L, low, pivotloc - 1);  //对低子表递归排序
        QuickSort(L, pivotloc + 1, high);  //对高子表递归排序
    }
}

int main()
{

}

The only difference is that the standard answer has specially set up a variable pivotkey for this comparison node

I feel that there is actually nothing to hang on to, and it’s useless. It’s not as simple and convenient as what I wrote.

That's it, Cnmd, writing this article and doing this quick queue has tossed me for at least 5 days a week, I am really NM speechless

that's all

Guess you like

Origin blog.csdn.net/Zz_zzzzzzz__/article/details/130567024