排序算法

一、冒泡排序

思路:冒泡排序算法需要遍歷幾次數組。每次遍歷都要比較連續相鄰的元素,如果某一對相鄰元素是降序,則互換它們的值,否則,保持不變。由于較小的值像“氣泡”一樣逐漸浮想頂部,而較大的值沉向底部,所以叫冒泡排序。平均時間復雜度為O(n^2)

最差的情況下,冒泡排序算法需要進行n-1次遍歷。第一次遍歷需要n-1次比較,第二次遍歷需要n-2次比較,依次進行;因此比較總數為:
(n-1)+(n-2)+...+2+1=n(n-1)/2=O(n2)
最差的情況冒泡排序的時間復雜度為O(n^2)

冒泡算法的改進:
冒泡排序的效率比較低,所以我們要通過各種方法改進。在上例中,第四輪排序之后實際上整個數組已經是有序的了,最后兩輪的比較沒必要進行。
注意:如果某次遍歷中沒有發生交換,那么就不必進行下一次遍歷,因為所有元素已經排好了
所以最好的情況是數據本來就有序,復雜度為O(n)

平均時間復雜度為O(n^2), 最差的情況冒泡排序的時間復雜度為O(n^2),最好的情況是數據本來就有序,復雜度為O(n)。
算法是穩定的,因為當a=b時,由于只有大于才做交換,故a和b的位置沒有機會交換,所以算法穩定。
空間復雜度為O(1),不需要額外空間。

二、選擇排序

思路:選擇排序改進了冒泡排序,將必要的交換次數從O(n2)減少到O(n),但是比較次數仍保持為O(n2)。冒泡排序每比較一次就可能交換一次,但是選擇排序是將一輪比較完后,把最小的放到最前的位置(或者把最大的放到最后)。

算法分析:選擇排序最好和最壞的情況一樣運行了O(N2)時間,平均復雜度也是O(N2)。
算法是不穩定的,假設a=b,且a在b前面,而某輪循環中最小值在b后面,而次最小值需要跟a交換,這時候b就在a前面了,所以選擇排序是不穩定的。
空間復雜度為O(1),不需要額外的空間。

三、插入排序

四、快速排序

思路:雖然快速排序稱為分治法,但分治法這三個字顯然無法很好的概括快速排序的全部步驟。比較完整的來說應該是:挖坑填數+分治法:
挖坑填數的說明:L:最左邊索引,R:最右邊索引
1.i =L; j = R; 將基準數即最左邊第一個數挖出形成第一個坑a[i]。
while循環{
2.j--由后向前找比基準數小的數,找到后挖出此數填前一個坑a[i]中。
3.i++由前向后找比基準數大的數,找到后也挖出此數填到前一個坑a[j]中。
}
4.再重復執行2,3二步,直到i==j,將基準數填入a[i]中。
5.這樣整個數據中就被基準數分為了兩個區間,左邊比它小,右邊比它大。
分治法說明:再通過遞歸對左右區間重復第二步,直到各區間只有一個數。
完整的代碼如下:

//快速排序:挖坑填數+分治法
void fastSort(int *arr, int left, int right)
{
    if (left<right && arr!=NULL) {
        int i = left;//left、right要保留,最后要用
        int j = right;
        int key = arr[left];
        
        /******挖坑填數******/
        //每個大while循環:對left作為基準值進行了分區,小的放在了左邊,大的放在了右邊
        while (i<j) {
            while (i<j && arr[j]>=key) {
                j--;
            }
            arr[i]=arr[j];//拿j(后邊)的數填到i(前邊)的坑里
            
            while (i<j && arr[i]<=key) {
                i++;
            }
            arr[j]=arr[i];//拿i(前邊)的數填到j(后邊)的坑里
        }
        arr[i]=key;
        /******挖坑填數******/
        
        /******分治法******/
        fastSort(arr, left, i-1);
        fastSort(arr, i+1, right);
        /******分治法******/

    }
}

1、n大時好,快速排序比較占用內存,內存隨n的增大而增大,但卻是效率高不穩定的排序算法。
2、最差的情況是本身是有序數組,劃分之后一邊是一個,一邊是n-1個,這種極端情況的時間復雜度就是O(N^2)。最壞的情況下退化成插入排序了。
3、理想的情況是,每次劃分所選擇的中間數恰好將當前序列幾乎等分,經過log2N趟劃分,便可得到長度為1的子表。這樣,整個算法的時間復雜度為O(Nlog2N)。
4、快速排序的平均時間復雜度也是O(Nlog2N)。因此,該排序方法被認為是目前最好的一種內部排序方法。

五、歸并排序

