【数据结构】图解八大排序(上)

这篇具有很好参考价值的文章主要介绍了【数据结构】图解八大排序(上)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、排序简介

生活中,我们经常能看到排序的应用。例如,我们在网购商品的时候,经常按销量从高到低排序。

常见的排序算法有:
【数据结构】图解八大排序(上)
先来介绍一下关于排序算法的几个概念。

稳定性:相等的元素排序之后相对次序不变
内部排序:数据全在内存中的排序
外部排序:数据太多不能同时在内存中

下面所有排序算法都以排升序为例。

二、直接插入排序

直接插入排序类似我们平时玩扑克牌的洗牌过程
【数据结构】图解八大排序(上)
基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

如图所示,我们手上已经洗好了 2、4、5、10 四张牌。再拿到一张 7,先跟 10 比,比 10 小,所以再往前比对。跟 5 比,比 5 大,就把 7 挨着插到 5 的后面。这样我们手上的牌就从四张有序的牌变成五张有序的牌了。

我们来看一个动图:
【数据结构】图解八大排序(上)
该动图完整展现了插入排序的过程。

在代码实现中,我们把牌插到哪个位置,这个位置后面的所有元素就要后挪一位。而后挪会覆盖我们拿出来的那张牌原本位置的元素,所以应该先保存待插元素的值。

完整代码如下:

void InsertSort(int* a, int n)
{
	for(int i = 1; i < n; i++)
	{
		int tmp = a[i];
		int j = i - 1;
		while(j >= 0 && tmp < a[j])
		{
			a[j + 1] = a[j];
			j--;
		}
		a[j + 1] = tmp;
	}
}
//小区间优化常用插入排序

因此,我们可以发现:

  1. 一开始越接近有序,插入排序的效率就越高。
  2. 插入排序后,相等元素的相对次序是不变的,故插入排序具有稳定性。

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

空间复杂度:O(1)

三、希尔排序

希尔排序,又称缩小增量排序。

上文提到,对于直接插入排序,一开始越接近有序,排序的效率越高。所以,希尔排序的优化方式就是先让数组接近有序。

希尔排序包括两个过程:预排序直接插入排序

  1. 预排序:让数组先接近有序。
    按照间隔gap分组,进行直接插入排序,可以一组排完再排另一组,也可以多组一起排。
  2. 直接插入排序
    【数据结构】图解八大排序(上)
    在代码实现中,预排序其实就是gap > 1时进行排序的过程,直接插入排序也就是gap == 1时的排序过程。
    完整代码如下:
void ShellSort(int* a, int n)
{
	int gap = n / 2;
	while (gap > 0)
	{
		for (int i = gap; i < n; i++)
		{
		    int tmp = a[i];
			int j = i;
			while (j >= gap && tmp < a[j - gap])
			{
				a[j] = a[j - gap];
				j -= gap;
			}
			a[j] = tmp;
		}
		gap /= 2;
	}
}
//对于接近有序的序列,直接插入排序最快
//对于无序的序列,希尔排序能大幅优化插入排序的效率

希尔排序的具体效率很难计算,我们暂且认为时间复杂度大约在O(N^1.3)

空间复杂度:O(1)

另外,在预排序中,相等元素的相对次序可能会发生变化,所以希尔排序是不稳定的。

四、选择排序

选择排序的思想其实非常简单,以升序为例,就是每次选出最小的换到左边
【数据结构】图解八大排序(上)
思路非常简单,来看看代码怎么写。

void SelectSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
	    int mini = i;
		for (int j = i; j < n; j++)
		{
			if (a[j] < a[mini]) mini = j;
		}
		swap(&a[i], &a[mini]);
	}
}

时间复杂度:O(N^2)
空间复杂度:O(1)

选择排序是不稳定的。

五、堆排序

堆排序是选择排序的一种,就是把原数组看成一个堆,利用堆的性质,对数组进行处理,达到排序的效果。

需要注意的是:排升序要建大堆,排降序建小堆

具体过程如下:
【数据结构】图解八大排序(上)
完整代码如下:

//向下调整时间复杂度 O(logN)
void adjustDown(int a[], int parent, int n)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && 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)
{
	//建堆时间复杂度 O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		adjustDown(a, i, n);
	}

	//排序时间复杂度 O(NlogN)
	for (int i = n - 1; i >= 0; i--)
	{
		swap(&a[0], &a[i]);

		adjustDown(a, 0, i);
	}
}

时间复杂度:O(NlogN)
空间复杂度:O(1)

由于存在堆顶与末尾元素交换的过程,所以堆排序是不稳定的。

六、冒泡排序

以排升序为例,从前到后依次比较相邻两个元素,如果第一个比第二个大,就两两交换。第一趟把最大的元素推到最右边,第二趟把次大的元素放到倒数第二个位置,以此类推。

整个过程就像冒泡一样,每趟都把当前区间最大的元素往右边推,最后只剩第一个元素的时候本身有序。
【数据结构】图解八大排序(上)

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)//n-1趟
	{
		bool flag = false;//检查是否交换
		for (int j = 0; j < n - 1 - i; j++)//每趟比较n-1-i次
		{
			if (a[j] > a[j + 1])
			{
				swap(&a[j], &a[j + 1]);
				flag = true;
			}
		}
		if (!flag)//如果没交换,说明已经有序
		{
			break;
		}
	}
}

