本文用于记录基本排序的算法,毕竟博主是个健忘的人emm......
先来总结一下寒假生活吧,不想看的童鞋可以直接跳过这部分嘤嘤嘤...还记得放寒假时的激动,是因为自己感觉真的能有自己独立的时间去学习算法了,但现在看来emm还是太年轻了......
寒假完成了寒假任务的不到1/4,寒假给4个亲戚补课你敢信?还好女朋友懂事给我留了好几百块钱钱现在想起还是很欣慰......不过还是得怪自己没能管得住自己,依稀记得自己发吃鸡9连杀截图
时候的嚣张和不屑emmm谁可知身边的同学都在默默努力学习而我却用电脑吃鸡,emm打住吧...今天是开学的第一天,算了一下留给自己学习算法的时间并不算多了,所以就希望自己能够管住
自己,和身边的同学一起沉下心来好好训练,毕竟自己的梦想是区域赛银......
emm我记得上个学期总结过排序,但是那时候感觉做的题目很少而且现在已经不在状态了所以现在又来重新整理一下...这次主要是跟着红宝书又学一遍排序。
一:排序
选择排序:首先,找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小的那么就和他自己交换)。再次,在剩下的元素中找到最小的元素,将他
和数组的第二个元素交换位置。如此往复,直到整个数组有序。

1 void Seletion_Sort(int *a, int len) { 2 for(int i = 0; i < len; i ++) { 3 int min = i; 4 for(int j = i + 1; j < len; j ++) 5 if(a[j] < a[min]) min = j; 6 swap(a[i], a[min]); 7 } 8 }
插入排序:对于1 ~ n - 1 之间的每一个i,将a[i] 与 a[0]到 a[i - 1]中比他小的所有元素依次有序的交换。在索引i由左向右变化的过程中,他左侧的元素总是有序的,所以当i到达数组的右端时
排序就完成了。

1 void Insert_Sort(int *a, int len) { 2 for(int i = 1; i < len; i ++) { 3 for(int j = i; j > 0 && a[j] < a[j - 1]; j --) 4 swap(a[j], a[j - 1]); 5 } 6 }
希尔排序:和插入排序思想类似,但是相比之下更适用于大型数组。和插入排序的差别是初始化一个增量,通过将增量逐渐减至一的方法逐渐实现插入排序,将插入排序的优势最大化。每次
将a[i] 插入到 a[i - h] ,a[ i - 2 * h] ...之中。

1 void Shell_Sort(int *a, int len) { 2 int h = 1;//增量 3 while(h < len / 3) h = 3 * h + 1;// h = 1, 4, 13, 40, 121, 364, 1093, ... 4 while(h >= 1) { 5 for(int i = h; i < len; i ++) { 6 for(int j = i; j >=h && a[j] < a[j - h]; j -= h) 7 swap(a[j], a[j - h]); 8 } 9 h /= 3; 10 } 11 }
归并排序:
① :原地归并的抽象方法:该方法是将所有待排序元素先存储起来,然后再归并到原数组中。

1 void Merge(int *a, int left, int mid, int right) { 2 int i = left, j = mid + 1; 3 for(int k = left; k <= right; k ++) 4 aux[k] = a[k]; 5 for(int k = left; k <= right; k ++) { 6 if(i > mid) a[k] = aux[j ++]; 7 else if(j > right) a[k] = aux[i ++]; 8 else if(aux[j] < aux[i]) a[k] = aux[j ++]; 9 else a[k] = aux[i ++]; 10 } 11 }
②: 自顶向下的归并排序:要对子数组进行排序,先将它分为两部分,分别通过递归将它们视为子数组再进行单独排序,最终将有序的子数组归并成最终的排序结果。

1 void MergeSort1(int *a, int left, int right) { 2 if(left >= right) return; 3 int mid = left + (right - left) / 2; 4 MergeSort1(a, left, mid); 5 MergeSort1(a, mid + 1, right); 6 Merge(a, left, mid, right);//Merge在上述原地归并实现 7 }
③: 自底向上的归并排序:首先我们进行两两合并,然后是四四合并,然后是八八合并,一直下去。

1 void MergeSort2(int *a, int len) {//自底向上 2 for(int sz = 1; sz < len; sz *= 2)//sz为子数组的大小 3 for(int left = 0; left < len - sz; left += 2 * sz)//left为子数组的索引 4 Merge(a, left, left + sz - 1, min(left + sz + sz - 1, len - 1)); 5 }
快速排序:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序
过程可以递归进行,以此达到整个数据变成有序序列。

1 int Median3(int *a, int Left, int Right) { 2 int Center = Left + (Right - Left) / 2;//避免数据类型溢出 3 if(a[Left] > a[Center]) 4 swap(a[Left], a[Center]); 5 if(a[Left] > a[Right]) 6 swap(a[Left], a[Right]); 7 if(a[Center] > a[Right]) 8 swap(a[Center], a[Right]); 9 // Invariant : a[Left] <= a[Center] <= a[Right] 10 swap(a[Center], a[Right - 1]);//Hide pivot 11 return a[Right - 1];// Return pivot 12 } 13 14 #define CutoffRange (10) 15 16 void QuicklySort(int *a, int Left, int Right) { 17 int pivot; 18 if(Left + CutoffRange <= Right) { //当待排序元素个数多于CutoffRange时采用快速排序 19 pivot = Median3(a, Left, Right);//选取枢纽元 20 //注意下方对i和j的操作为++ i, ++ j操作,即跳过了第一个和最后一个元素,这是因为在进行三数取中法的时候待排序的第一个和最后一个数已经在它正确的位置上了 21 int i = Left, j = Right - 1; 22 while(true) {//将数组中小于pivot的元素放到左边,大于pivot的元素放到右边 23 while(a[++ i] < pivot); 24 while(a[-- j] > pivot); 25 if(i < j)//当同时找到不满足上述条件的两个值时,将其交换就是最好的选择 26 swap(a[i], a[j]); 27 else 28 break; 29 } 30 swap(a[i], a[Right - 1]);//最后将枢纽元放到他在数组中的正确位置 31 QuicklySort(a, Left, i - 1); 32 QuicklySort(a, i + 1, Right); 33 } 34 else 35 Insert_Sort(a + Left, Right - Left + 1); 36 }
堆排序:这里采取先下沉后上浮的方法,每次将最大元素推至堆底,然后再上浮到正确的位置,直至排序完毕。

1 void sink(int *a, int k, int len) {//由上至下堆有序化 2 while(2 * k <= len) { 3 int j = 2 * k; 4 if(j < len && a[j] < a[j + 1]) j ++;//选取其中较大的儿子和父亲交换 5 if(a[k] >= a[j]) break; 6 swap(a[k], a[j]);//将沿路的所有值都进行比较,最终将最大值浮至堆底 7 k = j; 8 } 9 } 10 11 void HeapSort(int *a, int len) {//注意,为了方便起见,使用堆排序时从数组的一号下标开始存数,零号下标不会参与排序 12 for(int k = len / 2; k >= 1; k --) 13 sink(a, k, len);//初始化,i从最后一个父节点开始调整,使堆有序,堆的最大值在堆底 14 while(len > 1) {//初始状态下根元素为最大元素,将它放到数组的最后,然后再重新调整根底,再将根底的最大值放到次大的位置,直至排序结束,先下沉后上浮 15 swap(a[1], a[len --]); 16 sink(a, 1, len); 17 } 18 }