【问题标题】:Quicksort with Hoare partition使用 Hoare 分区进行快速排序
【发布时间】:2025-12-28 21:40:11
【问题描述】:

在阅读了有关它的 Wikipedia 文章后,我在 C++ 中实现了快速排序。只是想恢复我对学校的记忆,为换工作的面试做准备。

这是我的实现

#include <vector>

size_t partition(std::vector<int> & inputs, size_t leftIndex, size_t rightIndex)
{
    int pivotValue = inputs[leftIndex];
    size_t i = leftIndex - 1;
    size_t j = rightIndex + 1;

    while (true)
    {
        while (inputs[++i] < pivotValue);
        while (inputs[--j] > pivotValue);

        if (i >= j)
        {
            return j;
        }

        std::iter_swap(inputs.begin() + i, inputs.begin() + j);
    }

    return 0;
}

void sort(std::vector<int> & inputs, size_t leftIndex, size_t rightIndex)
{
    if (leftIndex < rightIndex)
    {
        size_t pivot = partition(inputs, leftIndex, rightIndex);
        sort(inputs, leftIndex, pivot);
        sort(inputs, pivot + 1, rightIndex);
    }
}

int main()
{
    std::vector<int> inputs = { 3,7,1,2,9,5,4,0,8,6 };
    sort(inputs, 0, inputs.size() - 1);

    return 0;
}

到目前为止,我提出的所有测试输入似乎都可以正常工作。

为了清楚起见,从这里编辑

如果我们改变

        if (i >= j)
        {
            return j;
        }

        if (i > j)
        {
            return j;
        }
        else if(i == j)
        {
            return j;
        }

我的问题是,哪一组输入会执行下面的分区函数块?

        if (i > j)
        {
            return j;
        }

【问题讨论】:

  • 最终随着您旋转的数组部分越来越小,i &gt;= j 将触发。
  • 边缘相关:认为你想要 do/while 而不是在 Hoare 分区中的 while (inputs[i] &lt; pivotValue) 上的普通旧 while
  • 最直接的行程将是两个元素的已排序序列。但是,如果你检测你的代码,你会发现你已经用你当前的序列绊倒了它。
  • @ChristopherPisz 会出于纯粹的迂腐原因开始这样做。如果你的目标是实现 Hoare 分区并且你做了一些不同的事情,那么你就没有实现目标。 Hoare 分区递增/递减,然后进行测试,得到至少一个 i++j--do/while 优于 while 的完美用例。结果会有所不同。这种变化的结果是输出或算法速度的显着差异,我不得不戳一下。我可以想象由于if (i &gt;= j) 不正确而演变成无限循环的情况。
  • @rcgldr & user4581301 嗯。我不认为*上的伪代码中如何实现算法的细节定义了特定的算法。算法是通过将问题分解为特定步骤来定义的。只要最终结果和获得结果的效率相同,您如何实施这些单独的步骤完全取决于您。查看我的代码和*上的代码,我发现结果或效率没有区别。坦率地说,我认为从 1 开始超出界限,只是重组为 do while 而不是 while,坦率地说,很愚蠢。

标签: c++ algorithm sorting


【解决方案1】:

出现了混乱,因为我错误地实现了算法。正如几个用户在原始问题的 cmets 中指出的那样(在编辑之前),*上的列表使用 do/while 而不是一段时间,这样 i 的增量和 j 的减量发生在条件评估之前.

我原以为没有功能差异,但确实存在。

所以,给定一个等价的配分函数:

size_t partition(std::vector<int> & inputs, size_t leftIndex, size_t rightIndex)
{
    int pivotValue = inputs[leftIndex];
    size_t i = leftIndex - 1;
    size_t j = rightIndex + 1;

    while (true)
    {
        while (inputs[++i] < pivotValue);
        while (inputs[--j] > pivotValue);

        if (i >= j)
        {
            return j;
        }

        std::iter_swap(inputs.begin() + i, inputs.begin() + j);
    }

    return 0;
}

很明显,当循环将每个边界处的标记更靠近中间时,当它们指向相同的元素或相互交叉时,对 i >= j 的检查将得到真正的命中。

通过修复增量和减量循环,主循环的每次迭代都会发生增量和减量。而以前,他们没有。

和往常一样,在处理算法时,在纸上一步一步地进行,它就会变得清晰。

【讨论】:

    【解决方案2】:

    while (true) { ... } 的正文中没有 break,因此它之后的任何内容都无法访问。

    随着您的测试用例运行完成,并且没有抛出或捕获异常,这意味着它们必须全部点击return j;每次你@987654324 @ed。

    【讨论】:

    • 让我改写一下....对于 i > j 而不是 i == j,这个评估是正确的输入是什么?