八大演算法總結之穩定性的比較及演算法的選擇
各種演算法的穩定性、時間複雜度、空間間複雜度的分析:
1.穩定:氣泡排序、直接插入排序、二分插入排序、歸併排序、基數排序和桶排序。
2.不穩定:直接選擇排序、快速排序、希爾排序、堆排序。
3.O(n^2):直接插入排序、簡單選擇排序、氣泡排序。
4.O(nlogn):快速排序、歸併排序、希爾排序、堆排序。
排序演算法的選擇:
1.資料規模較小
(1)序列基本有序,宜用直接插入排序。
(2)對穩定性不作要求,宜用簡單選擇排序,對穩定性有要求,宜用插入排序或氣泡排序。
2.資料規模一般
(1)序列雜亂無序,對穩定性沒有要求,宜用快速排序。
(2)序列基本有序,對穩定性有要求,空間允許下,宜用歸併排序。
3.資料規模較大
(1)對穩定性有求,宜用歸併排序。
(2)對穩定性沒要求,宜用堆排序。
4.序列初始基本有序(正序)
宜用直接插入,冒泡。
1、氣泡排序
演算法步驟:
1)比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
2)對每一對相鄰元素重複第一步,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
3)針對所有的元素重複以上的步驟,除了最後一個。
4)持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
氣泡排序示例:
程式碼示例:
void Bubble_Sort(int array[ ],int n) { int i,j,temp; for(int i;i<n-1;i++) { for(j=0;j<n-1-i;j++) { if(array[j]>array[j+1]) { temp=array[j]; array[j]=array[j+1]; arrray[j+1]=temp; } } } }
冒泡演算法的改進:<1>比如現在有一個 4 1 2 3 5 6 的數列,如果是按上面的排序演算法排序的話,第一趟排序後數列已經排序好了,不用做無謂的工作了。所以第一種改進演算法是設定一個標誌性變數pose,用於記錄每趟排序中最後一次進行交換的位置。由於pose位置之後的記錄均已交換到位,所以在進行下一趟排序時,只要掃描到pose的位置即可;
改進後的演算法:
void Bubble_Sort(int array[],int n) { int j,temp; int i=n-1; while(i>0){ int pose=0; for(j=0;j<i;j++) { if(array [j] > array[j+1]) { pose=j; temp = array[j]; array[j]=array[j+1]; array[j+1]=temp; } } i=pose; } }
冒泡演算法的改進:<2>傳統的氣泡排序演算法每一趟排序操作只能找到一個最大值或最小值,可以在每趟排序中進行正向和反向兩遍冒泡的方法中一次得到最小值和最大值,從而使排序的趟數幾乎減少一半
2、快速排序
演算法步驟:
1)從數列中挑出一個元素,稱為 “基準”(pivot)。
2)重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽退出之後,該基準就處於數列的中間位置。這個稱為分割槽(partition)操作。
3)遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
遞迴的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞迴下去,但是這個演算法總會退出,因為在每次的迭代(iteration)中,它至少會把一個元素擺到它最後的位置去。
二、插入類排序
1.直接插入排序
演算法步驟:
1)將第一待排序序列第一個元素看做一個有序序列,把第二個元素到最後一個元素當成是未排序序列。
2)從頭到尾依次掃描待排序列,將掃描到的每個元素插入有序序列的適當位置。(如果待插入的元素與有序序列中的某個元素相等,則將待插入元素插入到相等元素的後面。)
2、二分插入排序
演算法步驟:
1)將待排序序列第一個元素與有序序列最中間的元素比較,然後根據比較結果將待排序列折半。
2)重複第一步直到找到適當位置。
3、希爾排序
希爾排序的基本思想是:先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序。
演算法步驟:
1)選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
2)按增量序列個數k,對序列進行k 趟排序;
3)每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。
三、選擇類排序
1、簡單選擇排序
演算法步驟:
1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
2)再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。
3)重複第二步,直到所有元素均排序完畢。
2、堆排序
堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
堆排序的平均時間複雜度為Ο(nlogn) 。
演算法步驟:
1)建立一個堆H[0..n-1]
2)把堆首(最小值)和堆尾互換
3)把堆的尺寸縮小1,目的是把新的陣列頂端資料調整到相應位置
4) 重複步驟2,直到堆的尺寸為1
四、合併類排序
1、歸併排序
演算法步驟:
1)申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合併後的序列
2)設定兩個指標,最初位置分別為兩個已經排序序列的起始位置
3)比較兩個指標所指向的元素,選擇相對小的元素放入到合併空間,並移動指標到下一位置
4)重複步驟3直到某一指標達到序列尾
5)將另一序列剩下的所有元素直接複製到合併序列尾
2、桶排序
桶排序 (Bucket sort)或所謂的箱排序,工作的原理是將陣列分到有限數量的桶子裡。每個桶子再個別排序(有可能再使用別的排序演算法或是以遞迴方式繼續使用桶排序進行排序)。桶排序是鴿巢排序的一種歸納結果。當要被排序的陣列內的數值是均勻分配的時候,桶排序使用線性時間(O(n))。但桶排序並不是 比較排序,他不受到 O(n log n) 下限的影響。
演算法步驟:
1)首先,可以把桶設為大小為10的範圍
2)將資料放到其對應的桶內
3)對每個桶中的元素排序
五、基數排序
基數排序(Radix sort)是一種非比較型整數排序演算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較。由於整數也可以表達字串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是隻能使用於整數。
基數排序時間複雜度: O(d(n+k)) n個d位數,每個數位有k種取值
演算法步驟:
1)將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零.
2)從最低位開始, 依次進行一次排序.
這樣從最低位排序一直到最高位排序完成以後, 數列就變成一個有序序列.