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
前