Quackort优于Heap Sort

时间:2009-12-05 19:38:32

标签: algorithm sorting big-o quicksort heapsort

堆排序的最差情况复杂度为O(nlogn),而Quicksort的O(n^2)。 但是,经验证据表明,快速排序是优越的。那是为什么?

6 个答案:

答案 0 :(得分:48)

其中一个主要因素是快速排序具有更好的参考位置 - 接下来要访问的内容通常在内存中与您刚才看到的内容相近。相比之下,堆垛突然跳得更多。由于紧密相连的东西可能会被缓存在一起,因此快速排序会更快。

但是,quicksort的最坏情况性能明显比heapsort差。因为一些关键应用程序需要保证速度性能,所以heapsort是解决此类情况的正确方法。

答案 1 :(得分:15)

Heapsort是O(N log N)保证,比Quicksort的最坏情况要好得多。根据Mergesort的需要,Heapsort不需要为另一个数组提供更多内存来放置有序数据。那么为什么商业应用程序坚持使用Quicksort? Quicksort有什么特别优于其他实现?

我自己测试了算法,我看到Quicksort确实有一些特别的东西。它运行速度快,比Heap和Merge算法快得多。

Quicksort的秘诀在于:它几乎不会进行不必要的元素交换。交换非常耗时。

使用Heapsort,即使您的所有数据都已经订购,您也要交换100%的元素来订购数组。

使用Mergesort,情况更糟。您将在另一个数组中编写100%的元素,并将其写回原始数组中,即使已经订购了数据。

使用Quicksort,您不会交换已订购的内容。如果您的数据是完全订购的,那么几乎没有任何交换!虽然关于最坏情况有很多烦恼,但是对于枢轴选择的一点改进,除了获得数组的第一个或最后一个元素之外,可以避免它。如果从第一个,最后一个和中间元素之间的中间元素获得一个枢轴,那么避免最坏的情况就足够了。

Quicksort的优势不是最坏的情况,但是最好的情况!在最好的情况下,你做相同数量的比较,好吧,但你几乎没有交换。在一般情况下,您交换部分元素,但不是所有元素,如Heapsort和Mergesort。这就是Quicksort最好的时间。减少交换,提高速度。

我的计算机上的C#下面的实现,在发布模式下运行,使用中间数据枢轴击败Array.Sort 3秒,使用改进的数据透视表击败2秒(是的,有一个开销可以获得良好的支点)。

static void Main(string[] args)
{
    int[] arrToSort = new int[100000000];
    var r = new Random();
    for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);

    Console.WriteLine("Press q to quick sort, s to Array.Sort");
    while (true)
    {
        var k = Console.ReadKey(true);
        if (k.KeyChar == 'q')
        {
            // quick sort
            Console.WriteLine("Beg quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            QuickSort(arrToSort, 0, arrToSort.Length - 1);
            Console.WriteLine("End quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
        }
        else if (k.KeyChar == 's')
        {
            Console.WriteLine("Beg Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            Array.Sort(arrToSort);
            Console.WriteLine("End Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff"));
            for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length);
        }
    }
}

static public void QuickSort(int[] arr, int left, int right)
{
    int begin = left
        , end = right
        , pivot
        // get middle element pivot
        //= arr[(left + right) / 2]
        ;

    //improved pivot
    int middle = (left + right) / 2;
    int
        LM = arr[left].CompareTo(arr[middle])
        , MR = arr[middle].CompareTo(arr[right])
        , LR = arr[left].CompareTo(arr[right])
        ;
    if (-1 * LM == LR)
        pivot = arr[left];
    else
        if (MR == -1 * LR)
            pivot = arr[right];
        else
            pivot = arr[middle];
    do
    {
        while (arr[left] < pivot) left++;
        while (arr[right] > pivot) right--;

        if(left <= right)
        {
            int temp = arr[right];
            arr[right] = arr[left];
            arr[left] = temp;

            left++;
            right--;
        }
    } while (left <= right);

    if (left < end) QuickSort(arr, left, end);
    if (begin < right) QuickSort(arr, begin, right);
}

答案 2 :(得分:6)

以下是几个解释:

http://www.cs.auckland.ac.nz/software/AlgAnim/qsort3.html

http://users.aims.ac.za/~mackay/sorting/sorting.html

基本上,尽管快速排序的最坏情况是O(n ^ 2),但它平均表现更好。 : - )

答案 3 :(得分:1)

big-O表示法意味着对n个项目进行排序所需的时间超出了函数c*n*log(n),其中c是一些未指定的常量因子。对于cquicksort,常量heapsort没有理由相同。所以真正的问题是:为什么你会期望它们同样快?

Quicksort在实践中总是比heapsort快一些,但最近差异变大了,因为如前所述,内存访问的位置对执行速度变得如此重要。

答案 4 :(得分:0)

平均案例复杂性,以及您可以采取简单步骤来最小化Quicksort中最坏情况复杂性风险的事实(例如,选择枢轴作为三个元素的中位数而不是单个选定位置)。

答案 5 :(得分:0)

如前所述,与堆排序相比,快速排序具有更好的引用局部性,但最坏情况的复杂度为 O(n^2)。

std::sort 是使用自省排序实现的:它大部分时间运行快速排序,但如果它检测到由于错误的枢轴选择而导致运行时不好,它会切换到堆排序。在这种情况下,您将获得有保证的 O(nlog(n)) 复杂度以及快速排序的速度,几乎每次都会选择该速度。