七大排序算法總結

題記:

? 直接插入排序(穩定)-->希爾排序? : 屬于插入排序

? 簡單選擇排序(穩定)-->堆排序 :屬于選擇排序

? 冒泡排序算法(穩定)-->快速排序? :屬于交換排序

?? 歸并排序(穩定)


一.直接插入排序----O(n^2)-----穩定的

? ? ? ? 直接插入排序:是一種將一個記錄插入到已經安排好序的有序表中,從而得到一個新的記錄數增1的有序表。

1.算法流程:

1)初始時, a[0]自成一個有序區, 無序區為a[1, ... , n-1], 令i=1;

2)將a[i]并入當前的有序區a[0, ... , i-1];

3)i++并重復2)直到i=n-1, 排序完成。

時間復雜度:O(n^2)。

示意圖:初始無序數列為 49, 38, 65, 97, 76, 13, 27,49

2. C++實現

void InsertSort(SqList *L){

? ? ? ? ? int i, j;

? ? ? ? ? for( i=2;i<L->length;i++) ?{

? ? ? ? ? ? ? ?if(L->r[i]<L->r[i-1) {

? ? ? ? ? ? ? ? ? ?L->r[0]=L->r[i]; ? ?//L->r[0]是哨兵位置

? ? ? ? ? ? ? ? ? ?for(j=i-1;L->r[j]>L->r[0];j--)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?L->r[j+1]=L->r[j];

? ? ? ? ? ? ? ? ? ?L->r[j+1]=L->r[0];

? ? ? ? ? ? ? ? }

? ? ? ? ? }

}

//插入排序,版本3:用數據交換代替版本2的數據后移(比較對象只考慮兩個元素)

void StraightInsertionSort3(int a[],int n)

{

? ? ? ? for(inti=1; i<n; i++)

? ? ? ? ? ? ? ?for(int j=i-1; i=0 && a[j]>a[j+1] ; ?j--)

? ? ? ? ? ? ? ? ? ? ? ? ? Swap(a[j], a[j+1]);

}

二、冒泡排序算法----O(n^2)---穩定的

? ? ? ? ? 冒泡排序:是一種交換排序,它基本思想是,兩兩比較相鄰的關鍵字,如果反序則交換,直到沒有反序的記錄為止。

算法流程:

1)比較相鄰的兩個元素,如果前面的數據大于后面的數據,就將兩個數據進行交換;這樣對數組第0個元素到第n-1個元素進行一次遍歷后,最大的一個元素就沉到數組的第n-1個位置;

2)重復第2)操作,直到i=n-1。

時間復雜度分析:O(n^2),冒泡排序是一種不穩定排序算法。

冒泡排序的示例:

2. C++實現

void BubbleSort(int p[], int len){

? ? int temp;

? ? for (int i = 0; i < len; i++)

? ? ? ? for (int j = i + 1; j < len; j++){

? ? ? ? ? ? if (p[i]>p[j]){

? ? ? ? ? ? ? ? ?temp = p[i];

? ? ? ? ? ? ? ? p[i] = p[j];

? ? ? ? ? ? ? ?p[j] = temp;

? ? ? ? ? ? ? }

? ? ? ?}

}

三、簡單選擇排序-----O(n^2)-----穩定

? ? ? ? 簡單選擇排序算法:就是通過n-i次關鍵字間的比較,從n-i+1個記錄中選擇出關鍵字最小的記錄,并和第i(1<=i<=n)個記錄作交換。

算法流程:

1)初始時,數組全為無序區a[0, ... , n-1], 令i=0;

2)在無序區a[i, ... , n-1]中選取一個最小的元素與a[i]交換,交換之后a[0, ... , i]即為有序區;

3)重復2),直到i=n-1,排序完成。

時間復雜度分析O(n^2)。

直接選擇排序的示例:

2. C++實現

void SelectSort(int *p, int len)

