日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【君义精讲】排序算法

發布時間:2025/3/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【君义精讲】排序算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、概念

1. 排序的定義

一般定義:將一組無序的數據元素調整為有序的數據元素。
數學定義:假設含n個數據元素的序列為R1,R2,...,Rn{R_1,R_2,...,R_n}R1?R2?...Rn?,其相應的關鍵字序列為K1,K2,...,Kn{K_1,K_2,...,K_n}K1?K2?...Kn?,這些關鍵字相互之間可以進行比較,即在它們之間存在這樣的關系:Kp1≤Kp2≤...≤KpnK_{p1}\le K_{p2}\le...\le K_{pn}Kp1?Kp2?...Kpn?,按此固有關系將上式記錄序列重新排序為:{Rp1,Rp2,...,Rpn}\{R_{p1}, R_{p2}, ... ,R_{pn}\}{Rp1?,Rp2?,...,Rpn?}的操作稱為排序。

2. 排序的前提條件

首先,要排序的數據元素必須類型相同。
其次,數據元素之間是可以比較的,即只要給定兩個元素,就可以說出誰“大”誰“小”,當然,很多情況下“大”與“小”是人為定義的。

例:
整數或小數比較:可以直接比較數值大小。
字母比較:字母在前面的被認為更小,字母在后面的被認為更大。如’a’比’z’小。
字符串比較:按字典序比較,字典序在前的被認為更小,字典序在后面的被認為更大。
數對比較:數對的第一個數字更大的數對更大,數對第一個數字相同時,第二個數字更大的數對更大。

第三,要明確排成升序還是降序。
嚴格意義下,有序序列的順序有以下4種:

  • 升序序列:后面的數據元素大于前面的數據元素。
  • 不降序列:后面的數據元素大于等于前面的數據元素。
  • 不升序列:后面的數據元素小于等于前面的數據元素。
  • 降序序列:后面的數據元素小于前面的數據元素。

以下討論時,使用非嚴格敘述,即用升序表示升序序列及不降序列,用降序表示不升序列及降序序列。

