手撕各种排序
> 作者简介:დ旧言~,目前大一,现在学习Java,c,c++,Python等
> 座右铭:松树千年终是朽,槿花一日自为荣。> 目标:掌握每种排序的方法,理解每种排序利弊和复杂度。
> 毒鸡汤:船停在码头是最安全的,但那不是造船的目的;人呆在家里是最舒服的,但那不是人生的意义。最美好的生活方式,莫过于和一群志同道合的人奔跑在理想的路上!
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
🌟前言
谈起排序这个事情,大家都是耳熟能详的事了,从我们认识的斗地主,每一复牌都是按照从小到大的顺序排序的,如图:
排序的目的是为了让我们很好的管理,使无序的事情变成有序,当然排序的方法有很多,如由大到小,由大到小.....。而面对大量数据就需要排序。在数据结构中我们发明了多种排序,如我们最早认识的冒泡排序,本篇博客让大家对多种排序有一个很好的认知,闲话少谈。
⭐主体
咱们就掌握八种就行啦
🌙冒泡排序
这里博主在C语言刷题训练专栏中讲到过冒泡排序,咱们再回顾回顾
这里我们以接口的形式写代码,那咱们上代码咯
//冒泡排序测试
void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int exchange = 0;for (int j = 1; j < n - i; j++){if (a[i - 1] > a[i]){//这里是一个交换函数Swap(&a[i - 1], &a[i]);exchange = 1;}}//这里进行一趟有序时,直接跳出循环if (exchange == 0){break;}}
}
注意事项:
💦1.我们知道当给的数据有序时,就不再需要进行循环了,直接跳出循环(exchange作用)。
💦2. 第二个循环中 j < n - i 不能搞错。
时间复杂度:O(N²)
空间复杂度:O(1)
稳定性:稳定
复杂性:简单
🌙直接插入排序


这里我们以接口的形式写代码,那咱们上代码咯
//插入排序
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];//一个元素进行插入while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];}else{break;}--end;}//最后把插入的元素赋值回去a[end + 1] = tmp;}
}
注意事项:
💦1.我们先进行第end个元素移动,再进行n个元素进行移动。
💦2. 最后需要a[end + 1] = tmp赋值
时间复杂度:O(N²)
空间复杂度:O(1)
稳定性:稳定
复杂性:简单
🌙希尔排序
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。咱们看看下面的图解:
在看看一个动态的图解:
这里我们以接口的形式写代码,那咱们上代码咯
//希尔排序测试
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){//间隔gap的元素进行排序gap = gap / 3 + 1;//本质上是插入排序for (int i = 0; i < n - gap; ++i){int end = i;int tmp = a[end + gap];//一个元素进行插入while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end = end - gap;}else{break;}}//最后把插入的元素赋值回去a[end + gap] = tmp;}}
}
注意事项:
💦1.首先先嵌套一个插入排序,再把分组的元素进行交换。
💦2. gap = gap / 3 + 1这个不要忘记。
时间复杂度:O(N¹·²³)
空间复杂度:O(1)
稳定性:不稳定
复杂性:复杂
🌙选择排序
基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。咱们看看图解:
这里我们看一个动态的图解:
上代码:
//选择排序测试
void SelectSort(int* a, int n)
{//初始化第一个元素 和 最后一个元素int begin = 0;int end = n - 1;while (begin < end){//选出最大值和最小值int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; i++){if (a[i] > a[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}//最小值和最初的元素交换Swap(&a[begin], &a[mini]);//如果max被换走就需要重新赋值if (maxi == begin){maxi = mini;}//最大值和最末的元素交换Swap(&a[end], &a[maxi]);++begin;--end;}
}
注意事项:
💦1.这里先找到最小值和最大值,然后交换到最前面和最后面,一次进行。
💦2. 如果最大值被交换后,需要从新赋值。
时间复杂度:O(N²)
空间复杂度:O(1)
稳定性:不稳定
复杂性:简单
🌙堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。咱们看看图解:
再看看动态的图解:
上代码:
//堆排序测试
//实现向下调整建大堆
void AdjustDown(int* a, int n, int parent)
{//孩子和父亲的关系 int child = parent * 2 + 1;while (child < n){//找出小的那个孩子if (child + 1 < n && a[child + 1] < a[child]){++child;}//实现向下调整元素if (a[child] < a[parent]){//元素交换Swap(&a[child], &a[parent]);// 继续往下调整parent = child;child = parent * 2 + 1;}else{break;}}
}//堆排序测试
void HeapSort(int* a, int n)
{//向下调整建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){//实现向下调整建大堆AdjustDown(a, n, i);}int end = n - 1;while (end > 0){//元素交换Swap(&a[0], &a[end]);//实现向下调整建大堆AdjustDown(a, end, 0);--end;}
}
注意事项:
💦1.首先我们需要建大堆(以在二叉树讲解咯),交换,再建大堆
💦2. 每交换一个元素都需要再建一次大堆。
时间复杂度:O(NlogN)
空间复杂度:O(1)
稳定性:不稳定
复杂性:复杂
🌙快排
在快排这个板块中我将讲述四个方法,希小伙伴都能掌握。
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
💫Hoare方法
在动态图中,key称为基准值,默认把基准值定义在数组的首元素上,其次为了加快遍历的速度,需要用到两个变量分别去对应数组的首尾部分,L处在数列的首部,它需要从左往右寻找比Keyi位置的值大的,遇到后就停下来,没遇到就一直走;R处在数列的尾部,它需要从右往左去寻找比keyi位置的值小的,也是遇到后就停下来,没遇到就一直走。
当L与R都遇到停下来后开始互换位置,然后继续遍历,直到L==R时,双方都不会再走了,因为它们走到了同一个位置,这个位置被称为它俩的相遇点,之后便需要将keyi位置的值与相遇点的值互换位置,这样keyi位置的值就放到了中间,成为了数组的中心——基准值,它意味着将数组分为两个子序列,左序列全是小于Keyi的值,右序列全是大于Keyi的值,这样便可以利用递归,一层一层再对左右两个子序列进行相同的步骤——将一个大的无序序列转化为多个小的序列,从而进行排序,最终排列为完全有序的序列。
上代码:
//三数取中 (找出中间值)
int GetMidi(int* a, int left, int right)
{//中间值int mid = (left + right) / 2;//三个数比较找出中间值if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}// 右边找小,左边找大,交换,把左边的值跟a[keyi]交换
int PartSort1(int* a, int left, int right)
{//找中间值(相对)int midi = GetMidi(a, left, right);//把最左边(相对)跟中间值(相对)换Swap(&a[left], &a[midi]);//定位最左边(相对)int keyi = left;while (left < right){//找小while (left < right && a[right] >= a[keyi]){--right;}//找大while (left < right && a[left] <= a[keyi]){++left;}//左右交换Swap(&a[left], &a[right]);}//此时左边走到中间了,开始交换Swap(&a[keyi], &a[left]);//返回return left;
};
注意事项:
💦1.首先先找到中间值。
💦2.再和最左边元素互换💦3.左边找比(中间值)大的数,右边找(中间值)小的数,然后左右值交换。
💦4.此时左边走到中间了,开始交换
💦5.上述进行循环,知道排序完成
💫挖坑法
大家就看一下下面图解:
//三数取中 (找出中间值)
int GetMidi(int* a, int left, int right)
{//中间值int mid = (left + right) / 2;//三个数比较找出中间值if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}//挖坑法
int PartSort2(int* a, int left, int right)
{//找中间值(相对)int midi = GetMidi(a, left, right);//把最左边(相对)跟中间值(相对)换Swap(&a[left], &a[midi]);//定位最左边(相对)int key = a[left];//保存key值以后,左边形成第一个坑int hole = left;while (left < right){//右边先走,找小,填到左边的坑,右边形成新的坑位while (left < right && a[right] >= key){--right;}a[hole] = a[right];hole = right;//左边再走,找大,填到右边的坑,左边形成新的坑位while (left < right && a[left] <= key){++left;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}
💫前后指针
通过创建两个指针,prev指针指向数组序列首元素位置,cur指向prev的下一个位置,cur通过遍历去寻找比key基准值小的,若找到了,还得看prev的下一个位置是否为cur所处的位置,若prev的下一个位置确实为cur所处位置,则将cur指向的值与prev指向的值进行互换,若prev的下一个位置不是cur所处位置,则cur继续往后遍历,直到cur遍历结束,最后要将key基准值与prev指向的值进行互换,最终确认基准值处于数组序列的中间位置。
//三数取中 (找出中间值)
int GetMidi(int* a, int left, int right)
{//中间值int mid = (left + right) / 2;//三个数比较找出中间值if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}//前后指针
int PartSort3(int* a, int left, int right)
{//找中间值(相对)int midi = GetMidi(a, left, right);//把最左边(相对)跟中间值(相对)换Swap(&a[left], &a[midi]);//定义指针开头int prev = left;//定义指针开头后一个int cur = prev + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev], &a[cur]);}++cur;}//交换Swap(&a[prev], &a[keyi]);return prev;
}
💫快排非递归
1.初始化我们的栈。
2.入栈把我们的开始的left==0 right==n-1;
3.进入我们的循环体循环体进入的条件是判断栈中是否还有数值如果有数值的化什么其中的数值对应的范围还是没有序的就需要出栈(这个时候就需要进行出栈(注意栈的数值的左右范围的对应))进行我们的挖坑排序(对于挖坑我们应该把key返回出来通过key的数值进行我们再一次的入栈操作同时我们的范围)。
3.1[left key-1] 和[key+1 right] 这样的范围满足条件才能继续push 之后pop进行我们的排序;
4如果说我们的循环体结束了的话我们的数组就一定有序!
前面已经讲述了栈,这里就不再实现栈了
//快排非递归(采用栈的形式)(先进后出)
void QuickSortNonR(int* a, int begin, int end)
{//定义ST st;//初始化STInit(&st);//添加元素STPush(&st, end);STPush(&st, begin);while (!STEmpty(&st)){//剥离元素 让left取先入但是后出的元素(区间的左边)int left = STTop(&st);STPop(&st);//让right取栈顶元素(是我们后入的区间的右边)int right = STTop(&st);STPop(&st);// 右边找小,左边找大,交换,把左边的值跟a[keyi]交换int keyi = PartSort1(a, left, right);//分割成段 // [lefy,keyi-1] keyi [keyi+1, right]if (keyi + 1 < right){//添加元素STPush(&st, right);STPush(&st, keyi + 1);}if (left < keyi - 1){//添加元素STPush(&st, keyi - 1);STPush(&st, left);}}//销毁STDestroy(&st);
}
🌙归并排序
这里讲述两种方法,一种是递归型另一种则是非递归
💫归并排序递归型
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
void _MergeSort(int* a, int* tmp, int begin, int end)
{//尾小于头时if (end <= begin)return;//开始分割int mid = (end + begin) / 2;// [begin, mid][mid+1, end]_MergeSort(a, tmp, begin, mid);_MergeSort(a, tmp, mid + 1, end);//归并到tmp数据组,再拷贝回去//a->[begin, mid][mid+1, end]->tmpint begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int index = begin;//开始拷贝while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}//当第一段元素没有完全拷贝完就把剩下的拷贝进去while (begin1 <= end1){tmp[index++] = a[begin1++];}//当第二段元素没有完全拷贝完就把剩下的拷贝进去while (begin2 <= end2){tmp[index++] = a[begin2++];}//拷贝回原数组memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}//归并排序(分路并归)
void MergeSort(int* a, int n)
{//开辟空间int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}//调用_MergeSort(a, tmp, 0, n - 1);//释放内存free(tmp);
}
💫归并排序非递归型
第一次当 gap 为 1 的时候,我们将距离为 1 的两个数组(其实就是两个元素为 1 的数)进行归并,得到了拥有两个元素的一个有序数组,然后通过循环将后面的元素全部用同样的方式归并。然后就得到了如上图所示蓝色表示的元素排布。同时我们在将这一步骤之后的数组拷贝回到原数组。在进行接下来的操作(这里的意思和上递归的是一样的)。接着每次将 gap 的值增加 2 倍,直到 gap 的值不小于 n 结束归并。这个过程我们将其称为小区间优化。
//归并排序(非递归)
void MergeSortNonR(int* a, int n)
{//开辟空间int* tmp = (int*)malloc(sizeof(int) * n);//判断if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;// [begin1,end1] [begin2,end2] 归并// 如果第二组不存在,这一组不用归并了if (begin2 >= n){break;}// 如果第二组的右边界越界,修正一下if (end2 >= n){end2 = n - 1;}//printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);int index = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}// 拷贝回原数组memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));}printf("\n");gap *= 2;}free(tmp);
}
🌙计数排序
计数排序的朴素方法步骤:
1、从无序数组中取出最大值max,新建一个长度为max+1的数组。
2、遍历无序数组,取其中元素作为新建数组的索引,存在一个则新数组该索引所在的值自增。
3、遍历新数组,当存在不为0的元素,取该元素的索引放入最终数组,并且该元素自减,直到为0,返回最终数组。
上代码:
//计数排序测试
void CountSort(int* a, int n)
{//找最大值和最小值int min = a[0], max = a[0];for (size_t i = 0; i < n; i++){if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}//找出区间int range = max - min + 1;//开辟(区间)int* count = (int*)malloc(sizeof(int) * range);printf("range:%d\n", range);//判断if (count == NULL){perror("malloc fail");return;}//计入数字的个数memset(count, 0, sizeof(int) * range);// 统计数据出现次数for (int i = 0; i < n; i++){count[a[i] - min]++;}//排序int j = 0;for (int i = 0; i < range; i++){while (count[i]--){a[j++] = i + min;}}
}
⭐总结
咱看看每种排序的复杂度和稳定性。
🌠Sort.h代码
#include<stdio.h>//打印数据
void PrintArray(int* a, int n);//冒泡排序测试
void BubbleSort(int* a, int n);
//插入排序测试
void InsertSort(int* a, int n);
//希尔排序测试
void ShellSort(int* a, int n);
//选择排序测试
void SelectSort(int* a, int n);
//堆排序测试
void HeapSort(int* a, int n);//快排测试
void QuickSort1(int* a, int begin, int end);
void QuickSort2(int* a, int begin, int end);
//快排非递归测试
void QuickSortNonR(int* a, int begin, int end);//归并排序测试
void MergeSort(int* a, int n);
//归并排序(非递归)测试
void MergeSortNonR(int* a, int n);
//计数排序测试
void CountSort(int* a, int n);
🌠Test.c代码
#define _CRT_SECURE_NO_WARNINGS 1#include<time.h>
#include<stdlib.h>
#include"Sort.h"
#include"Stack.h"//希尔排序测试
void TestShellSort()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };ShellSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}//冒泡排序测试
void TestBubbleSort()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };BubbleSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}//堆排序测试
void TestHeapSort()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };HeapSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}//选择排序测试
void TestSelectSort()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };SelectSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}//测试代码
void TestOP()
{srand(time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);int* a7 = (int*)malloc(sizeof(int) * N);for (int i = N - 1; i >= 0; --i){a1[i] = rand();a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];}int begin1 = clock();//插入排序InsertSort(a1, N);int end1 = clock();int begin2 = clock();//希尔排序测试ShellSort(a2, N);int end2 = clock();int begin7 = clock();//冒泡排序测试BubbleSort(a7, N);int end7 = clock();int begin3 = clock();//选择排序测试SelectSort(a3, N);int end3 = clock();int begin4 = clock();//堆排序测试HeapSort(a4, N);int end4 = clock();int begin5 = clock();//快排测试//QuickSort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();//MergeSort(a6, N);//计数排序测试//CountSort(a6, N);int end6 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("BubbleSort:%d\n", end7 - begin7);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);printf("CountSort:%d\n", end6 - begin6);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);
}int main()
{TestOP();//TestBubbleSort();//TestHeapSort();//TestSelectSort();//TestQuickSort();//TestMergeSort();//TestCountSort();//printf("%d\n", Func(100000));return 0;
}
🌠Sort.c代码
#define _CRT_SECURE_NO_WARNINGS 1#include"Sort.h"
#include"Stack.h"//交换函数
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//打印数据
void PrintArray(int* a, int n)
{//循环for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}//冒泡排序测试
void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int exchange = 0;for (int j = 1; j < n - i; j++){if (a[i - 1] > a[i]){//这里是一个交换函数Swap(&a[i - 1], &a[i]);exchange = 1;}}//这里进行一趟有序时,直接跳出循环if (exchange == 0){break;}}
}//插入排序
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];//一个元素进行插入while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];}else{break;}--end;}//最后把插入的元素赋值回去a[end + 1] = tmp;}
}//希尔排序测试
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){//间隔gap的元素进行排序gap = gap / 3 + 1;//本质上是插入排序for (int i = 0; i < n - gap; ++i){int end = i;int tmp = a[end + gap];//一个元素进行插入while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end = end - gap;}else{break;}}//最后把插入的元素赋值回去a[end + gap] = tmp;}}
}//void ShellSort(int* a, int n)
//{/*int gap = 3;for (int j = 0; j < gap; j++){for (int i = j; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}} *///选择排序测试
void SelectSort(int* a, int n)
{//初始化第一个元素 和 最后一个元素int begin = 0;int end = n - 1;while (begin < end){//选出最大值和最小值int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; i++){if (a[i] > a[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}//最小值和最初的元素交换Swap(&a[begin], &a[mini]);//如果max被换走就需要重新赋值if (maxi == begin){maxi = mini;}//最大值和最末的元素交换Swap(&a[end], &a[maxi]);++begin;--end;}
}//堆排序测试
//实现向下调整建大堆
void AdjustDown(int* a, int n, int parent)
{//孩子和父亲的关系 int child = parent * 2 + 1;while (child < n){//找出小的那个孩子if (child + 1 < n && a[child + 1] < a[child]){++child;}//实现向下调整元素if (a[child] < a[parent]){//元素交换Swap(&a[child], &a[parent]);// 继续往下调整parent = child;child = parent * 2 + 1;}else{break;}}
}//堆排序测试
void HeapSort(int* a, int n)
{//向下调整建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){//实现向下调整建大堆AdjustDown(a, n, i);}int end = n - 1;while (end > 0){//元素交换Swap(&a[0], &a[end]);//实现向下调整建大堆AdjustDown(a, end, 0);--end;}
}//三数取中 (找出中间值)
int GetMidi(int* a, int left, int right)
{//中间值int mid = (left + right) / 2;//三个数比较找出中间值if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}// 右边找小,左边找大,交换,把左边的值跟a[keyi]交换
int PartSort1(int* a, int left, int right)
{//找中间值(相对)int midi = GetMidi(a, left, right);//把最左边(相对)跟中间值(相对)换Swap(&a[left], &a[midi]);//定位最左边(相对)int keyi = left;while (left < right){//找小while (left < right && a[right] >= a[keyi]){--right;}//找大while (left < right && a[left] <= a[keyi]){++left;}//左右交换Swap(&a[left], &a[right]);}//此时左边走到中间了,开始交换Swap(&a[keyi], &a[left]);//返回return left;
};//挖坑法
int PartSort2(int* a, int left, int right)
{//找中间值(相对)int midi = GetMidi(a, left, right);//把最左边(相对)跟中间值(相对)换Swap(&a[left], &a[midi]);//定位最左边(相对)int key = a[left];//保存key值以后,左边形成第一个坑int hole = left;while (left < right){//右边先走,找小,填到左边的坑,右边形成新的坑位while (left < right && a[right] >= key){--right;}a[hole] = a[right];hole = right;//左边再走,找大,填到右边的坑,左边形成新的坑位while (left < right && a[left] <= key){++left;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}//前后指针
int PartSort3(int* a, int left, int right)
{//找中间值(相对)int midi = GetMidi(a, left, right);//把最左边(相对)跟中间值(相对)换Swap(&a[left], &a[midi]);//定义指针开头int prev = left;//定义指针开头后一个int cur = prev + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev], &a[cur]);}++cur;}//交换Swap(&a[prev], &a[keyi]);return prev;
}//快排测试一
void QuickSort1(int* a, int begin, int end)
{if (begin >= end)return;//小区间优化,小区间不再递归分割排序,降低递归次数//当元素小于10时,采用插入排序if ((end - begin + 1) > 10){//找值int keyi = PartSort3(a, begin, end);// [begin, keyi-1] keyi [keyi+1, end]QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);}else{//插入排序InsertSort(a + begin, end - begin + 1);}
}//快排测试二
void QuickSort2(int* a, int begin, int end)
{if (begin >= end)return;//调用int keyi = PartSort3(a, begin, end);//[begin, keyi-1] keyi [keyi+1, end]QuickSort2(a, begin, keyi - 1);QuickSort2(a, keyi + 1, end);
}//快排非递归(采用栈的形式)(先进后出)
void QuickSortNonR(int* a, int begin, int end)
{//定义ST st;//初始化STInit(&st);//添加元素STPush(&st, end);STPush(&st, begin);while (!STEmpty(&st)){//剥离元素 让left取先入但是后出的元素(区间的左边)int left = STTop(&st);STPop(&st);//让right取栈顶元素(是我们后入的区间的右边)int right = STTop(&st);STPop(&st);// 右边找小,左边找大,交换,把左边的值跟a[keyi]交换int keyi = PartSort1(a, left, right);//分割成段 // [lefy,keyi-1] keyi [keyi+1, right]if (keyi + 1 < right){//添加元素STPush(&st, right);STPush(&st, keyi + 1);}if (left < keyi - 1){//添加元素STPush(&st, keyi - 1);STPush(&st, left);}}//销毁STDestroy(&st);
}void _MergeSort(int* a, int* tmp, int begin, int end)
{//尾小于头时if (end <= begin)return;//开始分割int mid = (end + begin) / 2;// [begin, mid][mid+1, end]_MergeSort(a, tmp, begin, mid);_MergeSort(a, tmp, mid + 1, end);//归并到tmp数据组,再拷贝回去//a->[begin, mid][mid+1, end]->tmpint begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int index = begin;//开始拷贝while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}//当第一段元素没有完全拷贝完就把剩下的拷贝进去while (begin1 <= end1){tmp[index++] = a[begin1++];}//当第二段元素没有完全拷贝完就把剩下的拷贝进去while (begin2 <= end2){tmp[index++] = a[begin2++];}//拷贝回原数组memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}//归并排序(分路并归)
void MergeSort(int* a, int n)
{//开辟空间int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}//调用_MergeSort(a, tmp, 0, n - 1);//释放内存free(tmp);
}//归并排序(非递归)
void MergeSortNonR(int* a, int n)
{//开辟空间int* tmp = (int*)malloc(sizeof(int) * n);//判断if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;// [begin1,end1] [begin2,end2] 归并// 如果第二组不存在,这一组不用归并了if (begin2 >= n){break;}// 如果第二组的右边界越界,修正一下if (end2 >= n){end2 = n - 1;}//printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);int index = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}// 拷贝回原数组memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));}printf("\n");gap *= 2;}free(tmp);
}//计数排序测试
void CountSort(int* a, int n)
{//找最大值和最小值int min = a[0], max = a[0];for (size_t i = 0; i < n; i++){if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}//找出区间int range = max - min + 1;//开辟(区间)int* count = (int*)malloc(sizeof(int) * range);printf("range:%d\n", range);//判断if (count == NULL){perror("malloc fail");return;}//计入数字的个数memset(count, 0, sizeof(int) * range);// 统计数据出现次数for (int i = 0; i < n; i++){count[a[i] - min]++;}//排序int j = 0;for (int i = 0; i < range; i++){while (count[i]--){a[j++] = i + min;}}
}
🌟结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小说手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。
相关文章:

手撕各种排序
> 作者简介:დ旧言~,目前大一,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:掌握每种排序的方法,理解每种排序利弊…...

视频号的链接在哪,视频号视频链接地址获取办法!
不少人问视频号的链接在哪里可以获取,本质的在腾讯微信中目前视频号的链接是无法获取的,但好事多磨今天就分享一个第三方的视频号视频链接地址获取办法,希望对你有所帮助! 1:在微信客户端中,我们可以通过搜…...

深度学习笔记之优化算法(六)RMSprop算法的简单认识
深度学习笔记之优化算法——RMSProp算法的简单认识 引言回顾:AdaGrad算法AdaGrad算法与动量法的优化方式区别AdaGrad算法的缺陷 RMProp算法关于AdaGrad问题的优化方式RMSProp的算法过程描述 RMSProp示例代码 引言 上一节对 AdaGrad \text{AdaGrad} AdaGrad算法进行…...

10架构管理之公司整体技术架构
一句话导读 公司的整体技术架构一般是公司的架构组、架构管理部、技术委员会等部门负责,需要对公司整体的技术架构进行把控和管理,确保信息系统的稳定性和可靠性,避免因技术架构不合理而导致的系统崩溃和数据丢失等问题,为公司的业…...

联邦学习综述
《Advances and Open Problems in Federated Learning》 选题:Published 10 December 2019-Computer Science-Found. Trends Mach. Learn. 联邦学习定义 联邦学习是一种机器学习设置,其中多个客户端在中央服务器或服务提供商的协调下协作解决机器学习…...

几行cmd命令,轻松将java文件打包成jar文件
1. 在任意目录下建立一个.java文件 2. 在当前目录下使用cmd命令: javac filename编译 如果报错则使用此命令javac -encoding UTF-8 filename 3.此时已成功生成.class文件 4. 可以手动添加MANIFEST.MF文件 Manifest-Version: 1.0 Main-Class: fileName 5.直接一…...

BuyVM 卢森堡 VPS 测评
description: 发布于 2023-07-05 BuyVM 卢森堡 VPS 测评 产品链接:https://my.frantech.ca/cart.php?gid39 1G口不限流量,续约3个月后升级为10G口突发。抗DMCA版权投诉。抗一般投诉。 大陆连通性还可以,延迟略高,不绕美。 CP…...

JavaScript 编写一个 数值转换函数 万以后简化 例如1000000 展示为 100万 万以下原来数值返回
很多时候 我们看一些系统 能够比较只能的展示过大的数值 例如 到万了 他就能展示出 多少 多少万 看着很奇妙 但实现确实非常的基础 我们只需要一个这样的函数 //数值转换函数 convertNumberToString(num) {//如果传入的数值 不是数字 且也无法转为数字 直接扔0回去if (!parse…...

PyG两个data Datsaset v.s. InMemoryDataset
可以看到InMemoryDataset 对CPU更加友好 https://pytorch-geometric.readthedocs.io/en/latest/modules/data.html#pytorch-lightning-wrappers...

ArcGIS Engine:视图菜单的创建和鹰眼图的实现
目录 01 创建项目 1.1 通过ArcGIS-ExtendingArcObjects创建窗体应用 1.2 通过C#-Windows窗体应用创建窗体应用 1.2.1 创建基础项目 1.2.2 搭建界面 02 创建视图菜单 03 鹰眼图的实现 3.1 OnMapReplaced事件的触发 3.2 OnExtentUpdated事件的触发 04 稍作演示 01 创建项目…...

POI 和 EasyExcel 操作 Excel
一、概述 目前操作 Excel 比较流行的就是 Apache POI 和阿里巴巴的 easyExcel。 1.1 POI 简介 Apache POI 是用 Java 编写的免费开源的跨平台的 Java API,Apache POI 提供 API 给 Java 程序对 Microsoft Office 格式文档读和写的常用功能。POI 为 “Poor Obfuscati…...

pytorch算力与有效性分析
pytorch Windows中安装深度学习环境参考文档机器环境说明3080机器 Windows11qt_env 满足遥感CS软件分割、目标检测、变化检测的需要gtrs 主要是为了满足遥感监测管理平台(BS)系统使用的,无深度学习环境内容swin_env 与 qt_env 基本一致od 用于…...

Sublime text启用vim模式
官方教程:https://www.sublimetext.com/docs/vintage.html vintage的github:https://github.com/sublimehq/Vintage...

爬虫进阶-反爬破解6(Nodejs+Puppeteer实现登陆官网+实现滑动验证码全自动识别)
一、NodejsPuppeteer实现登陆官网 1.环境说明 Nodejs——直接从官网下载最新版本,并安装 使用npm安装puppeteer:npm install puppeteer npm install xxx -registry https://registry.npm.taobao.org Chromium会自动下载,前提是网络通畅 2.实践操作…...

【Unity】RenderFeature笔记
【Unity】RenderFeature笔记 RenderFeature是在urp中添加的额外渲染pass,并可以将这个pass插入到渲染列队中的任意位置。内置渲染管线中Graphics 的功能需要在RenderFeature里实现,常见的如DrawMesh和Blit 可以实现的效果包括但不限于 后处理,可以编写…...

golang gin——controller 模型绑定与参数校验
controller 模型绑定与参数校验 gin框架提供了多种方法可以将请求体的内容绑定到对应struct上,并且提供了一些预置的参数校验 绑定方法 根据数据源和类型的不同,gin提供了不同的绑定方法 Bind, shouldBind: 从form表单中去绑定对象BindJSON, shouldB…...

办公技巧:Excel日常高频使用技巧
目录 1. 快速求和?用 “Alt ” 2. 快速选定不连续的单元格 3. 改变数字格式 4. 一键展现所有公式 “CTRL ” 5. 双击实现快速应用函数 6. 快速增加或删除一列 7. 快速调整列宽 8. 双击格式刷 9. 在不同的工作表之间快速切换 10. 用F4锁定单元格 1. 快速求…...

【jvm--方法区】
文章目录 1. 栈、堆、方法区的交互关系2. 方法区的内部结构3. 运行时常量池4. 方法区的演进细节5. 方法区的垃圾回收 1. 栈、堆、方法区的交互关系 方法区的基本理解: 方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区…...

智慧楼宇3D数据可视化大屏交互展示实现了楼宇能源的高效、智能、精细化管控
智慧园区是指将物联网、大数据、人工智能等技术应用于传统建筑和基础设施,以实现对园区的全面监控、管理和服务的一种建筑形态。通过将园区内设备、设施和系统联网,实现数据的传输、共享和响应,提高园区的管理效率和运营效益,为居…...

算法题:摆动序列(贪心算法解决序列问题)
这道题是一道贪心算法题,如果前两个数是递增,则后面要递减,如果不符合则往后遍历,直到找到符合的。(完整题目附在了最后) 代码如下: class Solution(object):def wiggleMaxLength(self, nums):…...

接口自动化测试yaml+requests+allure技术,你学会了吗?
前言 接口自动化测试是在软件开发过程中常用的一种测试方式,通过对接口进行自动化测试,可以提高测试效率、降低测试成本。在接口自动化测试中,yaml、requests和allure三种技术经常被使用。 一、什么是接口自动化测试 接口自动化测试是指通…...

android 获取局域网其他设备ip
Android 通过读取本地Arp表获取当前局域网内其他设备信息_手机查看arp-CSDN博客...

angular中使用 ngModel 自定义组件
要创建一个自定义的 Angular 组件,并使用 ngModel 进行双向数据绑定,您可以按照以下步骤操作: 创建自定义组件:首先,使用 Angular CLI 或手动创建一个新的组件。在组件的模板中,添加一个输入元素或其他适合…...

kubernetes pod日志查看用户创建
目录 1.创建用户 1.1证书创建 1.2创建用户 1.3允许用户登陆 1.4切换用户 1.5删除用户 2.RBAC 1.创建用户 1.1证书创建 进入证书目录 # cd /etc/kubernetes/pki创建key # openssl genrsa -out user1.key 2048 Generating RSA private key, 2048 bit long modulus .....…...

HTML5+CSSday4综合案例二——banner效果
bannerCSS展示图: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wi…...

关于红包雨功能的探索
【高并发优化手段】基于Springboot项目 【红包雨功能的】环境部署(弹性伸缩、负载均衡、Redis读写分离、云服务器部署) jemeter压测【2万用户每秒5次请求在30秒内处理完请求】 【红包雨压测】提供2万用户30秒内5次请求的并发服务支持 使用工厂模式、策略…...

【已解决】Python打包文件执行报错:ModuleNotFoundError: No module named ‘pymssql‘
【已解决】Python打包文件执行报错:ModuleNotFoundError: No module named pymssql 1、问题2、原因3、解决 1、问题 今天打包一个 tkinter pymssql 的项目的时候,打包过程很顺利,但是打开软件的时候,报错 ModuleNotFoundError: …...

华为云云耀云服务器L实例评测|测试CentOS的网络配置和访问控制
目录 引言 1 理解几个基础概念 2 配置VPC、子网以及路由表 3 配置安全组策略和访问控制规则 3.1 安全组策略和访问控制简介 3.2 配置安全组策略 3.3 安全组的最佳实践 结论 引言 在云计算时代,网络配置和访问控制是确保您的CentOS虚拟机在云环境中安全运行的…...

CSP模拟51联测13 B.狗
CSP模拟51联测13 B.狗 文章目录 CSP模拟51联测13 B.狗题目大意题目描述输入格式输出格式样例样例 1inputoutput 思路 题目大意 题目描述 小G养了很多狗。 小G一共有 n n n\times n nn 条狗,在一个矩阵上。小G想让狗狗交朋友,一条狗狗最多只能交一个…...

GEO生信数据挖掘(七)差异基因分析
上节,我们使用结核病基因数据,做了一个数据预处理的实操案例。例子中结核类型,包括结核,潜隐进展,对照和潜隐,四个类别。本节延续上个数据,进行了差异分析。 差异分析 计算差异指标step12 加载…...