2.3.6

2.3.6 #

解答 #

运行结果如下:

代码 #

新建一个 QuickSortAnalyze 类,在 QuickSort 的基础上添加一个 CompareCount 属性,用于记录比较次数。重写 Less 方法,每调用一次就让 CompareCount 增加 1 。

/// <summary>
/// 自动记录比较次数以及子数组数量的快速排序类。
/// </summary>
public class QuickSortAnalyze : BaseSort
{
    /// <summary>
    /// 比较次数。
    /// </summary>
    /// <value>排序用的比较次数。</value>
    public int CompareCount { get; set; }

    /// <summary>
    /// 是否启用打乱。
    /// </summary>
    /// <value>
    /// <list type="bullet">
    /// <item><c>true</c>: 启用排序前打乱。</item>
    /// <item><c>false</c>: 禁用排序前打乱。</item>
    /// </list>
    /// </value>
    public bool NeedShuffle { get; set; }

    /// <summary>
    /// 是否显示轨迹。
    /// </summary>
    /// <value>
    /// <list type="bullet">
    /// <item><c>true</c>: 输出排序轨迹。</item>
    /// <item><c>false</c>: 不输出排序轨迹。</item>
    /// </list>
    /// </value>
    public bool NeedPath { get; set; }

    /// <summary>
    /// 大小为 0 的子数组数量。
    /// </summary>
    /// <value>大小为 0 的子数组数量。</value>
    public int Array0Num { get; set; }

    /// <summary>
    /// 大小为 1 的子数组数量。
    /// </summary>
    /// <value>大小为 1 的子数组数量。</value>
    public int Array1Num { get; set; }

    /// <summary>
    /// 大小为 2 的子数组数量。
    /// </summary>
    /// <value>大小为 2 的子数组数量。</value>
    public int Array2Num { get; set; }

    /// <summary>
    /// 默认构造函数。
    /// </summary>
    public QuickSortAnalyze()
    {
        CompareCount = 0;
        NeedShuffle = true;
        NeedPath = false;
        Array0Num = 0;
        Array1Num = 0;
        Array2Num = 0;
    }

    /// <summary>
    /// 用快速排序对数组 a 进行升序排序。
    /// </summary>
    /// <typeparam name="T">需要排序的类型。</typeparam>
    /// <param name="a">需要排序的数组。</param>
    public override void Sort<T>(T[] a)
    {
        Array0Num = 0;
        Array1Num = 0;
        Array2Num = 0;
        CompareCount = 0;
        if (NeedShuffle)
            Shuffle(a);
        if (NeedPath)
        {
            for (var i = 0; i < a.Length; i++)
            {
                Console.Write("  ");
            }
            Console.WriteLine("\tlo\tj\thi");
        }
        Sort(a, 0, a.Length - 1);
        Debug.Assert(IsSorted(a));
    }

    /// <summary>
    /// 用快速排序对数组 a 的 lo ~ hi 范围排序。
    /// </summary>
    /// <typeparam name="T">需要排序的数组类型。</typeparam>
    /// <param name="a">需要排序的数组。</param>
    /// <param name="lo">排序范围的起始下标。</param>
    /// <param name="hi">排序范围的结束下标。</param>
    private void Sort<T>(T[] a, int lo, int hi) where T : IComparable<T>
    {
        if (hi - lo == 1)
            Array2Num++;
        else if (hi == lo)
            Array1Num++;
        else if (hi < lo)
            Array0Num++;

        if (hi <= lo)                   // 别越界
            return;
        var j = Partition(a, lo, hi);
        if (NeedPath)
        {
            for (var i = 0; i < a.Length; i++)
            {
                Console.Write(a[i] + " ");
            }
            Console.WriteLine("\t" + lo + "\t" + j + "\t" + hi);
        }
        Sort(a, lo, j - 1);
        Sort(a, j + 1, hi);
    }

    /// <summary>
    /// 对数组进行切分,返回枢轴位置。
    /// </summary>
    /// <typeparam name="T">需要切分的数组类型。</typeparam>
    /// <param name="a">需要切分的数组。</param>
    /// <param name="lo">切分的起始点。</param>
    /// <param name="hi">切分的末尾点。</param>
    /// <returns>枢轴下标。</returns>
    private int Partition<T>(T[] a, int lo, int hi) where T : IComparable<T>
    {
        int i = lo, j = hi + 1;
        var v = a[lo];
        while (true)
        {
            while (Less(a[++i], v))
                if (i == hi)
                    break;
            while (Less(v, a[--j]))
                if (j == lo)
                    break;
            if (i >= j)
                break;
            Exch(a, i, j);
        }
        Exch(a, lo, j);
        return j;
    }

    /// <summary>
    /// 打乱数组。
    /// </summary>
    /// <typeparam name="T">需要打乱的数组类型。</typeparam>
    /// <param name="a">需要打乱的数组。</param>
    private void Shuffle<T>(T[] a)
    {
        var random = new Random();
        for (var i = 0; i < a.Length; i++)
        {
            var r = i + random.Next(a.Length - i);
            var temp = a[i];
            a[i] = a[r];
            a[r] = temp;
        }
    }

    /// <summary>
    /// 比较第一个元素是否小于第二个元素。
    /// </summary>
    /// <typeparam name="T">要比较的元素类型。</typeparam>
    /// <param name="a">第一个元素。</param>
    /// <param name="b">第二个元素。</param>
    /// <returns>如果 <paramref name="a"/> 较小则返回 <c>true</c>,否则返回 <c>false</c>。</returns>
    protected new bool Less<T>(T a, T b) where T : IComparable<T>
    {
        CompareCount++;
        return a.CompareTo(b) < 0;
    }
}

主方法

Console.WriteLine("N\t准确值\t估计值\t比值");
var sort = new QuickSortAnalyze();
var n = 100;
var trialTime = 500;
for (var i = 0; i < 3; i++)
{
    var sumOfCompare = 0;
    var a = new int[n];
    for (var j = 0; j < trialTime; j++)
    {
        for (var k = 0; k < n; k++)
        {
            a[k] = k;
        }

        SortCompare.Shuffle(a);
        sort.Sort(a);
        sumOfCompare += sort.CompareCount;
    }

    var averageCompare = sumOfCompare / trialTime;
    var estimatedCompare = 2 * n * Math.Log(n);
    Console.WriteLine(
        n + "\t" + averageCompare + "\t" + (int)estimatedCompare + "\t" + averageCompare / estimatedCompare);
    n *= 10;
}

另请参阅 #

Quick 库