2.5.23

2.5.23 #

解答 #

这里我们使用 Floyd-Rivest 算法进行优化,大致思想是:

我们期望第 $k$ 大的元素位于 a[k] 附近,因此优先对 a[k] 附近的区域进行选择。

每次切分时枢轴都选择 a[k],先递归对样本区域选择,再对整个数组进行选择。

运行示意图:

测试结果:

代码 #

// Floyd–Rivest 方法优化,令 a[k] 变成第 k 小的元素。
static T SelectInternal<T>(T[] a, int lo, int hi, int k) where T : IComparable<T>
{
    if (k < 0 || k > a.Length)
    {
        throw new IndexOutOfRangeException("SelectInternal elements out of bounds");
    }
    while (hi > lo)
    {
        if (hi - lo > 600)
        {
            var n = hi - lo + 1;
            var i = k - lo + 1;
            var z = (int)Math.Log(n);
            var s = (int)(Math.Exp(2 * z / 3) / 2);
            var sd = (int)Math.Sqrt(z * s * (n - s) / n) * Math.Sign(i - n / 2) / 2;
            var newLo = Math.Max(lo, k - i * s / n + sd);
            var newHi = Math.Min(hi, k + (n - i) * s / n + sd);
            SelectInternal(a, newLo, newHi, k);
        }

        Exch(a, lo, k);
        var j = Partition(a, lo, hi);
        if (j > k)
        {
            hi = j - 1;
        }
        else if (j < k)
        {
            lo = j + 1;
        }
        else
        {
            return a[j];
        }
    }

    return a[lo];
}

另请参阅 #

Floyd–Rivest algorithm - Wikipedia