快速排序

45 阅读3分钟

说明

是冒泡排序的升级

每次选择基准数,把小于基准数的放到基准数的左边,把大于基准数的放到基准数的右边,采用“分治思想”处理剩余的序列元素,直到整个序列变为有序序列。

算法效率提升

  • 对于小段趋于有序的序列采用插入排序
  • 三数取中法。旨在挑选合适的基准数,防止快排退化成冒泡排序。
  • 随机数法。

时间空间复杂度

排序算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度稳定性
快速排序O(nlog2nn\log_{2}{n})O(nlog2nn\log_{2}{n})O(n2n^{2})O(log2n\log_{2}{n}) ~ O(n)不稳定

序列是有序的话再使用快速排序空间复杂度为O(n)

代码

#include <iostream>
#include <stdlib.h>
#include <time.h>

using namespace std;


// 快排分割处理函数
int Partation(int arr[], int begin, int end) {
    int val = arr[begin];  // 选取基准数, 我们选择第一个元素

    // 进行一次快排操作
    while (begin < end) {

        while (begin < end && arr[end] > val) {
            // 从后往前找到第一个小于val的元素
            end--;
        }

        if (begin < end) {
            // 找到了,将右边的元素放到左边
            arr[begin] = arr[end];
            begin++;  // 左边元素下标向右移动一位
        }

        while (begin < end && arr[begin] < val) {
            // 从前往后找到第一个大于val的元素
            begin++;
        }

        if (begin < end) {
            // 找到了,将左边的元素放到右边
            arr[end] = arr[begin];
            end--;  // 右边边元素下标向左移动一位
        }

    }

    // begin == end的位置,就是存放基准数的位置
    arr[begin] = val;
    return begin;
}

void QuickSort(int arr[], int begin, int end) {
    // 递归结束条件
    if (begin >= end) {
        return;
    }

    // 在[begin, end]区间的元素做一次快排分割处理
    int pos = Partation(arr, begin, end);  // pos为处理后基准数存放的位置

    // 对基准数的左边和右边的序列再分别进行快排
    QuickSort(arr, begin, pos - 1);
    QuickSort(arr, pos + 1, end);
}

void QuickSort(int arr[], int size) {

    return QuickSort(arr, 0, size - 1);

}

int main() {

    int arr[10];
    srand(time(nullptr));

    for (int i = 0; i < 10; i++) {
        arr[i] = rand() % 100 + 1;
    }

    for (int v : arr) {
        cout << v << " ";
    }

    cout << endl;

    QuickSort(arr, 10);

    for (int v : arr) {
        cout << v << " ";
    }

    cout << endl;

    return 0;
}

测试

➜  build git:(quick-sort) ✗ ./QuickSort 
91 77 90 78 41 2 95 55 5 64 
2 5 41 55 64 77 78 90 91 95

快速排序优化

优化一

随着快速排序的进行,数据逐渐趋于有序,此时对于快速排序已不再有优势了。对于趋于有序的序列,使用插入排序效率会更高。所以说在一定的范围内,我们可以使用插入排序代替快速排序。

void QuickSort(int arr[], int begin, int end) {
    // 递归结束条件
    if (begin >= end) {
        return;
    }

    // 优化一:
    if (end - begin <= 5) {  // 当序列的数据元素个数为5个以内的时候,调用插入排序
        InsertSort(arr, end - begin);
        return;
    }

    // 在[begin, end]区间的元素做一次快排分割处理
    int pos = Partation(arr, begin, end);  // pos为处理后基准数存放的位置

    // 对基准数的左边和右边的序列再分别进行快排
    QuickSort(arr, begin, pos - 1);
    QuickSort(arr, pos + 1, end);
}

优化二

  • middle = (L + R) / 2 , middle的位置。
  • 采用三数取中法(数值在中间的数),找合适的基准数。如果不找到一个合适的基准数的话会导致树的深度变大。