首页 > 编程语言 >【C】排序算法

【C】排序算法

时间:2024-01-03 21:00:59浏览次数:27  
标签:end int 复杂度 元素 gap 算法 排序

文章介绍了插入排序、希尔排序、选择排序、堆排序、冒泡排序、归并排序的实现思路与使用c编写的代码,同时对排序算法的三个评价要素:时间复杂度、空间复杂度、稳定性,分别进行了具体分析。

1、插入排序

实现思想:确定一个有序的数组,将后续的元素逐一插入此有序数组,确定其相对位置,直到所有元素插入完成; 代码如下:

void InsertSort(int* a, int n)
{
	for (int i = 1; i < n; i++)
	{
		int tmp = a[i];
		int end = i-1;
		// 将有序元素与待插入元素比较,确定其最终插入位置
		while (end >= 0)
		{
			// 若之前的元素大于待插入元素,则将其后移
			if (a[end] > tmp)
				a[end + 1] = a[end--];
			// 否则跳出循环
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

容易发现,当数组越接近有序,元素之间的比较与交换越少,时间效率越高;越接近倒序,发生的交换与比较越多,时间效率越低; 时间复杂度:$O(n^{2})$;第i个元素需要与前$i-1$个元素比较; 空间复杂度:$O(1)$; 稳定性:稳定;

2、希尔排序

实现思想:希尔排序是对插入排序的优化。将原数组的元素按照某个间隔$gap$划分为一组,同组的元素进行插入排序,$gap>1$时,实际上是对数组中的元素进行预排序,从而减少元素之间的比较和交换。   以升序为例,假设有10个元素,最大的元素恰巧在序列头部,此时采用插入排序,确定其最后位置时,需要与剩下的每个元素都进行比较,也就是9次;而采用希尔排序,若$gap=2$,此时需要比较预排序($gap=2$)时的4次,加上最后进行排序时($gap=1$)时的1次,共5次;   代码如下:

void ShellSort(int* a, int n)
{
	int gap = n / 2;
	while (gap > 0)
	{
		// i++时就进行了不同分组的切换
		for (int i = gap; i < n; i++)
		{
			// 在其内部仍然是插入排序的逻辑
			int tmp = a[i];
			int end = i - gap;
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
					break;
			}
			a[end + gap] = tmp;
		}
		// gap慢慢减小到1
		gap /= 2;
	}
}

时间复杂度:$O(nlogn)$; 空间复杂度:$O(1)$; 稳定性:不稳定;当两个相同的元素存在在不同组时,在预排序阶段它们的先后顺序可能会产生变化;

3、选择排序

实现思想:以升序为例,每次找剩余序列中最小的元素,将其插入最终位置; 代码如下:

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
void SelectSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int min = i;
		// 找到后续元素中最小的元素位置
		for (int j = i + 1; j < n; j++)
		{
			if (a[j] <= a[min])
			{
				min = j;
			}
		}
		// 交换
		Swap(&a[i], &a[min]);

时间复杂度:$O(n^2)$; 空间复杂度:$O(1)$; 稳定性:不稳定;进行元素交换时,会变换元素顺序,从而导致不稳定,例如{5,5,1};当5和1进行交换时,两个5的先后顺序已发生变化;

4、堆排序

实现思想:根据堆的性质,升序建大堆,每次选出最大的数,随后调整堆,让次大的数浮到堆顶,依次类推;降序建小堆,原理相同; 代码如下:

// 向下调整成大根堆,前提:左右子树都是堆
void AdjustDwon(int* a, int n, int root)
{
	int child = root * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}
		if (a[child] > a[root])
		{
			Swap(&a[child], &a[root]);
			root = child;
			child = root * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int n)
{
	// 从最后一个分支节点开始建堆
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDwon(a, n, i);
	}
	int end = n - 1;
	// 开始排序,每次出一个元素,随后调整剩下的元素
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);
		end--;
	}
}

时间复杂度:$O(nlogn)$;每次调整的复杂度为$logn$,共需调整n次;建堆的时间消耗小于排序时的消耗,根据时间复杂度的计算规则,略去; 空间复杂度:$O(1)$;原地调整堆排序即可,无需更多空间; 稳定性:不稳定;在调整堆再到排序的过程中,原在前的数可能会排序到后方,例如21 20a 20b 12 11 8 7,堆排序后为7 8 11 12 20a 20b 21(其中a,b仅为区分顺序)。

5、冒泡排序

实现思想:每趟排序都会将最大(小)的数据放到队尾,临近的两个数相比,将大(小)的数继续往后比,比较完成也就选出了最大(小)的元素;如此继续,经过n-1趟排序,序列便会有序; 增加优化:当一趟排序已经不发生交换时,此时序列已经有序,不用在进行多余比较,可防止元素有序后仍进行比较的时间消耗; 代码:

void BubbleSort(int* a, int n)
{
	for (int i = n - 1; i >= 0; i--)
	{
		int exchange = 0;
		for (int j = 0; j < i; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
				exchange = 1;
			}
		}
		// 一趟排序中,未发生交换,表明元素已有序,无须继续
		if (exchange == 0)
		{
			break;
		}
	}
}

时间复杂度:$O(n^2)$;共需要排序$n-1$趟,每次需要比较$n-i$次,其中i为排序的趟数; 空间复杂度:$O(1)$;原地排序,无需更多空间; 稳定性:稳定;当元素大小相同时,可不发生交换;

6、归并排序

实现思想:将本来无序的序列划组排序,随着组内元素的增多,最终有序;如下图: image.png 代码:

