此文章前半部分--》》简单选择排序是另一位大哥写的博客:http://blog.csdn.net/hguisu/article/details/7776068
但是我这想要说的是后半部分双向选择排序,因为这个排序虽然那位大哥也写了,但是bug大多,而且下面评论的人有回复和纠正,但是似乎纠正的代码也是错的,所以才想着写这篇博客。
思想:
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
操作方法:
第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
以此类推.....
第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
直到整个序列按关键码有序。算法实现:
public int[] selectSort(int[] arr) {
int tmp;
int minIndex = 0; // 接受Minnum返回的最小值的下标
for (int i = 0; i < arr.length; i++) {
minIndex = Minnum(arr, i, arr.length);
if (minIndex != i) { // 判断这个下标是不是就是当前待排序的下标,其实这个判断也可以不要,但是想着为了程序的美,最好加上
// Swap(arr[minIndex], arr[i]);
tmp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = tmp;
}
}
return arr;
}
简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:
算法实现一:那位大哥的写法
void SelectSort(int r[],int n) {
int i ,j , min ,max, tmp;
for (i=1 ;i <= n/2;i++) {
// 做不超过n/2趟选择排序
min = i; max = i ; //分别记录最大和最小关键字记录位置
for (j= i+1; j<= n-i; j++) {
if (r[j] > r[max]) {
max = j ; continue ;
}
if (r[j]< r[min]) {
min = j ;
}
}
//该交换操作还可分情况讨论以提高效率
tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp;
tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp;
}
}
算法实现二:评论大哥(暂且这么叫吧)的写法
public void selectTwoSort(int a[], int n) {
int i;
for (i = 0; i <= n / 2; i++) { // i从0开始,不然第一个数据,就没有参与比较
int min = i;
int max = n - i - 1;// 最大数据指针应该这么设置吧!
int tmp = 0;
int j;
for (j = i + 1; j < n - i - 1; j++) {
if (a[j] < a[min]) {
min = j;
continue;
}
if (a[j] > a[max]) {
max = j;
}
}
// 放置最小值
tmp = a[min];
a[min] = a[i];
a[i] = tmp;
// 放置最大值
tmp = a[max];
a[max] = a[n - i - 1];
a[n - i - 1] = tmp;
}
}
int max = n - i - 1;// 最大数据指针应该这么设置吧!
因为max值本来就是在比较下重动态更新的,所以将
int max = i;
还是等于 i 到 n - i - 1 之间的任何一个都是无所谓的;
最大的bug还是运行这两个算法基本上都会有错误,也许是因为那两位大哥举例子用的数组的缘故,所以没被发现程序思想没错,实现的方式也没错,想要解决bug需要解决两点
第一点:选出min和max的位置
按照上面的算法写的话,我举个例子
数组:10 8 9 7
i = 0 ; //从第一个数开始
for (j = i + 1; j < n - i - 1; j++) {
if (a[j] < a[min]) {
min = j;
continue;
}
if (a[j] > a[max]) {
max = j;
}
}
这里错的地方是因为 j = i + 1 ,从上面的数组很容易看出当第一位数是这组待排序的数中最大的时候,j = i + 1 就会直接跳到第一个数的后面去选择后面中的最大最小,所以当第一位不管是这组数最大还是最小值时,此上面两种算法绝对出错
换而言之,解决问题的办法就是将第一位数也拉进来比较。
j = i ; //这样就好了
第一点:min和max的位置都出来了, 关键的地方是先和第n - i - 1个位置交换max呢?还是先和第 i 个位置交换min呢
为什么要这么考虑?下面我同样举个例子
还是数组:[...排好序的数..] 10 8 7 5 6 9 [...排好序的数...],无疑此数组中max的位置是10的下表, min无疑是5的位置,假如按照上面大哥的写法,无论什么情况都是先交换最小的,然后再交换最大的数组一趟排序后的结果是[...排好序的数..9] 8 7 10 6 [5...排好序的数...],因为5的位置先和10的位置交换,但是max的值还是原先10的位置,也就是交换后5的位置,之后再将max位置上的5和n - i - 1位置上的9交换.由于排好序的数是不会参与比较的,所以此算法无论如何都是得不到正确结果的,这里只举出了这一种特殊的交换位置,还有两种特殊的交换位置自己想象一下立马就可以出来了
好了,到了这里,问题全部跑出来了,
因为要考虑交换位置的情况,所以代码的臃肿度立马提升了不少,下面给出我自己的写的代码,如果以后想到更好的解决办法,会优化的,
如果是那位大哥大姐能有更好的想法,好心的话也可以回复一下我
算法实现三:
public int[] selectSort(int[] arr, int n) {
int tmp;
int minIndex, maxIndex;
for (int i = 0; i <= n / 2; i++) {
minIndex = i;
maxIndex = i;
for (int j = i; j < n - i - 1; j++) {
if (arr[j] > arr[maxIndex]) {
maxIndex = j;
continue;
}
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (maxIndex == i && minIndex == n - i - 1) {
//特殊交换位置一,当最大的交换位置和最小的交换位置都在最前面和最后面
tmp = arr[maxIndex];
arr[maxIndex] = arr[minIndex];
arr[minIndex] = tmp;
} else
if (maxIndex == i) {
//特殊交换位置二,当最大的交换位置是最前面
// Swap(arr[n - i], arr[maxIndex]) ;
tmp = arr[maxIndex];
arr[maxIndex] = arr[n - i - 1];
arr[n - i - 1] = tmp;
// Swap(arr[i], arr[minIndex]) ;
tmp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = tmp;
} else
if (minIndex == n - i - 1) {
//特殊交换位置三,当最小的交换位置是最后面
// Swap(arr[i], arr[minIndex]) ;
tmp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = tmp;
// Swap(arr[n - i], arr[maxIndex]) ;
tmp = arr[maxIndex];
arr[maxIndex] = arr[n - i - 1];
arr[n - i - 1] = tmp;
} else {
//除了上面三种特殊交换,就剩普通交换了,普通交换随便哪个先交换都行
// Swap(arr[i], arr[minIndex]) ;
tmp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = tmp;
// Swap(arr[n - i], arr[maxIndex]) ;
tmp = arr[maxIndex];
arr[maxIndex] = arr[n - i - 1];
arr[n - i - 1] = tmp;
}
sortTest.dispaly(arr); //自己写的输出函数
}
return arr;
}