手撕八大排序算法(解析源码+图解)

news2024/12/24 0:16:34

八大排序算法


文章目录

  • 八大排序算法
  • 一、插入排序
    • 1.代码实现
    • 2.思路+图解
  • 二、希尔排序
    • 1.代码实现
    • 2.思路+图解
  • 三、选择排序(优化版)
    • 1.代码实现
    • 2.思路+图解
  • 四、堆排序
    • 1.代码实现
    • 2.思路+图解
  • 五、冒泡排序
    • 1.代码实现
    • 2.思路+图解
  • 六、快速排序
    • 1.递归版本
    • 2.非递归版本
    • 3.快速排序的两个优化
  • 七、归并排序
    • 1.递归版本(图解+源码)
    • 2.非递归版本(图解+源码)
    • 3.数组越界问题以及优化
  • 八、计数排序
    • 1.代码实现
    • 2.思路+图解
  • 九、八大排序对比
  • 总结


一、插入排序


1.代码实现

代码如下(示例):

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; ++i)
	{
		// [0,end]有序,把end+1位置的值插入,保持有序
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

2.思路+图解

为了方便画图解,我们直接来排序 9 1 2 这三个数!
在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


二、希尔排序


1.代码实现

代码如下(示例):

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//gap = gap / 2;

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

2.思路+图解

在这里插入图片描述


在这里插入图片描述


以上为希尔排序的第一步:预排序,以下为希尔排序的第二步:直接插入排序
在这里插入图片描述


三、选择排序(优化版)

在这里插入图片描述


1.代码实现

代码如下(示例):

void SelectSort(int* a, int n)
{
	assert(a);
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = 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[begin], &a[mini]);

		// 如果begin和maxi重叠,那么要修正一下maxi的位置
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

2.思路+图解

在这里插入图片描述


四、堆排序

堆排序详细讲解在这里!!!


1.代码实现

代码如下(示例):

void AdjustDwon(int* a, int size, int parent)
{
 int child = parent * 2 + 1;
 while (child < size)
 {
  // 选出左右孩子中小/大的那个
  if (child + 1 < size && 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)
{
 // 建堆方式2:O(N)
 for (int i = (n - 1 - 1) / 2; i >= 0; --i)
 {
  AdjustDwon(a, n, i);
 }
 // O(N*logN)
 int end = n - 1;
 while (end > 0)
 {
  Swap(&a[0], &a[end]);
  AdjustDwon(a, end, 0);
  --end;
 }
}

2.思路+图解

在这里插入图片描述


五、冒泡排序


1.代码实现

代码如下(示例):

void BubbleSort(int* a, int n)
{
	assert(a);

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

2.思路+图解

在这里插入图片描述


在这里插入图片描述


六、快速排序

1.递归版本

(1)Hoare版本



代码如下(示例):

// Hoare
int PartSort1(int* a, int begin, int end)
{
	int left = begin, 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[keyi], &a[left]);
	keyi = left;

	return keyi;
}

在这里插入图片描述


(2)挖坑法



代码如下(示例):

// 挖坑法
int PartSort2(int* a, int begin, int end)
{
	int key = a[begin];
	int piti = begin;
	while (begin < end)
	{
		// 右边找小,填到左边的坑里面去。这个位置形成新的坑
		while (begin < end && a[end] >= key)
		{
			--end;
		}
		a[piti] = a[end];
		piti = end;
		// 左边找大,填到右边的坑里面去。这个位置形成新的坑
		while (begin < end && a[begin] <= key)
		{
			++begin;
		}
		a[piti] = a[begin];
		piti = begin;
	}
	a[piti] = key;
	return piti;
}

在这里插入图片描述


(3)前后指针法



代码如下(示例):

//快速排序(前后指针法)
void QuickSort3(int* a, int begin, int end)
{
	if (begin >= end)//当只有一个数据或是序列不存在时,不需要进行操作
		return;

	//三数取中
	int midIndex = GetMidIndex(a, begin, end);
	Swap(&a[begin], &a[midIndex]);

	int prev = begin;
	int cur = begin + 1;
	int keyi = begin;
	while (cur <= end)//当cur未越界时继续
	{
		if (a[cur] < a[keyi] && ++prev != cur)//cur指向的内容小于key
		{
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	int meeti = prev;//cur越界时,prev的位置
	Swap(&a[keyi], &a[meeti]);//交换key和prev指针指向的内容
	QuickSort3(a, begin, meeti - 1);//key的左序列进行此操作
	QuickSort3(a, meeti + 1, end);//key的右序列进行此操作
}

在这里插入图片描述
在这里插入图片描述


2.非递归版本


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述


代码如下(示例):

//快速排序(非递归实现)
void QuickSortNonR(int* a, int begin, int end)
{
	Stack st;//创建栈
	StackInit(&st);//初始化栈
	StackPush(&st, begin);//待排序列的L
	StackPush(&st, end);//待排序列的R
	while (!StackEmpty(&st))
	{
		int right = StackTop(&st);//读取R
		StackPop(&st);//出栈
		int left = StackTop(&st);//读取L
		StackPop(&st);//出栈
		//该处调用的是Hoare版本的单趟排序
		int keyi = PartSort1(a, left, right);
		if (left < keyi - 1)//该序列的左序列还需要排序
		{
			StackPush(&st, left);//左序列的L入栈
			StackPush(&st, keyi - 1);//左序列的R入栈
		}
		if (keyi + 1 < right)// 该序列的右序列还需要排序
		{
			StackPush(&st, keyi + 1);//右序列的L入栈
			StackPush(&st, right);//右序列的R入栈
		}
	}
	StackDestroy(&st);//销毁栈
}

3.快速排序的两个优化

优化一:三数取中
在这里插入图片描述

在这里插入图片描述
三数取中的核心就是:用 if 和 else语句对数进行判断!!!


代码如下(示例):

int GetMidIndex(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid])
	{
		if (a[mid] < a[end])
		{
			return mid;
		}
		else if (a[begin] < a[end])
		{
			return end;
		}
		else
		{
			return begin;
		}
	}
	else // (a[begin] >= a[mid])
	{
		if (a[mid] > a[end])
		{
			return mid;
		}
		else if (a[begin] < a[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
}

优化二:小区间优化减少递归次数


在这里插入图片描述


在这里插入图片描述


代码如下(示例):

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	if (end - begin > 10)
	{
		int keyi = PartSort2(a, begin, end);
		// [begin, keyi-1] keyi [keyi+1, end]
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
	else
	{
		InsertSort(a + begin, end - begin + 1);
	}
}

七、归并排序

在这里插入图片描述


1.递归版本(图解+源码)

代码如下(示例):

void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;
	// [begin, mid] [mid+1, end] 分治递归,让子区间有序
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);


	//归并 [begin, mid] [mid+1, end]
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin1;
	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, (end - begin + 1) * sizeof(int));
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


2.非递归版本(图解+源码)

代码如下(示例):

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	// 休息11:48继续
	int gap = 1;
	while (gap < n)
	{
		//printf("gap=%d->", gap);
		for (int i = 0; i < n; i += 2 * gap)
		{
			// [i,i+gap-1][i+gap, i+2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			// end1越界或者begin2越界,则可以不归并了
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			//printf("[%d,%d] [%d, %d]--", begin1, end1, begin2, end2);
			int m = end2 - begin1 + 1;
			int j = begin1;
			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) * m);
		}
		gap *= 2;
	}
	free(tmp);
}

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


3.数组越界问题以及优化

当归并排序的数组个数是奇数个时,会出现数组越界问题,以致于程序崩溃


在这里插入图片描述


在这里插入图片描述


代码如下(示例):

	// 越界-修正边界
			if (end1 >= n)
			{
				end1 = n - 1;
				// [begin2, end2]修正为不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				// [begin2, end2]修正为不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if(end2 >= n)
			{
				end2 = n - 1;
			}

八、计数排序

1.代码实现

代码如下(示例):

// 时间复杂度:O(max(range, N))
// 空间复杂度:O(range)
void CountSort(int* a, int n)
{
	int min = a[0], max = a[0];
	for (int i = 1; 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);
	if (count == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	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)
	{
		// 出现几次就会回写几个i+min
		while (count[i]--)
		{
			a[j++] = i + min;
		}
	}
}

2.思路+图解

在这里插入图片描述
在这里插入图片描述


在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


九、八大排序对比


在这里插入图片描述


总结

以上就是今天要讲的内容,本文介绍了校招中重点的八大排序,到这里初阶数据结构就结束了,接下来带来c++和Linux的内容,感谢大家的点赞支持!
在这里插入图片描述

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

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

相关文章

Android之 Bitmap使用

一&#xff0c;简介 1.1 Bitmap是一种图片在内存中的表现形式&#xff0c;不管是png&#xff0c;还是jpg最终都是以bitmap的形式显示到控件上面。 Bitmap是一种位图&#xff0c;位图​是点阵图像​或栅格图像&#xff0c;是由称作像素&#xff08;图片元素&#xff09;的单个…

C++篇----类、封装、类访问权限、类实例化

文章目录 一、面向过程和面向对象二、类 一、面向过程和面向对象 c语言是面向过程的编程语言 c是面向对象的编程语言 面向过程&#xff1a;关注过程&#xff0c;对于求解问题的不走&#xff0c;调用函数逐步解决问题 就洗衣服来说&#xff1a;洗衣服需要放水&#xff0c;倒洗衣…

PCL点云库(2) — IO模块

目录 2.1 IO模块接口 2.2 PCD数据读写 &#xff08;1&#xff09; PCD数据解析 &#xff08;2&#xff09;PCD文件读写示例 2.3 PLY数据读写 &#xff08;1&#xff09;PLY数据解析 &#xff08;2&#xff09;PLY文件读写示例 2.4 OBJ数据读写 &#xff08;1&#xff…

QMS-云质说质量 - 10 我和我的客户投诉(2) - 客户逐利 驱除良币

云质QMS原创 转载请注明来源 作者&#xff1a;王洪石 上策伐谋 中策伐交 前面发过一篇关于客户投诉的文章“逢年过节要祈祷”&#xff0c;引起了很多质量人的共鸣&#xff0c;特别是汽车零部件行业曾经和正在负责客诉的质量同行们。 真实的产品质量问题&#xff0c;是否发生只…

Linux-初学者系列2——用户组管理和权限管理

用户组管理和权限管理 Linux-初学者系列2_用户组管理和权限管理一、所有者1、查看文件的所有者指令 2、修改文件所有者指令实操 二、组创建语法指令&#xff1a;实操&#xff1a; 三、所在组1、查看文件/目录所在组基本指令&#xff1a;实操&#xff1a; 2、修改文件所在组基本…

【读书笔记】高效能人士的7个习惯

高效能人士的7个习惯-史蒂芬柯维 个人成功1. 积极主动&#xff08;BE PROACTIVE&#xff09;2. 以终为始&#xff08;BEGIN WITH THE END IN MIND&#xff09;3. 要事第一&#xff08;PUT FIRST THINGS FIRST&#xff09;个人成功总结 集体成功4. 双赢思维&#xff08;THINK WI…

flex布局属性详解

Flex布局 flex-directionflex-wrapflex-flowjustify-contentalign-itemsalign-content其他orderflexalign-self 含义:Flex是Flexible Box的缩写&#xff0c;意为”弹性布局”&#xff0c;用来为盒状模型提供最大的灵活性。 flex-direction flex-direction属性决定主轴的方向&…

服务(第十二篇)LVS-DR模式

数据包流向分析&#xff1a; &#xff08;1&#xff09;客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。 &#xff08;2&#xff09;Director Server 和 Re…

022 - C++ 析构函数

上期我们讨论了构造函数。认识了它是什么以及如何使用它。如果你没有看上一期&#xff0c;那么你一定要回去看一下。 今天我们要讨论一下它的“孪生兄弟”&#xff0c;析构函数&#xff0c;它们在某些方面非常相似。 构造函数是你创建一个新的实例对象时运行&#xff0c;而析…

无线测温系统在煤矿高压电气设备上的应用

摘要&#xff1a;随着社会经济的不断发展&#xff0c;电力系统向着高电压、高容量的方向前进着&#xff0c;电力系统全新的技术与设备层出不穷&#xff0c;电力的输送能力不断提升。然而&#xff0c;高压电气设备承载的高压电力负荷也让其自身的温升问题成为了威胁电网稳定的元…

张驰咨询:企业如何在不确定的环境中逆势增长?

企业不确定环境主要包括以下几个方面&#xff1a; 1、宏观经济环境的不确定性 包括国内外经济形势、政策调整、外汇汇率等因素的变化&#xff0c;会对企业的发展带来不确定性。 2、市场需求的不确定性 市场需求的变化&#xff0c;包括消费者需求、市场规模、市场结构等方面…

【自制键盘01】CH9329代码两则,让任何单片机都能做键盘

简介 CH9329是一款由WCH&#xff08;Nanjing QinHeng Electronics Co. Ltd.&#xff09;生产的USB转串口芯片&#xff0c;可以方便地将USB接口转换为串口接口&#xff0c;它在键盘设计这块可以实现作为MCU和电脑设备的“中间人”&#xff0c;把串口信号转换为按键。 引脚定义 …

如何实现电脑通过手机上网?1分钟搞定!

案例&#xff1a;电脑没网时&#xff0c;如何通过手机上网&#xff1f; 【想用电脑看电影&#xff0c;但是附近没有Wi-Fi。朋友说可以说电脑可以通过手机上网&#xff0c;但我们都不知道具体如何操作&#xff0c;有没有小伙伴可以教教我们。】 在没有Wi-Fi或有线网络接入时&a…

《Left ventricular hypertrophy detection using electrocardiographic signal》阅读笔记

论文的摘要 Left ventricular hypertrophy (LVH) indicates subclinical organ damage, associating with the incidence of cardiovascular diseases. From the medical perspective, electrocardiogram (ECG) is a low-cost, non-invasive, and easily reproducible tool th…

低代码平台-宜搭的核心概念

宜搭的核心概念 文章目录 全局变量基本的变量使用查看输出内容以及调试方式事件绑定页面生命周期条件渲染循环渲染自定义样式表单校验 全局变量 在左侧数据源中添加变量&#xff0c;添加变量的写法和js的写法一致。 基本的变量使用 给文本绑定数据源&#xff0c;点击左侧刚才定…

蒸发器前氟离子超标的解决方法

深度除氟工艺 1、活性氧化铝&#xff1a;需PH调整至酸性 2、碳基/羟基磷灰石&#xff1a;再生次数有限制 3、反渗透膜&#xff1a;造价成本高 4、特种除氟树脂&#xff1a;预处理需做好 氟化物选择吸附树脂 Tulsimer CH-87 是一款去除水溶液中氟离子的专用的凝胶型选择性…

LVS负载均衡之DR模式

DR调度服务器 192.168.255.128 Nginx节点服务器1 192.168.255.130 Nginx节点服务器2 192.168.255.131 统一虚拟ip&#xff08;vip&#xff09; 192.168.255.188 访问客户端 192.168.255.134 第一步首先关掉所有虚拟机的防火墙 systemctl stop firewalld.service setenfor…

Maya - 后缀为xgen文件导出到虚幻引擎

Xgen是集成在Maya中的工具&#xff0c;可以在指定模型表面生成和控制大量物体的集成和离散&#xff1b;经常用于复杂的毛发制作&#xff0c;可以方便的用笔刷等控制曲线&#xff08;curves&#xff09;和导引线&#xff08;guides&#xff09;等线条来控制毛发的走向&#xff1…

【MySQL】插入文件路径,反斜杠消失

系列文章 C#底层库–MySQL脚本自动构建类&#xff08;insert、update语句生成&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129179216 C#底层库–MySQL数据库访问操作辅助类&#xff08;推荐阅读&#xff09; 本文链接&#xff1a;h…

克隆Linux系统(centos)

克隆前得保证你有一台Linux系统的虚拟机了。 如果没有&#xff0c;可以参考这篇文章&#xff1a; 安装VMware虚拟机、Linux系统&#xff08;CentOS7&#xff09;_何苏三月的博客-CSDN博客 按照示意图一步一步执行即可。 克隆前先关闭运行的虚拟机系统。 然后右键已安装的虚拟…