演算法01 排序演算法小結

2021-10-18 07:56:14 字數 4626 閱讀 6298

排序演算法是一模擬較基礎的演算法,也是在學習程式設計與演算法的過程中必須學習的一類問題。初學者經常在排序時摸不著頭腦,面對一眾的排序,不知從何處下手。下面筆者將以筆記的形式分享一下我在學習演算法時整理的一些排序演算法。

假設現有亂序陣列:5, 2, 7, 4, 6, 1, 8, 我們將其排序為公升序陣列,各種方法過程如下:

1.氣泡排序。

氣泡排序是最簡單的一種排序手段,也是新手最容易想到的一種演算法。它通過每一項與其後面的每一項依次比較,找到最大(最小)值並交換位置,經過一次遍歷,即可將陣列排序。其過程大致如下:

首先提取第乙個元素5, 與它後面每一項進行比較,找到最小項1,並與其交換位置,得到陣列:1, 2, 7, 4, 6, 5, 8。 

隨後的第二個元素2, 經比較後發現沒有比2更小的項,陣列不變。

再依次提取7, 4, 6, 5,分別與它們後面的每一項進行對比,找到最小項並交換位置,最終陣列將按照公升序排序,即1, 2, 4, 5, 6, 7, 8。

其**如下:

1

for (int i = 0; i < n - 1; i++)

2for (int j = i + 1; j < n; j++)

3if (arr[i] >arr[j])

4

由執行過程可以看到,氣泡排序執行中需要雙層巢狀迴圈,一旦需要排序的陣列過長,其效率也隨之迅速降低。

在日常使用中,筆者僅在小型陣列的排序中使用這種演算法,因為其**簡單直接,工程量比較小,**也易於維護。  

2.並歸排序。

並歸排序採用的是分治思想,利用函式的遞迴,將龐大的排序問題逐級化簡,最後並歸得到有序的陣列。它的思想是將乙個陣列一分為二,再將兩個子陣列分別排序,再依次比較陣列的首項,將較小值依次放入原陣列中,最終得到有序陣列。其過程大致如下:

首先將陣列拆分為array1:5, 2, 7, 4, 和array2:6, 1, 8, 分別對這兩個陣列進行排序:array1:2, 4, 5, 7, 和array2:1, 6, 8。

再將兩個陣列並歸為乙個陣列:首先比較兩個陣列的首項:由於1 < 2,將array2中的1提取,歸入原陣列。再比較兩個陣列的首項:由於2 < 6,本次提取值為2。。。以此類推。同時,為了防止陣列發生越界問題,需要在陣列末項之後人為新增乙個無窮項,保證該項大於陣列中的每一項,從而防止陣列越界的現象發生。經過並歸過程後,原陣列排序完成。

但是這裡會引出乙個問題,該如何排序這兩個子陣列呢?這裡便需要用到遞迴的思想。我們可以讓函式呼叫它自身從而完成對拆分開來的子項的排序。

但這樣又會產生接下來的問題,該如何控制遞迴結束?這裡由於最後無窮項的存在,當陣列長度為2時,陣列內有效值便只有乙個,在此時,遞迴結束,將這項返回,交由函式下面的內容完成並歸操作。

其**如下:

1

void merge(int* arr, int

n)  //由於採用指標傳遞的方式,函式將直接對原位址進行操作,故函式被定義為無返回值220

else

2125

}26   delete (arr1);  //釋放由new申請的記憶體

27   delete (arr2);

28 }

並歸排序採用了遞迴的方式,由於無需雙重迴圈,其執行效率相較氣泡排序在處理長陣列時有所提公升。但是由於其**相對複雜,筆者在日常使用中不常使用這種演算法。

3.堆排序。

堆排序利用了堆的性質,通過維護乙個最大堆或最小堆,提取堆頂元素放入原陣列完成排序。

這裡首先要理解二叉堆的性質:二叉堆是乙個陣列,可以近似為乙個完全二叉樹。 

如圖為乙個最大堆,a為展開為樹的形態,b為陣列形態。由堆的對應關係容易得到,任意節點的下標除以2即可得到其父節點的下標,而父節點下標乘2可獲得子節點的左節點,乘2加1可獲得右節點,而這個最大堆的根是最大值。

若要維護堆的性質,需要自上而下比較,找到父節點與兩個子節點中的最大項,將最大的值與父節點交換位置,直到到堆的底層結束。

而建立堆的過程就是對堆的每乙個節點執行維護堆的過程。而每次取出根節點,便需要重新執行維護堆,以確保堆始終為最大堆。

其**如下:

1

void heap(int* arr, int

n)  //利用堆進行排序,筆者傳值方式採用指標,無返回值212

}1314void heapify(int* arr, int n, int

size)  //維護堆的性質

1532 }

堆排序同樣利用了遞迴的思想,效率相對較高,其主要時間消耗在建堆的過程,後期僅需要迴圈取值,重新維護堆即可。但是堆排序亦或是建堆,可以用於決策演算法等其它演算法,這是堆的獨特優勢,因此我們在大型專案可以重複利用建堆**,發揮其獨到優勢,減少工程量。

