数据结构·选择排序(选择排序、堆排序)

一、选择排序

1、 核心思想

简单选择排序的基本思想是:首先在未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

2、算法分析

选择排序

初始状态:给定待排序的序列 arr,包含 n 个元素。
第一次遍历:从第一个元素开始,依次与后面的元素比较,找到最小的元素,将其与第一个元素交换位置。
第二次遍历:从第二个元素开始,依次与后面的元素比较,找到最小的元素,将其与第二个元素交换位置。
重复步骤2和步骤3,直到倒数第二个元素和最后一个元素比较完毕。
这样经过 n-1 次遍历后,整个序列就排好序了。

3、代码实现

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

对于选择排序可以进一步改进,即一次遍历找到最大和最小的元素

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

注意事项

  • 对于两种实现方法, head++; head++; end--;一但没有写函数会陷入死循环
  • 对于优化版本中的 if (maxi == head) maxi = mini;则是为了防止最大值位置在此时待排序元素的头部,由于先进行最小值的交换,假如最大值在头部,则此时最大值的位置应该在最小值开始的位置即mini

二、堆排序

1、基础知识

堆的结构可以分为大根堆和小根堆,是一个完全二叉树,而堆排序是根据堆的这种数据结构设计的一种排序。

  • 大根堆:每个结点的值都大于其左孩子和右孩子结点的值,称之为大根堆
    大根堆示意图
    数组

  • 小根堆:每个结点的值都小于其左孩子和右孩子结点的值,称之为小根堆
    小根堆
    数组

1.父结点索引:(i-1)/2(这里计算机中的除以2,省略掉小数)
2.左孩子索引:2i+1
3.右孩子索引:2
i+2
所以上面两个数组可以脑补成堆结构,因为他们满足堆的定义性质:
大根堆:arr(i)>arr(2i+1) && arr(i)>arr(2i+2)
小根堆:arr(i)<arr(2i+1) && arr(i)<arr(2i+2)

2、 核心思想

若在输出堆顶的最小值(最大值)后,使得剩余n-1个元素序列重新建成一个堆,则得到n个元素的次小值(次大值)…如此反复,便能得到一个有序序列,这个过程称之为堆排序。(每次从堆顶取一个元素)

  • 升序:建大堆
  • 降序:建小堆

3、算法分析

在这里插入图片描述

1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

构造堆
向下调整构造堆
向下调整建大堆
向下调整建大堆_数组

从最后一个父节点开始,与子节点比较,小于子节点则交换,一直到根节点。最后一个节点为n-1则其父节点为((n-1)-1)/2(n-2)/2

4、代码实现

  • 构造大根堆,升序排列
// 堆排序
void AdjustDwon(int* a, int n, int root)
{
	int child = root * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] < a[child + 1])
			child++;
		if (a[child] > a[root])
		{
			Swap(&a[child], &a[root]);
			root = child;
			child = root * 2 + 1;
		}
		else break;
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDwon(a, n, i);
	}
	for (int i = n - 1; i > 0; i--)
	{
		Swap(&a[0], &a[i]);
		AdjustDwon(a, i, 0);
	}
}
  • 构造小根堆,降序排列
void AdjustDwon(int* a, int n, int root)
{
	int child = root * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] > a[child + 1])
			child++;
		if (a[child] < a[root])
		{
			Swap(&a[child], &a[root]);
			root = child;
			child = root * 2 + 1;
		}
		else break;
	}
}

注意事项

  • 降序排列与升序排列,区别在于构造大根堆还是小根堆。
  • if (child + 1 < n && a[child] > a[child + 1])if (child + 1 < n && a[child] < a[child + 1])确保child + 1 < n在前面防止出现数组越界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值