c语言实现八大排序详细解析

首先先看排序算法的整体分类

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次 序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排 序算法是稳定的;否则称为不稳定的。

内部排序:数据元素全部放在内存中的排序。

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

1  插入排序

1.1 直接插入排序

其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为 止,得到一个新的有序序列 。

void InsertSort(int* a, int n)
{assert(a);//最后一次,是要把n - 1这个数进行排序,则已经//排好序的尾部为n-2for (int i = 0; i < n-1; ++i){//end表示已经排好序的尾标int end = i;//首先保存要排序的数,一会就会被覆盖了int tmp = a[end + 1];//只要前面的数大于end + 1,则前面的这些数都向后挪动一个位置while (end >= 0 && a[end] > tmp){a[end + 1] = a[end];--end;}a[end + 1] = tmp;}
}

总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1),它是一种稳定的排序算法

4. 稳定性:稳定

1.2 希尔排序

基本思想是:先选定一个整数,把待排序文件中所有记录分成个n/gap组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后重复上述分组和排序的工作。当到达gap=1时,所有记录在统一组内排好序。


void ShellSort(int* a, int n)
{assert(a);int gap = n;//不能写成大于0,因为gap的值始终>=1while (gap > 1){//只有gap最后为1,才能保证最后有序//所以这里要加1gap = gap / 3 + 1;//这里只是把插入排序的1换成gap即可//但是这里不是排序完一个分组,再去//排序另一个分组,而是整体只过一遍//这样每次对于每组数据只排一部分//整个循环结束之后,所有组的数据排序完成for (int i = 0; i < n - gap; ++i) //当gap==1时,等同于直接插入排序{int end = i;int tmp = a[end + gap];while (end >= 0 && a[end] > tmp){a[end + gap] = a[end];end -= gap;}a[end + gap] = tmp;}}
}

总结:

1. 希尔排序是对直接插入排序的优化。

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的 希尔排序的时间复杂度都不固定,因为我们此处的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按照: 

 4. 稳定性:不稳定

2  选择排序

基本思想: 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

2.1直接选择排序

/*
优化的选择排序,每次可以选择一个最大的和一个最小的
然后把他们放在合适的位置,
即最小的放在第一个位置,最大的放在最后一个位置,
然后再去选择次小的和次大的,依次这样进行,
直到区间只剩一个值或没有
*/
void SelectSort(int* a, int n)
{assert(a);int begin = 0, end = n - 1;while (begin < end){int min = begin, max = begin;for (int i = begin; i <= end; i++){if (a[i] >= a[max])max = i;if (a[i] < a[min])min = i;}//最小的放在Swap(&a[begin], &a[min]);//如果最大的位置在begin位置//说明min是和最大的交换位置//这个时候max的位置就发生了变换//max变到了min的位置//所以要更新max的位置if (begin == max)//防止被交换的数恰好是后续需要交换的最大或最小值max = min;Swap(&a[end], &a[max]);++begin;--end;//PrintArray(a, n);}
}

总结:

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1) 4. 稳定性:不稳定

2.2 堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

void AdjustDown(int* a, int n, int root)
{int parent = root;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)
{assert(a);// 建堆,先从最后两个叶子上的根(索引为(n - 2) / 2开始建堆// 先建最小的堆,直到a[0](最大的堆)// 这就相当于在已经建好的堆上面,新加入一个// 根元素,然后向下调整,让整个完全二叉树// 重新满足堆的性质for (int i = (n - 2) / 2; i >= 0; --i){AdjustDown(a, n, i);}//end:表示最后一个位置int end = n - 1;//只剩一个数时,就不需要调整了while (end > 0){//0位置和最后一个位置交换Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}

堆排序详解可以看往期文章:http://t.csdn.cn/fgffOicon-default.png?t=N6B9http://t.csdn.cn/fgffO

总结:

1. 堆排序使用堆来选数,效率就高了很多。

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(1)

4. 稳定性:不稳定

3  交换排序

3.1 冒泡排序

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void BubbleSort(int* a, int n)
{assert(a);int end = n;while (end > 0){/*加一个标记,如果中间没有发生交换说明前面的值都比后面的小即本身就是有序的,最好的情况下,它的时间复杂度就为N*/int flag = 0;for (int i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);flag = 1;}}if (flag == 0){break;}--end;}
}

3.2 快速排序