思路:將待排序序列R[0...n-1]看成是n個長度為1的有序序列,將相鄰的有序表成對歸并,得到n/2個長度為2的有序表;將這些有序序列再次歸并,得到n/4個長度為4的有序序列;如此反復進行下去,最后得到一個長度為n的有序序列。
那么歸并是如何進行的呢?
我們稱 R[low, mid] 第一段,R[mid+1, high] 為第二段。每次從兩個段中取出一個記錄進行關鍵字的比較,將較小者放入R2中。最后將各段中余下的部分直接復制到R2中。經過這樣的過程,R2已經是一個有序的序列,再將其復制回R中,一次合并排序就完成了。

迭代:

//輔助函數:合并兩個分組
//mid是第一個分組的末尾的index
void merge(int *arr, int *tmp, int start, int mid, int end)
{
    int i = start;
    int j = mid+1;
    int tmpIndex = start;
    //注意:兩個分組的意思,其實自始至終都是在原數組中,只是通過index我們把它看成了兩個分組
    while (i<=mid && j<=end) {//兩個分組從第一個開始對比,誰小誰先放進新數組中,直到有一個分組沒有數據了
        if (arr[i]<=arr[j]) {
            tmp[tmpIndex] = arr[i];
            tmpIndex++;
            i++;
        }
        else
        {
            tmp[tmpIndex] = arr[j];
            tmpIndex++;
            j++;
        }
    }
    
    //前一個分組沒放完,把剩余的直接都放到新數組后面即可
    while (i<=mid) {
        tmp[tmpIndex] = arr[i];
        tmpIndex++;
        i++;
    }
    //后一個分組沒放完,把剩余的直接都放到新數組后面即可
    while (j<=end) {
        tmp[tmpIndex] = arr[j];
        tmpIndex++;
        j++;
    }
    
    //把新數組里的排好序的放回到原數組中
    while (start<=end) {
        arr[start] = tmp[start];
        start++;
    }
}
//歸并排序入口
void mergeSort(int *arr,int length)
{
    int tmp[length];
    int gap = 1;//每組有幾個
    while (gap < length) {
        int start = 0;
        for (start = 0; start+2*gap-1 < length; start=start+2*gap) {
            merge(arr, tmp, start, start+gap-1, start+2*gap-1);
        }
        
        //此時,說明后邊還有數據(情況1:兩個分組,后邊那個分組,不滿gap;情況2:只有一個分組)
        if (start+gap-1<length) {
            merge(arr, tmp, start, start+gap-1, length-1);
        }
        
        gap = 2*gap;
    }
}

遞歸:

void merge(int arr[], int start, int mid, int end) {
    int result[ArrLen];
    int k = 0;
    int i = start;
    int j = mid + 1;
    while (i <= mid && j <= end) {
        if (arr[i] < arr[j]){
            result[k++] = arr[i++];
        }
        else{
            result[k++] = arr[j++];
        }
    }
    if (i == mid + 1) {
        while(j <= end)
            result[k++] = arr[j++];
    }
    if (j == end + 1) {
        while (i <= mid)
            result[k++] = arr[i++];
    }
    for (j = 0, i = start ; j < k; i++, j++) {
        arr[i] = result[j];
    }
}
 
void mergeSort(int arr[], int start, int end) {
    if (start >= end)
        return;
    int mid = ( start + end ) / 2;
    mergeSort(arr, start, mid);
    mergeSort(arr, mid + 1, end);
    merge(arr, start, mid, end);
}
 
int main()
{
    int arr[] = {4, 7, 6, 5, 2, 1, 8, 2, 9, 1};
    mergeSort(arr, 0, 9);
        return 0;
}

歸并排序的形式就是一棵二叉樹,它需要遍歷的次數就是二叉樹的深度,而根據完全二叉樹的可以得出它的時間復雜度是O(n*log2N)。
歸并排序是穩定的。

參考:歸并排序(Merge Sort)

六、堆排序

若從空間復雜度來考慮:首選堆排序,其次是快速排序,最后是歸并排序。
若從穩定性來考慮,應選取歸并排序,因為堆排序和快速排序都是不穩定的。
若從平均情況下的排序速度考慮,應該選擇快速排序。

資料:
常用的排序算法和時間復雜度
各種排序算法時間復雜度

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容

  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,211評論 0 52
  • 概述:排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,740評論 0 15
  • 一. 寫在前面 要學習算法,“排序”是一個回避不了的重要話題,在分析完并查集算法和常用數據結構之后,今天我們終于可...
    Leesper閱讀 2,545評論 0 40
  • 該系列文章主要是記錄下自己暑假這段時間的學習筆記,暑期也在實習,抽空學了很多,每個方面的知識我都會另起一篇博客去記...
    Yanci516閱讀 12,258評論 6 19
  • 以前,我很討厭打掃衛生這件事。其主要原因是因為我比較懶,另外我一直覺得打掃是一件沒有任何技術含量的工作,還一度偏執...
    燕子翩然閱讀 667評論 4 9