常见排序宝典:帮助快速上手常见基础排序算法(下)

这篇具有很好参考价值的文章主要介绍了常见排序宝典:帮助快速上手常见基础排序算法(下)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

五、归并排序

1、算法步骤

 2、动图演示​编辑

 3、代码实现

六、堆排序

1、算法步骤

2、动图演示 

3、代码实现

七、快速排序

1、基本思想

2、动图演示

3、代码实现 

八、计数排序

1、算法步骤

 2、动图演示

​编辑

3、代码实现 


五、归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即显示每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:

  • 自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
  • 自下而上的迭代;

和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间。

1、算法步骤

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

 2、动图演示

 3、代码实现

(递归实现)

void _Merge_sort(int* arr, int begin, int end, int* tmp)
{
	if (begin == end)
	{
		return;
	}//从单个元素开始递归

	int mid = (begin + end) / 2;//划分为两个空间,等待回归
	_Merge_sort(arr, begin, mid, tmp);
	_Merge_sort(arr, mid + 1, end, tmp);

	//开始归并
	int begin1 = begin, end1 = mid;
	int begin2 = end1 + 1, end2 = end;
	int i = begin;//i来记录此次归并开始的位置
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] <= arr[begin2])
		{
			tmp[i++] = arr[begin1++];
		}
		else
		{
			tmp[i++] = arr[begin2++];
		}
	}
	//当两部分有一方出现end大于begin时,退出循环,此时另外一方可能出现数据没完全拷贝回去的情况
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}

	//此时tmp中的数已经排好顺序,需要返回到原数组

	memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}

void Merge_sort(int* arr, int len)//由于我们打算用递归来写,所以最好将递归部分包装出去成为一个接口
{
	//开辟一个同等大小的数组
	int* tmp = (int*)malloc(sizeof(int) * len);
	if (tmp == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}

	_Merge_sort(arr, 0, len - 1, tmp);

	free(tmp);
	tmp = NULL;
}

(迭代实现)

