目录
选择排序的定义
选择排序,就是将从一组数据的最左边开始(最右也行),然后向右(左)遍历一遍,在找到最大(最小)的那个数据的时候,将其放在最初的位置(末尾),然后再回到其后一个(前一个)位置,再重新遍历,接着找出最大(最小).......把全部的数都遍历了之后,其数据也就有序了。
选择排序的优化
既然在选择排序中,每遍历一次就得找出一个最大或最小的值,那么我们可不可以将其两个极值都先找出来,然后分别放在数据的最左边和最右边呢?在当其左右遍历的值相同时或刚错过时停止。到这时,数据是不是也是有序的呢?
假设,我们将对下面这组数据进行选择排序。
那么,根据我们上面的思想可以写出下面的遍历一次的代码如下:
void Swap(int* child, int* parent) {//交换数组中下标为child和parent的值
int tmp;
tmp = *child;
*child = *parent;
*parent = tmp;
}
void SelectSort(int* a, int n) {
int begin = 0, end = n - 1;
//这里得让min和max的值初始化为一个相同的值,否则可能会在遍历比较的时候有产生遗漏或不准确。
int min = begin, max = begin;
//遍历找出begin和end之间的最大值和最小值的下标
for (int i = begin + 1; i <= end; i++) {
if (a[i] < a[min]) {
min = i;
}
if (a[i] > a[max]) {
max = i;
}
}
//到这就找到了最大值和最小值的下标
//然后将最大值放在最右边,最小值放在最左边。
Swap(&a[begin], &a[min]);
Swap(&a[end], &a[max]);
//最后,再进一步begin和end之间的范围。
begin++;
end--;
}
一次走完了之后,我们就该去想想它们停止的条件了:
假如这组数据为偶数个时,从左右两边开始遍历,它们会错过
当其数据为奇数个时,从左右两边开始遍历,其会相遇。
这就是其停止遍历的条件了。
代码如下:
while (begin < end) {//当数组为偶数个时,begin和end会错过。当为奇数个时,会相遇。
}
代码结合初次实现:
assert(a);
int begin = 0, end = n - 1;
while (begin < end) {//当数组为偶数个时,begin和end会错过。当为奇数个时,会相遇。
//这里得让min和max的值初始化为一个相同的值,否则可能会在遍历比较的时候有产生遗漏或不准确。
int min = begin, max = begin;
//遍历找出begin和end之间的最大值和最小值的下标
for (int i = begin + 1; i <= end; i++) {
if (a[i] < a[min]) {
min = i;
}
if (a[i] > a[max]) {
max = i;
}
}
//到这就找到了最大值和最小值的下标
//然后将最大值放在最右边,最小值放在最左边。
Swap(&a[begin], &a[min]);
Swap(&a[end], &a[max]);
//最后,再进一步begin和end之间的范围。
begin++;
end--;
}
产生Bug了!!!
以上代码看似好像逻辑上没啥问题,那么我们来运行一下,给我们最初的设置的数据来排下序。
运行结果:
这是咋回事呢?代码写错了么?可是逻辑上好像也没啥问题啊?
别急,让我们来调试一下就清楚了.
其实,在调试中就能发现了,原来我们需要排序的这组数据,其最大值一直就在最左边。
所以从第一次开始遍历到结束,其位置没变过。
但在当进行第一次交换的时候,我们将最小值和其交换了位置,从而导致最终结果排序混乱了。
由此,我们需要在第一次交换之后加上这么一段代码:
if (begin == max) {
max = min;
}
意思是:当begin和max下标相同时,其上面一步已经交换了其值,将最大值换到下标为min上去了
最终实现代码:
void SelectSort(int* a, int n) {
assert(a);
int begin = 0, end = n - 1;
while (begin < end) {//当数组为偶数个时,begin和end会错过。当为奇数个时,会相遇。
//这里得让min和max的值初始化为一个相同的值,否则可能会在遍历比较的时候有产生遗漏或不准确。
int min = begin, max = begin;
//遍历找出begin和end之间的最大值和最小值的下标
for (int i = begin + 1; i <= end; i++) {
if (a[i] < a[min]) {
min = i;
}
if (a[i] > a[max]) {
max = i;
}
}
//到这就找到了最大值和最小值的下标
//然后将最大值放在最右边,最小值放在最左边。
Swap(&a[begin], &a[min]);
//当begin和max下标相同时,其上面一步已经交换了其值,将最大值换到下标为min上去了
if (begin == max) {
max = min;
}
Swap(&a[end], &a[max]);
//最后,再进一步begin和end之间的范围。
begin++;
end--;
}
}
运行结果:
至此,选择排序也就结束了。