前言
上節,我們已經通過對冒泡算法的優化、能夠達到我們預想的結果。比較次數的減少、本節將繼續在冒泡排序的基礎上進行優化、能夠達到剛好的效果。
雞尾酒排序 🍸
爲什麼叫雞尾酒排序呢?可能這個名字起得比較特殊。它是基於冒泡排序做了一些小小的改動。也叫做快樂鐘擺排序
現在,大家的腦子裏肯定會想到一個古老的鐘擺。。。。。
左搖一下、右搖一下、重複不止
現在請記住這個場景。我們來學習一下這個雞尾酒排序
爲何方神聖!
冒泡排序特點
從左往右、依次循環比較、一遍將一個大元素推到最右側。
雞尾酒排序特點
- 將原有的單向改爲
雙向比較
- 每次循環則將
最小
元素和最大
元素分別推至左右兩側
畫圖理解
準備一個數組 [49, 38, 65, 97, 76, 13, 27, 49]
鐘擺循環1開始 從右至左
1、本次比較27<49
則無需改變循序。
2、本次比較13<27
則無需改變循序。
3、本次比較76<13
則需要交換位置。
4、直到比較到最左側。。。(這裏省略幾個步驟)
我們發現,這個最小的元素13
已經被確認出來了。
鐘擺循環2開始 從左至右
-
13<49
則不改變位置。繼續向下移步
-
49>38
則需要交換位置。交換位置後,繼續下一步
-
49<65
則位置不改變,繼續向下一步。
-
省略一些步驟。直到比較完最後一個元素。最大的一個元素
97
已經被確認出來了。
代碼示例
public static void sort4(int[] array) {
int temp = 0;
int allChange = 0;
/**
* 外層循環用於控制程序總體循環次數
*/
for (int i = 0; i < array.length / 2; i++) {
/**
* 有無交換元素標記
*/
boolean isChange = true;
//從左向右擺動
for (int j = i; j < array.length - i - 1; j++) {
if (array[j] > array[j + 1]) {
//交換元素
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
isChange = false;
}
allChange++;
}
if (isChange) break;
isChange = true;
//從右向左擺動
for (int g = array.length - i - 1; g > i; g--) {
if (array[g] < array[g - 1]) {
temp = array[g];
array[g] = array[g-1];
array[g-1] = temp;
isChange = false;
}
allChange++;
}
if (isChange) break;
}
System.out.println(Arrays.toString(array));
System.out.println("總體比較次數:"+allChange);
}
結果如下:
[13, 27, 38, 49, 49, 65, 76, 97]
總體比較次數:30
通過觀察我們發現,當前算法的比較次數還是相對來說較多的。外層大循環運行減少一半、但是內層兩個小循環還是需要注意的。有什麼辦法可以優化一下呢?我們可以採用之前的思路。通過設置有序長度
的方式來減少內層循環次數、已達到我們優化的目的。
有序優化
其實說白了,就是在發生變化的時候記錄當前位置,下次循環到本次記錄的位置停止則可以了。
通過兩個邊界變量leftBorder
與rightBorder
控制程序比較位置的終止。
左循環從左邊界比較到右邊界停止
右循環從右邊界比較到左邊界停止
public static void sort5(int[] array) {
int temp = 0;
int allChange = 0;
//記錄左邊界
int leftBorder = 0;
//右邊界
int rightBorder = array.length - 1;
//左邊發生變化的位置
int leftChangeIndex = 0;
//右邊發生變化的位置
int rightChangeIndex = 0;
/**
* 外層循環用於控制程序總體循環次數
*/
for (int i = 0; i < array.length / 2; i++) {
/**
* 有無交換元素標記
*/
boolean isChange = true;
//從左向右擺動
for (int j = leftBorder; j < rightBorder; j++) {
if (array[j] > array[j + 1]) {
//交換元素
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
isChange = false;
//記錄右邊邊界
rightChangeIndex = j;
}
allChange++;
}
//用於下次循環終止
rightBorder = rightChangeIndex;
if (isChange) break;
isChange = true;
//從右向左擺動
for (int g = rightBorder; g > leftBorder; g--) {
if (array[g] < array[g - 1]) {
temp = array[g];
array[g] = array[g-1];
array[g-1] = temp;
isChange = false;
//記錄左邊界
leftChangeIndex = g;
}
allChange++;
}
leftBorder = leftChangeIndex;
if (isChange) break;
}
System.out.println(Arrays.toString(array));
System.out.println("總體比較次數:"+allChange);
}
--------------
[13, 27, 38, 49, 49, 65, 76, 97]
總體比較次數:27
小結
至此我們已經學習了有關的三個排序方式、包括最基礎的冒泡排序、優化冒泡排序。最終的冒泡排序。通過鐘擺思想實現的雞尾酒排序法。越來覺得一個小小的排序,通過學習後發現,竟然有很多有意思的地方。