快速排序 - 学习记录

Java 实现

package com.littlefxc.examples.algorithm;

/**
 * 快速排序
 */
public class QuickSort {
    private QuickSort() {
    }

    public static void sort(Comparable[] arr) {

        sort(arr, 0, arr.length - 1);

    }

    /**
     * 版本更新:对于对于小规模数组, 使用插入排序
     *
     * @param arr
     * @param p
     * @param r
     * @version 2
     */
    private static void sort(Comparable[] arr, int p, int r) {
        // 对于小规模数组, 使用插入排序
        if (r - p <= 15) {
            InsertionSort.insertionSort(arr, p, r);
            return;
        }

        int q = partition3(arr, p, r);
        sort(arr, p, q - 1);
        sort(arr, q + 1, r);
    }

    /**
     * 写法 1 - 出自《算法导论》
     *
     * @param arr
     * @param p
     * @param r
     * @return
     */
    private static int partition1(Comparable[] arr, int p, int r) {
        // 写法 2
        Comparable key = arr[r];
        int i = p;
        for (int j = i; j < r; j++) {
            if (arr[j].compareTo(key) <= 0) {
                swap(arr, i++, j);
            }
        }
        swap(arr, i, r);
        return i;
    }

    /**
     * 写法 2
     * 版本更新:为解决排序近乎有序的数组, 快速排序退化成O(N^2)级别, 同时1000000大小的数组派出异常 java.lang.StackOverflowError
     * 解决办法:随机取分区点, 对于1000000大小的数组可以在1s内排序完成
     *
     * @param arr
     * @param p
     * @param r
     * @return
     * @version 2
     */
    private static int partition2(Comparable[] arr, int p, int r) {

        // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
        swap(arr, p, (int) (Math.random() * (r - p + 1)) + p);

        Comparable key = arr[p];
        int i = p;
        for (int j = p + 1; j <= r; j++) {
            if (arr[j].compareTo(key) < 0) {
                swap(arr, j, ++i);
            }
        }
        swap(arr, p, i);
        return i;
    }

    /**
     * 一旦有大量重复的元素,版本2又有问题:被排序的数组的树极不平衡,时间复杂度退化为O(N^2)
     * 解决办法:双路快速排序
     *
     * @param arr
     * @param p
     * @param r
     * @return
     */
    private static int partition3(Comparable[] arr, int p, int r) {
        // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
        swap(arr, p, (int) (Math.random() * (r - p + 1)) + p);

        Comparable key = arr[p];
        int i = p + 1, j = r;
        while (true) {
            while (i <= r && arr[i].compareTo(key) < 0) {
                i++;
            }
            while (j >= i + 1 && arr[j].compareTo(key) > 0) {
                j--;
            }
            if (i > j) {
                break;
            }
            swap(arr, i++, j--);
        }
        swap(arr, p, j);
        return j;
    }

    private static void swap(Object[] arr, int i, int j) {
        Object objTmp = arr[i];
        arr[i] = arr[j];
        arr[j] = objTmp;
    }
}

测试

package com.littlefxc.examples.algorithm;

import org.junit.Test;

import java.util.Arrays;

import static org.junit.Assert.*;

public class QuickSortTest {

    @Test
    public void sort() {
        int N = 10000000;
        Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N);
        Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);
        SortTestHelper.testSort("com.littlefxc.examples.algorithm.MergeSort", "sort", arr1);
        SortTestHelper.testSort("com.littlefxc.examples.algorithm.QuickSort", "sort", arr2);
    }

    /**
     * 排序近乎有序的数组:使用随机定点
     */
    @Test
    public void sortNearlyOrderedArray() {
        int N = 10000000;
        Integer[] arr1 = SortTestHelper.generateNearlyOrderedArray(N, 1);
        Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);
        SortTestHelper.testSort("com.littlefxc.examples.algorithm.QuickSort", "sort", arr1);
        SortTestHelper.testSort("com.littlefxc.examples.algorithm.MergeSort", "sort", arr2);
    }

    /**
     * 一旦有大量重复的元素,版本2又有问题:被排序的数组的树极不平衡,时间复杂度退化为O(N^2)
     * 解决办法是使用双路快速排序,经过测试可以在1秒内轻松处理100万数量集的数据同时要比归并排序快
     */
    @Test
    public void sort3() {
        int N = 1000000;
        Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, 10);
        Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);
        SortTestHelper.testSort("com.littlefxc.examples.algorithm.MergeSort", "sort", arr1);
        SortTestHelper.testSort("com.littlefxc.examples.algorithm.QuickSort", "sort", arr2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值