看漫画学java:什么是鸡尾酒排序?

在上一篇漫画中,小灰介绍了冒泡排序的思路和几种变化:

漫画:什么是冒泡排序?

那么,鸡尾酒排序又是何方神圣呢?我们这一期将会详细讲述。

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

让我们首先来回顾一下冒泡排序的思想:

冒泡排序的每一个元素都可以像小气泡一样,根据自身大小,一点一点向着数组的一侧移动。算法的每一轮从都是从左到右比较元素,进行单向的位置交换

那么鸡尾酒排序做了怎样的优化呢?

鸡尾酒排序的元素比较和交换过程是双向的。

让我们来举一个栗子:

看漫画学java:什么是鸡尾酒排序?

有8个数组成一个无序数列:2,3,4,5,6,7,8,1,希望从小到大排序。

如果按照冒泡排序的思想,排序的过程是什么样呢?

第一轮结果(8和1交换)

看漫画学java:什么是鸡尾酒排序?

第二轮结果(7和1交换)

看漫画学java:什么是鸡尾酒排序?

第三轮结果(6和1交换)

看漫画学java:什么是鸡尾酒排序?

第四轮结果(5和1交换)

看漫画学java:什么是鸡尾酒排序?

第五轮结果(4和1交换)

看漫画学java:什么是鸡尾酒排序?

第六轮结果(3和1交换)

看漫画学java:什么是鸡尾酒排序?

第七轮结果(2和1交换)

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

鸡尾酒排序是什么样子呢?让我们来看一看详细过程:

第一轮(和冒泡排序一样,8和1交换)

看漫画学java:什么是鸡尾酒排序?

第二轮

此时开始不一样了,我们反过来从右往左

比较和交换:

8已经处于有序区,我们忽略掉8,让1和7比较。元素1小于7,所以1和7交换位置:

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

接下来1和6比较,元素1小于6,所以1和6交换位置:

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

接下来1和5比较,元素1小于5,所以1和5交换位置:

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

接下来1和4交换,1和3交换,1和2交换,最终成为了下面的结果:

看漫画学java:什么是鸡尾酒排序?

第三轮(虽然已经有序,但是流程并没有结束)

鸡尾酒排序的第三轮,需要重新从左向右比较和交换:

1和2比较,位置不变;2和3比较,位置不变;3和4比较,位置不变......6和7比较,位置不变。

没有元素位置交换,证明已经有序,排序结束。

这就是鸡尾酒排序的思路。排序过程就像钟摆一样,第一轮从左到右,第二轮从右到左,第三轮再从左到右......

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

public class CockTailSort {

  1. private
  2. static
  3. void
  4. sort(
  5. int
  6. array[])
  7. {
  8. int tmp = 0;
  9. for
  10. (
  11. int
  12. i=
  13. 0
  14. ; i
  15. 2
  16. -1; i++)
  17. {
  18. //有序标记,每一轮的初始是true
  19. boolean
  20. isSorted =
  21. true
  22. ;
  23. //奇数轮,从左向右比较和交换
  24. for
  25. (
  26. int
  27. j=i; j
  28. ; j++)
  29. {
  30. if
  31. (array[j] > array[j+
  32. 1
  33. ])
  34. {
  35. tmp = array[j];
  36. array[j] = array[j+
  37. 1
  38. ];
  39. array[j+
  40. 1
  41. ] = tmp;
  42. //有元素交换,所以不是有序,标记变为false
  43. isSorted =
  44. false
  45. ;
  46. }
  47. }
  48. if
  49. (isSorted){
  50. break
  51. ;
  52. }
  53. //偶数轮,从右向左比较和交换
  54. for
  55. (
  56. int
  57. j=array.length-i-
  58. 1
  59. ; j>i; j--)
  60. {
  61. if
  62. (array[j] < array[j-
  63. 1
  64. ])
  65. {
  66. tmp = array[j];
  67. array[j] = array[j-
  68. 1
  69. ];
  70. array[j-
  71. 1
  72. ] = tmp;
  73. //有元素交换,所以不是有序,标记变为false
  74. isSorted =
  75. false
  76. ;
  77. }
  78. }
  79. if
  80. (isSorted){
  81. break
  82. ;
  83. }
  84. }
  85. }
  86. public
  87. static
  88. void
  89. main(
  90. String
  91. [] args){
  92. int
  93. [] array =
  94. new
  95. int
  96. []{
  97. 2
  98. ,
  99. 3
  100. ,
  101. 4
  102. ,
  103. 5
  104. ,
  105. 6
  106. ,
  107. 7
  108. ,
  109. 8
  110. ,
  111. 1
  112. };
  113. sort(array);
  114. System
  115. .
  116. out
  117. .println(
  118. Arrays
  119. .toString(array));
  120. }

}

这段代码是鸡尾酒排序的原始实现。代码外层的大循环控制着所有排序回合,大循环内包含两个小循环,第一个循环从左向右比较并交换元素,第二个循环从右向左比较并交换元素。

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

让我们来回顾一下冒牌排序针对有序区的优化思路:

