4.1 鸡尾酒排序:双向冒泡的智慧

历史背景

鸡尾酒排序,也称双向冒泡排序或摇动排序,是冒泡排序的一种改进变体。它的名字来源于调酒师摇动鸡尾酒的动作——来回摇动。该算法的具体发明者已不可考,但它在 20 世纪 70 年代开始出现在计算机科学教材中。鸡尾酒排序的设计初衷是解决冒泡排序中"乌龟"问题——较小的元素在数组末尾时,需要很多轮才能移动到正确位置。

算法原理

鸡尾酒排序与冒泡排序类似,但它在每一轮中先从左到右进行冒泡(将最大元素移到最后),然后从右到左进行冒泡(将最小元素移到最前)。这种双向遍历可以更快地将小的元素移动到数组前面。

Java 实现

java
复制代码
public class CocktailSort {
    
    /**
     * 鸡尾酒排序
     */
    public static void sort(int[] arr) {
        int left = 0;
        int right = arr.length - 1;
        boolean swapped = true;
        
        while (left < right && swapped) {
            swapped = false;
            
            // 从左到右冒泡,将最大元素移到 right 位置
            for (int i = left; i < right; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                    swapped = true;
                }
            }
            right--;
            
            if (!swapped) break;
            
            swapped = false;
            // 从右到左冒泡,将最小元素移到 left 位置
            for (int i = right; i > left; i--) {
                if (arr[i] < arr[i - 1]) {
                    swap(arr, i, i - 1);
                    swapped = true;
                }
            }
            left++;
        }
    }
    
    /**
     * 鸡尾酒排序(优化版本,记录最后一次交换位置)
     */
    public static void sortOptimized(int[] arr) {
        int left = 0;
        int right = arr.length - 1;
        int lastSwapLeft = 0;
        int lastSwapRight = arr.length - 1;
        
        while (left < right) {
            // 从左到右
            int newRight = left;
            for (int i = left; i < right; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                    newRight = i;
                }
            }
            right = newRight;
            if (left >= right) break;
            
            // 从右到左
            int newLeft = right;
            for (int i = right; i > left; i--) {
                if (arr[i] < arr[i - 1]) {
                    swap(arr, i, i - 1);
                    newLeft = i;
                }
            }
            left = newLeft;
        }
    }
    
    /**
     * 鸡尾酒排序(泛型版本)
     */
    public static <T extends Comparable<T>> void sortGeneric(T[] arr) {
        int left = 0;
        int right = arr.length - 1;
        boolean swapped = true;
        
        while (left < right && swapped) {
            swapped = false;
            for (int i = left; i < right; i++) {
                if (arr[i].compareTo(arr[i + 1]) > 0) {
                    T temp = arr[i];
                    arr[i] = arr[i + 1];
                    arr[i + 1] = temp;
                    swapped = true;
                }
            }
            right--;
            
            if (!swapped) break;
            
            swapped = false;
            for (int i = right; i > left; i--) {
                if (arr[i].compareTo(arr[i - 1]) < 0) {
                    T temp = arr[i];
                    arr[i] = arr[i - 1];
                    arr[i - 1] = temp;
                    swapped = true;
                }
            }
            left++;
        }
    }
    
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

效率分析

指标 最好情况 最坏情况 平均情况
时间复杂度 O(n) O(n²) O(n²)
空间复杂度 O(1) O(1) O(1)
比较次数 n-1 n(n-1)/2 n²/2
交换次数 0 n(n-1)/2 n²/4

鸡尾酒排序是稳定的排序算法,也是原地排序。相比普通冒泡排序,鸡尾酒排序在最好的情况下可以减少一半的比较次数,特别适合数据基本有序但小的元素在数组末尾的场景。

应用场景

  • 数据基本有序的场景
  • 教学演示
  • 极小规模数据