4.快速排序。

快速排序,顧名思義它是排序速度最快的排序方式。它也利用了遞迴的思想。同並歸排序類似,它通過將陣列一分為二,兩側分別排序,從而達到排序的目的。但與並歸不同的是,快速排序沒有後期並歸的過程。在拆分陣列的過程中,快速排序可以做到一側的最大值小於另一側的最小值,故最後無需比較,直接連線即可。其思想如下:

首先取陣列末尾值為中間值,對陣列前面的所有值依次比較,小於該中間值的向前移動,大於的向後移動。即,當乙個值小於中間值,它會與大於中間值部分的下標最小的值進行交換,同時令大於中間值部分的下標加一,實現大於中間支部分的移動。而當乙個值大於中間值時,將大於中間值部分擴張包含該值即可。當大於中間值部分移動到中間值處,將中間值移動至兩個子陣列之間。至此,中間值左側所有值都小於中間值,中間值右側所有值都大於中間值。將中間值兩側的陣列再次分別排序,直到陣列不可在分,排序結束。

其**如下:

1

void quick(int* arr, int m, int

n)  //快速排序遞迴部分29

}1011int partition(int* arr, int m, int

n)  //快速排序處理陣列部分

1224}25

int tmp = arr[i + 1

];        //將中間值插入兩個子數列之間

26 arr[i + 1] =arr[n];

27 arr[n] =tmp;

28return i + 1

;             //返回中間值

29 }

快速排序是執行效率極高的一種排序演算法,在處理大型陣列時極其有效,而且演算法消耗的記憶體少,適合對效能有要求的專案。當然,對於小型陣列的排序很難體現快速排序的優越性,微小型陣列排序更多還是冒泡等相對簡單的排序比較方便開發。

5.線性時間排序。

線性時間排序是一類特殊的排序演算法,這類演算法不是簡單的依賴比較陣列元素的大小,而是巧妙地利用陣列的下標對其進行排序。其時間消耗隨著需要排序的陣列的特點會有所變化,有時效率會很高,甚至超過快速排序,而有時效率則一般。但是這類演算法普遍的特點是會有記憶體消耗,記憶體的開銷會大於快速排序,因此僅適用於特定資料的排序,不如快速排序普適。

計數排序

計數排序適用於乙個連續正整數陣列,陣列的連續性越強,其效率也就越高。

計數排序巧妙地將陣列的元素當作陣列下標,開闢乙個新陣列,利用陣列元素充當新陣列的下標,利用新陣列的值來計數,最後直接將新陣列的下標從小到大按統計的數量輸出,完成排序。

其**如下:

1

void count(int* arr, intn)2

21for (int i = 0; i < n; i++)

22 arr[i] =tmp[i];

23delete

(tmp);    //釋放空間

24delete

(count);

25 }

基數排序:

基數排序同樣適用於連續正整數陣列,與計數排序不同的是,基數排序陣列最大值位數越低,其排序效率越高。

基數排序也是將陣列元素作為下標,依次統計在每一位上的資料的數量,經過多次迴圈排序統計後,可以得到有序陣列。

其**如下:

1

void radix(int* arr, intn)2

13int* tmp = new

int[n];       //用於儲存中間資料

14int* count = new

int[10

];     //用於位數的統計

15for (int i = 0, radix = 1; i <= digit; i++, radix *= 10)16

24for (int j = 1; j < 10; j++)

25 count[j] += count[j - 1

];26

for (int j = n - 1; j >= 0; j--)  //將一輪排列的值寫入中間陣列

2732

for (int j = 0; j < n; j++)    //將中間陣列值寫回

33 arr[j] =tmp[j];34}

35delete

(tmp);    //釋放空間

36delete

(count);

37 }

以上就是筆者最近統計的一些排序演算法,同時筆者也在不斷學習其它的演算法。歡迎指正。

(演算法參考自《演算法導論(原書第三版)》--機械工業出版社)

演算法01 排序演算法小結

排序演算法是一模擬較基礎的演算法,也是在學習程式設計與演算法的過程中必須學習的一類問題。初學者經常在排序時摸不著頭腦,面對一眾的排序,不知從何處下手。下面筆者將以筆記的形式分享一下我在學習演算法時整理的一些排序演算法。假設現有亂序陣列 5,2,7,4,6,1,8,我們將其排序為公升序陣列,各種方法過...

喝著JAVA看演算法 01 排序演算法之(1)氣泡排序

氣泡排序,可以說是最經典的排序演算法了,博主畢業那年去各大公司面試,被要求寫過多次氣泡排序,現在想想感慨良多 在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較小的往上冒,像冒泡一樣。即 每當兩相鄰的數比較後發現它們的排序與排序要求相...

四 排序演算法

1.快速排序 procedure qsort l,r integer var i,j,mid integer begin i l j r mid a l r div 2 repeat while a i mid do dec j if i j then begin swap a i a j inc ...