C语言基础排序算法详解:插入、选择与冒泡

林见深
林见深
发布时间:2026-10-16 | 7人浏览过
原创

本文深入解析C语言中的基础排序算法,涵盖直接插入排序、希尔排序、直接选择排序、堆排序及冒泡排序。详细阐述各算法的核心思想、代码实现逻辑及复杂度分析,帮助读者理解算法原理并掌握实际编码技巧。

排序的概念及其应用场景

排序的定义与稳定性

排序是将一组记录按照关键字的大小进行递增或递减排列的操作。理解排序时需关注以下核心概念:

  • 稳定性:若待排序序列中存在多个相同关键字的记录,排序后它们的相对次序保持不变,则该算法是稳定的;否则为不稳定。
  • 内部排序 vs 外部排序:数据全部在内存中处理的称为内部排序;数据量过大需借助外存辅助的称为外部排序。

排序的实际运用

排序算法广泛应用于各类场景,例如高校排名等基于指标数据的整理。正确理解排序逻辑是后续学习复杂数据结构的基础。

插入排序

第1步:直接插入排序

目的:将未排序元素逐个插入到已有序的子序列中,形成新的有序序列。

操作逻辑:

  1. 从第二个元素开始遍历数组。
  2. 保存当前待插入元素 x。
  3. 将有序序列中大于 x 的元素依次向后移动。
  4. 将 x 插入到正确位置。
// 直接插入排序
void InsertSort(int* a, int n)
{
	assert(a); // 确保传入数组指针有效
	int i;
	// 从第二个元素开始遍历
	for (i = 0; i < n - 1; i++)
	{
		int end = i;
		int x = a[end + 1]; // 保存当前待插入的值

		// 向前查找插入位置,大数后移
		while (end >= 0)
		{
			if (a[end] > x) // 升序判断
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break; // 找到合适位置
			}
		}
		a[end + 1] = x; // 插入元素
	}
}

第2步:希尔排序

目的:通过分组预排序缩小增量,使数组整体更接近有序,降低直接插入排序的移动次数。

操作逻辑:

  • 设定初始间隔 gap,通常为数组长度的一半。
  • 对间隔为 gap 的子序列进行直接插入排序。
  • 缩小 gap(通常除以2),重复上述过程,直到 gap == 1。
// 希尔排序
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap /= 2; // 缩小增量
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int x = a[end + gap]; // 保存带插入值
			// 组内直接插入逻辑
			while (end >= 0)
			{
				if (a[end] > x) // 升序
				{
					a[end + gap] = a[end];
					end -= gap; // 按间隔移动
				}
				else
					break;
			}
			a[end + gap] = x; // 插入元素
		}
	}
}

选择排序

第3步:直接选择排序

目的:每轮从未排序部分选出最小(或最大)元素,放到已排序部分的末尾。

代码实现:

// 选择排序
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1; // 左右边界索引
	while (begin < end)
	{
		int mini = begin;
		// 遍历找出最小值的下标
		for (int i = begin; i <= end; i++)
		{
			if (a[i] < a[mini])
				mini = i;
		}
		Swap(&a[begin], &a[mini]); // 交换到当前位置
		begin++; // 缩小范围
	}
}

性能总结:时间复杂度 O(N²),空间复杂度 O(1),不稳定。由于效率较低,生产环境中不推荐使用。

第4步:堆排序

目的:利用堆数据结构高效地选择最大值或最小值。

核心原则:

  • 建堆方向:排升序需建立大堆(父节点大于子节点),排降序建立小堆。
  • 排序过程:堆顶元素即为最大值,将其与堆尾元素交换,缩小堆的范围,并对新的堆顶执行向下调整。

向下调整算法

假设左右子树已满足堆结构,从父节点开始,与较大的子节点比较并交换,直至满足堆性质。

完整代码实现

// 向下调整:parent为父节点下标,n为堆大小
void Adjustdown(int* a, int n, int parent)
{
	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)
{
	int i;
	// 1. 建堆:从最后一个非叶子节点开始向下调整
	for (i = (n - 1 - 1) / 2; i >= 0; i--) // 修正公式: (parent-1)/2 对应倒数第二个元素的父节点,此处取(n-2)/2即(n-1-1)/2
	{
		Adjustdown(a, n, i);
	}
	// 2. 排序:交换堆顶与堆尾,调整堆顶
	for (i = n - 1; i >= 0; i--)
	{
		Swap(&a[0], &a[i]);     // 将最大值移至末尾
		Adjustdown(a, i, 0);     // 剩余 i 个元素重新调整为堆
	}
}

性能总结:时间复杂度 O(N·logN),空间复杂度 O(1),不稳定。相比直接选择排序效率显著提升。

交换排序之冒泡排序

冒泡排序逻辑

目的:通过相邻元素的比较和交换,将最大(或最小)元素“冒泡”至序列末端。

操作细节:

  1. 外层循环控制趟数,共需 n-1 趟。
  2. 内层循环控制每趟的比较次数,每趟结束后末尾元素就位,减少一次比较。
  3. 若相邻元素逆序,则交换它们。

代码实现

// 冒泡排序
void BubbleSort(int* a, int n)
{
	int i, j;
	// n-1 趟
	for (i = 0; i < n - 1; i++)
	{
		// 每趟需要比较的次数随趟数减少
		for (j = 0; j < n - 1 - i; j++)
		{
			if (a[j] > a[j + 1]) // 升序:前大于后则交换
			{
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
}

总结

本文梳理了C语言中几种基础的排序算法。直接插入排序适合小规模或部分有序的数据;希尔排序通过增量优化提升了插入排序的效率;堆排序利用完全二叉树结构实现了 O(N·logN) 的高效排序;而冒泡排序虽然简单但效率较低,通常用于教学演示。后续将介绍快速排序和归并排序等更高效的算法。以上就是C语言基础排序算法的详细解析,更多关于排序算法优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表