[排序算法] 7. 直接选择排序及优化、三次异或交换法暴露的坑点(选择排序、算法优化)

1. 基本思想

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

主要步骤如下:

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

在这里插入图片描述

2. 代码实现

// 直接选择排序(递增)
void Swap(int *x, int *y) {
	//int tmp = *x;
	//*x = *y;
	//*y = tmp;
	*x = ((*x) ^ (*y));
	*y = ((*x) ^ (*y));
	*x = ((*x) ^ (*y));
}

void SelectSort(int array[], int size) {
	for (int i = 0; i < size; ++i) {
		// [0, size - i)
		int m = 0;
		for (int j = 0; j < size - i; ++j) {
			if (array[j] > array[m]) {
				m = j;
			}
		}
		// m 就是最大数的下标了
		// 避免自己和自己异或三次结果为0了
		// 即当m = size - i - 1时就会出现该情况
		// 如果用异或的话需要进行判断
		if (array + m == array + size - i - 1)
			continue;
		Swap(array + m, array + size - i - 1);
	}
}

测试数据:int array[] = { 3, 9, 1, 4, 2, 8, 2, 7, 5, 3, 6, 11, 9, 4, 2, 5, 0, 6 };
在这里插入图片描述

当然,我们每次找遍历找到最小的元素,同时也可以找到最小元素啊,这样不就大大提高效率了吗?优化版本如下:

// 升级版直接选择排序,一次即找最大的,也找最小的
void SelectSortOP(int array[], int size) {
	int minSpace = 0;			// 用来放找到的最小数的下标
	int maxSpace = size - 1;	// 用来放找到的最大数的下标

	// 因为是闭区间,minSpace == maxSpace 时
	// [minSpace, maxSpace] 区间内只剩一个数了
	// 所有可以停止了
	while (minSpace < maxSpace) {
		int min = minSpace;	// 假设最小的是 minSpace 位置
		int max = minSpace; // 假设最大的是 minSpace 位置
		// 在 [minSpace + 1, maxSpace] 区间里找真正的最小和最大

		for (int j = minSpace + 1; j <= maxSpace; j++) {
			if (array[j] < array[min]) {
				min = j;
			}

			if (array[j] > array[max]) {
				max = j;
			}
		}

		// min 和 max 分别时最小和最大的数的下标
		// 先交换小的
		{
			int t = array[min];
			array[min] = array[minSpace];
			array[minSpace] = t;
		}
		// 再交换大的
		if (max == minSpace) {
			max = min;
		}
		{
			int t = array[max];
			array[max] = array[maxSpace];
			array[maxSpace] = t;
		}

		minSpace++;
		maxSpace--;
	}
}

测试数据:int array[] = { 3, 9, 1, 4, 2, 8, 2, 7, 5, 3, 6, 11, 9, 4, 2, 5, 0, 6 };
在这里插入图片描述

3. 三次异或法交换两数暴露的坑点

三次异或交换两数的值,不采用任何的辅助空间,也是常考的一个点。博主在测上面程序的时候,就很巧的出现了这个结果:
在这里插入图片描述
重复值怎么就成为 0 了呢?直接选择排序程序本身是没有问题的,那么问题就出现在了自己实现的 Swap() 函数中了,然后将 Swap() 函数修改为采用一个辅助空间 tmp 的方式进行交换,结果就正确了。那么就将问题锁定在了这三次异或上了,分析调用 Swap() 函数的条件能发现:

  • m = size - i - 1 时,传入的两个变量是同一个对象的话,那么同一个对象和自己进行三次异或肯定会被置为0
  • 如果两个变量是不同的两个变量,那么则不会出现这种情况,因为一个不同的变量在内存中的地址并不是相同的,一个变量的值的变换并不会影响另外一个变量的值,
  • 而如果是自己跟自己异或,那么在异或的函数中的第一步就会将自己置为0,显然结果就是0

在使用三处异或交换时一定要注意到这个坑点啊~

4. 性能分析

时间复杂度

  • 最坏 O ( n 2 ) O(n^2) O(n2) 对数据不敏感
  • 平均 O ( n 2 ) O(n^{2}) O(n2) 对数据不敏感
  • 最好 O ( n 2 ) O(n^2) O(n2) 对数据不敏感

空间复杂度

  • O ( 1 ) O(1) O(1)

排序稳定性

  • 不稳定 { 7, 8, 3a, 5, 3b } 交换无法保证3a 还在 3b
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

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

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

打赏作者

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

抵扣说明:

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

余额充值