为什么选择排序比插入和快速排序更好

时间:2015-04-26 03:52:17

标签: java algorithm sorting

我有以下算法分别用于插入排序,选择排序和快速排序。我已经运行了一个测试,为每个算法排序一个10,000个整数的数组,介于0到1000之间。奇怪的是,选择排序的平均值明显低于插入排序和快速排序。插入,选择和快速排序的平均值分别为182044.4,217.9和545.4毫秒。考虑到我可以对插入和快速排序进行小的改进,我不会期望缺少这些增强会导致如此大的差异。我在这里的实施错在哪里?同样,不是小的增强,如移动索引或检查,但实际错误。

我已经提供了测试代码和下面的结果

由于

public final static <E extends Comparable<E>> ArrayList<E> insertionSort(ArrayList<E> arr)
{
    for(int i = 1; i < arr.size(); i++)
    {
        for(int j = i; j > 0; j--)
        {
            if(arr.get(j).compareTo(arr.get(j - 1)) < 0)
            {
                swap(arr, j, j-1);
            }else{
                break;
            }
        }
    }       
    return arr;
}

`

public final static <E extends Comparable<E>> ArrayList<E> selectionSort(ArrayList<E> arr)
    {
        for(int i = 0; i < arr.size() - 1; i++){
            int min = i;
            for(int j = i + 1; j < arr.size(); j++){
                if(arr.get(j).compareTo(arr.get(min)) < 0){
                    min = j;
                }
            }
            swap(arr, i, min);
        }
        return arr;
    }`

`

public final static <E extends Comparable<E>> ArrayList<E> quickSort(ArrayList<E> arr)
    {
        quickSort(arr, 0, arr.size() -1);
        return arr;
    }
    private static <E extends Comparable<E>> void quickSort(ArrayList<E> arr, int lo, int hi)
    {
        if(hi - lo < 1)
        {
            return;
        }
        if((hi - lo) == 1)
        {
            if(arr.get(lo).compareTo(arr.get(hi)) > 0)
            {
                swap(arr, lo, hi);
            }
            return;
        }
        int pivot = (hi - lo) / 2 + lo;
        int j;
        swap(arr, pivot, hi);
        pivot = hi;
        for(int i = lo; i < pivot; i++)
        {
            if(arr.get(i).compareTo(arr.get(pivot)) > 0 )
            {
                for(j = i + 1; j < pivot; j++)
                {
                    if(arr.get(j).compareTo(arr.get(pivot)) < 0)
                    {
                        swap(arr, i, j);
                        break;
                    }
                }
                if(j == pivot)
                {
                    swap(arr, i, pivot);
                    pivot = i;
                }
            }
        }
        //do sort op here
        quickSort(arr, lo, pivot - 1);
        quickSort(arr, pivot + 1, hi);
    }

`

要测试的代码 - genRans(n)只生成0到n之间随机数的数组列表 `         int n = 10000;         ArrayList list = new ArrayList();

    int tries = 10;
    double[] times = new double[tries];
    double sum = 0;
    double time = 0;
    long startTime = 0;
    long endTime = 0;

    for(int i = 0; i < tries; i++)
    {
        list = genRans(n);
        startTime = System.nanoTime();
        //System.out.println("before " + list);
        Sort.selectionSort(list);
        //System.out.println("after" + list);
        endTime = System.nanoTime();

        time = (endTime - startTime) / 1000000; 
        times[i] = time; 
        sum += time; 
    }

    System.out.println("Times for selection sort = " + Arrays.toString(times));
    System.out.println("Avg time for selection = " + sum / tries);
    sum = 0;

    for(int i = 0; i < tries; i++)
    {
        list = genRans(n);
        startTime = System.nanoTime();
        Sort.insertionSort(list);
        endTime = System.nanoTime();

        time = (endTime - startTime) / 1000000; 
        times[i] = time; 
        sum += time; 
    }

    System.out.println("Times for insertion sort = " + Arrays.toString(times));
    System.out.println("Avg time for insertion = " + sum / tries);
    sum = 0;

    for(int i = 0; i < tries; i++)
    {
        list = genRans(n);
        startTime = System.nanoTime();
        Sort.quickSort(list);
        endTime = System.nanoTime();

        time = (endTime - startTime) / 1000000; 
        times[i] = time; 
        sum += time; 
    }

    System.out.println("Times for quick sort = " + Arrays.toString(times));
    System.out.println("Avg time for quick = " + sum / tries);
}

` 结果: 选择时间排序= [235.0,214.0,216.0,216.0,216.0,216.0,217.0,216.0,217.0,216.0] 平均选择时间= 217.9 插入时间排序= [182936.0,181976.0,182571.0,182448.0,180757.0,180567.0,181593.0,185073.0,181241.0,181282.0] 平均插入时间= 182044.4 快速排序的时间= [629.0,487.0,579.0,557.0,547.0,482.0,543.0,571.0,525.0,534.0] 平均快速时间= 545.4

2 个答案:

答案 0 :(得分:1)

衡量时间的方式肯定是错的。我用JMH计算了你的排序方法(加上java的内置排序)。以下是10000个元素的结果:

Benchmark            Mode  Cnt   Score   Error  Units
SortVSort.insertion  avgt   30  52.802 ± 0.103  ms/op
SortVSort.java       avgt   30   1.196 ± 0.004  ms/op
SortVSort.quick      avgt   30  23.923 ± 0.026  ms/op
SortVSort.selection  avgt   30  99.173 ± 0.105  ms/op

答案 1 :(得分:0)

您的快速分配步骤实际上是二次时间,几乎肯定是错误的。

当您找到string i时,您正在向arr[i] > arr[pivot]向前扫描,找到一个位置i+1来交换它:这将导致多次跳过相同的数组元素。假设前n / 2个项最初都是j,其余的都是> arr[pivot]:外部循环的前n / 2次迭代中的每一个(递增< arr[pivot])都将需要内部循环的n / 2次迭代,增加i以找到要交换的伙伴,总体上进行O(n ^ 2)次迭代。

此外,如果您找不到要与之交换j的合作伙伴j,您当前会将数据透视转换为i位置。这意味着如果枢轴上方的值多于它下方的值,则枢轴将在错误的位置结束 - 在所有小于耗尽的值(作为交换具有大于它的值的伙伴)之后,它将保持不变滑向右边。