C++标准库中的list是基于双向链表实现的序列容器,其提供的splice成员方法用于在不拷贝、不移动元素的前提下,将一个链表中的节点转移到另一个链表中,这是list独有的高效操作,因为链表节点通过指针关联,调整指针即可完成节点归属的变更。

splice的三个重载版本
list的splice方法共有三个重载形式,分别对应不同的节点转移场景,下面逐一介绍。
1. 转移整个链表的所有节点
该版本会将源链表的全部节点转移到目标链表的指定位置之前,转移后源链表会变为空链表。
函数原型为:
void splice(const_iterator pos, list& other);参数说明:
- pos:目标链表中插入位置的迭代器,转移过来的节点会放在pos指向的元素之前
- other:源链表,转移完成后other会变为空
示例代码如下:
#include <iostream>
#include <list>
int main() {
std::list<int> target = {1, 2, 3};
std::list<int> source = {4, 5, 6};
// 将source的所有节点转移到target的begin()位置之前,也就是target的头部
target.splice(target.begin(), source);
std::cout << "转移后target的元素:";
for (int num : target) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "转移后source的元素个数:" << source.size() << std::endl;
return 0;
}
运行结果为:
转移后target的元素:4 5 6 1 2 3
转移后source的元素个数:0
2. 转移源链表的单个节点
该版本仅转移源链表中指定迭代器指向的单个节点到目标链表的指定位置。
函数原型为:
void splice(const_iterator pos, list& other, const_iterator it);参数说明:
- pos:目标链表的插入位置迭代器
- other:源链表
- it:源链表中要转移的节点的迭代器
示例代码如下:
#include <iostream>
#include <list>
int main() {
std::list<int> target = {1, 2, 3};
std::list<int> source = {4, 5, 6};
// 获取source中值为5的节点迭代器
auto it = source.begin();
++it; // 现在it指向5
// 将source中的5转移到target的end()位置之前,也就是target的尾部
target.splice(target.end(), source, it);
std::cout << "转移后target的元素:";
for (int num : target) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "转移后source的元素:";
for (int num : source) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
运行结果为:
转移后target的元素:1 2 3 5
转移后source的元素:4 6
3. 转移源链表的区间节点
该版本转移源链表中[first, last)区间内的所有节点到目标链表的指定位置,区间是左闭右开的。
函数原型为:
void splice(const_iterator pos, list& other, const_iterator first, const_iterator last);参数说明:
- pos:目标链表的插入位置迭代器
- other:源链表
- first:要转移区间的起始迭代器(包含该节点)
- last:要转移区间的结束迭代器(不包含该节点)
示例代码如下:
#include <iostream>
#include <list>
int main() {
std::list<int> target = {1, 2, 3};
std::list<int> source = {4, 5, 6, 7, 8};
// 转移source中[5,7)区间的节点,也就是5和6两个节点
auto first = source.begin();
++first; // 指向5
auto last = first;
++last; ++last; // 指向7
target.splice(++target.begin(), source, first, last); // 插入到target的2之前
std::cout << "转移后target的元素:";
for (int num : target) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "转移后source的元素:";
for (int num : source) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
运行结果为:
转移后target的元素:1 5 6 2 3
转移后source的元素:4 7 8
使用splice的注意事项
- splice操作仅适用于list容器,因为其他顺序容器如vector、deque是基于连续内存或分段连续内存实现的,不支持这种指针级别的节点转移。
- 如果源链表和目标链表是同一个链表,那么转移操作相当于调整节点在链表中的顺序,此时要注意迭代器区间不能包含pos位置,否则会出现未定义行为。
- splice不会使指向被转移节点的迭代器、指针、引用失效,因为这些节点只是换了所属的链表,本身的内存地址和值都没有变化。
- splice的时间复杂度是O(1),因为它仅调整几个节点的指针指向,不需要拷贝元素,所以在需要移动链表节点的场景下,优先使用splice而不是先erase再insert。
常见使用场景
当需要合并两个有序链表,且不需要保留源链表时,可以使用splice快速转移节点,避免元素拷贝的开销。另外在链表节点的排序、分区场景中,也可以用splice调整节点位置,提升操作效率。