Data Structures - Introduction to Quick Sort

quick sort

Quick sort is a binary tree structure exchange sorting method proposed by Hoare in 1962. Quick sort is a commonly used sorting algorithm. Its basic idea is to divide the sequence to be sorted into two subsequences by selecting an element as the "base value". All elements of are greater than the base value. Then recursively sort the two subsequences separately, and finally combine the sorted subsequences to obtain a complete ordered sequence.

Thought

Any element in the element sequence to be sorted is taken as the reference value, and the set to be sorted is divided into two subsequences according to the sort code. All elements in the left subsequence are smaller than the reference value, and all elements in the right subsequence are greater than the reference value. Then the leftmost subsequence repeats the process until all elements are arranged in the corresponding positions.insert image description here

hall version

Hall version single-pass sorting code implementation

Define two subscripts left and right. Assuming keyi is on the far left, then the right goes first. Stop when you find something smaller than keyi on the right. Then start walking on the left, and stop when you find something bigger than keyi on the left. Swap the left and right values. Then until the left and right pointers meet, exchange the values ​​of left and keyi.
insert image description here
insert image description here

void QuickSort(int* arr,int begin, int end)
{
    
    
   	int left = begin;
    int right = end;
    int keyi = left;
    while (left < right)
    {
    
    
        //右边先走
        while (left < right && arr[right] >= arr[keyi])
        {
    
    
            right--;
        }
        while (left < right && arr[left] <= arr[keyi])
        {
    
    
           left++;
        }
        Swap(&arr[left], &arr[right]);
    }
    Swap(&arr[keyi], &arr[left]);
}

Hall version sorting code implementation

When the single-pass sort puts the data of the first keyi where it should squat, the array can be divided into three parts, namely [begin, keyi-1], keyi, [keyi+1, end]. At this time, according to the recursive divide-and-conquer idea of ​​the binary tree, the data of the sub-interval is again selected as the keyi on the left side to perform single-pass sorting until the sub-interval does not exist. Finally, the data can be sorted.
insert image description here

Optimization for sorted arrays

For the sorting of already ordered arrays, the above code has a fatal problem. That is, the time complexity of unoptimized quicksort is O(N^2) in the worst case. When the amount of data is large, there will be a problem of stack overflow due to recursion. The first optimization method is introduced below, that is, random number selection of keyi.

Random number selection keyi

By taking the random number of the length of the array as keyi, the time complexity problem of quick sorting in the case of complete order or close to order can be avoided to a certain extent. Here's a quick look at how it's handled
insert image description here

Take the middle of three

By comparing the values ​​at the leftmost, rightmost and middle positions, take the middle value as the subscript keyi. In this way, the problem of decreased efficiency of quick sorting can be optimized to a certain extent in the case of ordered or partially ordered.

insert image description here
insert image description here

pit digging

Single-pass sorting code implementation of digging method

Since Mr. Hall's method is relatively obscure and difficult to understand, there is the digging method we are going to introduce now. As the name suggests, the digging method is an optimized version based on the idea of ​​​​quick sorting. First assume that the key is the leftmost number, so the pit is on the left. Then start walking from the right, put it into the pit when you encounter one smaller than the key, and change the pit position to the stop position. The same goes for the left.
insert image description here

void QuickSort1(int* arr, int begin, int end)
{
    
    
    int left = begin;
    int right = end;
    随机选key
    通过随机数来选择key,优化了顺序和逆序的情况下的时间复杂度
    //int randi = left + (rand() % (right - left));
    //
    //Swap(&arr[randi], &arr[left]);

    //三数取中优化
    int ret = GetMidNum(arr, left, right);
    if (left != ret)
        Swap(&arr[ret], &arr[left]);

    //左边做key
    int key = arr[left];
    int hole = left;
    while (left < right)
    {
    
    
        //右边先走
        while (left < right && arr[right] >= key)
        {
    
    
            right--;
        }
        //右边比key小就把它填到坑里,并改变坑位
        arr[hole] = arr[right];
        hole = right;
        while (left < right && arr[left] <= key)
        {
    
    
            left++;
        }
        //左边比key大就把它填到坑里,并改变坑位
        arr[hole] = arr[left];
        hole = left;
    }
    //最后坑位就是key应该蹲的位置
    arr[hole] = key;
}

Digging method code implementation

insert image description here

forward and backward pointer method

Single-pass sorting code implementation of forward and backward pointer method

The implementation idea is as follows: first define two pointers prev and cur. Use the leftmost as the key, and cur finds the smaller value. When the content pointed to by cur is smaller than the key, ++prev, the value of prev and the value of cur are exchanged (the position coincides and can not be exchanged). When the value found by cur is greater than key, ++cur. The general situation of this method is divided into two types, one, prev followed by cur. 2. Prev and cur are directly separated by the value of the key. Then at this time, the value larger than the key will be gradually turned backward, and the value smaller than the key will be gradually thrown forward.
insert image description here
insert image description here
insert image description here

Front and rear pointer method code implementation

insert image description here

Optimization between cells

We know that quick sort actually uses the idea of ​​​​divide and conquer the binary tree structure to sort. In the binary tree structure, when the height is h, we consider the optimal case (full binary tree), and the nodes in the last layer are 2^(h-1). Take up half of the whole tree. The number of nodes in the penultimate layer is 2 ^ (h-2), accounting for 25% of the nodes in the entire tree. In fact, as long as the recursion of the bottom three layers is eliminated, the loss caused by recursion can be generally reduced, the efficiency can be improved, and the loss of stack space can be reduced. To optimize within a locally ordered small interval, there are many sorts we can choose. However, based on what we have learned before, we can find that the advantages of using heap sort and Hill sort over inter-cell optimization are not obvious. The former needs to build a heap, and the latter needs to be pre-sorted according to the gap. It is actually the most suitable to choose insertion sort for inter-cell optimization. Because in the locally ordered interval, the efficiency of insertion sort is the highest.
insert image description here

insert image description here

Non-recursive implementation of quick sort

Use the stack to store the interval that needs recursion, and then simulate the order of recursion according to the last-in-first-out nature of the stack. Convert the interval into subintervals for continued sorting. Stop adding the subscript of the interval to the stack until the interval does not exist or there is only one number in the interval.
insert image description here

insert image description here

Summary of Quick Sort Features

1. Quick sort is a sort with good comprehensive performance and wide usage scenarios.
Second, the time complexity of quick sorting is O(N LogN).
insert image description here
In the best case, the time complexity of quicksort is O(nlogn). When the sequence to be sorted can be evenly divided into two subsequences, each split operation can roughly divide the sequence equally. At this time, the number of recursive calling layers is logn, and the number of comparisons required for each layer is n, so the total number of comparisons is O( N
LogN). In the worst case, the time complexity of quicksort is O(n^2). When the sequence to be sorted is already ordered or basically ordered (for example, the sequence is already in ascending order, but the selected reference value is always the smallest or largest element), each split operation can only cut out one subsequence, and the layer of recursive calls The number is n, and the number of comparisons required for each layer is n, so the total number of comparisons is n^2. However, by randomly choosing the base value or using optimization methods such as the median of three, the possibility of the worst case can be reduced, thereby improving the average performance of quicksort. In practical applications, quick sort is commonly used.
3. Quick sort is an unstable sort.

code acquisition

Guess you like

Origin blog.csdn.net/m0_71927622/article/details/131339569