我很难实现Hoare的分区算法。基本上,我想要做的是将一个数组分成两部分,第一部分包含的数字小于给定的x
,另一部分包含更大的数字。但是,我只是想不出一个好的实现。这是我的代码:
void hoare(vector<int>&arr,int end, int pivot)
{
int i = 0;
int j = end;
while (i < j)
{
while (arr[i] < pivot)
i += 1;
while (arr[j] > pivot)
j -= 1;
swap(arr[i],arr[j]);
}
// return arr;
for (int i=0; i<end; i++)
printf("%d ", arr[i]);
}
现在我发现大量网站有(arr[i] <= pivot)
而不是我放在那里。但是,当我这样做时,对于这样的数组:
1 3 5 7 9 2 4 6 8
我明白了:
1 3 5 4 9 2 7 6 8
然而,在我的版本中,再次出现这样一个集合:
12 78 4 55 4 3 12 1 0
程序冻结,因为外循环中的条件都没有完成,它只是一遍又一遍地重复,而不会增加j
或i
。
数据透视表是指向数组中特定数字的指针,从1开始计算;例如,在第一个示例中传递给函数的数字3意味着pivot
等于arr[2]
,这是5
很抱歉,如果这是一个noob问题或已经得到回答,但我花了一整天的时间(也在网上搜索解决方案)无济于事,现在我有自杀念头。
提前致谢。
答案 0 :(得分:2)
当然,使用
对序列进行简单的回答auto it = std::partition(vec.begin(), vec.end(),
std::bind2nd(std::less<int>(), pivot));
该函数并不真正关心谓词,而是将序列重新排列为两个序列:一个谓词产生true
,另一个谓词产生false
。该算法将迭代器返回到第一个子序列的末尾(由谓词为true
的元素组成)。有趣的是,该算法应该适用于前向迭代器(如果它真的得到了前导迭代器,它可以使用相当多的交换)。您正在实现的算法显然需要双向迭代器,即,我将忽略也适用于前向序列的要求。
在实现算法时我会遵循完全相同的接口,因为迭代器抽象对序列算法非常有效。算法本身只使用std::find_if()
来查找it
范围内的迭代器[begin, end)
,使谓词不成立:
it = std::find_if(begin, end, not1(pred));
如果存在这样的迭代器,则使用std::find_if()
在[std::reverse_iterator<It>(end), std::reverse_iterator<It>(it))
中搜索迭代器rit
,以使谓词保持:
rit = std::find_if(std::reverse_iterator<It>(end), std::reverse_iterator<It>(it),
pred);
如果存在这样的迭代器,则std::swap()
为相应的位置,并相应地更新begin
和end
:
std::swap(*it, *rit);
begin = ++it;
end = (++rit).base();
如果找不到it
或rit
,算法将终止。将这种逻辑置于一致的算法中似乎相当直接。请注意,此算法甚至无法使用您尝试使用的运算符,即概念上只能比较x < pivot
和x >= pivot
的元素(与!(x < privot)
相同)。
下面的实现没有经过测试,但完整的算法看起来像这样:
template <typename BiIt, typename Pred>
BiIt partition(BiIt it, BiIt end, Pred pred) {
typedef std::reverse_iterator<BiIt> RIt;
for (RIt rit(end);
(it = std::find_if(it, end, std::not1(pred))) != end
&& (rit = std::find_if(RIt(end), RIt(it), pred)) != RIt(it);
++it, end = (++rit).base()) {
std::swap(*it, *rit);
}
return it;
}