【算法】鸡尾酒排序

354 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

鸡尾酒排序 :是冒泡排序的一种变体,又叫做双向冒泡排序。

它从两个方向对数组或序列进行冒泡排序:

  • 每一次从左向右进行冒泡排序后,紧接着一次从右向左的冒泡排序。
  • 这样从左向右,然后从右向左,来回反复地进行冒泡排序,就像在摇晃调制一杯鸡尾酒,因此得名鸡尾酒排序。

举个栗子:[2, 4, 6, 8, 0]

  1. 普通冒泡排序:需要 4 次遍历才能完成排序

鸡尾酒-2022-08-1308-50-17.png

  1. 鸡尾酒排序:只需要 2 次遍历即可完成排序

鸡尾酒-2022-08-1308-50-18.png

虽然在某些场景下,鸡尾酒排序比冒泡排序有稍微好点的性能,但对于分布均匀的数据这两性能都一样:

  • 最坏时间复杂度: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;
    }
}