零基礎(chǔ)學(xué)排序算法

在兩位亦師亦友的朋友的幫助下,準(zhǔn)備從零開始學(xué)習(xí)算法,第一課是從最簡單的排序算法開始學(xué)習(xí)九妈。
本篇文章中的內(nèi)容是看了別人的文章和書籍的總結(jié)丽蝎,再加上兩位朋友的細心教導(dǎo)猎拨,最后加上自己的理解膀藐。

1.排序介紹

在本文中,我們會依次來介紹10中排序方法分別是:冒泡排序红省、選擇排序额各、插入排序快速排序吧恃、希爾排序虾啦、歸并排序桶排序痕寓、計數(shù)排序傲醉、基數(shù)排序堆排序呻率。

在排序的過程中我們會分析算法中幾個基本的特點:

  • 時間復(fù)雜度:算法執(zhí)行需要的時間硬毕。
  • 空間復(fù)雜度:算法執(zhí)行需要的額外的空間(內(nèi)存)。
  • 穩(wěn)定性:算法執(zhí)行過程中相同的數(shù)字相對位置是否交換礼仗。
    在文章提到的所有的排序的數(shù)據(jù)都默認是正整數(shù)吐咳,排序的順序是遞增

2.排序詳解

2.1.冒泡排序(Bubble Sort)

冒泡排序是一種較簡單的排序方法元践,基本的流程就是:

  1. 遍歷所有的數(shù)據(jù)韭脊,比較相鄰的兩個數(shù)字,如果左邊的大于右邊的數(shù)字单旁,就交換沪羔。經(jīng)過一次遍歷之后,數(shù)組中最大的數(shù)字就會在數(shù)組的末尾了象浑。
  2. 重復(fù)1步驟蔫饰,直到數(shù)組中所有的數(shù)字右邊的都比左邊的大,這樣就完成了一個有序的數(shù)組融柬,就像水中的氣泡一樣死嗦,慢慢浮上了水面。
vector<int> bubbleSort1(vector<int> arr) {
    int count = arr.size();
    for (int i = 1; i < count; i++) {
        for (int j = 1; j < count - i; j++) {
            if (arr[j] < arr[j - 1]) {
                // 交換
                int temp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = temp;
            }
        }
    }
    return arr;
}

從上述代碼中可以看出粒氧,冒泡排序的需要兩層循環(huán)越除,所以時間復(fù)雜度為O(n^2),排序過程中只用到了常數(shù)級的額外空間外盯,所以空間復(fù)雜度為O(1)摘盆,算法中只有在左邊大于右邊的時候才交換,相同數(shù)字的相對位置沒有改變饱苟,所以是一種穩(wěn)定的排序算法孩擂。

優(yōu)化

但是如果數(shù)據(jù)已經(jīng)是有序的呢?上面的算法的時間復(fù)雜度還是O(n^2)箱熬,所以我們需要對算法改進一下类垦,讓它在有序的情況下只遍歷一次就夠了狈邑,下面的算法就是使用一個標(biāo)記來記錄數(shù)組是否被交換過,如果交換過蚤认,說明數(shù)組是無序的米苹,如果沒有交換過,說明數(shù)組已經(jīng)是有序的了砰琢。這樣在已經(jīng)有序的情況下時間復(fù)雜度為O(n)蘸嘶。

vector<int> bubbleSort2(vector<int> arr) {
    // 記錄是否交換
    bool flag = true;
    while (flag) {
        flag = false;
        for (int i = 1; i < arr.size(); i++) {
            if (arr[i] < arr[i - 1]) {
                // 交換
                int temp = arr[i];
                arr[i] = arr[i - 1];
                arr[i - 1] = temp;
                flag = true;
            }
        }
    }
    return arr;
}

2.2.選擇排序(Selection Sort)

選擇排序是一種簡單直觀的排序方法,基本的流程就是:

1.從待排序的數(shù)組中選出一個最小的數(shù)據(jù)陪汽,放在起始位置训唱。
2.從剩余待排序的數(shù)組中選出一個最小的數(shù)據(jù),放到已排序數(shù)組的末尾位置挚冤。
3.重復(fù)2步驟况增,直到所有的數(shù)組都變成已排序。

