C语言描述数据结构 —— 常见排序(1)直接插入排序、希尔排序、选择排序、堆排序

本文介绍了四种基本排序算法:直接插入排序、希尔排序、直接选择排序和堆排序。直接插入排序在有序或接近有序时效率较高;希尔排序通过分段插入提高效率;直接选择排序始终以O(N^2)时间复杂度运行;堆排序则提供了一个高效的选择,尤其适合大规模数据。文章通过实例代码展示了每种排序算法的工作原理,并讨论了它们的优缺点和适用场景。
摘要由CSDN通过智能技术生成

1.开始之前

在正式介绍排序之前,先了解一下排序的分类。当然是基于常用的排序算法分类。
常用排序算法
可以看到常用的排序算法是有8种的,这8种的效率各不相同,而每种排序都有不同版本的实现算法。那么排序这个章节呢,我也会分篇介绍。

那么排序的概念就不介绍了。排序最直接的应用在我们的日常购物软件或者一些搜索功能上。例如我们在购物软件上搜索“笔记本电脑”,我们可以按照热度排序、销量排序、价格排序等等排序方式来方便我们的选择。

在C语言中,封装了排序算法 qsort 函数,为什么还要学习其他的排序算法呢?因为在不同的环境当中、不同的使用场景当中,对排序的要求都是不一样的。而我们学习的目的不仅仅是学会排序这么简单,重要的是掌握其算法的核心思想,提升我们的技能。

2.直接插入排序

​我们可以试想一下我们正在打扑克牌,我们的手里有一些有序的牌,而此时我们摸进来了一张牌。如果是你,你会怎么把牌插入牌堆里?
在这里插入图片描述
那么如图所示的这种算法,叫做直接排序算法。当然了,每个人打扑克的习惯都不一样啊,有的人习惯抓进一张牌就调整牌堆顺序了,而有些人习惯先把牌摸完,然后一起调整牌堆顺序。无论是哪一种,我们都可以统一思想。例如这个例子:
在这里插入图片描述
这就是直接插入排序的基本思想,用简单的说法就是:从第二个数开始依次往前面比较,碰到了特定条件就插入。了解了核心思想,现在就是考研我们打代码的人的素养了,将思路转换为代码。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

void Print(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}

//插入排序
void InsertSort(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;//记录从后向前比较的开始位置
		int tmp = a[end + 1];
		//当 end<0 时数组越界
		while (end >= 0)
		{
			//如果tmp比比较的数字小,那么数组就进行覆盖调整
			//我们的画图是让tmp移动,但是我们的代码是让数组移动
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		//插入
		a[end + 1] = tmp;
	}
}

int main()
{
	//定义数组的大小
	int size = 10;
	int* arr = (int*)malloc(sizeof(int) * size);
	assert(arr);

	//给数组随机赋值
	srand((unsigned int)time(NULL));
	for (int i = 0; i < size; i++)
	{
		arr[i] = rand();
	}

	InsertSort(arr, size);
	Print(arr, size);
	return 0;
}

那么我们对随机的 10 个数字进行排序,可以看一下效果。实现了我们的排序功能。
在这里插入图片描述
那么我们可以在这里做一个总结:

  • 如果数组是有序数组或者是接近有序数组,那么这个算法的效率会非常高
  • 此排序算法的时间复杂度是 O(N^2)
  • 空间复杂度是 O(1)

正因为这个算法的时间复杂度太高了,处理百万、千万往上的数据时效率是有明显的降低的。大家可以把上面的代码拷贝一份,修改一下主函数中的 size 变量的初始化值,观察效率。

而正式因为效率不够,就在直接插入排序的基础上,研究出了希尔排序算法

3.希尔排序

希尔排序的思想非常简单,我们细细的想一下直接插入排序,每次比较都是挨个比较,那么我们如果不是挨个比较呢?我们以画图的形式来描述这个算法:
在这里插入图片描述
可以看到,希尔排序就是分段的直接插入排序。即分批次的进行直接插入排序。那么我们尝试用代码描述这个算法:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

void Print(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}

void ShellSort(int* a, int n)
{
	assert(a);
	int gap = n;//默认第一次的gap为n
	while (gap > 1)
	{
		gap /= 2;//依次减小区间,最后一次gap一定为 1

		//直接插入排序
		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;
		}
	}
}
int main()
{
	//定义数组的大小
	int size = 1000000;
	int* arr = (int*)malloc(sizeof(int) * size);
	assert(arr);

	//给数组随机赋值
	srand((unsigned int)time(NULL));
	for (int i = 0; i < size; i++)
	{
		arr[i] = rand();
	}

	//InsertSort(arr, size);
	ShellSort(arr, size);
	Print(arr, size);
	return 0;
}

可以看到我们要处理的数据很大,但是希尔排序能够处理这些庞大的数据。那么希尔排序一直有一个难题,那就是它的时间复杂度。希望有数学大佬能够把它计算出来。

所以希尔排序的效率是很高的,是效率跟堆排序差不多的排序算法
处理100万个数据

4.直接选择排序

直接选择排序,我们选择什么呢?我们要选择最小、最大的数字放在指定的位置。我们看图来理解这个算法:
在这里插入图片描述
那么我们用代码来描述它也非常简单:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

void Print(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}


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

		//如果begin位置上放的是最大的数
		//那么就要改变一下maxi的位置
		if (maxi == begin)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);
		begin++;
		end--;
	}
}
int main()
{
	//定义数组的大小
	int size = 10;
	int* arr = (int*)malloc(sizeof(int) * size);
	assert(arr);

	//给数组随机赋值
	srand((unsigned int)time(NULL));
	for (int i = 0; i < size; i++)
	{
		arr[i] = rand();
	}

	//InsertSort(arr, size);
	//ShellSort(arr, size);
	SelectSort(arr, size);
	Print(arr, size);
	return 0;
}

在这里插入图片描述
选择排序可以称之为效率最差的排序排序算法之一了,因为它与直接插入排序不同的是,无论是否有序,选择排序的时间复杂度恒为 O(N^2)

5.堆排序

堆排序这个算法是介绍过的,那这里就不做介绍了,单纯贴一份代码。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

void Print(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}


void Swap(int* p1, int* p2)
{
	assert(p1 && p2);
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustDown(int* heap, int n, int parent)
{
	assert(heap);
	int minchild = parent * 2 + 1;
	while (minchild < n)
	{
		if (minchild + 1 < n && heap[minchild + 1] > heap[minchild])
		{
			minchild++;
		}
		if (heap[minchild] > heap[parent])
		{
			Swap(&heap[minchild], &heap[parent]);
			parent = minchild;
			minchild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int n)
{
	assert(a);
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	while (n)
	{
		Swap(&a[0], &a[n - 1]);
		AdjustDown(a, n-1, 0);
		n--;
	}
}
int main()
{
	//定义数组的大小
	int size = 1000000;
	int* arr = (int*)malloc(sizeof(int) * size);
	assert(arr);

	//给数组随机赋值
	srand((unsigned int)time(NULL));
	for (int i = 0; i < size; i++)
	{
		arr[i] = rand();
	}

	//InsertSort(arr, size);
	//ShellSort(arr, size);
	//SelectSort(arr, size);
	HeapSort(arr, size);
	Print(arr, size);
	return 0;
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小龙向钱进

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值