void Merge_sort(int* arr, int len)//非递归思想
{
	int* tmp = (int*)malloc(sizeof(int) * len);
	if (tmp == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}

	int gap = 1;//gap代表是当前所分两组,每组的元数个数
	while (gap < len)
	{
		for (int i = 0; i < len; i += 2 * gap)
		{
			int begin1 = i, end1 = begin1 + gap - 1;
			int begin2 = begin1 + gap, end2 = begin2 + gap - 1;
			int j = i;

			if (end1 >= len || begin2 >= len)
			{
				break;//判断,小心此次循环出现数据越界
			}
			//还有一种end2越界但是begin2没有越界
			if (end2 >= len)
			{
				end2 = len - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] <= arr[begin2])
				{
					tmp[j++] = arr[begin1++];
				}
				else
				{
					tmp[j++] = arr[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[j++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = arr[begin2++];
			}
			memcpy(arr + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;
	}
	free(tmp);
	tmp = NULL;
}

六、堆排序

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

堆排序的平均时间复杂度为 Ο(nlogn)。

1、算法步骤

  1. 创建一个堆 H[0……n-1];

  2. 把堆首(最大值)和堆尾互换;

  3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;

  4. 重复步骤 2,直到堆的尺寸为 1。

2、动图演示 

常见排序宝典:帮助快速上手常见基础排序算法(下),排序算法,算法

常见排序宝典:帮助快速上手常见基础排序算法(下),排序算法,算法

3、代码实现

void AdjustDown(int* arr, int len, int parent)
{
	int child = parent * 2 + 1;
	while (child < len)
	{
		if (child + 1 < len && arr[child] < arr[child + 1])\
		{
			child += 1;
		}
		if (arr[parent] < arr[child])
		{
			Swap(arr[parent], arr[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void Heap_sort(int* arr, int len)
{
	int size = len;
	while (size > 0)
	{
		for (int i = (size - 1 - 1) / 2; i >= 0; i--)
		{
			AdjustDown(arr, size, i);
		}//挑选最大的到堆顶
		Swap(arr[0], arr[--size]);
	}
}

七、快速排序

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好。

1、基本思想

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割为两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

快速排序递归实现的主框架与二叉树前序遍历规则非常像,我们在写递归框架时可想想二叉树前序遍历的规则即可快速写出来,后序只需要分析如何按照基准值来对区间中数据进行划分的方式即可。

2、动图演示

(hoare版本)

常见排序宝典:帮助快速上手常见基础排序算法(下),排序算法,算法

(前后指针法) 

常见排序宝典:帮助快速上手常见基础排序算法(下),排序算法,算法

3、代码实现 

(hoare版本)

void Swap(int& a, int& b)
{
	int c = a;
	a = b;
	b = c;
}

//快速排序
//hoare版本
void Quick_sort(int* arr, int left, int right)
{
	if (left > right)
	{
		return ;
	}
	int key = left;
	int begin = left, end = right;

	while (left < right)
	{
		while (left < right && arr[right] >= arr[key])//在循环过程中,容易出现分循环一直没找到小于的值,right一直减,让right<小于left的情况
			//所以安排循环条件内加入left<right。a[right]>=a[left]的等于是怕出现相同的值,导致循环断掉,出现卡死循环的情况
		{
			right--;
		}//直到找到一个值小于a[key]

		while (left<right && arr[left]>arr[key])
		{
			left++;
		}

		Swap(arr[right], arr[left]);
	}
	//退出循环时right与left相遇
	Swap(arr[key], arr[left]);
	key = left;//交换之后更新key的值充当分割点坐标进行下面的递归
	Quick_sort(arr, begin, key - 1);
	Quick_sort(arr, key, end);
}

(双指针)

//双指针法
void Quick_sort(int* arr, int left, int right)
{
	if (right <= left)
	{
		return;
	}

	int prew = left, cur = left + 1;
	int keyi = left;

	while (cur <= right)
	{
		if (arr[cur] < arr[keyi] && ++prew != cur)
		{
			Swap(arr[cur], arr[prew]);
		}
		cur++;
	}
	Swap(arr[keyi], arr[prew]);
	keyi = prew;
	Quick_sort(arr, left, keyi - 1);
	Quick_sort(arr, keyi + 1, right);
}

八、计数排序

计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

1. 计数排序的特征

当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。

由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序中的算法来排序数据范围很大的数组。

通俗地理解,例如有 10 个年龄不同的人,统计出有 8 个人的年龄比 A 小,那 A 的年龄就排在第 9 位,用这个方法可以得到其他每个人的位置,也就排好了序。当然,年龄有重复时需要特殊处理(保证稳定性),这就是为什么最后要反向填充目标数组,以及将每个数字的统计减去 1 的原因。文章来源地址https://www.toymoban.com/news/detail-849740.html

1、算法步骤

  • (1)找出待排序的数组中最大和最小的元素
  • (2)统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  • (3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  • (4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

 2、动图演示

常见排序宝典:帮助快速上手常见基础排序算法(下),排序算法,算法

3、代码实现 

void Count_sort(int* arr, int len)
{
	int min = arr[0], max = arr[0];

	for (int i = 1; i < len; i++)
	{
		if (min > arr[i])
		{
			min = arr[i];
		}
		if (max < arr[i])
		{
			max = arr[i];
		}
	}

	int range = max - min + 1;
	int* tmp = (int*)calloc(range, sizeof(int));
	if (tmp == NULL)
	{
		perror("calloc fail!");
		exit(1);
	}

	for (int i = 0; i < len; i++)
	{
		tmp[arr[i] - min]++;
	}

	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (tmp[i]--)
		{
			arr[j++] = min + i;
		}
	}
}

到了这里,关于常见排序宝典:帮助快速上手常见基础排序算法(下)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 算法基础(一)| 快速排序和归并排序详解

    ⭐写在前面的话:本系列文章旨在复习算法刷题中常用的基础算法与数据结构,配以详细的图例解释,总结相应的代码模板,同时结合例题以达到最佳的学习效果。本专栏面向算法零基础但有一定的C++基础的学习者。若C++基础不牢固,可参考:10min快速回顾C++语法,进行语法

    2024年02月21日
    浏览(53)
  • 【基础算法】八大排序算法:直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序(快排),归并排序,计数排序

      🧑‍🎓 个人主页:简 料   🏆 所属专栏:C++   🏆 个人社区:越努力越幸运社区   🏆 简       介: 简料简料,简单有料~在校大学生一枚,专注C/C++/GO的干货分享,立志成为您的好帮手 ~ C/C++学习路线 (点击解锁) ❤️ C语言阶段(已结束) ❤️ 数据结构与算法(ing) ❤

    2024年02月01日
    浏览(47)
  • <C++>map 容器快速上手|自定义数据类型排序的避坑理解

    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:C++泛型编程 🔥前言 继 set 容器后,今天总结一下 map 容器的功能,从零到一快速掌握基本使用与常用接口。 map 在 STL 编程中与 vector 、 list 、 set 具有同等重要的地位,键值对的方式

    2024年02月02日
    浏览(43)
  • Go语言基础快速上手

    Go中没有明确意思上的 enum (枚举)定义,不过可以借用 iota 标识符实现一组自增常亮值来实现枚举类型。 切片(slice) 本身不是动态数组或动态指针。只是它内部采用数组存储数据,当数组长度达到数组容量时,会进行 动态扩容 。 大白话就是切片功能和Java中的List集合类似,

    2024年01月20日
    浏览(45)
  • kotlin基础--快速上手kotlin语言开发

    1.1 变量 var表示可变变量,val表示不可变变量,注意并不是常量。变量名写在前面,类型写在后面,编译器如果能推断出你的类型,那么类型是不用声明的 。 编译器自动推断类型。 空安全类型编译器报错 如果还是想给赋初始化值的话 注意:String和String?是两个完全不同的类

    2024年02月15日
    浏览(54)
  • 精简版Git基础操作(快速上手)

    Git是一个开源的 分布式 版本控制系统,用于敏捷高效地处理任何或大或小的项目。 Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源代码的版本控制软件。 Git与常用的版本控制工具CVS、Subversion等不同,它采用了分布式版本库的方式,不用服务器端软件支持,各

    2024年02月11日
    浏览(36)
  • 【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】

    目录 前言 1.冒泡排序 1.1冒泡排序动图 1.2冒泡排序源代码 1.3冒泡排序的特性总结 2.快速排序👑 2.1hoare版本实现思想 排序前 排序中 排序后 2.2hoare版本快排源代码 2.3分析先走 情况1🥇 情况2🥈 交换类排序两个常见的排序算法【冒泡排序】、【快速排序】 交换排序基本思想:

    2024年02月16日
    浏览(70)
  • 【一文到底】【0基础】【快速上手】Django基本使用

    和之前python一样,通过pip来安装即可 django和其他第三方Python模块一样,会在当前python环境下的 libsite-package 中,只是django是比较大的那种模块。 But,django这个包呢同时会生成 django-admin.exe 在 Scripts 文件夹中,这个exe可执行文件是帮助我们操作django项目的。目录情况大体如下:

    2023年04月09日
    浏览(72)
  • C++常见排序算法——冒泡排序算法

    首先说一下冒泡排序的基本算法思想: 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸

    2023年04月08日
    浏览(41)
  • 快速上手Python爬虫:网络爬虫基础介绍及示例代码

    网络爬虫,又称为 Web 爬虫、网络蜘蛛、网络机器人,在英文中被称为 web crawler,是一种自动化程序,能够在互联网上自动获取数据、抓取信息,并将其存储在本地或远程数据库中。它可以帮助我们自动化处理大量数据,提高工作效率,更好地利用互联网资源。 现代互联网上

    2024年02月09日
    浏览(40)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包