vector<int> selectionSort(vector<int> arr) {
    int minIndex;
    for (int i = 0; i < arr.size() - 1; i++) {
        minIndex = i;
        for (int j = i + 1; j < arr.size(); j++) {
            // 先找出一個最小的數(shù)
            minIndex = arr[j] < arr[minIndex] ? j : minIndex;
        }
        // 和第i位交換
        int temp = arr[minIndex];
        arr[minIndex] = arr[i];
        arr[i] = temp;
    }
    return arr;
}

從上述代碼中可以看出你辣,選擇排序需要兩層循環(huán)巡通,所以時間復(fù)雜度為O(n^2)尘执,排序過程中只用到了常數(shù)級的額外空間舍哄,所以空間復(fù)雜度為O(1),選擇排序雖然和冒泡排序雖然類似誊锭,但是卻是一種不穩(wěn)定的排序算法表悬。

舉個??:假如有數(shù)組[4, 5, 4, 3, 6],當(dāng)?shù)谝淮窝h(huán)的時候丧靡,3是最小的數(shù)據(jù)蟆沫,所以3和第一個4交換,如此以來兩個4的相對位置就已經(jīng)發(fā)生了變化温治,所以是不穩(wěn)定的排序算法饭庞。

2.3.插入排序(Insertion Sort)

插入排序是一種簡單直觀的排序方法,基本的流程就是:

1.先將數(shù)組分成兩部分:已排序和待排序熬荆,已排序的數(shù)組默認放一個舟山。
2.從剩余待排序的數(shù)組起始位置取一個,插入到已排序數(shù)組的相應(yīng)位置卤恳。
3.重復(fù)2步驟累盗,直到所有的數(shù)組都變成已排序。

vector<int> insertionSort(vector<int> arr) {
    // 已經(jīng)排好序的位置
    int sortedIndex = 0;
    int current;
    // 遍歷未排序的數(shù)組
    for (int i = 1; i < arr.size(); i++) {
        current = arr[i];
        // 把取出來的數(shù)據(jù)插入到已排序的數(shù)組中
        for (int j = sortedIndex; j >= 0; j--) {
            if (current > arr[j]) {
                arr[j + 1] = current;
                sortedIndex++;
                break;
            } else if (current < arr[j]) {
                arr[j + 1] = arr[j];
                if (j == 0) {
                    arr[0] = current;
                    sortedIndex++;
                    break;
                }
            }
        }
    }
    return arr;
}

從上述代碼中可以看出突琳,插入排序需要兩層循環(huán)若债,所以時間復(fù)雜度為O(n^2),排序過程中只使用了常數(shù)級的額外空間拆融,所以空間復(fù)雜度為O(1)蠢琳,插入排序是一種穩(wěn)定的排序算法啊终,插入過程中相同元素的相對位置不會變化。

2.4.快速排序(Quick Sort)

快速排序的基本思想是:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分傲须,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小孕索,然后再按此方法對這兩部分?jǐn)?shù)據(jù)分別進行快速排序,整個排序過程可以遞歸進行躏碳,以此達到整個數(shù)據(jù)變成有序序列搞旭。

1.先確定一個關(guān)鍵值,重新排列數(shù)組菇绵,把比關(guān)鍵值小的放在關(guān)鍵值的左邊肄渗,把比關(guān)鍵值大的放在關(guān)鍵值的右邊。
2.把數(shù)組從關(guān)鍵值分割成兩個數(shù)組咬最,分別把兩個新的數(shù)組執(zhí)行步驟1翎嫡。
3.重復(fù)2步驟,直到最小的數(shù)組不能分割永乌,現(xiàn)在數(shù)組就已經(jīng)是一個有序的數(shù)組了惑申。

void quickSorting(vector<int> &arr, int low, int high) {
    if (low >= high) {
        return;
    }
    // 先保存左右點
    int left = low;
    int right = high;
    // 默認關(guān)鍵值為數(shù)組的最左邊的點
    int key = arr[left];
    while (left < right) {
        while (left < right && arr[right] >= key) {
            right--;
        }
        arr[left] = arr[right];
        while (left < right && arr[left] <= key) {
            left++;
        }
        arr[right] = arr[left];
    }
    arr[left] = key;
    quickSorting(arr, low, left - 1);
    quickSorting(arr, left + 1, high);
}

