仅以此篇博客致敬掘金博客. 就在昨晚被
CSDN傻x般的交互方式折磨的怒火难泄的时候, 百度一起吐槽它, 偶遇一知乎贴吧, 正式集体吐槽CSDN的, 于是乎, 痛快的骂了几句, 也就是在哪里看到掘金的字样, 还有stackoverflow, 鉴于后者外网响应较慢. 于是瞅瞅掘金, 看看是个什么鬼.
进来一看, 首先, 界面较为简洁, 不想某些网站就像钓鱼网站一样. 进来后, 发现文章支持Markdown, 大大加分(本尊以为不支持markdown的博客直接pass得了). 更爽的是, 这里提供markdown很多快捷键, 而且左下角自带语法提示, 很像印象笔记的马克飞象(马克飞象私人化太重, 不能很好的分享, 这是我郁闷的小地方).
废话说完, 接下来简单的分享下昨晚研究的二元选择排序Java代码, 作为进驻掘金的第一炮.
代码
public static void selectSort2(int arr[]) {
int i, j, min, max;
int min_value = 0;
int max_value = 0;
int n = arr.length;
/**搜索比较**/
for (i = 0; i <= (n - 1) / 2; i++) {
// 做不超过n/2趟选择排序
// 分别记录最大和最小关键字记录位置 \ 每次循环更新索引, 一般待遍历区域的首末假设为最大值或最小值位置
max = i;
min = i;
for (j = i + 1; j <= n - 1 - i; j++) {
if (arr[j] >= arr[max]) { //>= -->保证相等数值中最后面的为最大值, 这样甩到后面去, 能够保证顺序稳定(升序情况下适用)
max = j;
continue;
}
if (arr[j] < arr[min]) { //相等数值中最前面的为最小值
min = j;
}
}
/**数据交换**/
// 交换的部分最需要注意了, 网上很多博客, 在这里其实是有bug的
// 总体思路是: 最小值放在搜索取首位, 最大值放在搜索区末位
//first:i last:n-1-i min max
if(i==max){
max_value = arr[i];
arr[i] = arr[min];
arr[min] = arr[n-1-i];
arr[n-1-i] = max_value;
}else if((n-1-i)==min) {
min_value = arr[n-1-i];
arr[n-1-i] = arr[max];
arr[max] = arr[i];
arr[i] = min_value;
}else {
//首位不和最大重叠且末尾不和最小重叠
min_value = arr[min];
arr[min] = arr[i];
arr[i] = min_value;
max_value = arr[max];
arr[max] = arr[n-1-i];
arr[n-1-i] = max_value;
}
}
}坑点
二元排序主要分两部分:
- 搜索比较, 确定最大, 最小值索引
- 交换数据
第一部分, 大家一般都没有问题。问题往往出在第二部分交换数据。很多朋友在这里没有细想,导致代码是有问题的。
如下面这个代码片(来源:八大排序算法之二元选择排序):
int maxtmp = 0;
int mintmp = 0; //注意:这里不能把a[max],a[min]直接和a[i]和a[n-i-1]调换
maxtmp = a[max];
mintmp = a[min];
a[max] = a[i];
a[min] = a[n-i-1];
a[i] = maxtmp;
a[n-i-1] = mintmp;通过观察这段代码片, 可知博主实现的降序。如果依据这个代码,当i和max重合或者n-i-1和min重合时, 基本就会出现数值丢失的情况,更不用谈排序正确与否了!
举个栗子:
数组截取: ... (2), 6, (9), 8, 5, (4), ...
首位2, 也是min.
首->max: 2, 6, 2, 8, 5, 4
末->min: 4, 6, 2, 8, 5, 4
max->首: 9, 6, 2, 8, 5, 4
min->末: 9, 6, 2, 8, 5, 2
到这一步, 不用看顺序, 只需要看数字的值, 已经变了, 多出了一个2! 4却丢失了!!
测试
通过比较二元选择排序和普通的二元排序,二元选择排序速度的确有所提升,但是由于增加了代码复杂度,提升效果有限!
1. 时间对比
int[] arr = MyUtils.randomArr(100000, 100000, -123); //自定义工具, 生成10万长度, 范围[0,100000)的整型数组, -123是种子值.
long t0 = System.currentTimeMillis(); //获取开始时间
//selectSort(arr); //普通选择排序
//selectSort2(arr); //二元选择排序
long t1 = System.currentTimeMillis(); //获取结束时间
System.out.println((t1-t0)+"ms");结果显示:
对于10万长度的整形数组,
- 二元选择排序耗时: 2,208ms
- 普通选择排序: 2,620ms
可见, 两者差异很小。
另外,
- 当数组长度增长到100万时,选择排序耗时太长,只能杀死它。。。
- 普通选择排序中,在搜索比较的时候是只更新索引, 找到最小的索引后再交换的思路。如果比较一次交换一次,耗时明显提高近5倍。
附录
普通选择排序——比较一次交换一次
public static void selectSort(int[] arr) {
int count = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
count ++;
}
}
System.out.println(count);
}普通选择排序——比较仅更新索引
public static void selectSort3(int[] arr) {
int min_idx = 0; //初始化最小值位置
int temp = 0;
for (int i = 0; i < arr.length - 1; i++) {
min_idx = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[min_idx] > arr[j]) {
min_idx = j; //先只保存最小值所在位置, 暂不做交换
}
// count ++;
}
temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
// System.out.println(count);
}