【数据结构】八大排序!(C语言)

本文详细介绍了排序算法,包括插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序和计数排序。讨论了各算法的时间复杂度、空间复杂度和稳定性,并提供了C语言实现。特别提到了快速排序的优化方法,如三数取中和针对小数组采用插入排序。最后探讨了归并排序的非递归版本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在练习编程或者做项目时,是否经常与排序打交道,这篇文章会带你熟悉常见的八大排序。

目录

插入排序

希尔排序

选择排序

堆排序

冒泡排序

快速排序

归并排序

计数排序


插入排序

对于要插入的一个数字,等待被插入数字的数组中是已经有序的,此时如果要插入一个数,就与有序数组中最后一个、倒数第二个......依次比较,直到此数字刚好位于前方数字小于它,后方数字大于它的位置,此时这个数字就被插入成功。依次插入剩下的数字重复上述步骤。 

 函数实现: 

void InsertSort(int* a, int n)
{
    //由第一个元素作为一个有序数组依次将第二个、第三个......插入进去
	for (int i = 0; i < n-1; i++)
	{
		int end = i;
        //将待插入的按照规则进行比较,插入到正确位置
		while (end >= 0)
		{
			if (a[end] > a[end + 1])//如果大于比较元素则交换数值
			{
				swap(&a[end], &a[end + 1]);
				end--;
			}
			else//否则就意味着已经插入到了正确位置跳出循环
			{
				break;
			}
		}
	}
}

数组越接近有序排序算法效率越高

最坏时间复杂度为:O(N^2),数组为逆序或者接近逆序时(一共需要比较1+2+...+n-1次)

最好时间复杂度为:O(N),数组为有序或者接近有序时(一共需要比较n次)

空间复杂度为:O(1)

稳定性:稳定

希尔排序

希尔排序又称缩小增量法排序,先选定一个整数gap,将待排序数组分为gap个组,每个组的相邻元素下标相差gap,将每一组进行插入排序。然后再重复上述步骤,将gap值依次缩小,当gap为1时数组就被排好序了。

函数实现: 

void ShellSort(int* a, int n)
{
	int gap = n;
    //方法一:一组一组进行排序,排好一组再排下一组,一个一个来
	while (gap > 1)
	{
		gap = gap / 3 + 1;//gap依次减少直到1
		for (int i = 0; i < gap; i++)//将数组分为gap组
		{
			for (int j = i; j < n - gap; j += gap)//对每一组进行插入排序
			{
				int end = j;
				while (end >= i)
				{
					if (a[end] > a[end + gap])
					{
						swap(&a[end], &a[end + gap]);
						end -= gap;
					}
					else
					{
						break;
					}
				}
			}
		}
	}
    //方法2:对每一组对应的位置先进行排序,再对下一个位置都进行排序,齐头并进
	//while (gap > 1)
	//{
	//	gap = gap / 3 + 1;
	//	for (int j = 0; j < n - gap; j++)//j不再是+=上gap步,注意齐头并进
	//	{
	//		int end = j;
	//		while (end >= 0)
	//		{
	//			if (a[end] > a[end + gap])
	//			{
	//				swap(&a[end], &a[end + gap]);
	//				end -= gap;
	//			}
	//			else
	//			{
	//				break;
	//			}
	//		}
	//	}
	//}
}

 这里gap每次减少,可以选择gap/=2,也可以为gap=gap/3+1

因为希尔排序的分析是一个复杂问题,时间复杂度涉及到数学上尚未解决的难题,所以经过某些人的统计与计算我们时间复杂度暂且按O(N^1.25)到O(1.6*N^1.25)来算

稳定度:不稳定

选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。在待排序元素集合中如果最大或最小的元素不在第一位,则交换数据,排好一个后,下一次待排元素集合应出去排好的元素,重复上述步骤,直到剩下一个元素。


 

但是我们不妨思考一下,此时是否可以再优化排序方式,这里针对升序来讲,既然遍历一遍选择出最小的数交换到最前面,那么我们是否也可同时选出最大的数放在最后面?上代码!