vector<int> quickSort(vector<int> arr) {
    quickSorting(arr, 0, arr.size() - 1);
    return arr;
}

從上述代碼中可以看出,快速排序是遞歸調(diào)用函數(shù)翅雏,所以時間復(fù)雜度為O(nlog_2n)圈驼,空間復(fù)雜度也為為O(nlog_2n)(作為算法新手的我其實并不會推導(dǎo),我也是死記的)望几,快速排序涉及到多次交換绩脆,是一種不穩(wěn)定的排序算法(這里不好舉??,等我想到合適的再加上)橄抹。

2.5.希爾排序(Shell Sort)

希爾排序是插入排序的一種靴迫,又稱“縮小增量排序”,是插入排序的改進版楼誓,基本的流程就是:

1.先設(shè)定一個步長玉锌,數(shù)組中距離為步長的數(shù)據(jù)都被編成一組,統(tǒng)一我們都取數(shù)組長度的二分之一疟羹,特別說明一下這里主守,如果步長是數(shù)組長度的二分之一,那么就是兩個數(shù)據(jù)一組阁猜,首先我們會先對這兩個數(shù)據(jù)進行排序丸逸。
2.完成所有組排序之后,把步長除2剃袍,那數(shù)組中的數(shù)據(jù)就變成了4個一組黄刚,繼續(xù)對數(shù)組中的數(shù)據(jù)排序。
3.完成所有組排序之后民效,把步長除2憔维,繼續(xù)對數(shù)組中的數(shù)據(jù)排序涛救。
4.重復(fù)步驟3,直到步長為1业扒,這時候數(shù)組就剩下一個了检吆,就是我們最終需要的已經(jīng)排序的數(shù)組。

vector<int> shellSort(vector<int> arr) {
    int step = arr.size() / 2;
    while(step >= 1) {
        // 把距離為step的元素編為一組 掃描所有組
        for (int i = step; i < arr.size(); i++) {
            int j = 0;
            int temp = arr[i];
            // 對距離為gap的元素組進行排序
            for (j = i - step; j >= 0 && temp < arr[j]; j = j - step) {
                arr[j + step] = arr[j];
            }
            arr[j + step] = temp;
        }
        // 減小步長
        step /= 2;
    }
    return arr;
}

從上述代碼中可以看出程储,希爾排序雖然是兩層循環(huán)蹭沛,但是時間復(fù)雜度并不是O(n^2),因為兩層循環(huán)的此次都是遠遠小于n的章鲤,所以平均時間復(fù)雜度為O(n^{1.3})(弱雞的我還是不知道是怎么推導(dǎo)出來的)摊灭,只使用了常數(shù)單位的額外空間,空間復(fù)雜度為O(1)败徊,是一種不穩(wěn)定的排序算法帚呼。

2.6.歸并排序(Merge Sort)

歸并排序是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應(yīng)用。將已有序的子序列合并皱蹦,得到完全有序的序列煤杀;即先使每個子序列有序,再讓子序列段合并成有序的序列沪哺,基本的流程就是:

1.先把現(xiàn)有的數(shù)組分成兩個數(shù)組沈自。
2.再分別對兩個數(shù)組進行歸并排序。
3.把已經(jīng)排序好的兩個數(shù)組合并成一個新的有序數(shù)組凤粗。

vector<int> merge(vector<int> left, vector<int> right) {
    vector<int> res;
    // 兩個數(shù)組歸并為一個數(shù)組
    int one = 0;
    int two = 0;
    while (one < left.size() && two < right.size()) {
        if (left[one] < right[two]) {
            res.push_back(left[one++]);
        } else {
            res.push_back(right[two++]);
        }
    }
    
    while (one < left.size()) {
        res.push_back(left[one++]);
    }
    
    while (two < right.size()) {
        res.push_back(right[two++]);
    }
    return res;
}

vector<int> mergeSort(vector<int> arr) {
    if (arr.size() < 2) {
        return arr;
    }
    // 先將數(shù)組分為兩個
    int mid = arr.size() / 2;
    vector<int> left, right;
    copy(arr.begin(), arr.begin() + mid, back_inserter(left));
    copy(arr.begin() + mid, arr.end(), back_inserter(right));
    
    return merge(mergeSort(left), mergeSort(right));
}

