携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情
鸡尾酒排序 :是冒泡排序的一种变体,又叫做双向冒泡排序。
它从两个方向对数组或序列进行冒泡排序:
- 每一次从左向右进行冒泡排序后,紧接着一次从右向左的冒泡排序。
- 这样从左向右,然后从右向左,来回反复地进行冒泡排序,就像在摇晃调制一杯鸡尾酒,因此得名鸡尾酒排序。
举个栗子:[2, 4, 6, 8, 0]
- 普通冒泡排序:需要 4 次遍历才能完成排序
- 鸡尾酒排序:只需要 2 次遍历即可完成排序
虽然在某些场景下,鸡尾酒排序比冒泡排序有稍微好点的性能,但对于分布均匀的数据这两性能都一样:
- 最坏时间复杂度:
O(n ^ 2)
- 空间复杂度:
O(1)
实现如下:
// Time: O(n^2), Space: O(1)
public void sort(int[] arr) {
if (arr == null || arr.length == 0) return;
int left = 0, right = arr.length - 1;
while (left < right) {
for (int i = left; i < right; ++i)
if (arr[i] > arr[i + 1])
swap(arr, i, i + 1);
--right;
for (int i = right; i > left; --i)
if (arr[i - 1] > arr[i])
swap(arr, i - 1, i);
++left;
}
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
优化:都是为了减少对比次数。
优化1:bool swapped
记录每一次遍历过程中,是否有元素交换
// 优化一: bool swapped, 记录每一次遍历过程中,是否有元素交换
// Time: O(n^2), Space: O(1)
public void sortEarlyReturn(int[] arr) {
if (arr == null || arr.length == 0) return;
int left = 0, right = arr.length - 1;
boolean swapped;
while (left < right) {
swapped = false;
for (int i = left; i < right; ++i) {
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
swapped = true;
}
}
--right;
for (int i = right; i > left; --i) {
if (arr[i - 1] > arr[i]) {
swap(arr, i - 1, i);
swapped = true;
}
}
++left;
if (!swapped) return;
}
}
优化2:int newLeft, newRight
- 记录从左向右最后发生交换的元素下标
- 记录从右向左最后发生交换的元素下标
// 优化2: int newLeft, newRight
// 记录从左向右最后发生交换的元素下标
// 记录从右向左最后发生交换的元素下标
// Time: O(n^2), Space: O(1)
public void sortSkip(int[] arr) {
if (arr == null || arr.length == 0) return;
int left = 0, right = arr.length - 1;
int newLeft, newRight;
while (left < right) {
newRight = left;
for (int i = left; i < right; ++i) {
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
newRight = i;
}
}
right = newRight;
newLeft = right;
for (int i = right; i > left; --i) {
if (arr[i - 1] > arr[i]) {
swap(arr, i - 1, i);
newLeft = i;
}
}
left = newLeft;
}
}