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 }