從上述代碼中可以看出酥泛,歸并排序是遞歸調(diào)用函數(shù),所以時間復(fù)雜度為O(nlog_2n)嫌拣,歸并排序的遞歸是在排序的過程中,但是內(nèi)存的消耗卻是在合并的過程中呆躲,所以空間復(fù)雜度為O(n)异逐,是一種穩(wěn)定的排序算法。

2.7.桶排序(Bucket Sort)

桶排序又稱箱排序插掂,工作的原理是將數(shù)組分到有限數(shù)量的桶子里灰瞻。每個桶子再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續(xù)使用桶排序進行排序)。
后面的幾種排序主要是偏理解辅甥,所以我都先舉個??來說明一下:

假如酝润,現(xiàn)在有1w個年齡不等的人(最大年齡為110, 最小年齡為1歲)璃弄,需要按照年齡的升序來排列要销。
那我們就可以創(chuàng)建11個桶,編號為1的桶都放1-10歲的人夏块,編號為2的桶都放11-20歲的人疏咐,把所有的人都依次放到桶中纤掸,分別排序每個桶中的數(shù)據(jù),最后按順序合并稱一個大數(shù)組浑塞,就是最后需要的已排序的數(shù)組了借跪。

基本的流程就是:

1.先計算需要的桶的數(shù)量。
2.分別把所有數(shù)組存放到相應(yīng)的桶中酌壕。
3.把每個桶中的數(shù)據(jù)使用快速排序進行排序掏愁。
4.把所有桶中的數(shù)據(jù)依次合并成一個大數(shù)組,就是最后需要的已排序的數(shù)組了卵牍。

vector<int> bucketSort(vector<int> arr) {
    if (arr.size() < 2) {
        return arr;
    }
    // 先找出排序數(shù)組中的最大和最小的元素
    int maxInt = arr.front();
    int minInt = arr.front();
    for (int i = 0; i < arr.size(); i++) {
        maxInt = max(arr[i], maxInt);
        minInt = min(arr[i], minInt);
    }
    
    // 初始化桶
    // 桶大小
    int bucketSize = 5;
    // 桶個數(shù) 向上取整
    int bucketCount = ceil((maxInt - minInt + 1) / 5.0);
    vector<vector<int>> bucket(bucketCount);
    
    for (int i = 0; i < arr.size(); i++) {
        // 判斷放到哪一個桶中
        int index = (arr[i] - minInt) / bucketSize;
        bucket[index].push_back(arr[i]);
    }
    
    // 分別對每個桶中的數(shù)據(jù)快速排序
    arr.resize(0);
    for (int i = 0; i < bucket.size(); i++) {
        if (bucket[i].size() > 0) {
            quickSort(bucket[i]);
            arr.insert(arr.end(), bucket[i].begin(), bucket[i].end());
        }
    }
    return arr;
}

從上述代碼中可以看出托猩,桶排序是兩個并列的循環(huán),如果數(shù)據(jù)的個數(shù)為n辽慕,桶的數(shù)量為m京腥,個人理解是因為已經(jīng)分成了m個桶了,快速排序的時間可以忽略不計溅蛉,所以時間復(fù)雜度為O(n+m)公浪,空間復(fù)雜度為O(n+m)(但是具體的公式還是需要通過推導(dǎo)出來),是一種穩(wěn)定的排序算法船侧。

2.8.計數(shù)排序(Counting Sort)

計數(shù)排序是一個非基于比較的排序算法欠气。它的優(yōu)勢在于在對一定范圍內(nèi)的整數(shù)排序時,它的復(fù)雜度為Ο(n+k)(其中k是整數(shù)的范圍)镜撩,快于任何比較排序算法预柒。
其實計數(shù)排序是桶排序的一種情況,舉個??來說明一下(還是上面的例子):

假如袁梗,現(xiàn)在有1w個年齡不等的人(最大年齡為110宜鸯, 最小年齡為1歲),需要按照年齡的升序來排列遮怜。
那我們就可以創(chuàng)建110個桶淋袖,編號為1的桶都放1歲的人,編號為2的桶都放2歲的人锯梁,把所有的人都依次放到桶中即碗,(這里并不需要再次排序,因為每一個桶中的人年齡都是相同的)陌凳,最后按順序合并稱一個大數(shù)組剥懒,就是最后需要的已排序的數(shù)組了。

