在阅读C ++ 11和N2543的FCD中的forward_list
时,我偶然发现splice_after
的一个特定重载(略微简化,让cit
成为{{ 1}}):
const_iterator
行为是void splice_after(cit pos, forward_list<T>& x, cit first, cit last);
之后pos
之间的所有内容都移至(first,last)
。因此:
this
描述包括复杂性:
复杂性:O(距离(第一,最后))
我可以看到这是因为需要调整 this: 1 2 3 4 5 6 x: 11 12 13 14 15 16
^pos ^first ^last
will become:
this: 1 2 13 14 3 4 5 6 x: 11 12 15 16
^pos ^first ^last
,PREDECESSOR(last).next = pos.next
不允许在O(1)中发生这种情况。
好的,但是没有加入两个单链表在O(1)这个简单数据结构的优势之一?因此我想知道 - 在forward_list
上是否有任何操作在O(1)中拼接/合并/连接任意数量的元素?
当然,算法非常简单。只需要一个操作名称(伪代码):( 通过集成Kerreks答案更新)
forward_list
结果略有不同,因为temp_this = pos.next;
temp_that = last.next;
pos.next = first.next;
last.next = temp_this;
first.next = temp_that;
未被移动,而是(first,last)
。
(first,last]
我认为这是一个像前者一样合理的操作,人们可能会这样做 - 特别是如果它有O(1)的好处。
this: 1 2 3 4 5 6 7 x: 11 12 13 14 15 16 17
^pos ^first ^last
will become:
this: 1 2 13 14 15 16 3 4 5 6 7 x: 11 12 17
^pos ^last ^first
作为移动范围可能有用吗? 答案 0 :(得分:7)
让我先给出一个O(1)拼接算法的修正版本,并举例说明:
temp_this = pos.next;
temp_that = last.next;
pos.next = first.next;
last.next = temp_this;
first.next = temp_that;
(理智检查是观察每个变量精确出现两次,一旦设定并且一次得到。)
示例:
pos.next last.next
v v
1 2 3 4 5 6 7 11 12 13 14 15 16 17 #
^ ^ ^ ^
pos first last end
becomes:
This: 1 2 13 14 15 16 3 4 5 6 7
That: 11 12 17
现在我们看到,为了拼接到that
列表的末尾,我们需要在end()
之前为提供一个迭代器。但是,在恒定时间内不存在这样的迭代器。所以基本上线性成本来自于发现最终迭代器,无论是这样还是另一种:要么在O(n)时间内预先计算它并使用你的算法,要么你只是在线性时间内逐个拼接。
(大概你可以实现你自己的单链表,它会为before_end
存储一个额外的迭代器,你必须在相关的操作中不断更新。)
答案 1 :(得分:4)
LWG内部就此问题进行了大量辩论。有关此问题的一些文档,请参阅LWG 897。
答案 2 :(得分:1)
当您将end()
作为last
传递时,您的算法会失败,因为它会尝试使用一个过去的节点并将其重新链接到另一个列表中。允许end()
用于除此之外的每个算法都是一个奇怪的例外。
另外我认为first.next = &last;
需要first.next = last.next;
,否则last
将出现在两个列表中。