您的分区函数不正确。在快速排序期间有两种主要的序列分区方法:squeeze 和 sweep。前一种方法是您正在尝试的方法,并且以更复杂的算法为代价,与后一种方法相比,交换次数很可能更少。
大扫除
我将首先向您展示更简单的 sweep 方法是什么样的,因为它确实是最容易理解的。一般来说,该算法执行以下操作:
- 如果序列长度小于 2,请提前退出。对于长度为 1 的序列,没有什么可划分的。
- 在序列中选择一个枢轴值。选择一个减少 O(N^2) 退化条件的机会的枢轴值是快速排序分区的圣杯,我不会在这里介绍它,但是有大量的命令该主题可在线获取。
- 用序列中的最后一个值交换枢轴值。
- 使用两个索引值遍历序列;读者索引和作者索引。当您向序列前进时,任何“小于”枢轴值的读取器索引值都会被交换到写入器索引处的较低序列,并且写入器索引会增加。
- 完成后,写入器索引是枢轴值交换到最终位置并且分区完成的点。返回的结果分区点是写入器索引位置。
这个算法其实更容易用代码理解:
size_t partition(int A[], size_t len)
{
if (len < 2) // 1.
return 0;
std::iter_swap(A+len/2, A+len-1); // 2. 3.
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i) // 4.
{
if (A[i] < A[len-1])
std::iter_swap(A + pvt++, A+i);
}
std::iter_swap(A + pvt, A+len-1); // 5.
return pvt;
}
请注意,完全可以想象大于的值可能会被交换多次次,因为在行进期间较低的分区被填满。最终一切都会解决,但最好避免这些额外的交换。这就是 squeeze 方法的目的,如下所示。
挤压
虽然 sweep 有其优点(最值得注意的是简单),但最小化交换不在其中。理想情况下,您仅在确定 两个 值在枢轴值的最终着陆位置的相对两侧不合适时才执行交换。要做到这一点,您需要同时执行从低到高的扫描和从高到低的扫描,并且一旦在不正确的位置找到一个元素,就交换那些时间>。最终,低指数和高指数相遇,这样你就找到了枢轴最终的休息地。
size_t partition(int A[], size_t len)
{
if (len < 2)
return 0;
std::iter_swap(A + len/2, A+len-1);
size_t low = 0, high = len;
while (1)
{
while (low < high && (A[low] < A[len-1]))
++low;
if (low == high--)
break;
while (low < high && !(A[high] < A[len-1]))
--high;
if (low == high)
break;
std::iter_swap(A+low++, A+high);
}
std::iter_swap(A+low, A+len-1);
return low;
}
这里发生了几件看起来很奇怪的事情。注意减少high 的second 内部while 循环的布尔逻辑。我本可以写(A[high] >= A[len-1]),但我想把一个常见的错误带回家。 critical 将high 降低的条件在逻辑上inverse 到提升low 的条件。如果low 被提升是因为它的元素严格小于我们这里的主元值,那么high 只有在其元素为not(严格小于主元值)时才能被减少.毫无疑问,当我们按照上面的代码显示时,它是正确的,我根本无法公正地估计特定要求被掩盖并导致神秘地破坏的分区算法的次数。
使用 QuickSort 的示例分区
以上任何一个都可以。对函数进行一些细微的修改以在发生交换时产生输出并对随机打乱的值数组进行排序证明了交换计数的减少。下面在两个分区函数中实现这两种算法,分别标记为sweep 和squeeze。它们都在相同的随机序列上变得松散,然后在完全排序的序列上再次变得松散,以展示交换计数差异。
#include <iostream>
#include <algorithm>
#include <random>
#include <numeric>
size_t sweep(int A[], size_t len)
{
if (len < 2)
return 0;
std::iter_swap(A+len/2, A+len-1);
size_t pvt = 0;
for (size_t i=0; i<len-1; ++i)
{
if (A[i] < A[len-1])
{
std::cout << "swap: " << A[pvt] << ',' << A[i] << '\n';
std::iter_swap(A + pvt++, A+i);
}
}
std::iter_swap(A + pvt, A+len-1);
return pvt;
}
size_t squeeze(int A[], size_t len)
{
if (len <= 1)
return 0;
std::iter_swap(A + len/2, A+len-1);
size_t low = 0, high = len;
while (1)
{
while (low < high && A[low] < A[len-1])
++low;
if (low == high--)
break;
while (low < high && !(A[high] < A[len-1]))
--high;
if (low == high)
break;
std::cout << "swap: " << A[low] << ',' << A[high] << '\n';
std::iter_swap(A+low++, A+high);
}
std::iter_swap(A+low, A+len-1);
return low;
}
void quicksort(int A[], size_t len, size_t (*part)(int[], size_t))
{
if (len < 2)
return;
size_t pvt = part(A, len);
quicksort(A, pvt++, part);
quicksort(A+pvt, len-pvt, part);
}
int main()
{
std::random_device rd;
std::mt19937 rng(rd());
int ar[31] = {0}, ar2[31];
std::iota(std::begin(ar), std::end(ar), 1);
std::shuffle(std::begin(ar), std::end(ar), rng);
std::copy(std::begin(ar), std::end(ar), std::begin(ar2));
for (auto x : ar)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Sweep Algorithm\n";
quicksort(ar, sizeof(ar)/sizeof(*ar), sweep);
for (auto x : ar)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Squeeze Algorithm\n";
quicksort(ar2, sizeof(ar2)/sizeof(*ar2), squeeze);
for (auto x : ar2)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Sweep Algorithm (sorted)\n";
quicksort(ar, sizeof(ar)/sizeof(*ar), sweep);
for (auto x : ar)
std::cout << x << ' ';
std::cout << '\n';
std::cout << "Squeeze Algorithm (sorted)\n";
quicksort(ar2, sizeof(ar2)/sizeof(*ar2), squeeze);
for (auto x : ar2)
std::cout << x << ' ';
std::cout << '\n';
}
输出(随机)
8 28 21 26 10 12 17 1 11 20 30 3 18 5 24 15 9 6 13 27 31 4 16 7 19 22 14 25 29 2 23
Sweep Algorithm
swap: 8,8
swap: 28,10
swap: 21,12
swap: 26,1
swap: 28,11
swap: 21,3
swap: 17,5
swap: 26,9
swap: 28,6
swap: 20,13
swap: 30,4
swap: 21,7
swap: 18,14
swap: 17,2
swap: 8,8
swap: 10,1
swap: 12,3
swap: 10,5
swap: 11,2
swap: 12,6
swap: 10,4
swap: 11,7
swap: 8,1
swap: 3,3
swap: 5,5
swap: 7,4
swap: 3,3
swap: 4,4
swap: 3,3
swap: 7,7
swap: 13,10
swap: 12,12
swap: 13,13
swap: 12,12
swap: 23,20
swap: 26,16
swap: 28,19
swap: 23,18
swap: 27,17
swap: 20,16
swap: 20,17
swap: 20,18
swap: 16,16
swap: 30,22
swap: 24,24
swap: 30,26
swap: 31,28
swap: 30,27
swap: 26,26
swap: 27,27
swap: 26,26
swap: 30,30
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Squeeze Algorithm
swap: 28,2
swap: 21,14
swap: 26,7
swap: 17,4
swap: 20,13
swap: 30,6
swap: 18,9
swap: 14,3
swap: 7,4
swap: 14,9
swap: 12,6
swap: 30,25
swap: 27,21
swap: 31,22
swap: 23,16
swap: 25,17
swap: 30,28
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Sweep Algorithm (sorted)
swap: 1,1
swap: 2,2
swap: 3,3
swap: 4,4
swap: 5,5
swap: 6,6
swap: 7,7
swap: 8,8
swap: 9,9
swap: 10,10
swap: 11,11
swap: 12,12
swap: 13,13
swap: 14,14
swap: 15,15
swap: 1,1
swap: 2,2
swap: 3,3
swap: 4,4
swap: 5,5
swap: 6,6
swap: 7,7
swap: 1,1
swap: 2,2
swap: 3,3
swap: 1,1
swap: 5,5
swap: 9,9
swap: 10,10
swap: 11,11
swap: 9,9
swap: 13,13
swap: 17,17
swap: 18,18
swap: 19,19
swap: 20,20
swap: 21,21
swap: 22,22
swap: 23,23
swap: 17,17
swap: 18,18
swap: 19,19
swap: 17,17
swap: 21,21
swap: 25,25
swap: 26,26
swap: 27,27
swap: 25,25
swap: 29,29
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Squeeze Algorithm (sorted)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
祝你好运。