C语言实现12种排序算法

1.冒泡排序

思路:比较相邻的两个数字,如果前一个数字大,那么就交换两个数字,直到有序。

  • 时间复杂度:O(n^2),
  • 稳定性:这是一种稳定的算法。
    代码实现:
void bubble_sort(int arr[],size_t len){size_t i,j;for(i=0;i<len;i++){	bool hasSwap = false;		//优化,判断数组是否已经有序,如果有序可以提前退出循环for(j=1;j<len-i;j++){		//这里j<len-i是因为最后面的肯定都是最大的,不需要多进行比较if(arr[j-1]>arr[j]){	//如果前一个比后一个大swap(&arr[j-1],&arr[j]);	//交换两个数据hasSwap = true;}	}if(!hasSwap){break;	}}
}

2.插入排序

思路:把一个数字插入一个有序的序列中,使之仍然保持有序,如对于需要我们进行排序的数组,我们可以使它的前i个数字有序,然后再插入i+1个数字,插入到合适的位置使之仍然保持有序,直到所有的数字有序。

  • 时间复杂度:O(n^2)
  • 稳定性:稳定的算法
    代码实现:
void insert_sort(int arr[],int len)
{int i,j;for(i=1;i<len;i++){int key = arr[i]; //记录当前需要插入的数据for(j= i-1;i>=0&&arr[j]>key;j--){	//找到插入的位置arr[j+1] = arr[j]; //把需要插入的元素后面的元素往后移}arr[j+1] = key;	//插入该元素}
}

3.折半插入排序

思路:本质上是插入排序,但是通过半分查找法找到插入的位置,让效率稍微快一点。

  • 时间复杂度:O(n^2),
  • 稳定性:稳定的算法。
    代码实现:
void half_insert_sort(int arr[],int len)
{int i,j;for(i=1;i<len;i++){int key = arr[i];int left = 0;int right = i-1;while(left<=right){	//半分查找找到插入的位置int mid = (left+right)/2;if(key<arr[mid]){right = mid-1;	}else{left = mid+1;	}	}for(j=i-1;j>=left;j--){		//把后面的元素往后移arr[j+1]=arr[j];	}arr[j+1] = key;	//插入元素}
}

4.希尔排序

思路:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止。

  • 时间复杂度:O(n^1.3) ,算法效率上大大提高 。
  • 稳定性:不稳定的算法。
  • 代码实现
void shell_sort(int arr[],int len)
{	//本质上也是一种插入排序,避免了大量数据的移动,在每一组排序过后,每个数据已经到了大致的位置。int i,j;int step=0;for(step = len/2;step>=1;step=step/2){	//分组  分为step组,对每组的元素进行插入排序for(i=step;i<len;i++){int key = arr[i];for(j=i-step;j>=0&&arr[j]>key;j=j-step){arr[j+step] = arr[j];	}	arr[j+step] = key;}}
}

5.选择排序

思路:通过循环找到最大值所在的位置,然后把最大值和最后一个元素进行交换,通过循环直到所有的数据有序。

  • 时间复杂度:O(n^2)
  • 稳定性:不稳定的算法
    代码实现:
void select_sort(int arr[],size_t len)
{size_t i,j;for(i=0;i<len-1;i++){int max = 0; //最大值下标for(j=1;j<len-i;j++){if(arr[max]<arr[j]){	//找到最大值的下标max = j;	}	}if(max!=j-1){	swap(&arr[max],&arr[j-1]);	//把最后一个元素和最大值进行交换}}
}

6.鸡尾酒排序

思路:选择排序的一种改进,一次循环直接找到最大值和最小值的位置,把最大值和最后一个元素进行交换,最小值和最前一个元素进行交换,所以最外层的循环只需要执行len/2次即可

  • 时间复杂度:O(n^2)
  • 稳定性:不稳定的算法
    代码实现:
void cocktail_sort(int arr[],size_t len)
{size_t i,j;for(i=0;i<len/2;i++){int max = i;	//最大值下标int min = i;	//最小值下标for(j=i+1;j<len-i;j++){if(arr[max]<arr[j]){	//找到最大值下标max = j;	}	if(arr[min]>arr[j]){	//找到最小值下标min = j;	}}if(max!=j-1){swap(&arr[max],&arr[j-1]);		//交换最大值和未进行排序的最后一个元素}if(min == j-1){	//如果最小值在未进行排序的最后一个位置,那么经过最大值的交换,已经交换到了最大值所在的位置min = max;		//把最小值的坐标进行改变}if(min!=i){swap(&arr[i],&arr[min]);	//交换最小值和未进行排序的最前的元素}}
}

7.堆排序

思路:把数据进行大堆化,然后依次交换堆顶(最大值)和最后一个元素,在使堆顶重新大堆化,最后循环过后数组便有序。
最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
创建最大堆(Build Max Heap):将堆中的所有数据重新排序
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

  • 时间复杂度:O(nlgn)
  • 稳定性:不稳定的算法
    代码实现:
void re_heap(int arr[],size_t index,size_t len)
{size_t child = 2*index+1;	//左节点坐标int key = arr[index];	//当前节点值while(child<len){if(child+1<len&&arr[child]<arr[child+1]){	//如果右节点存在且右节点的值比左节点大,那就child记录较大字节点的坐标child++;	}	if(arr[child]>key){	//如果子节点的值比根节点的值大arr[index] = arr[child];	//改变根节点的值}else{break;	}index = child;child = 2*index+1;}arr[index] = key;		//插入记录好的值
}
void heap_sort(int arr[],size_t len)
{int i;for(i=len/2;i>=0;i--){re_heap(arr,i,len);		//对第i个根节点进行大堆化}for(i=len-1;i>0;i--){swap(&arr[0],&arr[i]);	//交换第一个和最后一个元素re_heap(arr,0,i);	//对第一个元素进行大堆化}
}

8.快速排序

思路:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
过程:

(1)首先设定一个分界值,通过该分界值将数组分成左右两部分

(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。

(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。

(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

  • 时间复杂度:O(nlog2n)
  • 稳定性:不稳定的算法
  • 代码实现:
void quick_sort(int arr[],size_t left,size_t right)
{if(left>=right){	//如果只有一个元素,那就是有序的,返回return;	}int i = left;int j = right;int key = arr[left];	//基准值while(i<j){	//找到基准值的位置,使得基准值右边的元素都比基准值大,左边的元素都比基准值小while(i<j&&arr[j]>=key){	//从右边找一个比基准值小的数,--j;}arr[i] = arr[j];//把这个值放到基准值的位置处while(i<j&&arr[i]<=key){	//从左边找一个比基准值大的数++i;	}arr[j] = arr[i];		//把这个元素放到j的位置}arr[i] = key;if(i-left>1)	//元素个数至少两个才进行递归调用,这样可以少一次递归quick_sort(arr,left,i-1);	//对基准值左边的元素进行排序if(right-i>1)quick_sort(arr,i+1,right);	//对基准值右边的元素进行排序
}

9.归并排序

思路:对于两个有序的子序列,可以把它们合并在一起,变成一个新的完全有序的序列,因此归并排序和快排差不多,都是递归的进行。

  • 时间复杂度:O(nlog2n)
  • 稳定性:稳定的算法
    代码实现:
void merge(int arr[],int left,int right)
{int i,j,k;int mid = (left+right)/2;int len = mid-left+1;int *temp = malloc(sizeof(arr[0])*len);for(i=0;i<len;i++){temp[i] = arr[i+left];	//把这个数组的所有元素都复制到临时数组中}i=0,j=mid+1,k=left;while(i<len&&j<=right){if(temp[i]<arr[j]){	//把临时数组的元素和 [mid+1,right]这部分的元素一个一个的进行比较,如果谁小,那么arr里就存放谁的元素arr[k++] = temp[i++];	}else{arr[k++] = arr[j++];	}}while(i<len){	//如果temp这个数组的元素还没有全部遍历完,那就把temp后面的元素都复制到arr里面去,//因为arr[mid+1,right] 这部分的元素本来就是arr后面部分的有序的元素,所以如果arr[mid+1,right]这部分没有遍历完也没关系的,arr[k++] = temp[i++];	}free(temp);
}
void merge_sort(int arr[],int left,int right)
{if(left>=right){	//如果只有一个元素说明这个序列有序,那就返回return;	}	int mid = (left+right)/2;	//对两个有序的数组进行排序,merge_sort(arr,left,mid);	//对[left,mid]这个区间的元素进行排序merge_sort(arr,mid+1,right);	//对[mid+1,right]这个区间内的元素进行排序merge(arr,left,right);  //这个序列的[left,mid]为有序的序列 [mid+1,right]也为有序的序列
}

10.计数排序

思路:这是一种基于比较的算法,我们用一个大数组来存放这些数据,这些数据在这个大数组中的表现形式是以这个大数组的下标存在的,比如57,60,42这三个数字进行排序,那么用一个大数组,这个大数组的arr[57] = 1,arr[60] = 1,arr[42] = 1,然后遍历这个大数组就行了。

  • 时间复杂度:O(n+k),其中这个k为数据的范围,所以计数排序最适合数据比较集中的数组排序。
  • 稳定性:稳定的算法
  • 代码实现:
void count_sort(int arr[],size_t len)
{int max = arr[0];	//最大值int min = arr[0];	//最小值size_t i;for(i=0;i<len;i++){if(max<arr[i]){	//找到最大值max =arr[i];	}if(min > arr[i]){	//找到最小值min = arr[i];	}}int cnt = max-min+1;		//范围int *prr = malloc(cnt*sizeof(int));	//申请临时空间for(i=0;i<cnt;i++){	//这个临时数组全部置0prr[i] = 0;	}for(i=0;i<len;i++){	//对需要进行排序的序列进行遍历prr[arr[i]-min]++;		//让下标为(arr[i]-min)的临时大数组的值+1}size_t j=0;for(i=0;i<cnt;i++){	//遍历这个临时数组while(prr[i]){	//如果这个数组下标为i的值不等于0arr[j++] = i+min;	//那就让需要进行排序的数组的值为i+min;--prr[i];}	}free(prr);		//释放掉申请的动态内存
}

11.桶排序

思路:工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。
这是一种以消耗大量空间来换取高效率的排序方式,

  • 时间复杂度:O(N+C),其中C=N*(logN-logM),M为桶的数量。所以对于桶排序,桶的数量越多,其排序效率越高。
  • 稳定性:稳定的算法

代码实现:
首先定义桶这个类型:

typedef struct Bucket
{int vect[100];	//其实这里使用链表更好,但是我比较懒,就懒得用链表了int cnt;	//当前桶内存放数据的个数
}Bucket;void bucket_sort(int arr[],size_t len)
{int min = arr[0];int max = arr[0];size_t i;for(i=0;i<len;i++){if(min>arr[i]){	//找到最小值min = arr[i];	}if(max<arr[i]){	//找到最大值max = arr[i];	}}int size = max-min+1;Bucket bucket[5] = {};	//其实桶可以动态规划,但为了方便我这里直接分为5个桶for(i=0;i<len;i++){	//遍历待排序的数组,把每个元素放到相应的桶当中,//比如[0,200]之间的元素放到下标为0的桶中,[201,400]之间的元素放到下标为1的桶中..//以此类推,直到放完所有的数据int index = (arr[i]-min)/(size/5);	//用来判断当前元素arr[i]需要放到哪个桶当中bucket[index].vect[bucket[index].cnt++] = arr[i];}size_t j=0,k=0;for(i=0;i<5;i++){	//对这五个桶进行遍历count_sort(bucket[i].vect,bucket[i].cnt);	//首先对这个桶内的元素进行排序,//这里可以调用其他排序方法,也可以递归调用当前排序方法,但是为了节省内存,我选择调用其他排序方法,for(j=0;j<bucket[i].cnt;j++){arr[k++] = bucket[i].vect[j];	//对排序好的桶进行遍历,并且把里面的元素复制到arr中去	}}
}

12.基数排序

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

解法:

1.首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中;

2.接下来将这些桶子中的数值重新串接起来,接着再进行一次分配,这次是根据十位数来分配;

3.接下来将这些桶子中的数值重新串接起来,持续进行以上的动作直至最高位数为止。

  • 时间复杂度:设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(radix),共进行d趟分配和收集。
  • 稳定性:稳定的算法;

代码实现:
还是定义桶的类型:

typedef struct Bucket{int vect[100];	//同样的可以用链表int cnt;
}Bucket;void base_sort(int arr[],size_t len)
{size_t i;Bucket bucket[10] = {};	//十个桶int max = arr[0];for(i=0;i<len;i++){	//寻找最大值,就可以判断最大值的位数if(arr[i]>max){max = arr[i];	}	}size_t j,k;int num = 1;	//用来获得相应位数上的数字的关键参数,//比如要获得个位上的参数时num = 1;//获得十位上的数字时num = 10;//以此类推do{for(i=0;i<len;i++){	//遍历待排序的数组,把每个元素放入相应的桶中//比如251,当获得个位上的数字时,251放到下标为1的桶当中//当获得十位上的数字时,251放到下标为5的桶当中//当获得百位上的数字时,251放到下标为2的桶当中//当获得千位上的数字时,251放到下标为0的桶当中//以此类推int index = arr[i]/num%10;	//获得相应位数上的数字bucket[index].vect[bucket[index].cnt++] = arr[i];	//把这个数字放到相应的桶中}k=0;for(i=0;i<10;i++){for(j=0;j<bucket[i].cnt;j++){arr[k++] = bucket[i].vect[j];	//把这些桶按顺序依次遍历,//把桶中的元素重新放回arr当中}	bucket[i].cnt = 0;	//记得让桶中的cnt变为0,方便下一次存放}num*=10;	//num*10}while(max/=10);//循环条件
}

=========以上内容来自:用C语言完整实现12种排序方法_c语言快速排序解决排序问题-CSDN博客============

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

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

相关文章

服务攻防-端口协议桌面应用QQWPS等RCEhydra口令猜解未授权检测

知识点&#xff1a; 1、端口协议-弱口令&未授权&攻击方式等 2、桌面应用-社交类&文档类&工具类等 章节点&#xff1a; 1、目标判断-端口扫描&组合判断&信息来源 2、安全问题-配置不当&CVE漏洞&弱口令爆破 3、复现对象-数据库&中间件&…

【Jenkins】配置及使用|参数化|邮件|源码|报表|乱码

目录 一、Jenkins 二、Jenkins环境搭建 1、下载所需的软件包 2、部署步骤 3、其他 三、Jenkins全局设置 &#xff08;一&#xff09;Manage Jenkins——Tools系统管理->全局工具配置分别配置JDK、Maven、Allure、Git&#xff0c;可以配置路径或者直接选择版本安装 1…

判断当前设备是不是安卓或者IOS?

代码(重要点): 当前文件要是 xxx.js文件,就需要写好代码后调用才会执行: // 判断是不是安卓 const isAndroid () > {return /android/.test(navigator.userAgent.toLowerCase()); }// 判断是不是ios const isIOS () > {return /iphone|ipad|ipod/.test(navigator.use…

JUC并发编程01——进程,线程(详解),并发和并行

目录 1.进程和线程的概念及对比1.进程概述 2.线程3.对比 2.并行与并发1.并发2.并行 3.线程详解3.1.创建和运行线程3.1.1.Thread3.1.2.Runnable结合Thread 创建线程3.1.3.Callable 3.2线程方法APIrun startsleep yieldjoininterrupt打断线程打断 park终止模式 daemon不推荐使用的…

Kotlin 协程:用源码来理解 ‘viewModelScope‘

Kotlin 协程&#xff1a;用源码来理解 ‘viewModelScope’ Kotlin 协程是 Kotlin 语言的一大特色&#xff0c;它让异步编程变得更简单。在 Android 开发中&#xff0c;我们经常需要在后台线程执行耗时操作&#xff0c;例如网络请求或数据库查询&#xff0c;然后在主线程更新 UI…

坚持刷题 | 完全二叉树的节点个数

Hello&#xff0c;大家好&#xff0c;我是阿月&#xff01;坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天刷&#xff1a;完全二叉树的节点个数 题目 222.完全二叉树的节点个数 代码实现 class TreeNode {int val;TreeNode left, right;public TreeNode(int val) …

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?

我记得前年&#xff08;2022&#xff09;面试的时候有被问到 ViewPager 和 ViewPager2 有什么区别&#xff1f;当时因为之前工作一直在开发售货机相关的项目&#xff0c;使用的技术要求并不高&#xff0c;所以一直没去了解过 ViewPager2~ 去年的时候正好有相关的功能需求&#…

数学建模 - 线性规划入门:Gurobi + python

在工程管理、经济管理、科学研究、军事作战训练及日常生产生活等众多领域中&#xff0c;人们常常会遇到各种优化问题。例如&#xff0c;在生产经营中&#xff0c;我们总是希望制定最优的生产计划&#xff0c;充分利用已有的人力、物力资源&#xff0c;获得最大的经济效益&#…

pytorch_car_caring 排坑记录

pytorch_car_caring 排坑记录 任务踩坑回顾简单环境问题代码版本问题症状描述解决方法 cuda问题&#xff08;异步问题&#xff09;症状描述解决方法 任务 因为之前那个MPC代码跑出来的效果不理想&#xff0c;看了一天代码&#xff0c;大概看明白了&#xff0c;但要做改进还要有…

R-YOLO

Abstract 提出了一个框架&#xff0c;名为R-YOLO&#xff0c;不需要在恶劣天气下进行注释。考虑到正常天气图像和不利天气图像之间的分布差距&#xff0c;我们的框架由图像翻译网络&#xff08;QTNet&#xff09;和特征校准网络&#xff08;FCNet&#xff09;组成&#xff0c;…

【数睿】数睿常见问题处理

连接器请求地址修改 cat /home/sdata2/tomcat/bin/setenv.sh修改里面的 SYSTEM_URL 为数睿服务实际访问地址 如图所示 连接器执行 异常日志 2024-01-23 18:01:49,586 (conf-file-poller-0) [ERROR - org.apache.flume.node.PollingPropertiesFileConfigurationProvider$Fil…

全链路压测的关键点是什么?

全链路压测是一种重要的性能测试方法&#xff0c;用于评估应用程序或系统在真实生产环境下的性能表现。通过模拟真实用户行为和流量&#xff0c;全链路压测能够全面评估系统在不同负载下的稳定性和性能表现。本文将介绍全链路压测的关键点&#xff0c;以帮助企业更好地理解和应…

Redis核心技术与实战【学习笔记】 - 10.浅谈CPU架构对Redis性能的影响

概述 可能很多人都认为 Redis 和 CPU 的关系简单&#xff0c;Redis 的线程在 CPU 上运行&#xff0c;CPU 快 Reids 处理请求的速度也很快。 其实&#xff0c;这种认知是片面的&#xff0c;CPU 的多核架构及多 CPU 结构&#xff0c;也会影响到 Redis 的性能。如果不了解 CPU 对…

【目标检测】对DETR的简单理解

【目标检测】对DETR的简单理解 文章目录 【目标检测】对DETR的简单理解1. Abs2. Intro3. Method3.1 模型结构3.2 Loss 4. Exp5. Discussion5.1 二分匹配5.2 注意力机制5.3 方法存在的问题 6. Conclusion参考 1. Abs 两句话概括&#xff1a; 第一个真正意义上的端到端检测器最…

实习日志10

1.用户信息 1.1.在用户管理中编辑用户信息 1.2.绑定公司id 1.3.显示在页面 2.修改识别逻辑 2.1.分析 先识别&#xff0c;再判断&#xff0c;清空键把识别结果清空 2.2.写码 修改了发票识别逻辑&#xff0c;略... 3.接高拍仪 3.1.js引入报错 分析&#xff1a; 遇到的错误…

MySQL数据库基础第一篇(SQL通用语法与分类)

文章目录 一、SQL通用语法二、SQL分类三、DDL语句四、DML语句1.案例代码2.读出结果 五、DQL语句1.DQL-基本查询2.DQL-条件查询3.DQL-聚合函数4.DQL-分组查询5.DQL-排序查询6.DQL-分页查询7.DQL语句-执行顺序1.案例代码2.读出结果 六、DCL语句1.DCL-管理用户2.DCL-权限控制1.案例…

鸿蒙开发-UI-页面路由

鸿蒙开发-UI-组件 鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 文章目录 一、基本概念 二、页面跳转 1.router基本概念 2.使用场景 3.页面跳转参数传递 三、页面返回 1.普通页面返回 2.页面返回前增加一个询问框 1.系统默认询问框 2.自定义询问框 总…

【Mysql】数据库架构学习合集

目录 1. Mysql整体架构1-1. 连接层1-2. 服务层1-3. 存储引擎层1-4. 文件系统层 2. 一条sql语句的执行过程2-1. 数据库连接池的作用2-2. 查询sql的执行过程2-1. 写sql的执行过程 1. Mysql整体架构 客户端&#xff1a; 由各种语言编写的程序&#xff0c;负责与Mysql服务端进行网…

【安装记录】Chrono Engine安装记录

本文仅用于个人安装记录。 官方安装教程 https://api.projectchrono.org/8.0.0/tutorial_install_chrono.html Windows下安装 windows下安装就按照教程好了。采用cmake-gui进行配置&#xff0c;建议首次安装只安装核心模块。然后依此configure下irrlicht&#xff0c;sensor…

J-Link:STM32使用J-LINK烧录程序,其他MCU也通用

说明&#xff1a;本文记录使用J-LINK烧录STM32程序的过程。 1. J-LINK驱动、软件下载 1、首先拥有硬件J-Link烧录器。 2、安装J-Link驱动程序SEGGER 下载地址如下 https://www.segger.com 直接下载就可以了。 2.如何使用J-LINK向STM32烧写程序 1、安装好以后打开J-LINK Fl…