【数据结构】常见的排序算法 -- 选择排序

🫧个人主页: 小年糕是糕手

💫个人专栏:《数据结构(初阶)》** 《C/C++刷题集》 《C语言》**

🎨你不能左右天气,但你可以改变心情;你不能改变过去,但你可以决定未来!



目录

一、直接选择排序

1.1、算法思想

1.2、代码实现

二、堆排序

2.1、算法思想

2.2、代码实现


一、直接选择排序

选择排序的基本思想: 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

1.1、算法思想
  1. 在元素集合 array [i] - array [n - 1] 中选择关键码最大 (小) 的数据元素
  2. 若它不是这组元素中的最后一个 (第一个) 元素,则将它与这组元素中的最后一个(第一个)元素交换
  3. 在剩余的 array [i] - array [n - 2](array [i + 1] - array [n - 1])集合中,重复上述步骤,直到集合剩余 1 个元素

1.2、代码实现
cpp 复制代码
void SelectSort(int* arr, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin;
		int maxi = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}

		if (maxi == begin)
		{
			maxi = mini;
		}
		Swap(&arr[begin], &arr[mini]);
		Swap(&arr[end], &arr[maxi]);

		begin++;
		end--;
	}
}

直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好,实际中很少使用(我们主要是通过这个例子去了解更多的排序算法,锻炼自己的代码能力)
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)

二、堆排序

2.1、算法思想

堆排序的算法思想基于堆这种数据结构(完全二叉树),核心步骤如下:

  1. 构建初始堆:将待排序的数组视为一棵完全二叉树,调整其结构为大顶堆(或小顶堆,取决于排序需求)。大顶堆的特性是:每个父节点的值都大于或等于其左右子节点的值。

  2. 交换与调整堆:

    • 将堆顶元素(最大值,对应大顶堆)与堆的最后一个元素交换,此时最大值被放置到数组末尾(即已排序位置)。
    • 排除已排序的最后一个元素,将剩余元素重新调整为大顶堆(此时堆的规模减 1)。
  3. 重复操作:不断重复 "交换堆顶与当前堆的最后一个元素" 和 "调整剩余元素为大顶堆" 的步骤,直到堆的规模缩减为 1,此时整个数组完成排序。

通过堆的特性,每次能高效地选出剩余元素中的最大值(或最小值),最终实现整个数组的有序排列。堆排序的时间复杂度为O(n*logn),空间复杂度为O(1)。

大家要是想进一步详细了解可以参考:小年糕是糕手博客 -- 顺序结构二叉树详解

2.2、代码实现
cpp 复制代码
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}


//向上调整算法
void AdjustUp(int* arr, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//建大堆: >
		//建小堆: <
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			//不用调整符合情况
			break;
		}
	}
}

//向下调整算法
//n是堆里面的结点个数,如果越界了就不需要调整了
void AdjustDown(int* arr, int parent, int n)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//建大堆: <
		//建小堆: >
		if (child + 1 < n && arr[child] < arr[child + 1])
		{
			child++;
		}

		//孩子和父亲比较
		//建大堆: >
		//建小堆: <
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//堆排序
void HeapSort(int* arr, int n)
{
	//向下调整算法 -- 建堆n
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, i, n);
	}

	////向上调整算法 -- 建堆n*logn
	//for (int i = 0; i < n; i++)
	//{
	//	AdjustUp(arr, i);
	//}

	//n*logn
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}
}

堆排序的特性总结如下:

  1. 时间复杂度:O(n*logn)。构建初始堆的时间复杂度为O(n),后续每次调整堆的时间复杂度为O(logn),共需调整n−1次,整体复杂度稳定为O(n*logn),不受数据分布影响。

  2. 空间复杂度:O(1)。堆排序是原地排序算法,仅需常数级别的额外空间用于临时变量交换。

  3. 稳定性:不稳定。在交换堆顶元素与末尾元素时,可能会改变相同元素的相对顺序。

  4. 适用场景:适用于数据量较大的场景,对空间复杂度要求严格时表现较好,但由于交换和调整操作较频繁,实际应用中对小规模数据的效率可能不如快速排序。

  5. 其他特性:基于堆结构实现,排序过程中需要频繁进行堆的调整(下沉操作),逻辑相对直接选择排序更复杂,但效率更高。

相关推荐
huangyuchi.2 小时前
【Linux网络】Socket编程实战,基于UDP协议的Dict Server
linux·网络·c++·udp·c·socket
电子_咸鱼3 小时前
动态规划经典题解:单词拆分(LeetCode 139)
java·数据结构·python·算法·leetcode·线性回归·动态规划
yunhuibin4 小时前
无锁化编程——c++内存序使用
c++
zzzyyy5386 小时前
C++之vector容器
开发语言·c++
小安同学iter7 小时前
SQL50+Hot100系列(11.9)
算法·leetcode·职场和发展
uotqwkn89469s7 小时前
如果Visual Studio不支持C++14,应该如何解决?
c++·ide·visual studio
炼金士8 小时前
基于多智能体技术的码头车辆最快行驶路径方案重构
算法·路径规划·集装箱码头
Maple_land8 小时前
Linux复习:冯·诺依曼体系下的计算机本质:存储分级与IO效率的底层逻辑
linux·运维·服务器·c++·centos
ue星空8 小时前
UE核心架构概念
网络·c++·ue5