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

Why is splicing an entire list or a range linear for std::forward_list?

本文关键字:列表 范围是 线性 拼接 std forward 为什么 list      更新时间:2023-10-16

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

c++ 11改变了std::list的情况,要求size()为常数时间。例如,这打破了gcc的实现,参见[c++ 0x] std::list::size的复杂度。

除了范围splice(),是否还有其他原因导致 size() 不能在早期的,符合c++ 03的 std::list 实现中成为常数时间?

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

参见splice_after(), case(1)和(3)。参见23.3.4.6 forward_list operations [forwardlist. cn]。ops]在标准草案N3485中。std::forward_list甚至没有实现size()

我知道forward_list是一个单链表,但我不明白为什么不能在常数时间内做范围splice_after()。我可能在这里遗漏了一些琐碎的东西…


EDIT:好吧,这至少部分是我的误解,我期望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 << "nn";
    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 

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

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

范围拼接是size在以前的c++标准中不是常量时间的唯一原因。事实上,Sun CC编译器选择使size时间为常数,而所有版本的splice都是线性的。

拼接整个前向链表是线性的,因为你必须遍历你正在合并的链表,才能将它的最后一个节点链接回你正在拼接的链表。我不明白为什么范围拼接是线性的。

编辑:正如@Xeo指出的,基于范围的版本也需要线性时间,因为last不包括在范围内,它需要从first搜索以找到 last之前的迭代器