【数据结构】选择排序--直接选择排序和堆排序的实现

选择排序

1.直接选择排序

什么是直接选择排序呢?
直接选择排序是一个暴力选数的过程,每一次从待排序的数据元素中选出最小(或最大)的一个元素,放在序列的起始位置,再继续找最小(或最大)的一个元素,放在起始位置后面,直到所有的数据元素排完。
在这里插入图片描述
这样看起来时间复杂度有点高,我们在实现时可以同时找出一个最小的数和一个最大的数,分别放在起始位置和最后位置,这样就简便了许多。下面我们将按这种方法来实现直接选择排序~

1.2实现过程

begin指向起始位置,end指向尾部位置,mini和maxi初始情况下指向begin位置。
比较a[begin+1]和a[mini]大小,如果a[begin+1]小,mini赋值为begin;再比较a[begin+1]和a[maxi]的大小,如果a[begin+1]大,就修改maxi为begin;一趟查找后begin+1,end-1。再次重复以上动作。
在这里插入图片描述
(1)第一趟,a[begin+1] = 44 不满足小于a[mini],满足大于a[maxi] =3,maxi =1,找到最大值的下标,与尾部位置交换数据。
在这里插入图片描述
在这里插入图片描述
(2)第二趟,找到最小元素5,最大元素44
在这里插入图片描述
在这里插入图片描述
(3)第三趟,找到最小元素15,最大元素38
在这里插入图片描述
(4)找出最小元素34,最大元素36
在这里插入图片描述
至此,排序结束。

1.3代码实现

//直接选择排序  
void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}
		Swap(&a[begin], &a[mini]);
		if (begin == maxi) //若mini == begin时,第一次Swap后
		                   //begin处值为最小,所以需要更新maxi值
		    maxi = mini;
		Swap(&a[end], &a[maxi]);
		
		begin++;
		end--;
	}
}

运行结果:
在这里插入图片描述

<总结>
1.直接选择排序的过程容易理解,但是效率不是很好
2.时间复杂度为O(N^2)
3.空间复杂度为O(1)
4.稳定性:不稳定

2.堆排序

堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

堆排序(Heapsort) 是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大堆和小堆,是完全二叉树。大堆的要求是每个节点的值都不大于其父节点的值,小堆则是每个节点的值都不小于父节点的值。在数组的非降序排序中,需要使用的就是大堆,因为根据大堆的要求可知,最大的值一定在堆顶。
算法思路:先将元素依次建立大堆,然后将根节点与最后一个节点的值交换,后进行 向下调整算法 ,使除最后一个元素外的其余元素构成大堆。重复以上步骤。

2.1向下调整算法

我们先根据图解回顾一下向下调整算法是如何实现的。当它是堆时才可以调用该算法(以小堆为例)。
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
算法实现:
我们用 parent 接受根节点下标,size 来接收当前堆中节点(元素)个数。取左右节点中小的值和父节点比较,满足条件就交换值。如果将父节点小于左孩子和小于右孩子看做两种情况代码就会比较冗余,所以使用假设法。左大于右

//向下调整(建大堆)
void AdjustDown(int* a, int parent, int size)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;  //更新child
		}
		if (a[parent] < a[child])
		{
			Swap(&a[child], &a[parent]);
			int parent = child;
			int child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

2.2堆排序实现思路

堆排序时,首先要确保所给数据满足堆的特点,有向上调整建堆,也有向下调整建堆,因为我们在排序过程中使用向下调整,所以为了方便也采用该方法建堆。

2.2.1建堆

刚才说我们要采用向下调整建堆,那么如何实现呢?如何控制下标呢?
我们选择从最后面的节点入手,要使用向下调整,那么它的左右子树应该都是堆,叶子节点没有左右子树,所以本身就是堆,那么便找最后一个叶子节点的父节点,从它开始调整。完成后又开始调整该根节点的前一节点,… ,依次重复,直到调整完根节点便结束。
下面我们画图来看一下
在这里插入图片描述
代码实现:

	//建大堆
	for (int i = (n - 2) / 2; i >= 0; i--)
	{

		AdjustDown(a, i, n );
	}

2.2.2建大堆还是建小堆呢?

要排成升序序列,该建大堆还是小堆呢?
以[9, 1, 2, 5, 7, 4, 8, 6, 3]这10个数为例
(1)如果建小堆,只能先获得一个最小的值,剩下的数不满足堆的性质,要想找到次小值,又得将剩余数建堆再找,效率极低,甚至不及冒泡排序。

在这里插入图片描述

(2)如果建大堆,根节点是最大值,利用删除的思想,将根节点与最后一个节点交换值(相当于倒着排序,先找最大值,再找次大值,…,直到找到最小值),再在[0,8]内调用向下调整。重复以上步骤

在这里插入图片描述

相比而言,还是建大堆更简单,所以我们采用第二种方法。

2.3整体代码实现

void AdjustDown(int* a, int parent, int size)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child] < a[child + 1])
		{
			child++;  //更新child
		}
		if (a[parent] < a[child])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
void HeapSort(int* a, int n)
{
	//建大堆
	for (int i = (n - 2) / 2; i >= 0; i--)
	{

		AdjustDown(a, i, n );
	}
	int end = n - 1;
	while(end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, 0, end);  //end为最后一个元素下标,也是最后一个元素前的数据个数
		end--;
	}
}

以上就是我对两种排序算法的理解,谢谢观看,如有不对还请指出!

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天学习了吗•

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

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

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

打赏作者

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

抵扣说明:

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

余额充值