基本的流程就是:

1.先計算需要的桶的數(shù)量合敦。
2.分別把各個數(shù)據(jù)的個數(shù)存放到相應(yīng)的桶中初橘。
3.根據(jù)數(shù)據(jù)的數(shù)量,依次合并成一個大數(shù)組,就是需要的已排序的數(shù)組了壁却。

vector<int> countingSort(vector<int> arr) {
    if (arr.size() < 2) {
        return arr;
    }
    // 先找出排序數(shù)組中的最大和最小的元素
    int maxInt = arr.front();
    int minInt = arr.front();
    for (int i = 0; i < arr.size(); i++) {
        maxInt = max(arr[i], maxInt);
        minInt = min(arr[i], minInt);
    }
    
    // 創(chuàng)建一個數(shù)組可以承載最小到最大值中的所有的元素批狱。
    vector<int> bucket(maxInt - minInt + 1);
    for (int i = 0; i < arr.size(); i++) {
        bucket[arr[i] - minInt]++;
    }
    
    int index = 0;
    for (int i = 0; i < bucket.size(); i++) {
        while (bucket[i] > 0) {
            arr[index++] = i;
            bucket[i]--;
        }
    }
    return arr;
}

從上述代碼中可以看出,計數(shù)排序和桶排序一樣展东,所以時間復(fù)雜度為O(n+m)赔硫,空間復(fù)雜度為O(n+m)(但是具體的公式還是需要通過推導(dǎo)出來),是一種穩(wěn)定的排序算法盐肃。

但是一般都會有一個問題爪膊,既然和桶排序這么像,那為什么還叫計數(shù)排序砸王,直接叫桶排序好了推盛,原因是我們在存數(shù)據(jù)的時候并沒有存具體的數(shù)據(jù),而是存的數(shù)據(jù)的數(shù)量谦铃,所以叫計數(shù)排序耘成。

2.9.基數(shù)排序(Radix Sort)

基數(shù)排序屬于“分配式排序”,又稱“桶子法”驹闰,顧名思義瘪菌,它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中嘹朗,藉以達到排序的作用师妙。

舉個??來說明一下:

假如,現(xiàn)在有1w個11位的手機號碼屹培,需要按照升序來排列默穴。
那我們就可以創(chuàng)建10個桶(分別編號為0-10),把倒數(shù)第一個數(shù)字和桶編號相同的數(shù)據(jù)放在一個桶中褪秀。
已經(jīng)排序好的數(shù)據(jù)蓄诽,繼續(xù)把倒數(shù)第二個數(shù)字和桶編號相同的數(shù)據(jù)放在一個桶中,一直重復(fù)溜歪,直到把第一個數(shù)字和桶編號相同的數(shù)據(jù)放在一個桶中若专。
最后按順序合并稱一個大數(shù)組,就是最后需要的已排序的數(shù)組了蝴猪。

基本的流程就是:

1.先計算需要的桶的數(shù)量,如果是正整數(shù)膊爪,我們的桶數(shù)量一般都是10自阱。
2.把倒數(shù)第一個數(shù)字和桶編號相同的數(shù)據(jù)放在一個桶中。
3.重復(fù)步驟2米酬,直到把第一個數(shù)字和桶編號相同的數(shù)據(jù)放在一個桶中
4.把桶中的數(shù)據(jù)取出沛豌,依次合并成一個大數(shù)組,就是需要的已排序的數(shù)組了。

vector<int> radixSort(vector<int> arr) {
    if (arr.size() < 2) {
        return arr;
    }
    
    // 先找出排序數(shù)組中的最大和最小的元素
    int maxInt = arr.front();
    for (int i = 0; i < arr.size(); i++) {
        maxInt = max(arr[i], maxInt);
    }
    
    // 求出最大的位數(shù)
    int maxDigit = 1;
    while (maxInt / 10 > 0) {
        maxInt = maxInt / 10;
        maxDigit++;
    }
    
    for (int i = 0; i < maxDigit; i++) {
        // 創(chuàng)建一個數(shù)組為10的數(shù)組
        vector<vector<int>> bucket(10);
        // 分別放到不同的桶中
        for (int j = 0; j < arr.size(); j++) {
            // 計算i個位置的數(shù)
            int num = (arr[j] % (int)pow(10, i + 1)) / (int)pow(10, i);
            bucket[num].push_back(arr[j]);
        }
        
        // 把桶中的數(shù)據(jù)重新取出
        arr.resize(0);
        for (int j = 0; j < bucket.size(); j++) {
            if (bucket[j].size() > 0) {
                for (int k = 0; k < bucket[j].size(); k++) {
                    arr.push_back(bucket[j][k]);
                }
            }
        }
    }
    return arr;
}

