排序算法
排序是非常常用,非常基本的算法。排序的方法有很多,比如插入排序、選擇排序、希爾排序、歸并排序、快速排序、堆排序。
本次試驗重點實現:希爾排序、歸并排序、快速排序、堆排序
插入排序
簡單說就是每次選未排序的隊列中最小的條目插入到已排序隊列的最后:
選擇排序
選擇排序和插入有點像,是每次從拿未排序中的第一個條目插入到已排序中應該插入的位置:
希爾排序
希爾排序是插入排序的一種,是針對直接插入排序算法的改進。
希爾排序的基本思想是:先取一個小于count的增量increment,把表中Record分為increment組,所有距離為increment的Record為同一組,現在各組中進行直接插入排序(insert_sort),然后減小increment重復分組和排序,知道increment=1,即所有Record放在同一組中進行直接插入排序為止。
【相關實驗】
首先從類表List中派生子表Sortable_list。為了方便定義,我們可以重載這樣的構造函數
[cpp]?view plaincopy
template<class?Record>?? Sortable_list<Record>::Sortable_list(const?Record?A[],int?size)?? {?? ????for(int?i=0;i<size;i++)?? ????????insert(i,A[i]);?? }?? 1.編寫函數shell_sort。函數中我們從首先定義increment=0,觀察題目要求,可以得到在循環中有這樣的關系increment=increment/2;使用循環將每次分組后的每組排列,排列我們再增加函數sort_interval();在每組的排序中使用的直接插入排序,所以可以這樣實現sort_interval:每次定義一個臨時的Sortable_list對象tmp記錄每次分組的小組,對tmp使用insertion_sort,進而我們編寫函數insertion_sort();
2.實現表的排序一個重要的步驟是將Record換到相應位置,即交換,所以編寫函數swap;
3.為了輸出每趟排序結果,我們再編寫一個全局函數print_out,由List的成員函數traverse()調用;調用的過程在置于swap之中,即每次有交換(或看做移動)則看做一趟排序。
相應算法函數如下:
[cpp]?view plaincopy
template<class?Record>?? void?Sortable_list<Record>::shell_sort()?? ? ? ? ?? {?? ????int?increment,????? ????????start;????????? ????increment=count;?? ????do{?? ????????increment=increment/2;?? ????????for(start=0;start<increment;start++){?? ????????????sort_interval(start,increment);???? ????????????traverse(print_out);?? ????????????cout<<endl;?? ????????}?? ????}while(increment>1);?? }?? ?? template<class?Record>?? void?Sortable_list<Record>::sort_interval(int?start,int?increment)?? {?? ????Sortable_list<Record>?temp;?? ????int?j=0;?? ????for(int?i=start;i<size();i=i+increment){?? ????????Record?temp_record;?? ????????retrieve(i,temp_record);?? ????????temp.insert(j,temp_record);?? ????????j++;?? ????}?? ????temp.insertion_sort();?? ????j=0;?? ????for(int?k=start;k<size();k+=increment){?? ????????Record?temp_record;?? ????????temp.retrieve(j,temp_record);?? ????????replace(k,temp_record);?? ????????j++;?? ????}?? }?? template<class?Record>?? void?Sortable_list<Record>::insertion_sort()?? ? ? ? ?? {?? ????int?first_unsorted;?????????????? ????int?position;???????????????????? ????Record?current;?????????????????? ????for(first_unsorted=1;first_unsorted<count;first_unsorted++)?? ????????if(entry[first_unsorted]<entry[first_unsorted-1]){?? ????????????position=first_unsorted;?? ????????????current=entry[first_unsorted];??? ????????????do{?????????????????????? ????????????????entry[position]=entry[position-1];?? ????????????????position--;??????????? ????????????}while(position>0&&entry[position-1]>current);?? ????????????entry[position]=current;?? ????????}?? }?? ?? 【實驗結果】
歸并排序
歸并排序是采用分治的思想將兩個已排序的表合并成一個表
歸并排序算法的基本思想是:先將一個表分成兩個表(當個數是奇數時,使左表的元素比右表多一)。對兩個表分別進行歸并排序,然后將兩個已排序好的表合并。合并的思路就像將兩羅紙牌混成一摞,每次取頂上最小的紙牌。
【相關實驗】
1.仍舊使用上述的Sortable_list
2.根據歸并排序的思想,每次子表仍舊使用歸并排序,可以通過遞歸實現。所以編寫遞歸函數recursive_merge_sort(),要把已排序好的子表合并,所以編寫合并子表的輔助函數merge()
3.為了輸出每趟排序結果,在歸并時merge中加入traverse(print_out) ?//但因為遞歸調用的問題,很多次我們還是看不到過程
[cpp]?view plaincopy
template<class?Record>?? void?Sortable_list<Record>::merge(int?low,int?high)?? {?? ????Record?*tmp=new?Record[high-low+1];?? ????int?index=0;?? ????int?index1=low,mid=(low+high)/2,index2=mid+1;?? ????while(index1<=mid&&index2<=high){?? ????????if(entry[index1]<entry[index2])?? ????????????tmp[index++]=entry[index1++];?? ????????else?? ????????????tmp[index++]=entry[index2++];?? ????}?? ????while(index1<=mid)?? ????????tmp[index++]=entry[index1++];?? ????while(index2<=high)?? ????????tmp[index++]=entry[index2++];?? ????for(index=low;index<=high;index++)?? ????????entry[index]=tmp[index-low];?? ????delete?[]tmp;?? ????traverse(print_out);?? ????cout<<endl;?? ?????? }?? ?? template<class?Record>?? void?Sortable_list<Record>::recursive_merge_sort(int?low,int?high)?? ? ? ? ? ?? {?? ????if(high>low){?? ????????recursive_merge_sort(low,(high+low)/2);?? ????????recursive_merge_sort((high+low)/2+1,high);?? ????????merge(low,high);?? ????}?? }?? ?? template<class?Record>?? void?Sortable_list<Record>::merge_sort()?? ? ? ? ?? {?? ????recursive_merge_sort(0,size()-1);?? }?? 【實驗結果】
快速排序
快速排序是對冒泡排序的一種改進。
快速排序算法的基本思想是:每一趟排序中找一個點pivot,將表分割成獨立的兩部分,其中一部分的所有Record都比pivot小,另一部分比pivot大,然后再按此方法對這兩部分數據分別進行快速排序。?
【相關實驗】
1.仍舊使用上述的Sortable_list。
2.根據快速排序的思想,每趟排序將表分成兩部分然后仍舊進行快速排序,所以可以通過遞歸實現,而為了調用遞歸函數,我們首先編寫給定要排序范圍的參數的函數recursive_quick_sort(int low,int high)//之所以不將開始的quick_sort直接寫作遞歸函數,是為了避免輸入參數而方便用戶調用。另外需要一個partition函數,返回每趟排序之后的分點。
3.為了輸出每趟排序,我的想法是在每次遞歸中使用traverse(print_out)輸出,但卻不是想象的結果。因為遞歸是每次遞歸出來之后才執行函數print_out,除了前幾次可以看到結構,后來都是在排序好之后…所以我們仍舊通過swap函數實現輸出。
[cpp]?view plaincopy
template<class?Record>?? int?Sortable_list<Record>::partition(int?low,int?high)?? ? ? ? ? ? ? ? ? ?? {?? ????Record?pivot;?? ????int?i,????????????? ????????last_small;???? ????swap(low,(low+high)/2);?? ????pivot=entry[low];?? ????last_small=low;?? ????for(i=low+1;i<=high;i++)?? ?????? ?????? ?????? ???????if(entry[i]<pivot){?? ???????????last_small=last_small+1;?? ???????????swap(last_small,i);???? ???????}?? ????swap(low,last_small);????? ????return?last_small;?? }?? ?? template<class?Record>?? void?Sortable_list<Record>::recursive_quick_sort(int?low,int?high)?? ? ? ? ? ?? {?? ????int?pivot_postion;?? ????if(low<high){?? ????????pivot_postion=partition(low,high);?? ????????recursive_quick_sort(low,pivot_postion-1);?? ????????recursive_quick_sort(pivot_postion+1,high);?? ????}?? }?? ?? template<class?Record>?? void?Sortable_list<Record>::quick_sort()?? {?? ????recursive_quick_sort(0,count-1);?? }?? ?? 【實驗結果】
堆排序
堆排序是先將表中Record按大堆(或小堆)存放,使選取最大的Record變的極為容易,每次選取之后再提升堆。實現排序。
繼續:
【相關實驗】
1.仍舊使用上述的Sortable_list。
2.編寫heap_sort()函數。按思路,首先應該建堆,然后取堆頂元素,之后對剩下元素重新建堆(提升堆),所以我們需要編寫build_heap()函數,其通過inster_heap函數將元素一個個插入堆中。
最后實現heap_sort函數。
3.我們在每次插入堆時調用traverse(print_sort)實現對每趟排序的輸出。
[cpp]?view plaincopy
template<class?Record>?? void?Sortable_list<Record>::insert_heap(const?Record?¤t,int?low,int?high)?? ? ? ? ? ? ?? {?? ????int?large;????????? ????large=2*low+1;????? ????while(large<=high){?? ????????if(large<high&&entry[large]<entry[large+1])?? ????????????large++;?????? ????????if(current>=entry[large])?? ????????????break;???????? ????????else{????????????? ????????????entry[low]=entry[large];?? ????????????low=large;?? ????????????large=2*low+1;?? ????????}?? ????}?? ????entry[low]=current;?? ????traverse(print_out);?? ????cout<<endl;?? }?? ?? template<class?Record>?? void?Sortable_list<Record>::build_heap()?? ? ? ? ?? {?? ????int?low;?? ????for(low=count/2-1;low>=0;low--){?? ????????Record?current=entry[low];?? ????????insert_heap(current,low,count-1);?? ????}?? }?? ?? template<class?Record>?? void?Sortable_list<Record>::heap_sort()?? ? ? ? ?? {?? ????Record?current;????????? ????int?last_unsorted;?????? ????build_heap();??????????? ????for(last_unsorted=count-1;last_unsorted>0;last_unsorted--){?? ????????current=entry[last_unsorted];????? ????????entry[last_unsorted]=entry[0];???? ????????insert_heap(current,0,last_unsorted-1);???? ????}?? }?? ?? 【實驗結果】
結果分析
【希爾排序】
1.希爾排序是直接插入的一種改進,在效率上較直接插入排序有較大的改進。
直接插入排序每次只能將Record移動一個位置,即增量increment為1,而希爾排序開始時增量較大,分組較多,每組Record數目少,故各組內直接插入較快;increment遞減之后分組逐漸減少,Record增多,但由于已經在increment較大時進行過排序,表更接近于有序狀態,新一趟的排序也較快。
2.我在實驗中子表的排序是通過定義一個新表,然后調用直接插入函數。但這種方法效率并不高,而且浪費空間;直接對子表進行直接插入的排序是一種更好的方法。
3.希爾排序復雜度為:O(nlog2n) ? d =1希爾與直接插入排序基本一致
4.希爾排序是不穩定的排序算法,即相等元素的順序可能改變
【歸并排序】
1.歸并排序在實現上用鏈式表更為合理,因為合并中需要定義新的表,即使我們通過動態定義再刪除,以節省不必要的空間,這些工作仍是有些費時。而鏈式表只是返回指針,對節點Node的Node<Node_entry>*next部分進行操作,不需要數據的移動。但同時鏈式表的使用也需要對指針有熟悉的掌握,很容易出錯,先畫圖再編碼往往會有更清晰的思路。
2.歸并排序的復雜度為:O(nlog2n)
3.歸并排序是穩定的排序算法,即相等元素的順序不會改變
【快速排序】
1.復雜度:最好 O(nlog2n),最壞 O(n2)
2.快速排序的最壞情況基于每次劃分對主元的選擇。基本的快速排序選取第一個元素作為主元。這樣在數組已經有序的情況下,每次劃分將得到最壞的結果。一種比較常見的優化方法是隨機化算法,即隨機選取一個元素作為主元。這種情況下雖然最壞情況仍然是O(n^2),但最壞情況不再依賴于輸入數據,而是由于隨機函數取值不佳。
3.快速排序是一種不穩定的排序算法
【堆排序】
1.實現堆排序很重要的一個操作就是建堆
要將初始表調整為一個大根堆,就必須將它所對應的完全二叉樹中以每一結點為根的子樹都調整為堆。顯然只有一個結點的樹是堆,而在完全二叉樹中,所有序號大于n/2的結點都是葉子,因此以這些結點為根的子樹均已是堆。這樣,我們只需依次將以序號為n/2,…,1的結點作為根的子樹都調整為堆即可。
2.堆[排序的時間,主要由建立初始堆和反復重建堆這兩部分的時間開銷構成,堆排序的最壞時間復雜度為O(nlog2n)。
3.堆排序是不穩定的排序算法
4.由于建初始堆所需的比較次數較多,所以堆排序不適宜于記錄數較少的文件。
堆排序每次在大堆中直接選擇出頂即最大的元素,這與選擇排序極為相似,但他們是不同的,堆排序的性能更為優越。因為選擇排序中,為了從表中選出最小的記錄,必須進行n-1次比較,然后在剩余表中選出關鍵字最小的記錄,又需要做n-2次比較。事實上,后面的n-2次比較中,有許多比較可能在前面的n-1次比較中已經做過,但由于前一趟排序時未保留這些比較結果,所以后一趟排序時又重復執行了這些比較操作。堆排序可通過樹形結構保存部分比較結果,可減少比較次數。
(轉載請注明作者和出處:http://blog.csdn.net/xiaowei_cqu?未經允許請勿用于商業用途)
總結
以上是生活随笔為你收集整理的排序算法:希尔、归并、快速、堆排序的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。