为什么拼接整个列表或std :: forward_list的线性范围?

时间:2013-01-04 14:21:48

标签: c++ list c++11 forward-list

将一个范围从一个列表拼接到另一个列表可以在恒定时间内完成,但代价是size()的复杂度是线性的。

C ++ 11在std::list的情况下通过要求size()为常量时间而改变了。例如,这打破了gcc的实现,请参阅[C++0x] std::list::size complexity

除了范围splice()之外,还有其他原因 size() 无法在之前的C ++ 03符合< / strong> std::list 实施?

为什么拼接整个列表或线性范围为 std::forward_list

splice_after(),案例(1)和(3)。另见standard draft N3485中的23.3.4.6 forward_list操作[forwardlist.ops]。 std::forward_list甚至没有实现size()

我知道forward_list是一个单链表,但我不明白为什么人们不能在恒定时间内完成范围splice_after()。我可能在这里遗漏了一些小事......


编辑:好的,至少部分是我的误解,我预计4 会保留在源列表中。代码:

#include <algorithm>
#include <iostream>
#include <forward_list>

using namespace std;

void dump_list(const forward_list<char>& l) {
    for(char c : l)
        cout << c << ' ';
    cout << '\n';
}

int main()
{
    forward_list<char> trg = {'a','b','c'};
    forward_list<char> src = {'1','2','3','4'};

    auto first = src.begin();
    auto last  = find(src.begin(), src.end(), '4');
    cout << "first = " << *first << ", last = " << *last << "\n\n";

    trg.splice_after(trg.begin(), src, first, last);

    cout << "Target after splice:\n";
    dump_list(trg);

    cout << "Source after splice:\n";
    dump_list(src);

    cout << endl;
    return 0;
}

输出:

first = 1, last = 4
Target after splice:
a 2 3 b c
Source after splice:
1 4 

2 个答案:

答案 0 :(得分:2)

在forward_list的情况下,你如何使范围splice_after恒定时间?在源列表中,您只有迭代器。要从源转发链接列表中删除节点,您需要在last之前的节点,因此您需要线性搜索该链接列表节点的源。因此,为什么它在firstlast

之间的距离是线性的

使用整个源列表的版本仍然需要在源结束之前立即搜索节点,以便可以修改它以指向目标中拼接之后的元素。因此,它还需要源的大小的线性时间。

答案 1 :(得分:1)

范围拼接是size在以前的C ++标准中不是常数时间的唯一原因。实际上,Sun CC编译器选择使sizesplice的常量时间和所有版本的last线性。

拼接整个前向列表是线性的,因为你必须遍历你正在合并的列表,以便能够将它的最后一个节点链接回你正在拼接的列表中。我无法弄清楚为什么范围拼接是线性的。

编辑:正如@Xeo指出的那样,基于范围的版本也需要线性时间,因为first未包含在范围内,需要从last搜索以找到迭代器之前 {{1}}。