僅以此篇部落格致敬掘金部落格. 就在昨晚被
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);
}複製程式碼