{

? ? ? ? ?int minIndex, i, j;

? ? ? ? ?for (i = 0; i < len; i++)

? ? ? ? ? ? ?{

? ? ? ? ? ? ? ? ? ?minIndex = i;//記錄最小數值的下標

? ? ? ? ? ? ? ? ? ?for (j = i + 1; j < len; j++)

? ? ? ? ? ? ? ? ? ?{

? ? ? ? ? ? ? ? ? ? ? ? ? if (p[minIndex]>p[j])

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?minIndex = j;

? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ?

? ? ? ? ? ? ? ? ?if (minIndex != i)

? ? ? ? ? ? ? ? ? ?{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int temp;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? temp = p[i];

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? p[i] = p[minIndex];

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?p[minIndex] = temp;

? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? }

}

四、希爾排序算法------O(nlogn)~O(n^2)----不穩定

基本思想:先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序。

算法流程:

1)選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;

2)按增量序列個數k,對序列進行k 趟排序;

3)每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。

時間復雜度:O(n^(1+e))(其中0

希爾排序的示例:

2. C++實現

//希爾排序

void shellSort(int *p, int len){

? ? ? ? ?int i, j, increment, temp;

? ? ? ? ?for (increment = len / 2; increment > 0; increment /= 2){

? ? ? ? ? ? ? ? ?for (i = increment; i < len; i++){

? ? ? ? ? ? ? ? ? ? ? ? ? ?if (p[i] < p[i - increment]){

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? temp = p[i];

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? for (j = i - increment; j >= 0 && p[j] > temp; j -= increment)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? p[j + increment] = p[j];

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? p[j + increment] = temp;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ? ?}

? ? ? ?}

}

五、堆排序-----O(nlogn)-----不穩定

? ? ? ? ? ? ? 堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。

1.堆排序是利用堆(假設利用大頂堆)進行排序的方法;基本思想是,將待排序的序列構造成一個大頂堆。此時,整個序列的最大值就是堆頂的根節點。將它移走(其實就是將它與堆數組的末尾元素交換,此時末尾元素就是最大值)。然后將剩余的n-1個序列重新構造成一個堆,這樣就會得到n個元素中的次大值,如此反復執行,便能得到一個有序序列了。


2. C++實現

//堆排序

void HeapSort(int a[], int n){

//初始化堆,從最后一個有孩子節點的位置開始調整,最后一個有孩子節點的位置為(n-1)/2或n/2

? ? for (int i = n / 2; i >= 0; i--)

? ? ? ? ? ? HeapAdjusting(a, i, n);

? ? for (int i = n - 1; i>0; i--)//從最后一個節點開始進行調整

? ? {

? ? ? ? ? ? ? swap(a[0], a[i]); //每次交換后都要進行調整,交換堆頂元素和最后一個元素

? ? ? ? ? ? ? HeapAdjusting(a, 0, i);

? ? ? ? }

}

void HeapAdjusting(int a[], int root, int len) ? //大頂堆

{

int temp, child;

temp = a[root];

for (child = 2 * root + 1; child < len; child *= 2)

{

? ? ? ? ? ? if (child + 1 < len&&a[child] < a[child + 1])

? ? ? ? ? ? ? ? ? ?child++;

? ? ? ? ? ? if (temp >= a[child])

? ? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? a[root] = a[child];

? ? ? ? ? ? ? root = child;

}

a[root] = temp;

}


小頂堆

我們先看一個大數據top-K示例:

例子:搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255字節。假設目前有一千萬個記錄(這些查詢串的重復度比較高,雖然總數是1千萬,但如果除去重復后,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就是越熱門。),請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。

首先,我們知道這是一個典型的top-K問題。

針對大數據問題進行統計首先應該想到的就是Hash_map。所以第一步就是先遍歷全部的1千萬Query,構建出一個大小為3百萬的Hash_map,其中的key值為某條Query,對應的value值為該條Query的查詢次數。

建好Hash_map以后,我們接下來的問題就是如何在3百萬的Query中找出10個最熱門的Query,也就是要用到排序算法。排序算法中效率最高的時間復雜度為O(n*log(n)),

這是最簡單粗暴的方法,也是最直接的方法。或者我們進一步優化,該題目是要求尋找top-K問題,那么我們可以直接去前K個Query構建一個數組,然后

對其進行排序。遍歷剩余的全部Query,如果某條Query的查詢次數大于數組中最小的一個,將數組中最小的Query剔除,加入這條新的Query。

接著調整數組順序,依次進行遍歷,這樣的最壞情況下的復雜度為O(n*K)。

但是還可以繼續優化尋找top-K的操作,那就是借助小根堆來實現。基于以上的分析,我們想想,有沒有一種既能快速查找,又能快速移動元素的數據結構呢?回答是肯定的,那就是堆。

具體過程是,堆頂存放的是整個堆中最小的數,現在遍歷N個數,把最先遍歷到的k個數存放到最小堆中,并假設它們就是我們要找的最大的k個數,X1>X2...Xmin(堆頂),而后遍歷后續的(n-K)個數,一一與堆頂元素進行比較,如果遍歷到的Xi大于堆頂元素Xmin,則把Xi放入堆中,而后更新整個堆,更新的時間復雜度為logK,如果Xi

一個有關小根堆解決top-K問題的小動畫,請點擊這個鏈接

思想與上述算法二一致,只是算法在算法三,我們采用了最小堆這種數據結構代替數組,把查找目標元素的時間復雜度有O(K)降到了O(logK)。那么這樣,采用堆數據結構,算法三,最終的時間復雜度就降到了O(n*logK),和算法二相比,又有了比較大的改進。

六、歸并排序------O(nlogn)-------穩定


1. 歸并排序:是將兩個(或者兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列都是有序的。然后再把有序子序列合并為整體有序序列。


歸并排序示例

2. C++實現代碼

//merge兩個有序數列為一個有序數列

void MergeArr(int a[], int first, int mid, int last, int temp[])

{

int i = first, j = mid + 1;

int m = mid, n = last;

int k = 0;

//通過比較,歸并數列a和b

while (i <= m && j <= n)

{

? ? ? ? ?if(a[i]<a[j])

? ? ? ? ? ? ? ? ? temp[k++]=a[i++];

? ? ? ? ? else

? ? ? ? ? ? ? ? ? temp[k++]=a[j++];

?}

//將數列a或者b剩余的元素直接插入到新數列后邊

while (i <= m)

? ? ? ? ? ?temp[k++] = a[i++];

while (j <= n)

? ? ? ? ?temp[k++] = a[j++];

? for (i = 0; i<k;i++)

? ? ? ? ? ?a[first+i]=temp[i];

}

//歸并排序

void MSort(int a[], int first, int last, int temp[])

{

? ? ? ? ? if (first<last)

? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ?int mid=(first+last)/2;

? ? ? ? ? ? ? ? ? ? MSort(a, first, mid, temp);

? ? ? ? ? ? ? ? ? ? MSort(a, mid + 1, last, temp);

? ? ? ? ? ? ? ? ? ? ?MergeArr(a, first, mid, last, temp);

? ? ? ? ? }

void MergeSort(int a[], int len)//為了調用方便封裝了一個函數接口

{

? ? ? ?int *temp = new int[len];

? ? ? MSort(a, 0, len - 1, temp);

? ? ? delete temp;

}


歸并排序


歸并排序算法
合并兩個有序序列


主函數調用

七、快速排序------O(nlogn)-------不穩定

? ? ? ? ? ? ? 1. 快速排序:是通過一趟排序將待排序記錄分割成獨立的兩部分,其中一部分記錄的關鍵字比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。


基本思想

2.C++實現代碼

//快速排序

void QSort(int a[], int L, int R)

{

? ? ? ? ?if (L<R)

? ? ? ? {

? ? ? ? ? ? ? ? ? ?int low = L, high = R, pivotkey = a[low];// pivotkey:樞軸

? ? ? ? ? ? ? ? ? while (low<high)

? ? ? ? ? ? ? ? ?{//從右向左找小于基準值a[i]的元素

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? while (low<high&& a[high]>=pivotkey)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?high--;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(low<high)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?a[low++] = a[high];

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //從左向右找大于基準值a[i]的元素

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? while (low<high&& a[high]<pivotkey)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?low++;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(low<high)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? a[high--] = a[low];

? ? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ? ?//將基準值填入最后的坑中

? ? ? ? ? ? ? ? ? a[low] = pivotkey;

? ? ? ? ? ? ? ? ?//遞歸調用,分治法的思想

? ? ? ? ? ? ? ? ?QSort(a, L, low - 1);

? ? ? ? ? ? ? ? QSort(a, low + 1, R);

? ? ? ? ?}

}

void QuickSort(int a[], int len)? //封裝函數接口

{

? ? ? ? ? ? ? ?QSort(a, 0, len - 1);

}


快速排序



主函數調用


后記:

1.

(1)當原表有序或基本有序時,直接插入排序和冒泡排序將大大減少比較次數和移動記錄的次數,時間復雜度可降至O(n);

(2)而快速排序則相反,當原表基本有序時,將蛻化為冒泡排序,時間復雜度提高為O(n^2);

(3)原表是否有序,對簡單選擇排序、堆排序、歸并排序和基數排序的時間復雜度影響不大。

2.

穩定性:排序算法的穩定性:若待排序的序列中,存在多個具有相同關鍵字的記錄,經過排序, 這些記錄的相對次序保持不變,則稱該算法是穩定的;若經排序后,記錄的相對次序發生了改變,則稱該算法是不穩定的。

穩定性的好處:排序算法如果是穩定的,那么從一個鍵上排序,然后再從另

一個鍵上排序,第一個鍵排序的結果可以為第二個鍵排序所用。基數排序就是這樣,先按低位排序,逐次按高位排序,低位相同的元素其順序再高位也相同時是不會

改變的。另外,如果排序算法穩定,可以避免多余的比較。

穩定的排序算法:冒泡排序、插入排序、歸并排序、選擇排序。

不是穩定的排序算法:快速排序、希爾排序、堆排序。

3.

選擇排序算法準則:

每種排序算法都各有優缺點。因此,在實用時需根據不同情況適當選用,甚至可以將多種方法結合起來使用。

選擇排序算法的依據:

影響排序的因素有很多,平均時間復雜度低的算法并不一定就是最優的。相反,有時平均時間復雜度高的算法可能更適合某些特殊情況。同時,選擇算法時還得考慮它的可讀性,以利于軟件的維護。一般而言,需要考慮的因素有以下四點:

(1)待排序的記錄數目n的大小;

(2)記錄本身數據量的大小,也就是記錄中除關鍵字外的其他信息量的大小;

(3)關鍵字的結構及其分布情況;

(4)對排序穩定性的要求。

4.

設待排序元素的個數為n.

(1)當n較大,則應采用時間復雜度為O(n*logn)的排序方法:快速排序、堆排序或歸并排序。

快速排序:是目前基于比較的內部排序中被認為是最好的方法,當待排序的關鍵字是隨機分布時,快速排序的平均時間最短;

堆排序:如果內存空間允許且要求穩定性的;

歸并排序:它有一定數量的數據移動,所以我們可能過與插入排序組合,先獲得一定長度的序列,然后再合并,在效率上將有所提高。

(2)當n較大,內存空間允許,且要求穩定性:歸并排序

(3)當n較小,可采用直接插入或直接選擇排序。

直接插入排序:當元素分布有序,直接插入排序將大大減少比較次數和移動記錄的次數。

直接選擇排序:當元素分布有序,如果不要求穩定性,選擇直接選擇排序。

(4)一般不使用或不直接使用傳統的冒泡排序。


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Pitfall

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2016.6

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

推薦閱讀更多精彩內容

  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,762評論 0 33
  • 概述:排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,740評論 0 15
  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,209評論 0 52
  • 今晚看了一部紀錄片《迪奧與我》,講述的是迪奧的創意總監Raf Smisons加入迪奧之后第一次準備時裝秀的過程。講...
    行壹1閱讀 268評論 0 1
  • 入職第5天,第二周,周一。早上6點起,趕上6:30的832公車,到達黃石東7點。走一站進入廣外,7:20廣外發車。...
    夢海蓉閱讀 100評論 0 0