爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      【数据结构】排序算法大总结

      首页 知识中心 数据库 文章详情页

      【数据结构】排序算法大总结

      2024-05-28 08:15:10 阅读次数:49

      排序算法,数据结构

       

      1. 排序的概念及运用

      🐶 排序的概念:

      排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
      排序的稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
      内部排序:数据元素全部放在内存中的排序。
      外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

      😼 排序运用:

      排序在生活中随处可见,比如,商品的价格排序,中国各大高校的排名,福布斯2022全球富豪榜等等。

      【数据结构】排序算法大总结
      【数据结构】排序算法大总结

      🐹 常见的排序算法:
      【数据结构】排序算法大总结


      2. 常见排序算法的实现

      2.1 插入排序

      插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

      2.1.1 直接插入排序

      🐶 排序思想:

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


      直接插入排序时间复杂度为O(logN)的算法,它有着稳定和速度快的优点,缺点是比较次数越少,插入点后的数据移动就越多,特别是数据庞大的时候就需要大量的移动数据。

      🐱 代码实现:

      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 (a[end] > tmp)
      			{
      				a[end + 1] = a[end];
      				end--;
      			}
      			else
      			{
      				break;
      			}
      		}
      		a[end + 1] = tmp;
      	}
      }
      

      2.1.2 希尔排序

      🐶 排序思想:

      希尔排序法又称缩小增量法。是直接插入排序的改进版本,先选定一个整数gap,将其值赋为数据量的个数,然后将数据分为以gap为间隔的组先进行预排序。

      【数据结构】排序算法大总结
      预排序的规则和直接插入排序很相似,只不过直接插入排序是每次将相邻的两个数据进行比较并插入,而希尔排序则是每次将下标为n和n+gap的两个数据进行比较并插入,每一趟比较完成之后,gap变为gap/2或者gap/3+1,直到gap=1循环结束。

      当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序了。最后一趟经过gap==1的直接插入排序后,数组就成功变成了有序。

      希尔排序的时间复杂度并不是很好计算,因此有许多不同的书籍给出的结论都有所差异,大约在O(n1.25)~O(1.6*n1.25) 之间。这里我们就折中一下记作O(n1.3) 。

      🐱 代码实现:

      void ShellSort(int* a, int n)
      {
      	int gap = n;
      	while (gap > 1)
      	{
      		gap = gap / 3 + 1;
      		for (int i = 0; i < n - gap; i++)
      		{
      			int end = i;
      			int tmp = a[end + gap];
      			while (end >= 0)
      			{
      				if (a[end] > tmp)
      				{
      					a[end + gap] = a[end];
      					end -= gap;
      				}
      				else
      				{
      					break;
      				}
      			}
      			a[end + gap] = tmp;
      		}
      	}
      }
      

      2.2 选择排序

      选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。

      2.2.1 直接选择排序

      🐶 排序思想:

      直接选择排序思想是对每个下标i,从i后面的元素中选择最小的那个和s[i]交换。

      首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。这里我们每次都选择出一个最大值和最小值,每次将最大值与待排序的区间的最前面的位置交换,将最小值与待排序的区间的最后面的位置交换。

      这里我们需要注意的是:如果已经将最小值和待排序区间最前面的位置交换后,发现最前面的位置和最大值的位置发生了冲突,需要特殊判断并处理一下。


      直接选择排序的时间复杂度是O(N^2),它的思考非常好理解,但是效率不是很好。所以实际中很少使用。

      🐱 代码实现:

      void SelectSort(int* a, int n)
      {
      	int begin = 0, end = n - 1;
      	while (begin < end)
      	{
      		int maxi = begin;
      		int mini = begin;
      		for (int i = begin+1; i <= end; i++)
      		{
      			if (a[i] < a[mini])
      			{
      				mini = i;
      			}
      			if (a[i] > a[maxi])
      			{
      				maxi = i;
      			}
      		}
      		Swap(&a[mini], &a[begin]);
      		if (begin == maxi)
      			 maxi = mini;
      		Swap(&a[maxi], &a[end]);
      		begin++;
      		end--;
      	}
      }
      

      2.2.2 堆排序

      🐶 排序思想:

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

      由于博主在之前的博客中已经详细介绍过了堆排序,所以在这里就不再仔细分析建堆的过程了,如果有需请参考:二叉树&&优先级队列——堆

      这里我们简单来分析一下堆排序的思路,每次取堆顶数据和最后一个数据进行交换,然后再将除了最后一个元素外的所有元素进行向下调整使其再次成为一个堆。直到待调整的堆中的元素个数为0。


      堆排序使用了堆来选数。效率高了很多,它的时间复杂度为O(N*logN)

      🐱 代码实现:

      //交换函数
      void Swap(int* a, int* b)
      {
      	int tmp = *a;
      	*a = *b;
      	*b = tmp;
      }
      //向下调整建大堆
      void AjustDown(int* a, int parent, int n)
      {
      	int child = parent * 2 + 1;
      	while (child < n)
      	{
      		if (child + 1 < n && a[child + 1] > a[child])
      			child++;
      		if (a[parent] < a[child])
      		{
      			Swap(&a[parent], &a[child]);
      			parent = child;
      			child = parent * 2 + 1;
      		}
      		else
      		{
      			break;
      		}
      	}
      }
      //堆排序
      void HeapSort(int* a, int n)
      {
      	//先建堆
      	for (int i = (n - 2) >> 1; i >= 0; i--)
      	{
      		AjustDown(a, i, n);
      	}
      	//堆排序
      	int size = n - 1;
      	while (size > 0)
      	{
      		Swap(&a[0], &a[size]);
      		AjustDown(a, 0, size);
      		size--;
      	}
      }
      

      2.3 交换排序

      所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

      2.3.1 冒泡排序

      🐶 排序思想:

      冒泡排序是交换排序中的一种简单的排序方法,他的思想是对所有相邻记录的关键值进行比较,如果是逆序(a[j]>a[j+1])就将其交换,最终达到有序化。

      每经一趟冒泡排序,都使无序区中关键值最大的记录进入有序区,对于由n个记录组成的记录序列,最多经过n-1趟冒泡排序,就可以将这n个记录重新按照关键字顺序排列。


      在冒泡排序中,第1轮需要比较n-1次,第2轮需要比较n-2次……第n-1轮需要比较1次。因此,总的比较次数为(n-1)+(n-2)+…+1≈n2/2。所以冒泡排序的时间复杂度是O(n2)。

      🐱 代码实现:

      void BubbleSort(int* a, int n)
      {
      	for (int i = 0; i < n-1; i++)
      	{
      		int flag = 0;
      		for (int j = 0; j < n-i-1; j++)
      		{
      			if (a[j + 1] < a[j]) {
      				Swap(&a[j + 1], &a[j]);
      				flag = 1;
      			}
      		}
      		if (flag == 0)
      			break;
      	}
      }
      

      2.3.1 快速排序

      快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:

      任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

      这里我们先来写一下非递归版本的整体代码框架,然后再来写单趟排序。

      void QuickSort(int* a, int begin, int end)
      {
      	if (begin >= end)
      		return;
      	int keyi = PartSort1(a, begin, end);
      	QuickSort(a, begin, keyi - 1);
      	QuickSort(a, keyi + 1, end);
      }
      

      小区间优化

      🐶 小区间优化:

      如果数据量特别大的时候,那么快排的递归调用次数将会非常的多,为了解决这个问题,我们可以使用小区间优化的方式,就是如果发现待排序区间的元素个数<=10时,我们可以使用直接插入排序来进行排序。这样就可以减少很多次递归调用来提升程序的性能。

      🐱 三数取中:

      如果我们每次都取最左边的元素作为key值,如果要是待排序的数有序或者接近有序的时候,那么我们每次递归左区间的长度为0,右区间的长度为n-1,这样的话递归的深度就极有可能变为n。但如果要是数据量过大的话递归深度太深就容器造成栈溢出。这里使用一种三数取中的方法进行优化。

      //三数取中的代码
      int GetMidIndex(int* a, int begin, int end)
      {
      	int mid = (begin + end) >> 1;
      	if (a[begin] < a[mid])//a[begin]<a[mid]
      	{
      		if (a[mid] < a[end])
      			return mid;
      		else if (a[end] < a[begin])
      			return begin;
      		else
      			return end;
      	}
      	else//a[begin]>a[mid]
      	{
      		if (a[mid] > a[end])
      			return mid;
      		else if (a[end] > a[begin])
      			return begin;
      		else
      			return end;
      	}
      }
      
      //小区间优化
      void QuickSort(int* a, int left, int right)
      {
      
      	 if (right <= left)
      	  return;
      
       	//小区间优化——当递归到元素个数小于等于10的区间时,为了提高效率直接使用插入排序
      	 if ((right - left) + 1 <= 10)  
      	 {
      	  	InsertSort(a + left, right - left + 1);
      	 }
      	 else
      	 {
      		  int keyi = PartSort1(a, left, right);
      		  //递归左区间
      		  QuickSort(a, left, keyi - 1);
      		  //递归右区间
      		  QuickSort(a, keyi + 1, right);
      	 }
      }
      
      hoare版本

      🐶 排序思想:

      hoare版本的思想:取最左边的元素作为key,这里我们用a[keyi]表示,定义左指针和右指针分别指向待排序区间的左端点和右端点,然后右指针先走,找比key小的值,找到后左指针开始走,找比key大的值,找到后停下来,然后交换左右指针位置的值。

      然后右指针继续往左走找小,左指针继续往右走找大,这个过程反复进行,当左指针的值小于右指针时,循环会一直进行,当左指针和右指针相遇时,循环结束,然后交换key和左右指针相遇的位置的值,自此,单趟排序结束,
      然后将左右指针相遇的位置返回,分割出左右子区间,分别进行左右子区间的递归单趟过程。

      快速排序的最好的时间复杂度为O(NlogN),最坏的时间复杂度为O(N2),平均时间复杂度为O(NlogN),快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序。

      🐱 代码实现:

      int PartSort1(int *a,int begin,int end)//Hoare版本
      {
      	int mid = GetMidIndex(a, begin, end);
      	Swap(&a[begin], &a[end]);
      	int left = begin;
      	int right = end;
      	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[left], &a[keyi]);
      	keyi = left;
      	return keyi;
      }
      

      挖坑法

      🐶 思路:

      挖坑法是快排的第二种写法,在这里我们需要注意的是:使用挖坑法时,必须多定义一个变量hole来记录坑的位置。同时,与hoare不同的是:挖坑法中直接使用key表示key值,而不是用下标的方法。

      还是一样,取最左边的元素作为key,然后先定义坑hole的位置为left的位置,然后right先走,找到比key小的数后停下来,然后将a[right]填到坑里,也就是,a[hole] = a[right],然后再将right位置变成新的坑,hole = right,然后左指针left开始向右边走找比key大的数,找到后,将a[right]填到坑里,a[hole] = a[right],然后将left的位置变成新的坑,hole = left,和上面一样,只要left<right,循环此过程,当left和right相遇时,跳出循环,将key填到新的坑的位置(也就是left和right相遇的位置),最后返回坑hole的下标。

      🐱 代码实现:

      //挖坑法
      int PartSort2(int* a, int begin, int end)
      {
      	int mid = GetMidIndex(a, begin, end);
      	Swap(&a[begin], &a[end]);
      	int left = begin;
      	int right = end;
      	int key = a[left];
      	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;
      }
      

      前后指针法

      🐶 思路:

      快排最后一种写法是前后指针法,这里我们需要定义三个变量:keyi = left、prev = left 和 cur = left+1。

      其中的 keyi 代表 key 值所在的下标,而 prev 和 cur 。我们让 cur 先走,当找到小于 a[keyi] 的元素时停下来,然后先让 prev++,再判断 prev 是否等于 cur,如果不等于就交换二者对应元素的值,然后重复前面的步骤,直到 cur > right;最后交换 a[keyi] 和 a[prev] 。

      🐱 代码实现:

      int PartSort3(int* a, int begin, int end)
      {
      	int mid = GetMidIndex(a, begin, end);
      	Swap(&a[begin], &a[end]);
      	int keyi = begin;
      	int prev = begin, cur = begin + 1;
      	while (cur <= end)
      	{
      		if (a[cur] < a[keyi] && ++prev != cur)
      			Swap(&a[cur], &a[prev]);
      		cur++;
      	}
      	Swap(&a[prev], &a[keyi]);
      	keyi = prev;
      	return keyi;
      }
      

      2.3.2 快排非递归

      🐶 思路:

      我们知道,任何一个算法只要它可以递归肯定是也可以进行非递归的,当我们把快排的递归版本写完后,接下来让我们看一下它的非递归版本:
      这里我们需要借助一个栈来完成非递归的实现。
      【数据结构】排序算法大总结
      🐱 代码实现:

      void QuickSortNonR(int* a, int begin, int end)
      {
      	ST st;
      	StackInit(&st);
      	StackPush(&st, begin);
      	StackPush(&st, end);
      
      	while (!StackEmpty(&st))
      	{
      		int right = StackTop(&st);
      		StackPop(&st);
      		int left = StackTop(&st);
      		StackPop(&st);
      
      		int keyi = PartSort3(a, left, right);
      		// [left, keyi-1] keyi [keyi+1, right]
      		if (keyi + 1 < right)
      		{
      			StackPush(&st, keyi + 1);
      			StackPush(&st, right);
      		}
      
      		if (left < keyi - 1)
      		{
      			StackPush(&st, left);
      			StackPush(&st, keyi - 1);
      		}
      	}
      	StackDestroy(&st);
      }
      

      2.4 归并排序

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

      2.4.1 归并排序递归

      🐶 排序思想:

      归并排序中,我们先找到数组的中间下标mid,然后以这个mid为中心,对两边分别进行排序,之后我们再根据两边已排好序的子数组,重新开一块空间进行合并,合并完成后在将新的空间中已经排好的数据拷贝回原数组。当然我们需要借助递归的思想,不断分割左右区间并进行上面的操做,直到左右区间不能再分割了为止,然后就开始了归并并回退继续归并的操作。

      【数据结构】排序算法大总结

      归并排序无论在什么情况下的时间复杂度都是O(N*logN),它的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

      🐱 代码实现:

      //归并排序子函数
      void _MergeSort(int* a, int begin, int end, int* tmp)
      {
      	if (begin >= end)
      		return;
      	int mid = begin + end >> 1;
      	//递归左右子区间
      	_MergeSort(a, begin, mid, tmp);
      	_MergeSort(a, mid + 1, end, tmp);
      
      	//归并过程
      	int begin1 = begin, end1 = mid;
      	int begin2 = mid + 1, end2 = end;
      	int i = begin;
      	while (begin1 <= end1 && begin2 <= end2)
      	{
      		if (a[begin1] < a[begin2])
      		{
      			tmp[i++] = a[begin1++];
      		}
      		else
      		{
      			tmp[i++] = a[begin2++];
      		}
      	}
      	while (begin1 <= end1) tmp[i++] = a[begin1++];
      	while (begin2 <= end2) tmp[i++] = a[begin2++];
      	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
      }
      //归并排序
      void MergeSort(int* a, int n)
      {
      	int* tmp = (int *)malloc(sizeof(int) * n);
      	if (!tmp) {
      		perror("malloc fail;;");
      		exit(-1);
      	}
      	_MergeSort(a, 0, n - 1, tmp);
      	free(tmp);
      	tmp = NULL;
      }
      

      2.4.1 归并排序非递归

      🐶 排序思想:

      归并排序非递归的实现需要借助递归版本归并的逆过程,大概思路就是这样的:我们先让序列中的两个为一组的相邻的元素有序,也就是两两归并,然后四个四个归并,八个八个归并,最后直至所有的元素都有序为止,虽然这个思路听起来很容易但是代码却不是很好写,因为可能在两两归并,四四归并…的过程中左右区间可能会出现越界的情况,这就需要我们自己在写代码的过程中进行特判设置跳出循环或者修正区间来进行避免这种情况的发生。

      【数据结构】排序算法大总结
      🐱 代码实现:

      void MergeSortNonR2(int* a, int n)
      {
      	int* tmp = (int*)malloc(sizeof(int) * n);
      	if (!tmp) {
      		perror("malloc fail::");
      		exit(-1);
      	}
      	int rangeN = 1;
      	// 归并每组数据个数,从1开始,因为1个认为是有序的,可以直接归并
      	while (rangeN < n)
      	{
      		for (int i = 0; i < n; i += 2 * rangeN)
      		{
      			int begin1 = i, end1 = i + rangeN - 1;
      			int begin2 = i + rangeN, end2 = i + 2 * rangeN - 1;
      			int j = i;
      			//区间越界直接跳出循环
      			if (end1 >= n)
      				break;
      			else if (begin2 >= n)
      				break;
      			else if (end2 >= n)
      				end2 = n - 1;
      			printf("[%d,%d][%d,%d]\n", begin1, end1, begin2, end2);
      			while (begin1 <= end1 && begin2 <= end2)
      			{
      				if (a[begin1] < a[begin2])
      				{
      					tmp[j++] = a[begin1++];
      				}
      				else
      				{
      					tmp[j++] = a[begin2++];
      				}
      			}
      			while (begin1 <= end1) tmp[j++] = a[begin1++];
      			while (begin2 <= end2) tmp[j++] = a[begin2++];
      			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
      		}
      		rangeN *= 2;
      	}
      	free(tmp);
      	tmp = NULL;
      }
      

      2.5 非比较排序

      计数排序

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

      1. 统计相同元素出现次数
      2. 根据统计的结果将序列回收到原来的序列中

      🐶 排序思想:

      开一个额外的数组来统计原来数组中每个数字出现的次数,新开的数组的下标使用原数组的值,遍历原数组,数组中每个元素出现了几次,就在对应下标的位置++。

      遍历原来的数组,当原数组遍历一遍后就将元素组中每个数字出现的次数一一映射到了新开的数组中。

      遍历新数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填充到原数组中去,每处理一次,新数组中的该元素值减1,直到该元素值不大于0,依次处理新数组中剩下的元素。最后将新开辟的数组释放掉即可。

      这里我们可以优化一下使用相对位置作为映射的方式,这样可以相对节省空间,先遍历一边数组,然后找到最大值和最小值,开辟空间的大小为最大-最小+1的空间。计数的时候用该元素-最小值作为映射的下标,第二次排序的时候下标+最小值就是原来数组中的元素。 计数排序的时间复杂度为O(MAX(N,范围)), 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。仅仅适用于整形数据的排列。

      🐱 代码实现:

      void CountSort(int* a, int n)
      {
      	int max = a[0];
      	int 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* count = (int*)calloc(max - min + 1, sizeof(int));
      	if (!count)
      	{
      		perror("calloc fail::");
      		exit(-1);
      	}
      	for (int i = 0; i < n; i++)
      		count[a[i] - min]++;
      	int index = 0;
      	for (int i = 0; i < max-min+1; i++)
      	{
      		while (count[i]--)
      		{
      			a[index++] = i + min;
      		}
      	}
      }
      

      3. 排序算法复杂度及稳定性分析

      【数据结构】排序算法大总结
      【数据结构】排序算法大总结


      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://chenjiale.blog.csdn.net/article/details/128468261,作者:星河万里᭄ꦿ࿐,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:最短独占单词缩写。 给一个字符串数组strs和一个目标字符串target。target的简写不能跟strs打架。

      下一篇:考研数据结构之树(6.10)——练习题之输出先序遍历序列中第k个结点的值(C表示)

      相关文章

      2025-05-19 09:04:14

      【牛客网+LeetCode】链表 OJ强训题——高效解法

      【牛客网+LeetCode】链表 OJ强训题——高效解法

      2025-05-19 09:04:14
      数据结构 , 链表
      2025-05-19 09:04:14

      《剑指Offer》按之字形顺序打印二叉树——最容易理解的思路,两分钟学会~

      《剑指Offer》按之字形顺序打印二叉树——最容易理解的思路,两分钟学会~

      2025-05-19 09:04:14
      二叉树 , 数据结构
      2025-05-19 09:04:14

      《剑指Offer》二叉搜索树的第k个节点——真没你想象中那么难,一招教你秒杀它~

      《剑指Offer》二叉搜索树的第k个节点——真没你想象中那么难,一招教你秒杀它~

      2025-05-19 09:04:14
      二叉树 , 数据结构
      2025-05-09 08:20:32

      MySQL——索引(概述和结构介绍)

      索引(index)是帮助 MySQL 高效获取数据的数据结构(是一种有序的数据结构)。

      2025-05-09 08:20:32
      Tree , 存储 , 引擎 , 数据结构 , 查询 , 索引 , 结构
      2025-05-07 09:10:01

      DS初阶:顺序表的实现

      DS初阶:顺序表的实现

      2025-05-07 09:10:01
      函数 , 指针 , 数据 , 数据结构 , 数组 , 空间 , 顺序
      2025-04-18 07:11:40

      Java数据结构之《最短路径》

      Java数据结构之《最短路径》

      2025-04-18 07:11:40
      代码 , 数据结构 , 样例 , 路径 , 输入 , 输出 , 顶点
      2025-04-15 09:19:55

      Redis经典问题:BigKey问题

      在Redis中,每个Key都会对应一个Value,而这个Value的大小会影响Redis的性能表现。当我们存储的Value特别大时,就会出现BigKey问题。

      2025-04-15 09:19:55
      Key , Redis , 数据结构 , 系统 , 缓存 , 问题
      2025-04-15 09:19:45

      文心一言 VS 讯飞星火 VS chatgpt (262)-- 算法导论20.1 1题

      在Go语言中,如果你想要一个数据结构支持重复的关键字(或键),你不能简单地使用内建的map,因为map在Go中是基于键的唯一性设计的。

      2025-04-15 09:19:45
      map , 关键字 , 数据结构 , 示例 , 重复
      2025-04-15 09:19:45

      文心一言 VS 讯飞星火 VS chatgpt (263)-- 算法导论20.1 2题

      在Go语言中,为了支持带有卫星数据的关键字,我们可以定义一个结构体(struct)来表示这个关键字,其中可以包含一个字段用于存储关键字本身,以及另一个字段用于存储与该关键字相关联的卫星数据。

      2025-04-15 09:19:45
      关键字 , 存储 , 数据 , 数据结构
      2025-04-14 09:26:51

      STL详解(九)—— priority_queue的使用与模拟实现

      优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中的元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。

      2025-04-14 09:26:51
      c++ , stl , 数据结构
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5239098

      查看更多

      最新文章

      【牛客网+LeetCode】链表 OJ强训题——高效解法

      2025-05-19 09:04:14

      《剑指Offer》按之字形顺序打印二叉树——最容易理解的思路,两分钟学会~

      2025-05-19 09:04:14

      《剑指Offer》二叉搜索树的第k个节点——真没你想象中那么难,一招教你秒杀它~

      2025-05-19 09:04:14

      DS初阶:顺序表的实现

      2025-05-07 09:10:01

      文心一言 VS 讯飞星火 VS chatgpt (262)-- 算法导论20.1 1题

      2025-04-15 09:19:45

      STL详解(九)—— priority_queue的使用与模拟实现

      2025-04-14 09:26:51

      查看更多

      热门文章

      Pandas之:深入理解Pandas的数据结构

      2023-03-22 09:34:26

      数据结构与算法之四 搜索算法

      2022-11-17 12:37:20

      数据结构入门(第一天)

      2023-02-24 09:05:57

      Pandas数据结构

      2023-05-29 10:43:59

      数据结构这进制转换

      2023-05-31 08:43:33

      【算法】双指针、位运算、离散化、合并区间

      2023-07-26 08:09:55

      查看更多

      热门标签

      数据库 mysql 字符串 数据结构 MySQL 算法 redis oracle java sql python 数据 索引 SQL 查询
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      考研数据结构之队列(3.3)——练习题之利用两个栈s1和s2来模拟一个队列,然后利用栈的运算来实现队列的enQueue、deQueue及isQueueEmpty运算(C表示)

      Collection工具类基本使用

      给定一个无向、连通的树 树中有 n 个标记为 0...n-1 的节点以及 n-1 条边 。

      判断链表是否为环形链表

      考研数据结构之线性表(1.4)——循环单链表的操作(C表示)

      Redis底层数据结构的映射关系

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号