從上述代碼中可以看出加派,基數(shù)排序是兩層循環(huán)叫确,如果數(shù)據(jù)的個數(shù)為n,桶的數(shù)量為m(桶的數(shù)量是根據(jù)數(shù)據(jù)的最大位數(shù)決定的)芍锦,所以時間復(fù)雜度為O(n*m)竹勉,空間復(fù)雜度為O(n+m)(但是具體的公式還是需要通過推導(dǎo)出來),是一種穩(wěn)定的排序算法娄琉。

2.10.堆排序(Heap Sort)

堆排序是指利用這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計的一種排序算法次乓。是一個近似完全二叉樹的結(jié)構(gòu),并同時滿足堆積的性質(zhì):即子結(jié)點的鍵值或索引總是小于(或者大于)它的父節(jié)點孽水。
最后一個堆排序可能相對于其他的排序比較難理解一點票腰,需要一定的二叉樹的知識,才可以理解堆的結(jié)構(gòu)女气,其實就是一個父節(jié)點比子節(jié)點大(或者子節(jié)點比父節(jié)點大)的完全二叉樹杏慰。

基本的流程就是:

1.先創(chuàng)建一個大頂堆,就是父節(jié)點比子節(jié)點大的堆炼鞠。
2.把大頂堆最頂部的一個數(shù)據(jù)缘滥,和最末尾的一個數(shù)據(jù)交換,這樣最大的數(shù)據(jù)就到了最后面了簇搅。
3.重新把剩余的數(shù)據(jù)完域,重新變成一個大頂堆,然后在把大頂堆最頂部的一個數(shù)據(jù)瘩将,和未排序最末尾的一個數(shù)據(jù)交換吟税。
4.重復(fù)3步驟,最后就得到了一個有序的數(shù)組姿现。

int len;
// 堆化
void adjustHeap(vector<int> &arr, int i) {
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    int top = i;
    
    if (left < len && arr[left] > arr[top]) {
        top = left;
    }
    if (right < len && arr[right] > arr[top]) {
        top = right;
    }
    
    if (top != i) {
        int temp = arr[i];
        arr[i] = arr[top];
        arr[top] = temp;
        adjustHeap(arr, top);
    }
}

// 創(chuàng)建大頂堆
void buildHeap(vector<int> &arr) {
    // 現(xiàn)在是一個無序的普通的堆
    for (int i = arr.size() / 2 - 1; i >= 0; i--) {
        adjustHeap(arr, i);
    }
}

vector<int> heapSort(vector<int> arr) {
    if (arr.size() < 2) {
        return arr;
    }
    len = arr.size();
    buildHeap(arr);
    
    for (int i = arr.size() - 1; i > 0; i--) {
        // 交換
        int temp = arr[i];
        arr[i] = arr[0];
        arr[0] = temp;
        // 需要實時記錄一個len
        len--;
        // 重新排列成為大頂堆 從上到下
        adjustHeap(arr, 0);
    }
    return arr;
}

從上述代碼中可以看出肠仪,堆排序主要時間消耗是在堆化的過程,是一個遞歸的過程备典,所以時間復(fù)雜度為O(nlog_2n)异旧,(但是具體的公式還是需要通過推導(dǎo)出來),堆是原地排序提佣,所以空間復(fù)雜度為O(1)吮蛹,是一種不穩(wěn)定的排序算法。

