了解Quicksort实现

时间:2013-12-27 01:33:09

标签: c# algorithm sorting quicksort

我在浏览不同的快速配置实施时在网上找到了这个代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Quicksort
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create an unsorted array of string elements
            string[] unsorted = { "z","e","x","c","m","q","a"};

            // Print the unsorted array
            for (int i = 0; i < unsorted.Length; i++)
            {
                Console.Write(unsorted[i] + " ");
            }

            Console.WriteLine();

            // Sort the array
            Quicksort(unsorted, 0, unsorted.Length - 1);

            // Print the sorted array
            for (int i = 0; i < unsorted.Length; i++)
            {
                Console.Write(unsorted[i] + " ");
            }

            Console.WriteLine();

            Console.ReadLine();
        }

        public static void Quicksort(IComparable[] elements, int left, int right)
        {
            int i = left, j = right;
            IComparable pivot = elements[(left + right) / 2];

            while (i <= j)
            {
                while (elements[i].CompareTo(pivot) < 0)
                {
                    i++;
                }

                while (elements[j].CompareTo(pivot) > 0)
                {
                    j--;
                }

                if (i <= j)
                {
                    // Swap
                    IComparable tmp = elements[i];
                    elements[i] = elements[j];
                    elements[j] = tmp;

                    i++;
                    j--;
                }
            }

            // Recursive calls
            if (left < j)
            {
                Quicksort(elements, left, j);
            }

            if (i < right)
            {
                Quicksort(elements, i, right);
            }
        }

    }
}

我理解它几乎所有的工作原理但我想知道为什么在递归调用中他们使用left,j和i,正好用于高和低。我认为你想要使用left,pivotIndex和pivotIndex,对。我试过这个并没有用,但我真的不明白为什么。我在其他语言中发现了更多相同的内容,所以我猜它是正确的(它似乎工作,这也是一个很好的指示,它的工作正常)。我也不明白为什么每个人似乎都存储了pivot的值而不是pivot的索引。如果我修改它以使用数据透视表的索引而不是似乎也可以工作的值,但是他们这样做似乎相当重要,而快速排序的许多其他实现也是这样做的。有人可以帮我理解这个吗?

1 个答案:

答案 0 :(得分:5)

该代码实际上是quicksort的一个相当清晰的实现。我整整一个夏天都在排序算法,所以这个问题引起了我的注意。

理解这个特定程序的关键是每个递归函数调用都会收到相同的完整字符串。所以函数必须设置自己的边界,即&#34; left&#34;和&#34;对&#34;并忽略字符串的其余部分。每个递归函数调用仅处理对字符串的一小部分进行分区。

原始函数调用将字符串分成小于枢轴(下部)和大于枢轴(上部)的部分,留下任何字符串值等于&#34;无人的枢轴土地&#34;在左右两部分之间的某处。

一旦进入递归函数调用,它就会变得更加模糊,重要的是要理解这些下部和上部与&#34; left&#34;和&#34;对&#34;变量。 &#34;左边&#34;变量是函数调用应该对其进行操作的整个字符串的左边界的原始位置,对于&#34; right&#34;变量

该算法的关键是理解第一个函数调用不会在上部或下部分别排序。但是,它会从考虑中删除一个或多个分区值,并确保它们高于所有较低的字符串值,并且低于所有较高的字符串值,即在&#34; middle&#34;字符串。

这个过程在字符串的未排序的上部和下部递归地重复,直到最终达到两个或更少值的上部或下部大小,这些值必须在分区后已经被分类。

如果枢轴值始终为极值,则快速排序算法的最差情况为O(N ^ 2),如果枢轴值接近中间值,则最佳情况表现为O(N log N)字符串。例如,当排序31个字符并假设完美的中间选择枢轴时,

第一阶段:

低15,枢轴,高15

第二阶段:

(下7,枢轴,上7),枢轴,(下7,枢轴,上7)

第三阶段:

((下3,枢轴,上3),枢轴,(下3,枢轴,上3)),枢轴,((下3,枢轴,上3),枢轴,(下3,枢轴,上3) ))

第四阶段:

<(>(下部1,枢轴,上部1),枢轴,(下部1,枢轴,上部1)),枢轴,((下部1,枢轴,上部1),枢轴,(下部1,枢轴,上部) 1))),枢轴,(((下部1,枢轴,上部1),枢轴,(下部1,枢轴,上部1)),枢轴,((下部1,枢轴,上部1),枢轴,(下部1) ,pivot,upper 1)))

四个阶段小于或等于31的基数 - 2对数,并且在每个阶段算法都是线性的(尽管在每个级别的多个不同的递归函数调用中执行),因此执行31个步骤,即N步骤。

因此,算法的总顺序是4阶段乘以31,或大约N乘以log N。

实际上,有时枢轴不是中位数,甚至可能是一个极值,在这种情况下,&#34;更低&#34;或&#34;鞋面&#34;部分是空的。该算法可能在每个阶段仅从考虑中移除一个值,导致总共N-1个阶段来完成算法。在每个阶段,都会有N个工作步骤,因此总顺序为N次(N-1),即O(N ^ 2),因为N与N的乘积。

您可以在此处查看1000万个项目的排序操作的嵌套列表:

http://www.myersdaily.org/joseph/unix/sort/in10.html

并阅读我在Unix上对quicksort算法所做的一些观察:

http://www.myersdaily.org/joseph/unix/sort.html

(通过使用免费分析统计数据,可以忽略不计的7%,以避免在&#34;幸运&#34;场合出现时)采取措施。

总而言之,quicksort是一个非常好的算法,并且很难改进,只要一个人的算法避免故意的有害案例,否则可以利用这些案例为已知的代码库提供输入,从而产生一个支点序列极端价值观。