原始的冒泡排序,有序区的长度和排序的轮数是相等的。比如第一轮排序过后的有序区长度是1,第二轮排序过后的有序区长度是2 ......

要想优化,我们可以在每一轮排序的最后,记录下最后一次元素交换的位置,那个位置也就是无序数列的边界,再往后就是有序区了。

对于单向的冒泡排序,我们需要设置一个边界值,对于双向的鸡尾酒排序,我们需要设置两个边界值。请看代码:

public class CockTailSort {

  1. private
  2. static
  3. void
  4. sort
  5. (
  6. int
  7. array
  8. [])
  9. {
  10. int
  11. tmp
  12. =
  13. 0
  14. ;
  15. //记录右侧最后一次交换的位置
  16. int
  17. lastRightExchangeIndex
  18. =
  19. 0
  20. ;
  21. //记录左侧最后一次交换的位置
  22. int
  23. lastLeftExchangeIndex
  24. =
  25. 0
  26. ;
  27. //无序数列的右边界,每次比较只需要比到这里为止
  28. int
  29. rightSortBorder
  30. =
  31. array
  32. .
  33. length
  34. -
  35. 1
  36. ;
  37. //无序数列的左边界,每次比较只需要比到这里为止
  38. int
  39. leftSortBorder
  40. =
  41. 0
  42. ;
  43. for
  44. (
  45. int
  46. i
  47. =
  48. 0
  49. ;
  50. i
  51. <
  52. array
  53. .
  54. length
  55. /
  56. 2
  57. ;
  58. i
  59. ++)
  60. {
  61. //有序标记,每一轮的初始是true
  62. boolean
  63. isSorted
  64. =
  65. true
  66. ;
  67. //奇数轮,从左向右比较和交换
  68. for
  69. (
  70. int
  71. j
  72. =
  73. leftSortBorder
  74. ;
  75. j
  76. <
  77. rightSortBorder
  78. ;
  79. j
  80. ++)
  81. {
  82. if
  83. (
  84. array
  85. [
  86. j
  87. ]
  88. >
  89. array
  90. [
  91. j
  92. +
  93. 1
  94. ])
  95. {
  96. tmp
  97. =
  98. array
  99. [
  100. j
  101. ];
  102. array
  103. [
  104. j
  105. ]
  106. =
  107. array
  108. [
  109. j
  110. +
  111. 1
  112. ];
  113. array
  114. [
  115. j
  116. +
  117. 1
  118. ]
  119. =
  120. tmp
  121. ;
  122. //有元素交换,所以不是有序,标记变为false
  123. isSorted
  124. =
  125. false
  126. ;
  127. lastRightExchangeIndex
  128. =
  129. j
  130. ;
  131. }
  132. }
  133. rightSortBorder
  134. =
  135. lastRightExchangeIndex
  136. ;
  137. if
  138. (
  139. isSorted
  140. ){
  141. break
  142. ;
  143. }
  144. //偶数轮,从右向左比较和交换
  145. for
  146. (
  147. int
  148. j
  149. =
  150. rightSortBorder
  151. ;
  152. j
  153. >
  154. leftSortBorder
  155. ;
  156. j
  157. --)
  158. {
  159. if
  160. (
  161. array
  162. [
  163. j
  164. ]
  165. <
  166. array
  167. [
  168. j
  169. -
  170. 1
  171. ])
  172. {
  173. tmp
  174. =
  175. array
  176. [
  177. j
  178. ];
  179. array
  180. [
  181. j
  182. ]
  183. =
  184. array
  185. [
  186. j
  187. -
  188. 1
  189. ];
  190. array
  191. [
  192. j
  193. -
  194. 1
  195. ]
  196. =
  197. tmp
  198. ;
  199. //有元素交换,所以不是有序,标记变为false
  200. isSorted
  201. =
  202. false
  203. ;
  204. lastLeftExchangeIndex
  205. =
  206. j
  207. ;
  208. }
  209. }
  210. leftSortBorder
  211. =
  212. lastLeftExchangeIndex
  213. ;
  214. if
  215. (
  216. isSorted
  217. ){
  218. break
  219. ;
  220. }
  221. }
  222. }
  223. public
  224. static
  225. void
  226. main
  227. (
  228. String
  229. []
  230. args
  231. ){
  232. int
  233. []
  234. array
  235. =
  236. new
  237. int
  238. []{
  239. 2
  240. ,
  241. 3
  242. ,
  243. 4
  244. ,
  245. 5
  246. ,
  247. 6
  248. ,
  249. 7
  250. ,
  251. 8
  252. ,
  253. 1
  254. };
  255. sort
  256. (
  257. array
  258. );
  259. System
  260. .
  261. out
  262. .
  263. println
  264. (
  265. Arrays
  266. .
  267. toString
  268. (
  269. array
  270. ));
  271. }

}

代码中使用了左右两个边界值,rightSortBorder 代表右边界,leftSortBorder代表左边界。

在比较和交换元素时,奇数轮从 leftSortBorder 遍历到 rightSortBorder 位置,偶数轮从 rightSortBorder 遍历到 leftSortBorder 位置。

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

看漫画学java:什么是鸡尾酒排序?

—————END—————


分享到:


相關文章: