C++ n_元素是如何实现的?
有很多关于StackOverflow和其他地方的声明称,C++ n_元素是如何实现的?,c++,algorithm,stl,selection,nth-element,C++,Algorithm,Stl,Selection,Nth Element,有很多关于StackOverflow和其他地方的声明称,n\u元素为O(n),并且通常使用Introselect实现: 我想知道如何才能做到这一点。我看着他,这让我更加困惑。算法如何在QSort和中值之间切换 我在这里找到了介绍文件,上面写着: 在本文中,我们集中讨论排序问题,并在后面的部分中简要地回到选择问题 我试图通读STL本身,以了解第n个元素是如何实现的,但这很快就会变得棘手 有人能给我看一下如何实现Introselect的伪代码吗?或者更好的是,除了STL以外的实际C++代码当然是:免
n\u元素
为O(n),并且通常使用Introselect实现:
我想知道如何才能做到这一点。我看着他,这让我更加困惑。算法如何在QSort和中值之间切换
我在这里找到了介绍文件,上面写着:
在本文中,我们集中讨论排序问题,并在后面的部分中简要地回到选择问题
我试图通读STL本身,以了解第n个元素是如何实现的,但这很快就会变得棘手
有人能给我看一下如何实现Introselect的伪代码吗?或者更好的是,除了STL以外的实际C++代码当然是:免责声明:我不知道如何<代码> STD::NthyEngult<代码>在任何标准库中实现。 如果您知道快速排序的工作原理,您可以轻松地对其进行修改,以执行此算法所需的操作。快速排序的基本思想是,在每个步骤中,将数组划分为两部分,使小于轴的所有元素都位于左侧子数组中,而等于或大于轴的所有元素都位于右侧子数组中。(对称为“三元快速排序”的快速排序的修改会创建第三个子数组,其中所有元素都等于轴。然后,右侧子数组只包含严格大于轴的条目。)然后,快速排序通过递归排序左侧和右侧子数组来进行 如果您只想将第n个元素移动到位,而不是递归到两个子数组中,那么您可以在每一步中判断是否需要下降到左或右子数组中。(之所以知道这一点,是因为排序数组中的第n个元素有索引n,所以这就成了比较索引的问题。)因此,除非快速排序遇到最坏情况下的退化,否则在每一步中,您都会将剩余数组的大小大约减半。(您永远不会再看另一个子数组。)因此,平均而言,您在每个步骤中处理的数组长度如下:
模板
void第n个元素(\u RandomAccessIter第1个元素,\u RandomAccessIter第n个元素,
_随机存取器uuu last,u Tp*){
而(\uuuu last-\uuuu first>3){
_随机存取器=
__无防护分区(uuu第一,最后,
_Tp(uuu中值(*uuuu优先,
*(uu first+(u last-u first)/2),
*(uu last-1));
如果你问了两个问题,那就是名义上的问题
n_元素是如何实现的
你已经回答了:
有很多关于StackOverflow和其他地方的声明称,n_元素是O(n),并且它通常是用Introselect实现的
我也可以通过查看我的stdlib实现来确认这一点
还有一个你不明白答案的地方:
算法如何在QSort和中值之间切换
让我们看看我从stdlib中提取的伪代码:
nth_element(first, nth, last)
{
if (first == last || nth == last)
return;
introselect(first, nth, last, log2(last - first) * 2);
}
introselect(first, nth, last, depth_limit)
{
while (last - first > 3)
{
if (depth_limit == 0)
{
// [NOTE by editor] This should be median-of-medians instead.
// [NOTE by editor] See Azmisov's comment below
heap_select(first, nth + 1, last);
// Place the nth largest element in its final position.
iter_swap(first, nth);
return;
}
--depth_limit;
cut = unguarded_partition_pivot(first, last);
if (cut <= nth)
first = cut;
else
last = cut;
}
insertion_sort(first, last);
}
第n个元素(第一个、第n个、最后一个)
{
if(first==last | | n==last)
返回;
插入选择(第一、第n、最后、log2(最后-第一)*2);
}
插入选择(第一个、第n个、最后一个、深度限制)
{
而(最后一个-第一个>3个)
{
如果(深度限制==0)
{
//[编者注]这应该是中位数的中间值。
//[编辑注]见下面阿兹米索夫的评论
堆_选择(第一个,第n+1个,最后一个);
//将第n个最大图元放置在其最终位置。
国际热核实验堆交换(第一次,第n次);
返回;
}
--深度限制;
切割=无防护分区轴(第一个、最后一个);
如果切切请注意这似乎是一个纯粹的快速排序实现,没有算法切换,所以它不是内省。我不明白你想说什么,@ NoBODY。你回答标题,而不是问题>代码>有人能告诉我如何执行内省选择的伪代码吗?或者更好的是,除了T之外的实际C++代码。他当然会说:)
。除此之外,我认为目前还不清楚每个分区的大小是否为原来的一半。我不确定无防护分区的大小,但选定的轴只是当前范围的第一个、中间和最后一个元素的中间值,它只能保证轴的每一侧至少有一个元素,因此需要在许多分区步骤中线性循环。关于最后一部分,我刚刚注意到您(和标准)正在讨论平均情况,所以这是可以的,但最好补充一点,最坏的情况可能(也将)会更糟。我的主要观点(从第一条评论)OP似乎对在introselect中切换算法比对nth\u元素的实际实现更感兴趣。我也通过从已安装的stdlib实现中创建伪代码进行了欺骗,但我试图解决这些问题。您是否注意到该算法应仅为O(n)
根据cppreference的平均情况。它没有说明最坏的情况。这意味着quickselect将是可行的,因为它的平均情况是O(n)
。@cppreference中没有人还声明可以使用除Introselect之外的其他内容
template <class Iter, class T>
void nth_element(Iter first, Iter nth, Iter last) {
while (last - first > 3) {
Iter cut =
unguarded_partition(first, last,
T(median(*first,
*(first + (last - first)/2),
*(last - 1))));
if (cut <= nth)
first = cut;
else
last = cut;
}
insertion_sort(first, last);
}
nth_element(first, nth, last)
{
if (first == last || nth == last)
return;
introselect(first, nth, last, log2(last - first) * 2);
}
introselect(first, nth, last, depth_limit)
{
while (last - first > 3)
{
if (depth_limit == 0)
{
// [NOTE by editor] This should be median-of-medians instead.
// [NOTE by editor] See Azmisov's comment below
heap_select(first, nth + 1, last);
// Place the nth largest element in its final position.
iter_swap(first, nth);
return;
}
--depth_limit;
cut = unguarded_partition_pivot(first, last);
if (cut <= nth)
first = cut;
else
last = cut;
}
insertion_sort(first, last);
}