// 三数取中法,三个中取一个中间值int GetMidIndex(int* a, int begin, int end)
{int mid = begin + ((end - begin) >> 1);if (a[begin] < a[mid]){if (a[mid] < a[end]){return mid;}else if (a[begin] > a[end]){return begin;}else{return end;}}else // begin >= mid{if (a[mid] > a[end]){return mid;}else if (a[begin] < a[end]){return begin;}else{return end;}}}int PartSort1(int* a, int begin, int end)
{int midindex = GetMidIndex(a, begin, end);Swap(&a[begin], &a[midindex]);int key = a[begin];int start = begin;/*这里要从右边走,如果从左边走,可能最后一步,如果找不到大于基准值的,会导致begin == end即相遇,但是右边还没有走,所以这里的值一定大于基准值,最后交换就会出问题,所以一定要从右边走,即使最后一次找不到小于基准值的,会和左边相遇,而左边此时还没走,一定比基准值小,最后交换肯定没有问题*/while (begin < end){// end 找小while (begin < end && a[end] >= key)--end;// begin找大while (begin < end && a[begin] <= key)++begin;Swap(&a[begin], &a[end]);}//最后的交换一定要保证a[begin] < a[start], 所以要从右边走Swap(&a[begin], &a[start]);return begin;
}int PartSort2(int* a, int begin, int end)
{//begin是坑int key = a[begin];while (begin < end){while (begin < end && a[end] >= key)--end;// end给begin这个坑,end就变成了新的坑。a[begin] = a[end];while (begin < end && a[begin] <= key)++begin;// end给begin这个坑,begin就变成了新的坑。a[end] = a[begin];}a[begin] = key;return begin;
}/*
前后指针法
*/
int PartSort3(int* a, int begin, int end)
{int midindex = GetMidIndex(a, begin, end);Swap(&a[begin], &a[midindex]);int key = a[begin];int prev = begin;int cur = begin + 1;while (cur <= end){// cur找小,把小的往前翻,大的往后翻if (a[cur] < key && ++prev != cur)Swap(&a[cur], &a[prev]);++cur;}Swap(&a[begin], &a[prev]);return prev;
}// []
void QuickSort(int* a, int left, int right)
{if (left >= right)return;if (right - left + 1 < 10){InsertSort(a+left, right - left + 1);}else{int div = PartSort3(a, left, right);//[left, div-1]//[div+1, right]QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);}
}
//用栈模拟递归,用队列也可以实现
void QuickSortR(int* a, int left, int right)
{Stack st;StackInit(&st, 10);//先入大区间if (left < right){StackPush(&st, right);StackPush(&st, left);}//栈不为空,说明还有没处理的区间while (StackEmpty(&st) != 0){left = StackTop(&st);StackPop(&st);right = StackTop(&st);StackPop(&st);//快排单趟排序int div = PartSort1(a, left, right);// [left div-1]// 把大于1个数的区间继续入栈if (left < div - 1){StackPush(&st, div - 1);StackPush(&st, left);}// [div+1, right]if (div+1 < right){StackPush(&st, right);StackPush(&st, div + 1);}}}

3.3 三路快排

快排的总结:

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4. 稳定性:不稳定

4 归并排序

归并排序的思想: 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 

递归版

void _MergeSort(int* a, int left, int right, int* tmp)
{if (left >= right)return;int mid = left + ((right - left) >> 1);// [left, mid]// [mid+1, right]_MergeSort(a, left, mid, tmp);_MergeSort(a, mid+1, right, tmp);int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int index = left;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2])tmp[index++] = a[begin1++];elsetmp[index++] = a[begin2++];}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}memcpy(a+left, tmp+left, sizeof(int)*(right - left+1));
}void MergeSort(int* a, int n)
{assert(a);int* tmp = (int*)malloc(sizeof(int)*n);_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

非递归版

/*
非递归排序与递归排序相反,将一个元素与相邻元素构成有序数组,
再与旁边数组构成有序数组,直至整个数组有序。
要有mid指针传入,因为不足一组数据时,重新计算mid划分会有问题
需要指定mid的位置
*/
void merge(int* a, int left, int mid, int right, int* tmp)
{// [left, mid]// [mid+1, right]int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int index = left;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2])tmp[index++] = a[begin1++];elsetmp[index++] = a[begin2++];}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}memcpy(a+left, tmp+left, sizeof(int)*(right - left+1));
}/*
k用来表示每次k个元素归并
*/
void mergePass(int *arr, int k, int n, int *temp)
{int i = 0;//从前往后,将2个长度为k的子序列合并为1个while(i < n - 2*k + 1){merge(arr, i, i + k - 1, i + 2*k - 1, temp);i += 2*k;}//合并区间[i, n - 1]有序的左半部分[i, i + k - 1]以及不及一个步长的右半部分[i + k, n - 1]if(i < n - k ){merge(arr, i, i + k - 1,n-1, temp);}}// 归并排序非递归版
void MergeSortNonR(int *arr,int length)
{int k = 1;int *temp = (int *)malloc(sizeof(int) * length);while(k < length){mergePass(arr, k, length, temp);k *= 2;}free(temp);
}

归并排序的总结:

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(N)

4. 稳定性:稳定

5 计数排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。

操作步骤:

1. 统计相同元素出现次数

2. 根据统计的结果将序列回收到原来的序列中

void CountSort(int* a, int n)
{int max = a[0], min = a[0];for (int i = 0; i < n; ++i){if (a[i] > max)max = a[i];if (a[i] < min)min = a[i];}//找到数据的范围int range = max - min + 1;int* countArray = (int*)malloc(range*sizeof(int));memset(countArray, 0, sizeof(int)*range);//存放在相对位置,可以节省空间for (int i = 0; i < n; ++i){countArray[a[i] - min]++;}//可能存在重复的数据,有几个存几个int index = 0;for (int i = 0; i < range; ++i){	while (countArray[i]--){a[index++] = i+min;}}
}

计数排序的总结:

1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。

2. 时间复杂度:O(MAX(N,范围))

3. 空间复杂度:O(范围) 

4. 稳定性:稳定

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/20334.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为Android构建现代应用——应用导航设计

在前一章节的实现中&#xff0c;Skeleton: Main structure&#xff0c;我们留下了几个 Jetpack 架构组件&#xff0c;这些组件将在本章中使用&#xff0c;例如 Composables、ViewModels、Navigation 和 Hilt。此外&#xff0c;我们还通过 Scaffold 集成了 TopAppBar 和 BottomA…

yolov3-spp 训练结果分析:网络结果可解释性、漏检误检分析

1. valid漏检误检分析 ①为了探查第二层反向找出来的目标特征在最后一层detector上的意义&#xff01;——为什么最后依然可以框出来目标&#xff0c;且mAP还不错的&#xff1f; ②如何进一步提升和改进这个数据的效果&#xff1f;可以有哪些优化数据和改进的地方&#xff1f;让…

《ChatGPT原理最佳解释,从根上理解ChatGPT》

【热点】 2022年11月30日&#xff0c;OpenAI发布ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c; 即聊天机器人程序 &#xff0c;开启AIGC的研究热潮。 ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够…

竞争之王CEO商战课,聚百家企业在京举行

竞争之王CEO商战课&#xff0c;于2023年7月29-31日在北京临空皇冠假日酒店举办&#xff0c;近百家位企业家齐聚一堂&#xff0c;共享饕餮盛宴。 竞争之王CEO商战课是打赢商战的第一课。 竞争环境不是匀速变化&#xff0c;而是加速变化。 在未来的市场环境中&#xff0c;企业间…

Day12-1-Webpack前端工程化开发

Webpack前端工程化 1 案例-webpack打包js文件 1 在index.html中编写代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><me…

基于Kubernetes环境的高扩展机器学习部署利器——KServe

随着ChatGPT的发布&#xff0c;人们越来越难以回避利用机器学习的相关技术。从消息应用程序上的文本预测到智能门铃上的面部识别&#xff0c;机器学习&#xff08;ML&#xff09;几乎可以在我们今天使用的每一项技术中找到。 如何将机器学习技术交付给消费者是企业在开发过程中…

【Spring Boot】请求参数传json数组,后端采用(pojo)新增案例(103)

请求参数传json数组&#xff0c;后端采用&#xff08;pojo&#xff09;接收的前提条件&#xff1a; 1.pom.xml文件加入坐标依赖&#xff1a;jackson-databind 2.Spring Boot 的启动类加注解&#xff1a;EnableWebMvc 3.Spring Boot 的Controller接受参数采用&#xff1a;Reque…

构建vue项目配置和环境配置

目录 1、环境变量process.env配置2、vue package.json多环境配置vue-cli-service serve其他用法vue-cli-service build其他用法vue-cli-service inspect其他用法3、vue导出webpack配置4、配置打包压缩图片文件5、打包去掉多余css(由于依赖问题暂时未实现)6、打包去除console.…

【Linux】进程间通信——管道

目录 写在前面的话 什么是进程间通信 为什么要进行进程间通信 进程间通信的本质理解 进程间通信的方式 管道 System V IPC POSIX IPC 管道 什么是管道 匿名管道 什么是匿名管道 匿名管道通信的原理 pipe()的使用 匿名管道通信的特点 拓展代码 命名管道 什么是命…

IDEA离线环境搭建远程开发-Windows

公司的云桌面实在太卡&#xff0c;多个微服务项目跑起来&#xff0c;直接无法进行其它编码工作&#xff0c;所以想到使用Idea提供的远程开发功能&#xff0c;将服务运行在服务器&#xff0c;电脑只提供给开发页面展示&#xff0c;提高效率。 环境介绍&#xff1a; 开发环境&…

SystemVerilog数组参数传递及引用方法总结

一、将常数数组传递给task/function 如下面的程序&#xff0c;将一个常数数组传递给function module my_array_test();function array_test(int array[4]);foreach(array[i]) begin$display("array[%0d] %0d", i, array[i]);endendfunctioninitial beginarray_tes…

景联文科技高质量成品数据集上新啦!

景联文科技近期上新多个成品数据集&#xff0c;包含图像、视频等多种类型的数据&#xff0c;涵盖丰富的场景&#xff0c;可满足不同模型的多元化需求。 高质量成品数据集可用于训练和优化模型&#xff0c;使得模型能够更加全面和精准地理解和处理任务&#xff0c;更好地应对复…

anaconda创建虚拟环境在D盘

【看一看就行&#xff0c;又是挺水的一期&#xff08;每一季都掺和一点子水分也挺好&#xff09;】 一、创建&#xff1a; conda create --prefixD:\python37\py37 python3.7 这下就在D盘了&#xff1a; 二、激活刚刚那个环境&#xff1a; activate D:\pyhton37\py37​ &…

如何微调医疗大模型llm:llama2学习笔记

三个微调方向&#xff1a;简单医疗问答 临床问答 影像学 一般流程&#xff1a; 1 数据集准备 2 模型基座选择 3 微调 4 案例拆解 1 数据集准备&#xff1a;两种类型&#xff0c;一种文本一种影像 扩展&#xff0c;多模态 2 模型基座选择 多模态处理所有视频&#xff0c;文本…

【汇总】解决Ajax请求后端接口,返回ModelAndView页面不跳转

【汇总】解决Ajax请求后端接口&#xff0c;返回ModelAndView不跳转 问题发现问题解决方法一&#xff1a;直接跳转到指定URL&#xff08;推荐&#xff09;方法二&#xff1a;将返回的html内容&#xff0c;插入到页面某个元素中方法三&#xff1a;操作文档流方法四&#xff1a;使…

redis的安装和配置

一、nosql 二、redis的安装和配置 redis的安装&#xff1a; redis常见配置&#xff1a; 配置文件redis.conf

【FAQ】调用EasyDSS返回的直播快照接口,无法编辑只能新建的原因排查与解决

EasyDSS视频直播点播平台集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景上&#xff0c;平台可以运用在互联网教育、在线课堂、游戏直播等领域…

【H5移动端】常用的移动端方案合集-键盘呼起、全面屏适配、图片大小显示、300ms点击延迟、首屏优化(不定期补充~)

文章目录 前言键盘呼起问题靠近底部的输入项被键盘遮挡底部按钮被顶上去 全面屏适配图片大小显示问题解决300ms延迟首屏优化 前言 这篇文章总结了我在工作中做H5遇到的一些问题&#xff0c;包括我是怎么解决的。可能不是当下的最优解&#xff0c;但是能保证解决问题。 单位适…

智慧防汛,数字科技的力量

随着夏日的脚步临近&#xff0c;台风季节即将降临。对于那些居住在沿海地区的人们来说&#xff0c;台风是一种常见的自然灾害&#xff0c;其带来的风雨可能对生命和财产造成严重威胁。然而&#xff0c;随着数字科技的飞速发展&#xff0c;可视化技术为防汛抗台工作带来了全新的…

【C++】STL——queue的介绍和使用、queue的push和pop函数介绍和使用、queue的其他成员函数

文章目录 1.queue的介绍2.queue的使用2.1queue构造函数2.2queue的成员函数&#xff08;1&#xff09;empty() 检测队列是否为空&#xff0c;是返回true&#xff0c;否则返回false&#xff08;2&#xff09;size() 返回队列中有效元素的个数 &#xff08;3&#xff09;front() 返…