生活随笔
收集整理的這篇文章主要介紹了
排序算法总结与C代码
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
??最近參加筆試,感覺排序算法需要好好的整理一下,感覺部分排序算法理解得不是很清楚;通過這段時間的整理與總結來對排序算法的一個復習吧。
? ? ? ? 主要參考了《大話數據結構》:
1. 冒泡排序的基本思想:兩兩比較相鄰記錄的關鍵字,如果反序則交換,直到沒有反序的記錄為止。
[cpp]?view plaincopy
#define?length?10?? ?? void?swap(int?*a,int?*b)?? {?? ????int?temp;?? ????temp=*a;?? ????*a=*b;?? ????*b=temp;?? }?? ?? void?BubbleSort0(int?a[],int?len)???? {?? ????int?i,j;?? ????for?(i=0;i<len;i++)?? ????{?? ????????for?(j=i+1;j<len;j++)?? ????????{?? ????????????if?(a[i]>a[j])?? ????????????????swap(&a[i],&a[j]);?? ????????}?? ????}?? }?? 冒泡算法的改進1,主要講相鄰兩個數比較
[cpp]?view plaincopy
void?BubbleSort1(int?a[],int?len)?? {?? ????int?i,j;?? ????for?(i=0;i<len;i++)?? ????{?? ????????for(j=len-2;j>=i;j--)?? ????????{?? ????????????if?(a[j]>a[j+1])?????? ????????????????swap(&a[j],&a[j+1]);?? ????????}?? ????}?? }?? 冒泡算法的進一步優化,使用一個標志位來記錄是否有數據的交換,如果數據有交換,則將標志flag賦值為true
[cpp]?view plaincopy
void?BubbleSort2(int?a[],int?len)?? {?? ????int?i,j;?? ????int?flag=1;?? ????for?(i=0;i<len?&&flag;i++)?? ????{?? ????????flag=0;?? ????????for?(j=len-2;j>=i;j--)?? ????????{?? ????????????if?(a[j]>a[j+1])?? ????????????{?? ????????????????swap(&a[j],&a[j+1]);?? ????????????????flag=1;?? ????????????}?? ????????}?? ????}?? }?? 冒泡排序復雜度分析:當最好的情況(序列已經有序),根據最后的改進的代碼,只有n-1次的比較,沒有數據之間的交換,時間復雜度為O(n);當最壞的情況下(序列為逆序),此時需要比較n(n-1)/2,并且還需要進行等數量級的記錄移動,因此總的時間復雜度為O(n^2)
2.簡單選擇排序 簡單選擇排序:就是通過n-i次關鍵字間的比較,從n-i+1個記錄中選出關鍵字最小的記錄,并和第i個記錄交換:
[cpp]?view plaincopy
void?SelectSort(int?a[],int?len)?? {?? ????int?i,j,min;?? ????for?(i=0;i<len;i++)?? ????{?? ????????min=i;?? ????????for?(j=i+1;j<len;j++)?? ????????{?? ????????????if?(a[min]>a[j])?? ????????????????min=j;?? ????????}?? ????????if?(i!=min)?? ????????????swap(&a[min],&a[i]);?? ????}?? }?? 簡單選擇排序復雜度分析:它的比較次數一定:n(n-1)/2。也因此無論在序列何種情況下,它都不會有優秀的表現,可見對數據的有序性不敏感。它雖然比較次數多,而對于交換次數而言,當最好的時候,交換為0次,最壞的時候交換次數為n-1次,所以數據交換量卻很少,基于時間復雜度是比較與交換次數的總和,因此總的時間復雜度依然為O(n^2)。盡管與冒泡排序算法的時間復雜度相同,但是簡單選擇排序的性能上還是略優于冒泡排序。
3.直接插入排序 直接插入排序的基本操作是將一個記錄插入到已經排好序的有序表中,從而得到一個新的記錄數增1的有序表
[cpp]?view plaincopy
void?InsertSort(int?a[],int?len)?? {?? ????int?i,j,temp;?? ????for?(i=1;i<len;i++)?? ????{?? ????????if?(a[i]<a[i-1])?? ????????{?? ????????????temp=a[i];?? ????????????for?(j=i-1;a[j]>temp;j--)?? ????????????????a[j+1]=a[j];?? ????????????a[j+1]=temp;?? ????????}?? ????}?? }?? 直接插入排序復雜度分析:
每次比較后最多移掉一個逆序,因此與冒泡排序的效率相同。但它在速度上還是要高點,這是因為在冒泡排序下是進行值交換,而在插入排序下是值移動,所以直接插入排序將要優于冒泡排序。直接插入法也是一種對數據的有序性非常敏感的一種算法。在有序情況下只需要經過n-1次比較,由于每次都是a[j]>a[j-1]因此沒有移動記錄,時間復雜度為O(n);在最壞情況下,將需要(n+2)(n-1)/2次比較,并且將移動次數也達到最大值(n+4)(n-1)/2。如果排序記錄是隨機的,根據概率相同原則,平均比較和移動次數約為你(n^2)/4,所以直接插入排序的時間復雜度為O(n^2). 4.希爾排序
希爾排序的基本思想:先將整個待排記錄序列分割成為若干子序列分別進行直接插入排序,并使整個序列中的記錄基本有序,再對全體記錄進行一次排序。
[cpp]?view plaincopy
?? void?ShellSort(int?a[],int?len)?? {?? ????int?i,j,temp,increment;?? ????increment=len;?? ????do?? ????{?? ????????increment=increment/3+1;?? ????????for?(i=increment;i<len;i++)?? ????????{?? ????????????if(a[i]<a[i-increment])?? ????????????{?? ????????????????temp=a[i];?? ????????????????for?(j=i-increment;j>=0&&a[j]>temp;j-=increment)??? ????????????????{?? ????????????????????a[j+increment]=a[j];?? ????????????????}?? ????????????????a[j+increment]=temp;?? ????????????}?? ????????}?? ????}while(increment>1);?? }?? 希爾排序復雜度分析:希爾排序的“增量”的選取非常關鍵,大量的研究表示,當增量序列為2^(t-k+1)-1(0=<k=<t=<log2(n+1))時,可以獲得不錯的效果,其時間復雜度為O(n^1.5)。增量的選擇將影響希爾排序的效率。但是無論怎樣選擇增量,最后一定要使增量為1,進行一次直接插入排序。但它相對于直接插入排序,由于在子表中每進行一次比較,就可能移去整個經性表中的多個逆序,從而改善了整個排序性能。希爾排序算是一種基于插入排序的算法,所以對數據有序敏感。
5.堆排序
堆排序的思想:首先將待排序列構建為大頂堆(大頂堆:每個結點的值大于或等于其左右孩子結點的值)。此時整個序列的最大值就是堆頂的根結點。將它移走(其實就是講其與堆數組的末尾元素交換,此時末尾元素就是最大值),然后將剩余的n-1個子序列重新構造成一個堆,這樣就會得到n個元素中的次大值。如此反復執行,便得到一個有序序列。
[cpp]?view plaincopy
void?HeapAdjust(int?a[],int?s,int?m)?? {?? ????int?temp,j=0;?? ????temp=a[s];?? ????for?(j=2*s+1;j<m;j=j*2+1)????? ????{?? ????????if?(j<m?&&?a[j]<a[j+1])?? ????????????++j;?? ????????if?(j<m?&&?temp>=a[j])?? ????????????break;?? ????????a[s]=a[j];?? ????????s=j;?? ????}?? ????a[s]=temp;?? }?? [cpp]?view plaincopy
void?HeapSort(int?a[],int?len)?? {?? ????int?i;?? ????for?(i=(len)/2-1;i>=0;i--)?? ????????HeapAdjust(a,i,len);?? ????for?(i=len-1;i>=1;i--)?? ????{?? ????????swap(&a[0],&a[i]);?? ????????HeapAdjust(a,0,i-1);?? ????}?? }?? 堆排序的復雜度分析:在構建堆的過程中,因為是完全二叉樹從最下層最后邊的非終端結點開始構建,將它與其孩子進行比較和若有必要的交換,對于每個非終端結點來說,其中最多進行兩次比較和互換操作,因此整個構建堆的時間復雜度為O(n)。
? ? 在正式排序時,第i次取堆頂記錄重建堆需要用O(logi)的時間(完全二叉樹的某個結點到根節點的距離為[logi]+1),并且需要取n-1次堆頂記錄,因此重建堆的時間復雜度為O(nlogn)。
? ? 所以總體來說,堆排序的時間復雜度為O(nlogn)。由于堆排序對原始記錄的排序狀態并不敏感,因此無論最好、最壞平均時間復雜度均為O(nlogn)。所以一般在小規模的序列中不合適,但對于較大的序列,將表現出優越的性能。
6.歸并排序
歸并排序思想:假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度的長度為1,然而兩兩歸并,得到[n/2]([x]表示不小于x的最小整數)個長度為2或1的有序子序列;再兩兩歸并,.....,如此重復,直至得到一個長度為n的有序序列為止。
[cpp]?view plaincopy
?? void?Merge(int?SR[],int?TR[],int?i,int?m,int?n)?? {?? ????int?j,k,l;?? ????for?(j=m+1,k=i;i<=m?&&?j<=n;k++)???? ????{?? ????????if?(SR[i]<SR[j])?? ????????????TR[k]=SR[i++];?? ????????else??? ????????????TR[k]=SR[j++];?? ????}?? ????if?(i<=m)???? ????{?? ????????for?(l=0;l<=m-i;l++)?? ????????????TR[k+l]=SR[i+l];?? ????}?? ????if?(j<=n)?????? ????{?? ????????for?(l=0;l<=n-j;l++)?? ????????????TR[k+l]=SR[j+l];?? ????}?? }?? ?? void?MSort(int?SR[],int?TR1[],int?s,int?t)?? {?? ????int?m=0;?? ????int?TR2[length+1]={0};?? ????if?(s==t)?? ????????TR1[s]=SR[s];?? ????else?? ????{?? ????????m=(s+t)/2;?? ????????MSort(SR,TR2,s,m);?? ????????MSort(SR,TR2,m+1,t);?? ????????Merge(TR2,TR1,s,m,t);?? ????}?? }?? 歸并排序復雜度分析:一趟歸并需要將SR[0]~SR[n-1]中相鄰的長度為h的有序序列進行兩兩歸并。并將結果放到TR[0]~TR[n-1]中,這需要將待排序列中的記錄掃描一遍,因此耗費O(n)時間,而由完全二叉樹的深度可知,整個歸并排序需要進行[log(n)]次,因
此,總的時間復雜度為O(nlogn),而且這是歸并排序算法中最好、最壞、平均的時間性能。并且歸并排序是一種穩定的排序算法。對數據的有序性不敏感。若數據節點數據量大,那將不適合。但可改造成索引操作,效果將非常出色。 對于歸并排序的非遞歸實現在此就不在說明。
7.快速排序
快速排序的思想:通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。
[cpp]?view plaincopy
int?Partiton(int?a[],int?low,int?high)?? {?? ????int?pivotkey;?? ????pivotkey=a[low];?? ????while?(low<high)?? ????{?? ????????while?(low<high?&&a[high]>=pivotkey)?? ????????????high--;?? ????????swap(&a[high],&a[low]);?? ????????while?(low<high?&&?a[low]<=pivotkey)?? ????????????low++;?? ????????swap(&a[high],&a[low]);?? ????}?? ????return?low;?? }?? ?? int?Partiton1(int?a[],int?low,int?high)?? {?? ????int?temp,pivotkey;?? ????pivotkey=a[low];?? ????temp=pivotkey;?? ????while?(low<high)?? ????{?? ????????while?(low<high?&&a[high]>=pivotkey)?? ????????????high--;?? ????????a[low]=a[high];?? ????????while?(low<high?&&?a[low]<=pivotkey)?? ????????????low++;?? ????????a[high]=a[low];?? ????}?? ????a[low]=temp;?? ????return?low;?? }?? ?? void?Qsort(int?a[],int?low,int?high)?? {?? ????int?pivot;?? ????if?(low<high)?? ????{?? ????????pivot=Partiton1(a,low,high);?? ????????Qsort(a,low,pivot-1);?? ????????Qsort(a,pivot+1,high);?? ????}?? ?? }?? 快速排序復雜度分析:
它同樣是冒泡排序的改進,它通過一次交換能消除多個逆序,這樣可以減少逆序時所消耗的掃描和數據交換次數。在最優情況下,它的排序時間復雜度為O(nlog2n)。即每次劃分序列時,能均勻分成兩個子串。但最差情況下它的時間復雜度將是O(n^2)。即每次劃分子串時,一串為空,另一串為m-1(如果程序中采用每次取序列中部數據作為劃分點,那將在正序和逆時達到最優)。有的書上這解釋“快速排序”,在理論上講,如果每次能均勻劃分序列,它將是最快的排序算法,因此稱它作快速排序。雖然很難均勻劃分序列,但就平均性能而言,它仍是基于關鍵字比較的內部排序算法中速度最快者。 按平均時間將排序分為類:?
(1)?平方階(O(n2))排序
各類簡單排序,例如直接插入、簡單選擇和冒泡排序;?
(2)?線性對數階(O(nlog2n))排序
如快速排序、堆排序和歸并排序;?
(3)?O(n1+§))排序
§是介于0和1之間的常數。希爾排序便是一種;
排序方法的選擇
因為不同的排序方法適應不同的應用環境和要求,所以選擇合適的排序方法很重要
(1)若n較小,可采用直接插入或直接選擇排序。
當記錄規模較小時,直接插入排序較好,它會比選擇更少的比較次數;
但當記錄規模較大時,因為直接選擇移動的記錄數少于直接插人,所以宜用選直接選擇排序。
這兩種都是穩定排序算法。
(2)若文件初始狀態基本有序(指正序),則應選用直接插人、冒泡或隨機的快速排序為宜(這里的隨機是指基準取值的隨機,原因見上的快速排序分析);這里快速排序算法將不穩定。
(3)若n較大,則應采用時間復雜度為O(nlog2n)的排序方法:快速排序、堆排序或歸并排序序。
快速排序是目前基于比較的內部排序中被認為是最好的方法,當待排序的關鍵字是隨機分布時,快速排序的平均時間最短;
堆排序雖不會出現快速排序可能出現的最壞情況。但它需要建堆的過程。這兩種排序都是不穩定的。
?歸并排序是穩定的排序算法,但它有一定數量的數據移動,所以我們可能過與插入排序組合,先獲得一定長度的序列,然后再合并,在效率上將有所提高。
總結
以上是生活随笔為你收集整理的排序算法总结与C代码的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。