結(jié)束語

  • 以上算法還沒有經(jīng)過大數(shù)據(jù)的測試拌屏,所以在有些情況下也許沒有達到排序的效果潮针,發(fā)現(xiàn)有問題的同學(xué)可以幫忙指出不足。
  • 后面的三種排序算法:桶排序倚喂、基數(shù)排序每篷、計數(shù)排序,雖然效率更快,但是有一定的場景的限制焦读,所以平時使用的并不是很多子库。
  • 看時間復(fù)雜度和空間復(fù)雜度,堆排序比快速排序感覺還更好一點矗晃,但是在實際的運用中快速排序的運用更加的廣泛仑嗅,因為堆排序的操作比較繁瑣,建堆和堆化喧兄,而且堆是基于二叉樹的无畔,所以一般都是使用指針來存儲,創(chuàng)建堆的時候內(nèi)存就有一定的消耗吠冤,還有對計算機的內(nèi)存是不友好的浑彰,是碎片存儲的。
  • 快速排序也不是萬能的排序拯辙,也有自己的不足之處郭变,比如日常運用中數(shù)據(jù)量比較大的情況下,需要防止堆棧溢出涯保,解決方法是:我們可以模擬遞歸的出棧入棧诉濒,達到遞歸相同的效果。
  • 如果是已經(jīng)有序的數(shù)組夕春,快速排序的時間復(fù)雜度為O(n^2)未荒,但是怎么才可以較好的優(yōu)化這個算法呢?希望知道的朋友可以教我一下及志,謝謝了片排。

參考資料

《編程珠璣》,《算法第四版》速侈,王爭的《數(shù)據(jù)結(jié)構(gòu)與算法之美》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末率寡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子倚搬,更是在濱河造成了極大的恐慌冶共,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件每界,死亡現(xiàn)場離奇詭異捅僵,居然都是意外死亡,警方通過查閱死者的電腦和手機眨层,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門命咐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谐岁,你說我怎么就攤上這事。” “怎么了伊佃?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵窜司,是天一觀的道長。 經(jīng)常有香客問我航揉,道長塞祈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任帅涂,我火速辦了婚禮议薪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘媳友。我一直安慰自己斯议,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布醇锚。 她就那樣靜靜地躺著哼御,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焊唬。 梳的紋絲不亂的頭發(fā)上恋昼,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音赶促,去河邊找鬼液肌。 笑死,一個胖子當(dāng)著我的面吹牛鸥滨,可吹牛的內(nèi)容都是我干的嗦哆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爵赵,長吁一口氣:“原來是場噩夢啊……” “哼吝秕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起空幻,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烁峭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后秕铛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體约郁,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年但两,在試婚紗的時候發(fā)現(xiàn)自己被綠了鬓梅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡谨湘,死狀恐怖绽快,靈堂內(nèi)的尸體忽然破棺而出芥丧,到底是詐尸還是另有隱情,我是刑警寧澤坊罢,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布续担,位于F島的核電站,受9級特大地震影響活孩,放射性物質(zhì)發(fā)生泄漏物遇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一憾儒、第九天 我趴在偏房一處隱蔽的房頂上張望询兴。 院中可真熱鬧,春花似錦起趾、人聲如沸诗舰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽始衅。三九已至,卻和暖如春缭保,著一層夾襖步出監(jiān)牢的瞬間汛闸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工艺骂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诸老,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓钳恕,卻偏偏與公主長得像别伏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子忧额,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文總結(jié)十大經(jīng)典排序算法及變形厘肮,并提供Java實現(xiàn)。參考文章:十大經(jīng)典排序算法總結(jié)(Java語言實現(xiàn))快速排序算法...
    Steven1997閱讀 17,618評論 3 186
  • 概述 排序有內(nèi)部排序和外部排序睦番,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序类茂,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    蟻前閱讀 5,186評論 0 52
  • 概述:排序有內(nèi)部排序和外部排序托嚣,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序巩检,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,732評論 0 15
  • 滿地的智能手機和更快更強的移動網(wǎng)絡(luò)讓閱讀這件事改變了很多示启,尤其是在如何利用碎片時間這件事上兢哭,技術(shù)已經(jīng)為我們創(chuàng)造了充...
    MindStore閱讀 385評論 0 1
  • 滔滔湘水迟螺,巍巍麓山 “仁術(shù)”之光冲秽,圣潔綻放 看,一百零六年的變遷 醫(yī)樓在煮仇,多幾間 一院四區(qū)劳跃,舊貌換新顏 憶,櫛風(fēng)沐...
    陽光下的海棠閱讀 596評論 0 1