3. 內部排序和外部排序:

  • 內部排序:整個排序過程不需要訪問外存便能完成。
  • 外部排序:待排序的數據元素數量很大,整個序列的排序過程需要借助外存才能完成。
  • 本文只介紹內部排序。

    4. 排序中的關鍵操作

  • 比較:任意兩個數據元素通過比較確定先后順序。
    根據元素間比較規則寫出元素比較的表達式或函數,表達式的值必須是布爾值,表示第一個元素是否比第二個元素小。
  • 交換:兩個數據元素之間數據值發生了交換。
    常用到STL中的swap函數,可以實現任意類型變量值的交換。其實現為:
  • template<class T> void Swap(T &a, T &b)//STL中的函數為swap(),這里為了加以區分,寫為Swap() {T t = a;a = b;b = t; }

    5. 排序算法的穩定性

    假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,那么這個排序算法就是穩定的。
    即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,則稱這種排序算法是穩定的;否則稱為不穩定的。

    5. 對排序的評價

  • 時間復雜度:關鍵性能差異體現在比較和交換的次數
  • 輔助空間復雜度:為完成排序操作需要的額外的存儲空間的大小
    本身存儲數據的存儲空間不算在內,只考慮由于選擇這一排序算法帶來的額外空間開銷。
  • 6. 排序圖示及動畫

    這個已經有很多人做過了,本文不再給出相關圖示及動畫。排序動畫可以在這個網站看:
    visualgo.net排序動畫

    二、排序算法

    n為待排序的元素個數。
    本文以該題為例題:洛谷 P1177 【模板】快速排序
    復雜度為O(n2)O(n^2)O(n2)的排序算法只能通過該題的一個測試點。
    計數排序無法完成該題。
    希爾排序、基數排序以及多數復雜度為O(nlogn)O(nlogn)O(nlogn)的排序算法可以完全通過該題所有測試點。
    該題幾個測試點測試了極端情況,比如序列一開始就是有序的,或都是同一個值。一些排序算法在極端情況下復雜度會退化為O(n2)O(n^2)O(n2),因而可能有幾個點過不了。
    本文只給出下標從1開始的數組的排序算法代碼。下標從0開始的數組的排序算法代碼與之類似,不再贅述。
    本文只給出升序排序算法代碼,降序排序一般只需要將比較符號修改一下即可實現,不再贅述。

    1. 直接選擇排序

    • 基本思想:每一趟從待排序的數據元素中選出最小(或最大)的一個元素,順序放在待排序的數列的最前面,直到全部待排序的數據元素排完。
      第1次在下標1~n中選擇最小值,與下標1的元素進行交換。
      第2次在下標2~n中選擇最小值,與下標2的元素進行交換。
      第i次在下標i~n中選擇最小值,與下標i的元素進行交換。
      第n-1次,在下標n-1~n中選擇最小值,與下標n-1的元素進行交換。
    • 復雜度:時間復雜度:O(n2)O(n^2)O(n2) ?? 額外空間復雜度:O(1)O(1)O(1)
    • 穩定性:不穩定
    • 示例:
      粗體數字為當前要替換的位置,斜體數字為已經確定的有序序列

    8 3 2 6 4 從8到末尾的范圍內,選擇最小值2與8交換
    2 3 8 6 4 從3到末尾的范圍內,選擇最小值3與3交換(實際不變)
    2 3 8 6 4 從8到末尾的范圍內,選擇最小值4與8交換
    2 3 4 6 8 從6到末尾的范圍內,選擇最小值6與6交換(不變)
    2 3 4 6 8 所有數字都已確定位置

    • 代碼:
      寫法1:找出最小值而后交換
    #include <bits/stdc++.h> using namespace std; int main() {int a[100005], n, mi;cin >> n;for(int i = 1; i <= n; ++i)//輸入數據 cin >> a[i];for(int i = 1; i <= n-1; ++i)//直接選擇排序 升序排序 {mi = i;//mi:從i到n的最小值的下標 for(int j = i+1; j <= n; ++j)if(a[j] < a[mi])//如為 > 即可實現降序排序 mi = j;swap(a[i], a[mi]);}for(int i = 1; i <= n; ++i)//輸出數據 cout << a[i] << ' ';return 0; }

    寫法2:不斷和第i位置交換

    #include <bits/stdc++.h> using namespace std; int main() {int a[100005], n;cin >> n;for(int i = 1; i <= n; ++i)//輸入數據 cin >> a[i];for(int i = 1; i <= n-1; ++i)//直接選擇排序 升序排序 for(int j = i+1; j <= n; ++j)if(a[j] < a[i])//如為 > 即可實現降序排序swap(a[i], a[j]);for(int i = 1; i <= n; ++i)//輸出數據 cout << a[i] << ' ';return 0; }

    2. 冒泡排序

    • 基本思想:重復地訪問要排序的元素序列,依次比較兩個相鄰的元素,如果兩元素的順序與預期順序不符,就將二者交換。
      每趟冒泡后,最大(最小)的元素會經由交換“浮”到序列右端(或左端)。多次冒泡后,即可完成排序。
    • 復雜度:時間復雜度:O(n2)O(n^2)O(n2) ?? 額外空間復雜度:O(1)O(1)O(1)
    • 穩定性:穩定
    • 示例
      粗體數字為要比較的兩個數字,斜體數字為已經確定位置的數字

    第1趟冒泡
    8 3 2 6 4
    3 8 2 6 4
    3 2 8 6 4
    3 2 6 8 4
    3 2 6 4 8
    第2趟冒泡
    3 2 6 4 8
    2 3 6 4 8
    2 3 6 4 8
    2 3 4 6 8
    第3趟冒泡
    2 3 4 6 8
    2 3 4 6 8
    2 3 4 6 8
    第4趟冒泡
    2 3 4 6 8
    2 3 4 6 8
    最后,第一個數字的位置自然被確定
    2 3 4 6 8

    • 代碼:
      寫法1:易記版本:i從1到n-1,j從1到n-i
    #include <bits/stdc++.h> using namespace std; int main() {int n, a[100005];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];for(int i = 1; i <= n-1; ++i)//i:需要冒泡n-1次。for(int j = 1; j <= n-i; ++j)//j: 要交換的數對中第一個數的位置。最后一次比較的數對的第一個數字位于n-i位置。if(a[j] > a[j+1])swap(a[j], a[j+1]);for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    寫法2:符合概念的寫法

    #include <bits/stdc++.h> using namespace std; int main() {int n, a[100005];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];for(int i = n; i >= 2; --i)//i:此次冒泡,最大值最后落在的位置。for(int j = 1; j <= i - 1; ++j)//j: 要交換的數對中第一個數的位置if(a[j] > a[j + 1])swap(a[j], a[j + 1]);for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    寫法3:將最小值冒泡到前面的寫法

    #include <bits/stdc++.h> using namespace std; int main() {int n, a[100005];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];for(int i = 1; i <= n - 1; ++i)//i:從后向前冒泡,最小值所在位置 for(int j = n; j >= i + 1; --j)//j:比較數對第二個數的位置 if(a[j] < a[j - 1])swap(a[j], a[j - 1]); for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    3. 插入排序

    • 基本思想:插入排序是指在待排序的元素中,假設前面n-1個數已經是排好順序的,現將第n個數插到前面已經排好的序列中,使得插入后這個序列也是排好順序的。按照此法對所有元素進行插入,直到整個序列排為有序的過程。
    • 復雜度:時間復雜度:O(n2)O(n^2)O(n2) ?? 額外空間復雜度:O(1)O(1)O(1)
    • 穩定性:穩定
    • 示例
      粗體數字為當前正在插入的數字,斜體數字為已經構造好的有序序列
      每次將正在插入的數字和其前面的數字比較

    第1個數字自然構成一個有序序列
    8 3 2 6 4
    插入第2個數字
    8 3 2 6 4
    3 8 2 6 4
    插入第3個數字
    3 8 2 6 4
    3 2 8 6 4
    2 3 8 6 4
    插入第4個數字
    2 3 8 6 4
    2 3 6 8 4
    插入第5個數字
    2 3 6 8 4
    2 3 6 4 8
    2 3 4 6 8
    2 3 4 6 8

    • 代碼
      寫法1:不斷交換元素
    #include <bits/stdc++.h> using namespace std; int main() {int n, a[100005];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];for(int i = 2; i <= n; ++i)//將第i個數字插入有序序列{for(int j = i; j > 1; j--)//j指向待插入數字{if(a[j] < a[j - 1])swap(a[j], a[j - 1]);elsebreak;}}for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    寫法2:用一個變量保存要插入的值

    #include <bits/stdc++.h> using namespace std; int main() {int n, a[100005];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];for(int i = 2; i <= n; ++i)//將第i個數字插入有序序列{int j, num = a[i];//num:待插入數字 for(j = i; j > 1; j--)//j指向待插入數字的位置{if(num < a[j - 1])a[j] = a[j - 1];elsebreak;}a[j] = num;} for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    寫法3:可以一邊輸入一邊做插入排序(常用)

    #include <bits/stdc++.h> using namespace std; int main() {int n, a[100005];cin >> n;for(int i = 1; i <= n; ++i){cin >> a[i];for(int j = i; j > 1; --j){if(a[j] < a[j-1])swap(a[j], a[j-1]);elsebreak;}}for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    4. 希爾排序

    • 基本思想:希爾排序是把數據元素按下標的一定增量分組,對每組使用直接插入排序算法排序。隨著增量逐漸減少,每組包含的元素越來越多,當增量減至 1 時,整個數組就已經完成排序。
    • 復雜度:時間復雜度:O(n1.3)~O(n2)O(n^{1.3}) \sim O(n^2)O(n1.3)O(n2) ?? 額外空間復雜度:O(1)O(1)O(1)
    • 穩定性:不穩定
    • 示例:

    初始增量n/2,為3
    8 3 2 6 4 1??8與6一組,3與4一組,2與1一組,分別做插入排序,結果為:6 3 1 8 4 2
    增量除以2,為1
    6 3 1 8 4 2 ?? 做插入排序,結果為:1 2 3 4 6 8

    • 代碼:
    #include <bits/stdc++.h> using namespace std; int main() {int n, a[100005];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];for(int g = n/2; g > 0; g /= 2)//g:增量{for(int i = g+1; i <= n; ++i){for(int j = i; j > g; j-=g){if(a[j] < a[j-g])swap(a[j], a[j-g]);elsebreak;}}} for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    5. 計數排序

    • 基本概念:
      散列思想:通過散列函數把數據值對應到一個數組下標
      桶排序:將數據通過散列函數分配到有限數量的有序的桶里,每個桶內分別排序,最后將各個桶中的數據合并。
      計數排序:是一種特殊的桶排序。桶的個數是可能出現的數據種類數。
      計數數組:數組下標為數據值,數組的值表示數據個數。即a[i]表示數字i出現的個數。
      計數排序只適用于對范圍有限的整數進行排序。
    • 復雜度:
      數據個數為n,數據范圍為k
      時間復雜度:O(n+k)O(n+k)O(n+k)
      額外空間復雜度:O(k)O(k)O(k)
    • 穩定性:穩定
    • 示例:

    待排序數據:1, 5, 3, 1, 3, 2
    輸入數據并統計后,計數數組c為

    下標12345
    21201

    遍歷計數數組,將i輸出c[i]次,為:1 1 2 3 3 5

    • 代碼
      用計數排序做不了P1177,這里用計數排序完成的問題為:
      給定n個1~1000范圍內的整數(n≤104n \le 10^4n104),輸出其升序序列。
    #include <bits/stdc++.h> using namespace std; int main() {int n, a, c[1005] = {};cin >> n;for(int i = 1; i <= n; ++i){cin >> a;c[a]++;}for(int i = 1; i <= 1000; ++i){for(int j = 1; j <= c[i]; ++j)cout << i << ' ';}return 0; }

    6. 基數排序

    • 基本概念:基數排序是一種整數排序算法,其原理是將整數按每個位數分別比較。相當于多趟桶排序,適合位數有限的整數排序。
    • 復雜度:
      d是數字位數,n是數字個數,r是基數(進制)
      時間復雜度:O(d(n+r))O(d(n+r))O(d(n+r))
      額外空間復雜度:O(r+n)O(r+n)O(r+n)
    • 穩定性:穩定
    • 示例:

    待排序數字:53, 542, 3, 63, 14, 214
    第一趟按個位排序后:542, 53, 3, 63, 14, 214
    第二趟按十位排序后:3, 14, 214, 542, 53, 63
    第三趟按百位排序后:3, 14, 53, 63, 214, 542

    • 代碼:
    #include <bits/stdc++.h> using namespace std; #define N 100005 int countBit(int x)//輸出整數x的位數 {return floor(log10(x)) + 1; } int main() {int a[N], temp[N], n, mxBit = 0, buk[15];//mxBit:數字中的最大位數 cin >> n;for(int i = 1; i <= n; ++i){cin >> a[i];mxBit = max(mxBit, countBit(a[i]));}for(int b = 1, rad = 1; b <= mxBit; ++b, rad *= 10)//每趟針對第b位進行桶排序,位權rad {memset(buk, 0, sizeof(buk));//桶清零 for(int i = 1; i <= n; ++i)//按第b位加入桶中計數 buk[i]表示第b位為i的數字數量 buk[a[i]/rad%10]++;for(int i = 1; i <= 9; ++i)//buk[i]的概念變為第b位為i的數字的最大下標 buk[i] += buk[i-1];for(int i = n; i >= 1; --i)//臨時數組temp的buk[k]位置添加數字a[i],buk[k]作為下一次添加數字的下標,應該減1。由大向小遍歷。 {int k = a[i]/rad%10; temp[buk[k]--] = a[i];}for(int i = 1; i <= n; ++i)a[i] = temp[i]; }for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    7. 歸并排序

    • 基本概念:
  • 合并有序序列
    給定兩個長為a、b的有序序列,合并成長為a+b的有序序列。
  • 歸并排序對序列的元素進行逐層折半分組,然后從最小分組開始合并有序序列,逐層進行,最后得到整個序列的排序結果。
    • 復雜度:
      時間復雜度:O(nlogn)O(nlogn)O(nlogn)
      空間復雜度:O(n)O(n)O(n)
    • 穩定性:穩定
    • 示例:

    待排序數字序列 1 5 6 3 2 7 9 8
    先進行折半分組,對于分組后的每一組,再進行折半分組,直到每組只有1個元素
    1 5 6 3 | 2 7 9 8
    1 5 | 6 3 | 2 7 | 9 8
    1 | 5 | 6 | 3 | 2 | 7 | 9 | 8
    依次對于每次分開的兩組合并有序序列
    1 5 | 3 6 | 2 7 | 8 9
    1 3 5 6 | 2 7 8 9
    1 2 3 5 6 7 8 9

    • 代碼
    #include<bits/stdc++.h> using namespace std; #define N 100005 int a[N], t[N];//t:臨時數組 void mergeSort(int l, int r)//a數組下標l到r進行歸并排序 {if(l >= r)return;int mid = (l + r)/2;mergeSort(l, mid);mergeSort(mid+1, r);int ti = l, li = l, ri = mid+1;//li第一個段數組中的下標 ri第二段數組中的下標 ti臨時數組中的下標while(li <= mid && ri <= r){if(a[li] < a[ri])t[ti++] = a[li++];elset[ti++] = a[ri++];}while(li <= mid)//如果其中一段數組都填入t了,那么將另一段數組剩下的所有數字都填入tt[ti++] = a[li++];while(ri <= r)t[ti++] = a[ri++];for(int i = l; i <= r; ++i) a[i] = t[i]; } int main() {int n;cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];mergeSort(1, n);for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    8. 快速排序

    • 基本概念:
      通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小。分別對這兩部分記錄繼續進行快速排序,以達到整個序列有序。

    • 復雜度:
      時間復雜度:O(nlogn)O(nlogn)O(nlogn)
      額外空間復雜度:O(1)O(1)O(1)

    • 穩定性:不穩定

    • 代碼

    #include<bits/stdc++.h> using namespace std; #define N 100005 int a[N]; void quickSort(int l, int r) { if(l >= r)return;int i = l, j = r;int mid = a[(l+r)/2];//將當前序列在中間位置的數定義為分隔數while(i <= j)//注意這里不能少了等號{while(a[i] < mid) i++; //在左半部分尋找大于等于中間數的數while(a[j] > mid)j--; //在右半部分尋找小于等于中間數的數if(i <= j) //若找到一組與排序目標不一致的數對{ swap(a[i], a[j]);//則交換它們 i++;j--;}}quickSort(l, j);//遞歸搜索左右區間quickSort(i, r); } int main() {int n;cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];quickSort(1, n);for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }

    9. 堆排序

    • 基本概念:堆是一種基于二叉樹的數據結構,借助堆可以在O(logn)O(logn)O(logn)時間復雜度下獲取n個元素中的最值。
    • 復雜度:
      時間復雜度:O(nlogn)O(nlogn)O(nlogn)
      額外空間復雜度:O(1)O(1)O(1)
    • 穩定性:不穩定

    實現堆排序有兩種方法
    方法1:似于選擇排序,構造小頂堆,每次取出所有元素中的最小值,并刪除堆頂。循環n次即可獲得有序序列。

    #include <bits/stdc++.h> using namespace std; #define N 100005 int heap[N], n; //b是否比a高級。堆頂是最高級的元素 bool isPrior(int a, int b) {return a > b;//a > b:小頂堆, a < b:大頂堆 } //第i結點上移 void shiftUp(int i) {if(i == 1)//p是根結點 return;if(isPrior(heap[i/2], heap[i])){swap(heap[i], heap[i/2]);shiftUp(i/2);} } //第i結點下沉 void shiftDown(int i) {if(i > n/2)//如果i是葉子結點return; int sel;//選擇交換的結點位置 if(2*i+1 <= n && isPrior(heap[2*i],heap[2*i+1]))//有右孩子且右孩子值更大 sel = 2*i + 1;else//沒有右孩子或左孩子值更大 sel = 2*i; if(isPrior(heap[i],heap[sel])){swap(heap[sel], heap[i]);shiftDown(sel); } } //建堆 void buildHeap() {for(int i = n/2; i >= 1; --i)shiftDown(i); } //插入元素 void insert(int val) {heap[++n] = val;shiftUp(n); } //刪除元素 void del() {swap(heap[1], heap[n]);n--;shiftDown(1); } //獲取堆頂 int top() {return heap[1]; } int main() {cin >> n;for(int i = 1; i <= n; ++i)cin >> heap[i];buildHeap();while(n > 0)//堆不空 {cout << top() << ' ';del();}return 0; }

    方法2:構造大頂堆

  • 首先將待排序的數組構造成一個大根堆,此時,整個數組的最大值就是堆結構的頂端
  • 將頂端的數與末尾的數交換,此時,末尾的數為最大值,剩余待排序數組個數為n-1
  • 將剩余的n-1個數再構造成大根堆,再將頂端數與n-1位置的數交換,如此反復執行,便能得到有序數組
  • #include <bits/stdc++.h> using namespace std; #define N 100005 int heap[N], n; //b是否比a高級。堆頂是最高級的元素 bool isPrior(int a, int b) {return a < b;//a > b:小頂堆, a < b:大頂堆 } //第i結點上移 void shiftUp(int i) {if(i == 1)//p是根結點 return;if(isPrior(heap[i/2], heap[i])){swap(heap[i], heap[i/2]);shiftUp(i/2);} } //第i結點下沉 void shiftDown(int i) {if(i > n/2)//如果i是葉子結點return; int sel;//選擇交換的結點位置 if(2*i+1 <= n && isPrior(heap[2*i],heap[2*i+1]))//有右孩子且右孩子值更大 sel = 2*i + 1;else//沒有右孩子或左孩子值更大 sel = 2*i; if(isPrior(heap[i],heap[sel])){swap(heap[sel], heap[i]);shiftDown(sel); } } //建堆 void buildHeap() {for(int i = n/2; i >= 1; --i)shiftDown(i); } //插入元素 void insert(int val) {heap[++n] = val;shiftUp(n); } //刪除元素 void del() {swap(heap[1], heap[n]);n--;shiftDown(1); } //獲取堆頂 int top() {return heap[1]; } //堆排序 void heapSort() {int tot = n;for(int i = 1; i <= tot - 1; ++i)del();for(int i = 1; i <= tot; ++i)cout << heap[i] << ' '; } int main() {cin >> n;for(int i = 1; i <= n; ++i)cin >> heap[i];buildHeap();heapSort();return 0; }

    10. 二叉排序樹排序

    • 基本概念
      二叉排序樹或者是一顆空樹,或者是具有如下性質的二叉樹:
  • 若它的左子樹不空,則左子樹上所有結點的值均小于它的根結點的值;
  • 若它的右子樹不空,則右子樹上所有結點的值均大于它的根結點的值;
  • 它的 左、右子樹又分別為二叉排序樹 。
  • 二叉排序樹插入操作:如果要插入的值比當前結點的值大,則看右孩子。若要查找的值比當前結點的值小,則看左孩子。如果要查找的值與當前結點的值相等,則該數值出現的次數加1。如果找到空結點,那么就將其插入到這個位置。
    每次插入操作復雜度:O(logn)O(logn)O(logn)

    輸入n個數字,將每個數字插入到二叉排序樹中,對二叉排序樹做中序遍歷,即可得到有序序列。

    • 復雜度:
      時間復雜度:O(nlogn)O(nlogn)O(nlogn)
      額外空間復雜度:O(1)O(1)O(1)
    • 穩定性:穩定
    • 代碼
    #include <bits/stdc++.h> using namespace std; #define N 100005 struct Node {int val, ct, left, right;//val:值 ct:個數 }; Node node[N]; int p, root; void ins(int r, int val) {if(node[r].val == val)node[r].ct++;else if(node[r].val < val){if(node[r].right == 0){node[++p].val = val;node[p].ct = 1;node[r].right = p;}elseins(node[r].right, val);}else if(node[r].val > val){if(node[r].left == 0){node[++p].val = val;node[p].ct = 1;node[r].left = p;}elseins(node[r].left, val);} } void midOrder(int r)//中序遍歷 {if(r == 0)return;midOrder(node[r].left);for(int i = 1; i <= node[r].ct; ++i) cout << node[r].val << ' ';midOrder(node[r].right); } int main() {int n, v;cin >> n;cin >> v;node[++p].val = v;node[p].ct = 1;root = p;//樹根 for(int i = 2; i <= n; ++i){cin >> v;ins(root, v);}midOrder(root);return 0; }

    11. 排序總結

    三、STL中的排序函數

    1. sort函數

    內部原理是快速排序,不穩定,時間復雜度O(nlogn)O(nlogn)O(nlogn)
    sort(起始元素指針/迭代器,最后一個元素的指針/迭代器的后一個位置, 比較函數);
    對數組a下標1到n進行排序
    sort(a+1,a+1+n, cmp);
    對STL容器a進行排序
    sort(a.begin(), a.end(), cmp);
    若不寫比較函數cmp,默認為升序排序。如果對結構體變量進行排序,也可以通過重載小于號運算符來指定排序規則。
    比較函數的寫法:傳入兩個數組中元素類型的量,返回傳入第一個參數排在前面的條件

    bool cmp(int a, int b) {return a < b;//a < b:升序 a > b:降序 }

    bool cmp(const int &a, const int &b) {return a < b;//a < b:升序 a > b:降序 }

    解決:P1177 【模板】快速排序

    • 用數組保存數據
    #include <bits/stdc++.h> using namespace std; #define N 100005 bool cmp(const int &a, const int &b) {return a < b; } int main() {int n, a[N];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];sort(a+1, a+1+n, cmp);//或sort(a+1, a+1+n) for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }
    • 用vector保存數據
    #include <bits/stdc++.h> using namespace std; #define N 100005 bool cmp(const int &a, const int &b) {return a < b; } int main() {vector<int> vec;int n, a;cin >> n;for(int i = 1; i <= n; ++i){cin >> a;vec.push_back(a);}sort(vec.begin(), vec.end(), cmp); for(int i = 0; i < vec.size(); ++i)cout << vec[i] << ' ';return 0; }

    2. stable_sort函數

    內部原理是歸并排序,穩定,時間復雜度O(nlogn)O(nlogn)O(nlogn)
    stable_sort(起始元素指針/迭代器,最后一個元素的指針/迭代器的后一個位置, 比較函數);
    對數組a下標1到n進行排序
    stable_sort(a+1,a+1+n, cmp);
    對STL容器a進行排序
    stable_sort(a.begin(), a.end(), cmp);
    若不寫比較函數cmp,默認為升序排序。
    比較函數的寫法:傳入兩個數組中元素類型的量,返回傳入第一個參數排在前面的條件

    bool cmp(int a, int b) {return a < b;//a < b:升序 a > b:降序 }

    bool cmp(const int &a, const int &b) {return a < b;//a < b:升序 a > b:降序 }

    解決:P1177 【模板】快速排序

    • 用數組保存數據
    #include <bits/stdc++.h> using namespace std; #define N 100005 bool cmp(const int &a, const int &b) {return a < b; } int main() {int n, a[N];cin >> n;for(int i = 1; i <= n; ++i)cin >> a[i];stable_sort(a+1, a+1+n, cmp);//或sort(a+1, a+1+n) for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0; }
    • 用vector保存數據
    #include <bits/stdc++.h> using namespace std; #define N 100005 bool cmp(const int &a, const int &b) {return a < b; } int main() {vector<int> vec;int n, a;cin >> n;for(int i = 1; i <= n; ++i){cin >> a;vec.push_back(a);}stable_sort(vec.begin(), vec.end(), cmp); for(int i = 0; i < vec.size(); ++i)cout << vec[i] << ' ';return 0; }

    總結

    以上是生活随笔為你收集整理的【君义精讲】排序算法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。