1. 程式人生 > >第3節、時間和空間的均衡——快速排序

第3節、時間和空間的均衡——快速排序

算法 快速排序

1、引入

第一節講的計數排序有很好的運行時間表現,但因為占用空間的問題,只適用於數字非常有限的情況;

第二節講的冒泡排序解決了計數排序空間的問題,但時間復雜度卻變成了O(n^2)。

技術分享圖片

對冒泡排序的過程進行分析,我們可以發現,在每一輪的排序過程中,需要對所有相鄰的數字進行比較(當然,除了最後幾個最大數字),有的數字第一輪比較過了,第二輪可能還要比較,這就存在了優化的空間。
技術分享圖片
針對這個重復比較的問題,快速排序提出了基準數,減小比較次數,實現了算法的優化。

快速排序的過程:

1、選擇一個數作為基準數,也就是比較的標準
2、比這個數字小的放左邊,大的放右邊
3、再對左右區間重復第二步,直到各區間只有一個數

這種重復左右區間直至一個終止條件的方式,就是分治或者遞歸的過程,所以快速排序算法也叫做分治算法,可以簡單理解為分為左右兩部分,分別求解。

分治和遞歸的聯系和區別,之後會在《算法思想》中說明,這裏可以簡單理解為遞歸是實現分治的方法。
技術分享圖片

2、核心算法

下面一個動態表現了快速排序的過程
技術分享圖片

快速排序的過程也是一個分治或者遞歸的過程,因為需要遞歸,即函數調用自身,只是參數不一樣,所以需要單獨寫一個函數。

  • 遞歸函數內部首先就要設置終止條件,具體到快速排序,就是最終只剩下一個數,自然就排序完成了,就不用再遞歸了;

  • while循環內部就是主體,從左到右找到比基準數大的,從右到左找到比基準數小的,交換位置,直至兩個過程相遇;循環完成之後將基準數歸位

  • 左邊和右邊分別遞歸
    技術分享圖片

核心代碼如下,使用C語言:

//快速排序
void quicksort(int left, int right) {
    int i, j, temp;

    //終止條件
    if (left>right)
        return;
    temp = a[(left + right) / 2];
    i = left;
    j = right;

    //從左到右找到比基準數大的,從右到左找到比基準數小的,交換位置,直至兩個過程相遇即全部比較完成
    while (i != j) {
        while (a[j] >= temp && i<j)
            j--;
        while (a[i] <= temp && i<j)
            i++;
        if (i < j) {
            int l = a[i];
            a[i] = a[j];
            a[j] = l;
        }
    }

    //將基準數歸位
    a[(left + right) / 2] = a[i];
    a[i] = temp;

    //左邊遞歸過程
    quicksort(left, i - 1);
    //右邊遞歸過程
    quicksort(i + 1, right);
}

3、代碼

這裏是完整代碼,使用C語言

//輸入:6 5 3 1 8 7 2 4
//輸出:1 2 3 4 5 6 7 8
#include <stdio.h> 
using namespace std;
int a[100001];
//快速排序
void quicksort(int left, int right) {
    int i, j, temp;

    //終止條件
    if (left>right)
        return;
    temp = a[(left + right) / 2];
    i = left;
    j = right;

    //從左到右找到比基準數大的,從右到左找到比基準數小的,交換位置,直至兩個過程相遇即全部比較完成
    while (i != j) {
        while (a[j] >= temp && i<j)
            j--;
        while (a[i] <= temp && i<j)
            i++;
        if (i < j) {
            int l = a[i];
            a[i] = a[j];
            a[j] = l;
        }
    }

    //將基準數的位置放在大小分割的中間
    a[(left + right) / 2] = a[i];
    a[i] = temp;

    //左邊遞歸過程
    quicksort(left, i - 1);
    //右邊遞歸過程
    quicksort(i + 1, right);
}

int main() {
    int n=8;
    //讀入數據
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);

    //快速排序
    quicksort(1, n);

    //輸出結果
    for (int i = 1; i <= n; i++)
        printf("%d ", a[i]);

    getchar(); getchar();
    return 0;
}

4、後續

至此,計數排序、冒泡排序、快速排序的就全部完成了,你學會了嗎?具體掌握還需要加強練習,下一節,我們通過習題,鞏固學習!

技術分享圖片

第3節、時間和空間的均衡——快速排序