list是C++ STL中的一个双向链表容器,可以存储任何类型的元素。它提供了一组成员函数,可以在链表的任何位置插入和删除元素,也可以在链表的任何位置访问元素。与数组不同,list不需要连续的内存空间,因此可以动态地分配和释放内存。在插入和删除元素时,不会影响其他元素的位置,因此可以保持链表中元素的相对顺序。list支持双向迭代器,可以在正向和反向方向遍历链表。list是STL中的一个标准容器,可以在头文件中找到。它的常用操作包括创建list、插入元素、删除元素、访问元素、遍历std::list和其他常用函数等。
总的来说,list具有如下几个特性:
函数名 | 特性 |
---|---|
默认构造函数:list() | 该构造函数创建一个空的list对象。 |
拷贝构造函数:list(const list& l) | 该构造函数创建一个与l相同的list对象。 |
带有元素数量和元素值的构造函数:list(size_type n, const value_type& val = value_type()) | 该构造函数创建一个包含n个元素,每个元素的值都为val的list对象。 |
带有迭代器范围的构造函数:list(InputIterator first, InputIterator last) | 该构造函数创建一个包含[first, last)范围内的元素的list对象。其中,first和last是迭代器,用于指定要复制到新list中的元素范围。 |
示例代码:
#include
#include
using namespace std;
void print(list<int> ls)
{
for (auto e : ls)
{
cout << e << ' ';
}
cout << endl;
}
int main()
{
list<int> ls;
print(ls);
list<int> ls2(10, 6);
print(ls2);
list<int> ls3(ls2);
print(ls3);
list<int> ls4(ls2.begin(), ls2.end());
print(ls4);
return 0;
}
注意:
- 在使用带有元素数量和元素值的构造函数时,如果元素类型是自定义类型,则必须提供接受一个值的构造函数,否则会导致编译错误。
- 在使用带有迭代器范围的构造函数时,迭代器的类型必须与list的元素类型相同,否则会导致编译错误。
list提供了双向迭代器(bidirectional iterator),可以用于遍历list中的元素。list的迭代器定义在头文件中,可以使用list::iterator和list::const_iterator来表示可变和不可变的迭代器。
list的迭代器支持以下操作:
++it:将迭代器it移动到下一个元素。
–it:将迭代器it移动到上一个元素。
*it:返回迭代器it所指向的元素。
it->member:返回迭代器it所指向元素的成员member。
it == it2:比较两个迭代器是否相等。
it != it2:比较两个迭代器是否不相等。
函数声明 | 特性 |
---|---|
begin()和end() | 分别返回指向list首元素和尾元素下一个位置的迭代器。 |
rbegin()和rend() | 分别返回指向list尾元素和首元素前一个位置的逆向迭代器。 |
示例代码:
#include
#include
using namespace std;
int main()
{
string arr = "abcdefg";
list<char> ls(arr.begin(), arr.end());
list<char>::iterator it = ls.begin();
list<char>::reverse_iterator rit = ls.rbegin();
while (it != ls.end())
{
cout << *it << ' ';
it++;
}
cout << endl;
while (rit != ls.rend())
{
cout << *rit << ' ';
rit++;
}
cout << endl;
return 0;
}
注意:
- list的迭代器不支持随机访问,因此不能使用it[n]来访问迭代器中的元素,也不能使用+和-运算符来移动迭代器。
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
list的容量是动态变化的,因此并没有capacity()函数来返回list的容量。以下是list的容量相关函数:
函数 | 说明 |
---|---|
size() | 返回list中元素的数量。 |
max_size() | 返回list最多可以容纳的元素数量,这个数量取决于系统的限制。 |
empty() | 判断list是否为空,如果list为空,则返回true,否则返回false。 |
示例代码:
#include
#include
using namespace std;
int main()
{
string arr = "abcdefg";
list<char> ls(arr.begin(), arr.end());
cout << "是否为空:" << ls.empty() << endl;
cout << "最大存储数量:" << ls.max_size() << endl;
cout << "当前存储数量:" << ls.size() << endl;
return 0;
}
注意
- list的容量是动态变化的,因此在插入或删除元素时,list的容量可能会发生变化。
list提供了一些用于访问元素的函数,如下
函数名 | 说明 |
---|---|
front() | 返回list中第一个元素的引用。 |
back() | 返回list中最后一个元素的引用。 |
示例代码:
#include
#include
using namespace std;
int main()
{
string arr = "abcdefg";
list<char> ls(arr.begin(), arr.end());
char front = ls.front();
char back = ls.back();
cout << front << " " << back << endl;
return 0;
}
注意
- 当容器为空时,调用front()和back()可能导致未定义行为,因此在使用这些方法之前最好检查容器是否为空。
在C++中,list提供了多种操作函数来方便地对链表进行操作。其中,list中的modifiers函数主要用于修改链表中的元素,包括插入、删除和替换等操作。
以下是常用的list modifiers函数:
函数 | 说明 |
---|---|
push_back | 在链表的末尾插入一个元素。 |
push_front | 在链表的头部插入一个元素。 |
insert | 在指定位置插入一个元素。 |
erase | 删除指定位置的元素。 |
clear | 清空链表中的所有元素。 |
pop_back | 删除链表末尾的元素。 |
pop_front | 删除链表头部的元素。 |
swap | 用于交换两个链表的内容 |
示例代码:
#include
#include
using namespace std;
template<typename T>
void Print(list<T> ls)
{
for (const auto& e : ls)
{
cout << e << ' ';
}
cout << endl;
}
int main()
{
list<char> ls;
ls.push_back('A');
ls.push_back('B');
ls.push_back('C');
Print(ls);
ls.push_front('1');
ls.push_front('2');
ls.push_front('3');
Print(ls);
ls.pop_back();
ls.pop_front();
Print(ls);
string arr = "abcd";
ls.insert(ls.begin(), 'M');
ls.insert(ls.end(), arr.begin(), arr.end());
ls.insert(ls.begin(), 3, 'O');
Print(ls);
list<char> ls2(ls);
ls.erase(ls.begin());
Print(ls);
ls.swap(ls2);
Print(ls);
Print(ls2);
ls2.clear();
ls.erase(ls.begin(), ls.end());
Print(ls);
Print(ls2);
return 0;
}
注意:
- 在插入或删除元素时,要确保链表中已有足够的元素或者已经分配了足够的内存空间,否则可能会导致程序崩溃。
- 在使用insert函数时,需要注意插入位置的有效性,如果插入到了一个无效的位置,可能会导致程序出错。
- 在使用erase函数时,需要注意被删除元素的有效性,如果删除了一个无效的元素,可能会导致程序出错。
- 在使用clear函数时,需要注意清空链表后,链表中的所有元素都会被删除,因此需要确保不再需要这些元素的情况下才能使用该函数。
- swap函数只是交换了两个链表中的元素,而不是交换了它们的内存空间,因此不会对链表的容量和内存使用产生影响。
在使用list容器时,需要注意到删除节点会导致迭代器失效的问题。因为list容器的底层实现是双向链表,当我们删除某个节点时,该节点的前后节点会重新建立连接,而被删除节点的迭代器就会失效,指向该节点的迭代器将无法访问该节点,也无法访问其后继节点。
示例代码
#include
#include
using namespace std;
int main()
{
list<char> ls = {'a', 'b', 'c', 'd'};
list<char>::iterator it = ls.begin();
while (it != ls.end())
{
if (*it == 'b')
{
ls.erase(it);
}
cout << *it << ' ';
it++;
}
return 0;
}
运行结果:
我们可以先保存指向要删除的节点的迭代器,然后将迭代器指向下一个节点,在进行删除操作。
int main()
{
list<char> ls = {'a', 'b', 'c', 'd'};
list<char>::iterator it = ls.begin();
while (it != ls.end())
{
if (*it == 'b')
{
auto tmp = it++;
ls.erase(tmp);
}
else
{
cout << *it << ' ';
it++;
}
}
return 0;
}
另外,当我们将迭代器保存到外部使用时也会导致迭代器失效。
#include
#include
using namespace std;
int main() {
std::list<int> mylist = { 1, 2, 3, 4, 5 };
auto it = mylist.begin();
// 将迭代器保存到外部使用
auto outside_it = it;
// 对list容器进行删除操作
mylist.erase(mylist.begin());
// 使用保存在外部的迭代器访问list容器
std::cout << *outside_it << std::endl;
return 0;
}
注意:
- 在使用迭代器遍历list容器时,不要在遍历过程中删除节点,因为删除节点会导致迭代器失效。如果需要在遍历过程中删除节点,可以先将迭代器指向下一个节点,再删除当前节点。
- 在使用迭代器遍历list容器时,不要将迭代器保存到容器外部使用,因为当我们对list容器进行插入或删除操作时,迭代器会失效,可能会导致程序崩溃或出现未定义行为。
list是C++ STL中的一个双向链表容器,可以在任意位置高效地插入和删除元素,不需要像vector那样移动其他元素。总之,list容器是一个高效的双向链表容器,可以在任意位置高效地插入和删除元素,是一个非常实用的容器。