由于比较相邻元素时,相等不会交换位置,所以冒泡排序具有稳定性。

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

空间复杂度:O(1)

可以发现,冒泡排序的时间复杂度无论最坏情况还是最好情况,都和直接插入排序相同。

因此我们可以比较一下二者的效率。

七、冒泡排序与直接插入排序效率对比

数组有序时:一样
接近有序时:冒泡慢一些
数组局部有序:冒泡慢很多

所以,虽然冒泡排序可能是我们最早接触的一种排序算法,但是它的效率真的很一般,甚至不如直接插入排序。

测试函数:文章来源地址https://www.toymoban.com/news/detail-426401.html

void TestOP()
{
	srand(time(0));
	const int N = 50000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);
	int* a8 = (int*)malloc(sizeof(int) * N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
		a8[i] = a1[i];
	}

	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	BubbleSort(a5, N);
	int end5 = clock();

	int begin6 = clock();
	quicksort(a6, 0, N - 1);
	int end6 = clock();

	int begin7 = clock();
	MergeSort(a7, N);
	int end7 = clock();

	int begin8 = clock();
	CountSort(a8, N);
	int end8 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("BubbleSort:%d\n", end5 - begin5);
	printf("QuickSort:%d\n", end6 - begin6);
	printf("MergeSort:%d\n", end7 - begin7);
	printf("CountSort:%d\n", end8 - begin8);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
	free(a7);
	free(a8);
}

到了这里,关于【数据结构】图解八大排序(上)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (数据结构)八大排序算法

       排序 (Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个 数据元素 (或记录)的任意序列,重新排列成一个有序的序列。 介绍 :将待排的数,和有序的数比较,直到一个合适的位置,然后进行插入。 示图 : 将待排数4,与有序数对比,然后插入到

    2023年04月21日
    浏览(32)
  • 【数据结构】八大排序(一)

    😛作者:日出等日落 📘 专栏:数据结构         珍惜自己的时间,利用好每一份每一秒。做事不放过没一个细节,小心谨慎,细致,能够做到这些,还有什么是不可能的呢? 目录 ​编辑 ✔排序的概念: ✔排序的应用: ✔常见的排序算法: ✔常见排序算法的实现: ✔插入

    2024年02月03日
    浏览(49)
  • 「数据结构」八大排序1

    🎇 个人主页 :Ice_Sugar_7 🎇 所属专栏 :初阶数据结构 🎇 欢迎点赞收藏加关注哦! 插排就是将一个元素插入一个有序序列中合适的位置,分为 直接插入排序 和 希尔排序 流程如下: ①保存待插入的值:假设某一趟中有序序列最后一个元素下标为end,先 保存(end+1)位置的

    2024年02月03日
    浏览(26)
  • 数据结构--八大排序

    插入排序的思想跟摸牌很相似,从我们摸到第二张牌的开始,依次将摸到到牌依次有序的插入到手中,最后手牌变成了有序。 有了大致的思路,接下来就要细分插入排序的细节。 首先考虑某一趟的插入排序。为什么先考虑某一趟呢?因为当需要解决的问题比较复杂时先将问

    2024年02月02日
    浏览(30)
  • 【数据结构】八大排序(二)

    😛作者:日出等日落 📘 专栏:数据结构 在最黑暗的那段人生,是我自己把自己拉出深渊。没有那个人,我就做那个人。                                                                                                                                     

    2024年02月03日
    浏览(23)
  • 【数据结构】八大排序 (三)

    目录 前言: 快速排序 快速排序非递归实现 快速排序特性总结 归并排序 归并排序的代码实现 归并排序的特性总结 计数排序 计数排序的代码实现 计数排序的特性总结 前文快速排序采用了递归实现,而递归会开辟函数栈帧,递归的深度越深,占用栈区的空间就越大, 栈区的

    2024年02月01日
    浏览(35)
  • 【数据结构】八大排序详解

    🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🐌 个人主页:蜗牛牛啊 🔥 系列专栏:🛹数据结构、🛴C++ 📕 学习格言:博观而约取,厚积而薄发 🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与

    2024年02月13日
    浏览(26)
  • 【数据结构】八大排序(一)

    目录 前言: 直接插入排序 直接插入排序代码实现 直接插入排序特性总结 希尔排序 希尔排序代码实现 希尔排序特性总结 直接选择排序 直接选择排序代码实现 直接选择排序特性总结 堆排序 堆的向下调整算法 建堆 堆排序代码实现 堆排序特性总结 排序即使得一串记录,按照

    2024年02月03日
    浏览(30)
  • 【数据结构】八大排序算法

    目录 一、直接插入排序 二、希尔排序 三、选择排序  3.1、简单选择排序  3.2、快速选择排序(Top K问题) 四、堆排序 五、冒泡排序 六、快速排序   1、递归版本      1.1 hoare 法      1.2 挖坑法      1.3 前后指针法   2、非递归版本   3、快速排序的优化      3.1 三数取中

    2024年02月09日
    浏览(57)
  • 【数据结构与算法】八大排序

    初看这些概念可能一脸懵,但是没有关系,等下面学完几种排序之后在来看这些概念非常容易理解。 排序:所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在多个具有相同的

    2024年02月01日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包