将 std::p artition 与快速排序结合使用
Using std::partition with Quick Sort
使用下面分区的quickSort方法有什么问题?第 N 个元素似乎工作正常,但我认为分区也可以工作。我看到一个有 2 个分区调用的示例,但我不应该只需要一个吗?
#include <iostream>
#include <algorithm>
#include <iterator>
template <typename It>
void quickSortWorks (const It& lowerIt, const It& upperIt)
{
auto d = upperIt - lowerIt ;
if ( d < 2 )
return;
auto midIt = lowerIt + d / 2;
std::nth_element ( lowerIt, midIt, upperIt);
quickSortWorks( lowerIt, midIt );
quickSortWorks( midIt+1, upperIt );
}
template <typename It>
void quickSort (const It& lowerIt, const It& upperIt)
{
auto d = upperIt - lowerIt ;
if ( d < 2 )
return;
auto midIt = lowerIt + d / 2;
auto pIt = std::partition ( lowerIt, upperIt, [midIt](int i) { return i < *midIt; } );
quickSort( lowerIt, pIt );
quickSort( pIt + 1, upperIt );
}
int main ( )
{
unsigned int N = 10;
std::vector<int> v(N);
// srand (time(nullptr));
for_each(v.begin(), v.end(), [](int& cur){ cur = rand()%100; });
std::vector<int> vorig(v);
auto print_vec = [](std::ostream& out, const std::vector<int>& v) {
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(out, ", "));
out << std::endl;
};
std::cout << " Using Partition: " << std::endl;
std::cout << " Before: " << std::endl;
print_vec(std::cout,v);
quickSort(v.begin(), v.end());
std::cout << " After: " << std::endl;
print_vec(std::cout,v);
v = vorig;
std::cout << " Using Nth Element: " << std::endl;
std::cout << " Before: " << std::endl;
print_vec(std::cout,v);
quickSortWorks(v.begin(), v.end());
std::cout << " After: " << std::endl;
print_vec(std::cout,v);
}
输出:
Using Partition:
Before:
83, 86, 77, 15, 93, 35, 86, 92, 49, 21,
After:
21, 15, 77, 35, 49, 83, 86, 92, 86, 93,
Using Nth Element:
Before:
83, 86, 77, 15, 93, 35, 86, 92, 49, 21,
After:
15, 21, 35, 49, 77, 83, 86, 86, 92, 93,
编写的代码只会意外工作,原因如下,
std::partition
通过谓词完成其工作,前向序列包含计算结果为 true 的元素,其余的计算结果为 false。这意味着std::partition
将大于透视的元素和等于透视的元素视为等效。
不能保证序列[middle,last)
的顺序!
显然这不是你想要的。您希望压缩等于枢轴到序列[middle,last)
前面的元素。这就是为什么您查看的示例代码使用第二个分区来将此顺序强加给尾随序列的原因(至少您需要将枢轴元素置于正确的位置(。
为了清楚
起见,template<typename Ran>
void quicksort(Ran first, Ran last)
{
typedef typename std::iterator_traits<Ran>::value_type value_type;
if (last - first < 2)
return;
Ran middle = first + (last - first)/2;
// save pivot.
std::iter_swap(middle, last-1);
middle = std::partition(first, last-1,
[last] (const value_type& x) { return x < *(last-1); });
// restore pivot.
std::iter_swap(middle, last-1);
quicksort(first, middle);
quicksort(middle+1, last);
}
使用 lambda 修复,它也不会起作用,因为std::partition
与 std::nth_element
不同,它不返回适合分而治之递归的迭代器。
对 std::partition
的调用的返回值是分区"上层"范围内第一个值的迭代器,其中谓词失败。 除非偶然,否则这不会是该范围内"最小">的迭代器。
相比之下,std::nth_element
中的"透视"操作正是实现了这一点,这对于分叉递归也是必要的。
可以通过手动处理第一次迭代的示例来查看失败。 使用此包含 10 个元素的测试序列:
83, 86, 77, 15, 93, 35, 86, 92, 49, 21
第一个"枢轴"将是第 6 个元素(索引 0 + 10/2 = 5(,即 35。 使用条件"小于 35"std::partition
将在第一步将数组重新排列为
21, 15, 77, 86, 93, 35, 86, 92, 49, 83
并返回指向第三个元素 (=77( 的指针。 很明显,值 77 将在算法持续时间内保持在第三个位置。 这显然是错误的。
我刚刚遇到了同样的问题。在花了几个小时进行分析后,我终于找到了解决方案,瞧!!
如前所述std::partition
使用提议的谓词会将数组分成两部分,第一部分由小于枢轴的元素组成,第二部分由大于或等于枢轴的元素组成。但不能保证枢轴将是第二部分中的第一个元素。
好吧,std::stable_partition
可以完成这项工作。只需将枢轴作为first element
并应用stable_partition即可。因为它将在分区时保持出现的顺序。现在可以保证枢轴将是第二部分中的第一个元素。
PS:不要与两部分混淆。我用这个词来更清楚地解释事情。
template <typename It>
void quickSort (const It& lowerIt, const It& upperIt)
{
auto d = upperIt - lowerIt ;
if ( d < 2 )
return;
auto pIt = lowerIt;
auto pValue = *pIt;
pIt = std::stable_partition ( lowerIt, upperIt, [pValue](int i) { return i < pValue; } );
quickSort( lowerIt, pIt );
quickSort( pIt + 1, upperIt );
}
您的闭包应该捕获 *midIt 的值,而不是 midIt:数量 "*midIt" 将在分区期间发生变化。
int midValue = *midIt;
std::partition(lowerIt, upperIt, [midValue](int i)...
- 如何修复我的快速排序实现?
- C++运行时错误与快速排序算法抛出堆栈转储错误
- 在 MIPS 中快速排序
- 不正确的比较和交换计数器输出用于快速排序功能
- 使用 std::vector C++快速排序,EXC_BAD_ACCESS代码 2
- 使用快速排序对 C++ 中的可视化工具错误进行排序
- 快速排序 - 三个中位数枢轴选择 - 某些元素顺序不正确
- 并行快速排序分区中的隔离错误
- 实现 3 路分区以实现快速排序
- 为什么这个快速排序实现给出了一个奇怪的输出
- 我的快速排序在对预排序的项目进行排序时失败,如何改进?
- 快速排序不适用于大型数组
- 快速排序;分段错误,但找不到位置?
- 快速排序函数在快速排序算法中如何工作?
- 3路随机快速排序分区功能
- 在最坏的情况下试验快速排序.它运行良好,但在最坏的情况下发生未知错误.我想
- 当给出预先排序的输入时,为什么我的快速排序实现很慢
- 我想要一个改变数组快速排序的2个数字的函数
- C++一个具有 2 个参数(___ _____,整数长度)的函数中的快速排序
- 将 std::p artition 与快速排序结合使用