void MergeSort(int* a, int begin, int end)
{
	int n = end - begin + 1;
	int* tmp = (int*)malloc(sizeof(int) * n);
	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;

			// 第二组不存在
			if (begin2 >= n)
				break;
			// 第二组结尾越界,进行修正
			if (end2 >= n)
				end2 = n - 1;
			// 测试在归并的区间
			// printf("[%d,%d][%d,%d]", begin1, end1, begin2, end2);
			// 将归并结果放到tmp数组中
			int cur = 0; 
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
					tmp[i + cur++] = a[begin1++];
				else
					tmp[i + cur++] = a[begin2++];
			}
			while (begin1 <= end1)
			{
				tmp[i + cur++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[i + cur++] = a[begin2++];
			}
			// 将归并好的数据拷贝回原数组
			memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));
		}
		// printf("\n");
		gap *= 2;
	}
}

时间复杂度:$O(nlogn)$;共需$logn$层,每层都需访问n个元素; 空间复杂度:$O(n)$;需建立一个与原序列相同大小的空间,用于归并数据; 稳定性:稳定;比较排序都发生在相邻的空间,因此不会影响序列的稳定性。

标签:end,int,复杂度,元素,gap,算法,排序
From: https://blog.51cto.com/u_15423682/9089004

相关文章

  • 智能边缘一体机视频汇聚平台:实时检测室内消防逃生通道占用算法的革新
    随着科技的不断发展,安防监控技术也在不断进步。其中,智能边缘一体机视频汇聚平台的出现,为室内消防逃生通道的实时检测提供了新的可能。本文将详细介绍这种新型技术的工作原理和应用价值。首先,我们需要了解什么是智能边缘一体机视频汇聚平台。简单来说,这是一种集成了视频采集、处理......
  • AI边缘计算智能分析网关V4如何配置周界入侵检测算法
    旭帆科技的智能分析网关V4内含近40种智能分析算法,包括人体、车辆、消防、环境卫生、异常检测等等,在消防安全、生产安全、行为检测等场景应用十分广泛,如常见的智慧工地、智慧校园、智慧景区、智慧城管等等,还支持抓拍、记录、告警、平台级联等功能。算法稳定,识别高效,感兴趣的用户可以......
  • 视频汇聚平台V4一体机视频算法分析平台消防通道异物堵塞算法检测预警
    随着城市化进程的加速,高层建筑如雨后春笋般崛起。然而,这也带来了一系列的安全问题,其中消防通道的畅通无阻是至关重要的。一旦发生火灾,消防通道的畅通与否直接关系到人们的生命安全。因此,如何有效地检测和预警消防通道的异物堵塞问题,成为了一个亟待解决的问题。为此,我们推出了V4一体......
  • 【迅搜13】搜索技巧(三)排序与评分算法
    搜索技巧(三)排序与评分算法今天要学习的,第一部分是排序相关的功能,第二部分则是跟排序密切相关的另一块功能,评分算法。又是算法了,也就是说,又是一大块的理论知识了。今天的文章不长,因为我们的功能测试非常少,但却很重要,因为我们要讲到的理论算法是现在最主流的,也是各种搜索引擎的都在使......
  • 逻辑回归算法来了
    逻辑回归(LogisticRegression)是一种广义的线性回归分析模型,常用于数据挖掘、疾病自动诊断、经济预测等领域。它根据给定的自变量数据集来估计事件的发生概率。变量的范围在0和1之间,通常用于二分类问题,最终输出的预测是一个非线性的S型函数,称为logisticfunction,g()。逻辑递归(Recu......
  • 数据结构简介之算法
    时间复杂度算法的时间复杂度_算法时间复杂度-CSDN博客时间复杂度可以参考这篇文章超级详细!!!为什么不使用算法的绝对运行时间来衡量算法的时间效率?同一个算法处理不同数量的数据时,所花的绝对运行时间可能不同。同一个算法处理相同数量的数据时,在不同配置的电脑上的绝对运行时间也可能......
  • 应用于指纹门锁上的安全芯片ACM32FP421系列,内核性能高,安全性高,内建 AES、CRC、TRNG 等
     ACM32FP421芯片的内核基于ARMv8-M架构,支持Cortex-M33和Cortex-M4F指令集。内核支持一整套DSP指令用于数字信号处理,支持单精度FPU处理浮点数据,同时还支持MemoryProtectionUnit(MPU)用于提升应用的安全性。内核性能高于ARMv7-M架构的M4F20%。ACM32FP421系列芯......
  • class086 动态规划中得到具体决策方案的技巧【算法】
    class086动态规划中得到具体决策方案的技巧【算法】算法讲解086【必备】动态规划中得到具体决策方案的技巧code1最长公共子序列//最长公共子序列其中一个结果//给定两个字符串str1和str2//输出两个字符串的最长公共子序列//如果最长公共子序列为空,则输出-1//测试链接:......
  • 软件开发算法为王,编程语言各取所好——我看IT圈茶余饭后的“鄙视链”
    IT圈茶余饭后的“鄙视链”IT圈茶余饭后的“鄙视链”,简直就是一场瞬间的情感大戏!“我们写xxx的看不起写xxxx“,无处不见这种互相鄙视的情绪就像一场刺激的游戏,每个人都觉得自己是鄙视链的最顶端。快来看看这个IT圈里的“鄙视链”究竟是怎样的吧!一、书店感受前几天到广西壮族自治区首......
  • 【C++】STL 容器 - set 集合容器 ⑤ ( 仿函数 functor 简介 | 仿函数 functor 调用 |
    文章目录一、仿函数functor1、仿函数functor简介2、仿函数functor调用3、代码示例-仿函数functor调用二、为自定义类元素设置排序规则-仿函数functor1、自定义类排序规则2、仿函数-实现自定义类排序规则3、重载<运算符函数-实现自定义类排序规则一、仿函数fu......