在上一篇漫畫中,小灰介紹了冒泡排序的思路和幾種變化:
漫畫:什麼是冒泡排序?
那麼,雞尾酒排序又是何方神聖呢?我們這一期將會詳細講述。
讓我們首先來回顧一下冒泡排序的思想:
冒泡排序的每一個元素都可以像小氣泡一樣,根據自身大小,一點一點向著數組的一側移動。算法的每一輪從都是從左到右比較元素,進行單向的位置交換。
那麼雞尾酒排序做了怎樣的優化呢?
雞尾酒排序的元素比較和交換過程是雙向的。
讓我們來舉一個栗子:
有8個數組成一個無序數列:2,3,4,5,6,7,8,1,希望從小到大排序。
如果按照冒泡排序的思想,排序的過程是什麼樣呢?
第一輪結果(8和1交換)
第二輪結果(7和1交換)
第三輪結果(6和1交換)
第四輪結果(5和1交換)
第五輪結果(4和1交換)
第六輪結果(3和1交換)
第七輪結果(2和1交換)
雞尾酒排序是什麼樣子呢?讓我們來看一看詳細過程:
第一輪(和冒泡排序一樣,8和1交換)
第二輪
此時開始不一樣了,我們反過來從右往左 比較和交換:
8已經處於有序區,我們忽略掉8,讓1和7比較。元素1小於7,所以1和7交換位置:
接下來1和6比較,元素1小於6,所以1和6交換位置:
接下來1和5比較,元素1小於5,所以1和5交換位置:
接下來1和4交換,1和3交換,1和2交換,最終成為了下面的結果:
第三輪(雖然已經有序,但是流程並沒有結束)
雞尾酒排序的第三輪,需要重新從左向右比較和交換:
1和2比較,位置不變;2和3比較,位置不變;3和4比較,位置不變......6和7比較,位置不變。
沒有元素位置交換,證明已經有序,排序結束。
這就是雞尾酒排序的思路。排序過程就像鐘擺一樣,第一輪從左到右,第二輪從右到左,第三輪再從左到右......
public class CockTailSort {
- private
- static
- void
- sort(
- int
- array[])
- {
- int tmp = 0;
- for
- (
- int
- i=
- 0
- ; i
- 2
- -1; i++)
- {
- //有序標記,每一輪的初始是true
- boolean
- isSorted =
- true
- ;
- //奇數輪,從左向右比較和交換
- for
- (
- int
- j=i; j
- ; j++)
- {
- if
- (array[j] > array[j+
- 1
- ])
- {
- tmp = array[j];
- array[j] = array[j+
- 1
- ];
- array[j+
- 1
- ] = tmp;
- //有元素交換,所以不是有序,標記變為false
- isSorted =
- false
- ;
- }
- }
- if
- (isSorted){
- break
- ;
- }
- //偶數輪,從右向左比較和交換
- for
- (
- int
- j=array.length-i-
- 1
- ; j>i; j--)
- {
- if
- (array[j] < array[j-
- 1
- ])
- {
- tmp = array[j];
- array[j] = array[j-
- 1
- ];
- array[j-
- 1
- ] = tmp;
- //有元素交換,所以不是有序,標記變為false
- isSorted =
- false
- ;
- }
- }
- if
- (isSorted){
- break
- ;
- }
- }
- }
- public
- static
- void
- main(
- String
- [] args){
- int
- [] array =
- new
- int
- []{
- 2
- ,
- 3
- ,
- 4
- ,
- 5
- ,
- 6
- ,
- 7
- ,
- 8
- ,
- 1
- };
- sort(array);
- System
- .
- out
- .println(
- Arrays
- .toString(array));
- }
}
這段代碼是雞尾酒排序的原始實現。代碼外層的大循環控制著所有排序回合,大循環內包含兩個小循環,第一個循環從左向右比較並交換元素,第二個循環從右向左比較並交換元素。
讓我們來回顧一下冒牌排序針對有序區的優化思路:
原始的冒泡排序,有序區的長度和排序的輪數是相等的。比如第一輪排序過後的有序區長度是1,第二輪排序過後的有序區長度是2 ......
要想優化,我們可以在每一輪排序的最後,記錄下最後一次元素交換的位置,那個位置也就是無序數列的邊界,再往後就是有序區了。
對於單向的冒泡排序,我們需要設置一個邊界值,對於雙向的雞尾酒排序,我們需要設置兩個邊界值。請看代碼:
public class CockTailSort {
- private
- static
- void
- sort
- (
- int
- array
- [])
- {
- int
- tmp
- =
- 0
- ;
- //記錄右側最後一次交換的位置
- int
- lastRightExchangeIndex
- =
- 0
- ;
- //記錄左側最後一次交換的位置
- int
- lastLeftExchangeIndex
- =
- 0
- ;
- //無序數列的右邊界,每次比較只需要比到這裡為止
- int
- rightSortBorder
- =
- array
- .
- length
- -
- 1
- ;
- //無序數列的左邊界,每次比較只需要比到這裡為止
- int
- leftSortBorder
- =
- 0
- ;
- for
- (
- int
- i
- =
- 0
- ;
- i
- <
- array
- .
- length
- /
- 2
- ;
- i
- ++)
- {
- //有序標記,每一輪的初始是true
- boolean
- isSorted
- =
- true
- ;
- //奇數輪,從左向右比較和交換
- for
- (
- int
- j
- =
- leftSortBorder
- ;
- j
- <
- rightSortBorder
- ;
- j
- ++)
- {
- if
- (
- array
- [
- j
- ]
- >
- array
- [
- j
- +
- 1
- ])
- {
- tmp
- =
- array
- [
- j
- ];
- array
- [
- j
- ]
- =
- array
- [
- j
- +
- 1
- ];
- array
- [
- j
- +
- 1
- ]
- =
- tmp
- ;
- //有元素交換,所以不是有序,標記變為false
- isSorted
- =
- false
- ;
- lastRightExchangeIndex
- =
- j
- ;
- }
- }
- rightSortBorder
- =
- lastRightExchangeIndex
- ;
- if
- (
- isSorted
- ){
- break
- ;
- }
- //偶數輪,從右向左比較和交換
- for
- (
- int
- j
- =
- rightSortBorder
- ;
- j
- >
- leftSortBorder
- ;
- j
- --)
- {
- if
- (
- array
- [
- j
- ]
- <
- array
- [
- j
- -
- 1
- ])
- {
- tmp
- =
- array
- [
- j
- ];
- array
- [
- j
- ]
- =
- array
- [
- j
- -
- 1
- ];
- array
- [
- j
- -
- 1
- ]
- =
- tmp
- ;
- //有元素交換,所以不是有序,標記變為false
- isSorted
- =
- false
- ;
- lastLeftExchangeIndex
- =
- j
- ;
- }
- }
- leftSortBorder
- =
- lastLeftExchangeIndex
- ;
- if
- (
- isSorted
- ){
- break
- ;
- }
- }
- }
- public
- static
- void
- main
- (
- String
- []
- args
- ){
- int
- []
- array
- =
- new
- int
- []{
- 2
- ,
- 3
- ,
- 4
- ,
- 5
- ,
- 6
- ,
- 7
- ,
- 8
- ,
- 1
- };
- sort
- (
- array
- );
- System
- .
- out
- .
- println
- (
- Arrays
- .
- toString
- (
- array
- ));
- }
}
代碼中使用了左右兩個邊界值,rightSortBorder 代表右邊界,leftSortBorder代表左邊界。
在比較和交換元素時,奇數輪從 leftSortBorder 遍歷到 rightSortBorder 位置,偶數輪從 rightSortBorder 遍歷到 leftSortBorder 位置。
—————END—————
閱讀更多 java進階架構師 的文章