2018-12-10-17:22:29
1.排序
定義 : 排序是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列。分內部排序和外部排序,若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序。反之,若參加排序的記錄數量很大,整個序列的排序過程不可能在內存中完成,則稱此類排序問題為外部排序。內部排序的過程是一個逐步擴大記錄的有序序列長度的過程。
分類 :
2.具體排序算法描述
① 冒泡排序:
算法描述:
將數組分為無序和有序兩部分,初始時無序數列的邊界下標為總數組的長度減去一的位置,從無序數列的第一個數開始做交換,如果發現這個數比下一個數大則交換這兩個數,直至無序數列的最后一個數為一輪交換,這輪交換使得無序數列的最大元素放到了它邊界值的下一個位置,每完成一輪交換都更新一次無序數列的邊界下標,直至整個數組都變為有序則完成整個排序。
優劣:1 void Babble_Sort1(ElementType *array, int length) { 2 for(int i = 0; i < length - 1; i ++) 3 for(int j = 0; j < length - 1 - i; j ++) 4 if(array[j] > array[j + 1]) 5 swap(array[j], array[j + 1]); 6 }
1 void Babble_Sort2(ElementType *array, int length) { 2 bool IsSorted; 3 int LastPosition;//記錄每回合最后一次交換位置的元素的下標 4 int SortBorder = length - 1;//記錄無序數列的邊界 5 for(int i = 0; i < length - 1; i ++) { 6 IsSorted = true; 7 for(int j = 0; j < SortBorder; j ++) 8 if(array[j] < array[j + 1]) { 9 swap(array[j], array[j + 1]); 10 IsSorted = false; 11 LastPosition = j; 12 } 13 SortBorder = LastPosition; 14 if(IsSorted) 15 break; 16 } 17 }
② 直接插入排序:
算法描述:
1.將原數組分為未排序和已排序兩部分。
2.從第一個元素開始,該元素可以認為已經被排序;
3.取出下一個元素,在已經排序的元素序列中從后向前掃描;
4.如果該元素(已排序)大於新元素(待插入),將該元素移到下一位置;
5.重復步驟4,直到找到已排序的元素小於或者等於新元素的位置;
6.將新元素插入到該位置后;
7.重復步驟3~6。
算法分析:
1 void Insertion_Sort(ElementType *array, int length) { 2 int i, j; 3 ElementType Tmp; 4 for(i = 1; i < length; i ++) { 5 Tmp = array[i]; 6 for(j = i; j > 0 && array[j-1] > Tmp; j --) 7 array[j] = array[j-1]; 8 array[j] = Tmp; 9 } 10 }
③ 選擇排序:
算法描述:
將數組分為無序和有序兩個部分,起初視數組都為無序,每次讓數組無序部分的第一個元素作為擂主,讓其后的元素開始打擂,每次打至數組的最后一個元素,如果擂主變了(后續數組中存在比擂主小的元素)
,則將擂主歸並作為有序部分的最后一個元素並按照上述規則繼續打擂,直至數組的最后一個元素。
優劣:
1 void Seletion_Sort(ElementType *array, int length) { 2 int index; 3 for(int i = 0; i < length - 1; i ++) { 4 index = i; 5 for(int j = i + 1; j < length; j ++) 6 if(array[j] > array[index]) 7 index = j; 8 if(index != i) 9 swap(array[index], array[i]); 10 } 11 }
改進思路:
每次打擂只能找出無序數組元素中最大(或最小)的元素,可以考慮每次找出最大和最小的元素,減少循環的次數,從而提高查找的效率。
二元選擇排序
c++代碼:
1 void Double_Seletion_Sort(ElementType *array, int length) { 2 int MinPos, MaxPos;//這里我們將i看做是擂主,j看作是打擂者 3 for(int i = 0; i < length / 2; i ++) { 4 MinPos = MaxPos = i;//讓擂主同時與最大最小值打比賽 5 for(int j = i + 1; j < length - i; j ++) { 6 if(array[j] > array[MaxPos]) { 7 MaxPos = j; 8 continue;//如果發現array[j] > array[MaxPos]則其一定不會小於array[MinPos] 9 } 10 if(array[j] < array[MinPos]) 11 MinPos = j; 12 } 13 if(MinPos != i)//將新星擂主納入有序序列的最后一位,如果擂主沒變則不用納入 14 swap(array[i], array[MinPos]); 15 if(MaxPos == i)//如果擂主位置為最大值,則剛剛交換最小值時已經將最大值換到了最小值打雷成功的打擂者身上即(MinPos) 16 MaxPos = MinPos; 17 if(MaxPos != length - 1 - i)//如果擂主不是無序部分最后一位則將其與最后一位交換,納入有序序列 18 swap(array[length - 1 - i], array[MaxPos]); 19 } 20 }
④ 歸並排序:
定義:
歸並排序(MERGE-SORT)是建立在歸並操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合並,得到完全有序的序列;即先使每個
子序列有序,再使子序列段間有序。若將兩個有序表合並成一個有序表,稱為二路歸並。
算法描述:
1.把長度為n的輸入序列分成兩個長度為n/2的子序列;
2.對這兩個子序列分別采用歸並排序;
3.將兩個排序好的子序列合並成一個最終的排序序列。
優劣:
1 void MergeSort(ElementType *array, ElementType *TmpArray, int Left, int Right) { 2 int Center; 3 if(Left < Right) { 4 Center = Left + (Right - Left) / 2; //避免數據類型溢出 5 MergeSort(array, TmpArray, Left, Center); 6 MergeSort(array, TmpArray, Center + 1, Right); 7 Merge(array, TmpArray, Left, Center + 1, Right); 8 } 9 } 10 11 void Merge(ElementType *array, ElementType *TmpArray, int LPos, int RPos, int RightEnd) { 12 int LeftEnd = RPos - 1, TmpPos = LPos, NumElements = RightEnd - LPos + 1; 13 while(LPos <= LeftEnd && RPos <= RightEnd) { 14 if(array[LPos] <= array[RPos]) 15 TmpArray[TmpPos ++] = array[LPos ++]; 16 else 17 TmpArray[TmpPos ++] = array[RPos ++]; 18 } 19 while(LPos <= LeftEnd) 20 TmpArray[TmpPos ++] = array[LPos ++]; 21 while(RPos <= RightEnd) 22 TmpArray[TmpPos ++] = array[RPos ++]; 23 for(int i = 0; i < NumElements; i ++, RightEnd --) //Copy TmpArray back 24 array[RightEnd] = TmpArray[RightEnd]; 25 }
⑤ 快速排序:
基本思想:
通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到
整個數據變成有序序列。
算法描述:
1.如果需排序元素大於指定數目則執行快速排序,否則執行其它簡單排序。(一般元素多於10個時進入快速排序,事實證明,當元素個數過少時使用快速排序會比其它程序慢的多,並且這里我們選擇三值取
中法,如果元素個數過少會產生未預知的錯誤)。
2.利用三值取中法獲取一個樞紐元(於此同時將樞紐元放在待排序序列的最后一位)。
3.將數組分為三個不相交的集合,即x < pivot , pivot , x > pivot 。
4.對x < pivot執行上述1,2,3操作, pivot, 對 x > pivot執行上述1,2,3操作。
優劣:ElementType Median3(ElementType *array, int Left, int Right) { int Center = Left + (Right - Left) / 2;//避免數據類型溢出 if(array[Left] > array[Center]) swap(array[Left], array[Center]); if(array[Left] > array[Right]) swap(array[Left], array[Right]); if(array[Center] > array[Right]) swap(array[Center], array[Right]); // Invariant : array[Left] <= array[Center] <= array[Right] swap(array[Center], array[Right - 1]);//Hide pivot return array[Right - 1];// Return pivot } #define CutoffRange (10) void QuicklySort(ElementType *array, int Left, int Right) { ElementType pivot; if(Left + CutoffRange <= Right) { //當待排序元素個數多於CutoffRange時采用快速排序 pivot = Median3(array, Left, Right);//選取樞紐元 //注意下方對i和j的操作為++ i, ++ j操作,即跳過了第一個和最后一個元素,這是因為在進行三數取中法的時候待排序的第一個和最后一個數已經在它正確的位置上了 int i = Left, j = Right - 1; while(true) {//將數組中小於pivot的元素放到左邊,大於pivot的元素放到右邊 while(array[++ i] < pivot); while(array[-- j] > pivot); if(i < j)//當同時找到不滿足上述條件的兩個值時,將其交換就是最好的選擇 swap(array[i], array[j]); else break; } swap(array[i], array[Right - 1]);//最后將樞紐元放到他在數組中的正確位置 QuicklySort(array, Left, i - 1); QuicklySort(array, i + 1, Right); } else Double_Seletion_Sort(array + Left, Right - Left + 1); }
⑥希爾排序:
1959年Shell發明,第一個突破O(n2)的排序算法,是簡單插入排序的改進版。它與插入排序的不同之處在於,它會優先比較距離較遠的元素。希爾排序又叫縮小增量排序。
算法思路:先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序。
算法描述:
1.選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
2.按增量序列個數k,對序列進行k 趟排序;
3.每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。
1 void ShellSort(ElementType *array, Length length) { 2 int i, j, Increment = 1; 3 ElementType Tmp; 4 while(Increment < length / 3) Increment = 3 * Increment + 1;//找到起初最大的間隔序列之首 5 while(Increment >= 1) { 6 for(i = Increment; i < length; i ++) { 7 Tmp = array[i];//用來保存待插入元素 8 for(j = i; 9 j >= Increment && array[j - Increment] > Tmp; 10 j -= Increment)//將原數組以Increment為間隔分開,然后對每部分分別進行插入排序 11 array[j] = array[j - Increment]; 12 array[j] = Tmp; 13 } 14 Increment /= 3; 15 } 16 }