C++List插入、删除和遍历操作详解
C++ List 插入、删除和遍历操作详解
在 C++ 标准模板库 (STL) 中,std::list
是一个双向链表容器,它提供了在常数时间内在任何位置插入和删除元素的能力,而序列访问则需要线性时间。这使得 std::list
非常适合于频繁插入和删除操作的场景,但不太适合需要随机访问元素的场景。本文将详细介绍 std::list
的插入、删除和遍历操作。
一、List 简介
std::list
是一个模板类,定义在 <list>
头文件中。与 std::vector
和 std::deque
不同,std::list
不支持随机访问,也就是说,你不能通过索引直接访问元素(如 myList[5]
)。这是因为链表中的元素在内存中不是连续存储的,而是通过指针链接在一起的。
优点:
- 高效的插入和删除: 无论在列表的哪个位置插入或删除元素,时间复杂度都是 O(1),即常数时间。
- 动态大小: 列表可以根据需要动态地增长或缩小。
- 不需要移动元素: 插入或删除元素时,不需要像
std::vector
那样移动其他元素。
缺点:
- 不支持随机访问: 访问特定位置的元素需要遍历链表,时间复杂度为 O(n),其中 n 是元素的位置。
- 额外的内存开销: 每个元素都需要额外的内存来存储指向前一个和后一个元素的指针。
二、插入操作
std::list
提供了多种插入元素的方法,以下是常用的几种:
1. push_front(const T& value)
和 push_back(const T& value)
push_front()
:在列表的头部插入一个新元素。push_back()
:在列表的尾部插入一个新元素。
```c++
include
include
int main() {
std::list
myList.push_front(10); // 在头部插入 10
myList.push_back(20); // 在尾部插入 20
myList.push_front(5); // 在头部插入 5
myList.push_back(30); // 在尾部插入 30
// 现在 myList 包含: 5, 10, 20, 30
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 5 10 20 30
return 0;
}
```
2. insert(iterator position, const T& value)
- 在指定迭代器
position
之前插入一个新元素。 - 返回一个指向新插入元素的迭代器。
```c++
include
include
int main() {
std::list
// 在第三个元素之前插入 10
std::list
std::advance(it, 2); // 将迭代器移动到第三个元素的位置
myList.insert(it, 10);
// 现在 myList 包含: 1, 2, 10, 3, 4, 5
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 1 2 10 3 4 5
return 0;
}
```
3. insert(iterator position, size_type n, const T& value)
- 在指定迭代器
position
之前插入n
个值为value
的元素。 - 返回一个指向第一个新插入元素的迭代器。
```c++
include
include
int main() {
std::list
// 在第二个元素之前插入 3 个值为 5 的元素
std::list
std::advance(it, 1);
myList.insert(it, 3, 5);
// 现在 myList 包含: 1, 5, 5, 5, 2, 3
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 1 5 5 5 2 3
return 0;
}
```
4. insert(iterator position, InputIterator first, InputIterator last)
- 在指定迭代器
position
之前插入一个范围内的元素,范围由迭代器first
和last
指定(不包括last
)。 - 返回一个指向第一个新插入元素的迭代器。
```c++
include
include
include
int main() {
std::list
std::vector
// 在列表的开头插入 myVector 的内容
myList.insert(myList.begin(), myVector.begin(), myVector.end());
// 现在 myList 包含: 4, 5, 6, 1, 2, 3
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 4 5 6 1 2 3
return 0;
}
```
5. emplace_front(Args&&... args)
和 emplace_back(Args&&... args)
emplace_front()
:在列表的头部构造一个新元素。emplace_back()
:在列表的尾部构造一个新元素。Args&&... args
是传递给元素构造函数的参数。- 这些方法比
push_front()
和push_back()
更高效,因为它们避免了不必要的拷贝或移动操作。
```c++
include
include
include
int main() {
std::list
myList.emplace_front("Hello"); // 直接在列表头部构造一个字符串
myList.emplace_back("World"); // 直接在列表尾部构造一个字符串
for (const std::string& str : myList) {
std::cout << str << " ";
}
std::cout << std::endl; // 输出: Hello World
return 0;
}
```
6. emplace(iterator position, Args&&... args)
- 在指定迭代器
position
之前构造一个新元素。 Args&&... args
是传递给元素构造函数的参数。- 返回一个指向新构造的元素的迭代器。
- 同样,
emplace()
比insert()
更高效,因为它避免了不必要的拷贝或移动操作。
```c++
include
include
int main() {
std::list
// 在列表的开头构造一个 pair 对象
myList.emplace(myList.begin(), 1, "One");
myList.emplace(myList.end(), 2, "Two");
for (const auto& p : myList) {
std::cout << "(" << p.first << ", " << p.second << ") ";
}
std::cout << std::endl; // 输出: (1, One) (2, Two)
return 0;
}
```
三、删除操作
std::list
提供了多种删除元素的方法,以下是常用的几种:
1. pop_front()
和 pop_back()
pop_front()
:删除列表的第一个元素。pop_back()
:删除列表的最后一个元素。
```c++
include
include
int main() {
std::list
myList.pop_front(); // 删除第一个元素 (1)
myList.pop_back(); // 删除最后一个元素 (5)
// 现在 myList 包含: 2, 3, 4
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 2 3 4
return 0;
}
```
2. erase(iterator position)
- 删除指定迭代器
position
指向的元素。 - 返回一个指向被删除元素之后元素的迭代器。
```c++
include
include
int main() {
std::list
// 删除第三个元素 (3)
std::list
std::advance(it, 2);
it = myList.erase(it);
// 现在 myList 包含: 1, 2, 4, 5
// it 指向 4
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 1 2 4 5
std::cout << *it << std::endl; // 输出: 4
return 0;
}
```
3. erase(iterator first, iterator last)
- 删除一个范围内的元素,范围由迭代器
first
和last
指定(不包括last
)。 - 返回一个指向最后一个被删除元素之后元素的迭代器。
```c++
include
include
int main() {
std::list
// 删除前三个元素 (1, 2, 3)
std::list
std::advance(it, 3);
myList.erase(myList.begin(), it);
// 现在 myList 包含: 4, 5
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 4 5
return 0;
}
```
4. remove(const T& value)
- 删除列表中所有值等于
value
的元素。
```c++
include
include
int main() {
std::list
myList.remove(2); // 删除所有值为 2 的元素
// 现在 myList 包含: 1, 3, 4, 5
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 1 3 4 5
return 0;
}
```
5. remove_if(UnaryPredicate pred)
- 删除列表中所有满足谓词
pred
的元素。 UnaryPredicate
是一个一元谓词,它接受一个元素作为参数,并返回一个布尔值。
```c++
include
include
// 一个谓词,用于判断一个数是否为偶数
bool isEven(int n) {
return n % 2 == 0;
}
int main() {
std::list
myList.remove_if(isEven); // 删除所有偶数
// 现在 myList 包含: 1, 3, 5
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 1 3 5
return 0;
}
```
6. clear()
- 删除列表中的所有元素,使列表为空。
```c++
include
include
int main() {
std::list
myList.clear(); // 清空列表
std::cout << myList.size() << std::endl; // 输出: 0
return 0;
}
```
四、遍历操作
由于 std::list
不支持随机访问,所以遍历 std::list
通常使用迭代器。
1. 使用前向迭代器
```c++
include
include
int main() {
std::list
for (std::list
std::cout << *it << " ";
}
std::cout << std::endl; // 输出: 1 2 3 4 5
return 0;
}
```
2. 使用范围基于的 for 循环 (C++11 及以后)
```c++
include
include
int main() {
std::list
for (int x : myList) {
std::cout << x << " ";
}
std::cout << std::endl; // 输出: 1 2 3 4 5
return 0;
}
```
3. 使用反向迭代器
```c++
include
include
int main() {
std::list
for (std::list
std::cout << *rit << " ";
}
std::cout << std::endl; // 输出: 5 4 3 2 1
return 0;
}
```
4. 使用 const_iterator
遍历常量列表
```c++
include
include
int main() {
const std::list
for (std::list
std::cout << *it << " ";
}
std::cout << std::endl; // 输出: 1 2 3 4 5
return 0;
}
```
五、总结
std::list
是一个强大的双向链表容器,它提供了高效的插入和删除操作。了解如何正确地使用插入、删除和遍历方法对于有效地使用 std::list
至关重要。
关键点回顾:
- 插入:
push_front()
,push_back()
,insert()
,emplace_front()
,emplace_back()
,emplace()
- 删除:
pop_front()
,pop_back()
,erase()
,remove()
,remove_if()
,clear()
- 遍历: 前向迭代器, 范围基于的 for 循环, 反向迭代器,
const_iterator
希望这篇文章能够帮助你更好地理解和使用 C++ 中的 std::list
容器。 在实际编程中,应根据具体需求选择合适的容器。 如果需要频繁插入和删除元素,std::list
是一个不错的选择。 如果需要随机访问元素,std::vector
或 std::deque
可能更合适。