函数实现: 

void SelectSort(int* a, int n)
{
    //定义两个下标,一个记录当前最左边下标,一个记录当前最右边下标
	int end = n - 1;
	int begin = 0;
	while (begin < end)
	{
        //定义两个变量一个找出待排序数组的最大值对应的下标max和最小值对应的下标min
		int min = begin;
		int max = begin;
		for (int i = begin+1; i <= end; i++)
		{
			if (a[min] > a[i])
			{
				min = i;//最大值对应的下标
			}
			if (a[max] < a[i])
			{
				max = i;//最小值对应的下标
			}
		}
        //找到后将最小值与待排数组最左边元素进行交换,最大值与最右边元素进行交换
        //但是!如果最大值就在最左边,先将最小值与最左边交换,最大值就不再是原来的位置了
        //就被交换到了交换前最小值所在的下标处了,所以我们要修正最大值下标为min
		swap(&a[min], &a[begin]);
		if (max == begin)//修正最大值
		{
			max = min;
		}
		swap(&a[max], &a[end]);
		begin++;
		end--;
	}
}

时间复杂度: O(N^2)最好情况与最坏情况一样

空间复杂度:O(1)

稳定性:不稳定

在实际中选择排序好理解,但是由于它的效率太低,不常使用

堆排序

所谓堆排序,就是利用数据结构中堆的特点性质来进行排序。在排序前我们应该先建立堆,对于升序要建立大根堆而降序则要建立小根堆,因为这样对于后面数据排序处理会非常简单。

 建立大根堆利用向下调整算法来建立。

建立好大根堆后将大根堆最上面的一个数与最后一个数交换,此时将最后一个数映射在数组中的下标减去一。再对交换后最上面的数进行向下调整算法,使得第二大的数位于堆顶,再将下标减去一后的最后一个元素与堆顶元素交换重复上述步骤,就可依次将数据由大到小从数据的末尾往前排。

函数实现:

//因为要排升序,向下调整法来建立大根堆
void AdjustDown(int* a, int n, int parent)//向下调整法的条件是左右子树必须为大根堆或小根堆
{
    //因为要建立升序,所以向下调整算法里应该将左右孩子里最大的那一个与父母交换,利用parent
    //与child的关系来进行递推,当算出下标child大于数组的个数n时则一次调整结束
	int child = parent * 2 + 1;
	while (child < n)
	{
        //当右孩子存在,并且左孩子小于右孩子,要将child++
		if (child + 1 < n && a[child] < a[child + 1])
		{
			child++;
		}
        //当parent小于child时,要满足建立大根堆,将大的那个数往上去置换
		if (a[parent] < a[child])
		{
			swap(&a[parent], &a[child]);
		}
        //当不用进行交换时则代表一次调整好了
		else
		{
			break;
		}
		parent = child;
		child = parent * 2 + 1;
	}
}
void HeapSort(int* a, int n)
{
	//要排升序先建立大根堆
    //从最后一个元素的长辈系元素开始向下调整,当来到第一个元素时大根堆已经建立好了
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//开始排序
    //循环n-1次第k次将第一个元素与第n-k+1个元素进行交换,同时参与下次调整的元素个数为前n-k个
    //再对交换到第一个元素位置的数据向下调整
	for (int j = n - 1; j > 0; j--)
	{
		swap(&a[0], &a[j]);
		AdjustDown(a, j, 0);
	}
}

 时间复杂度:O(N*logN)因为在建立大根堆时,由最后一个元素的长辈系元素往上直到第一个元素都要进行近似logN次交换,在排序时每个元素都要向下调整一次,每次向下调整近似进行logN次交换。

空间复杂度:O(1)

稳定度:不稳定

冒泡排序

对于一个数组,针对升序,将前一个数与后一个数比若小于后一个数,则交换,再将后一个数与后一个数的后一个数比,以此类推,当比到最后一个元素时就可以缺定最大值。此时将数组往前缩小到n-1个再重复上述步骤,直到数组缩小为2个时,排序完成。

函数实现: 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值