目录
1.List的介绍
2.List的使用
3List和Vector的比较
下面让我们来了解一下List及List和Vector的区别
正文开始-->
1.List的介绍
- 在 C++ 中,list是标准模板库(STL)中的一个容器。它是一个双向链表,与vector不同,list中的元素在内存中不是连续存储的。这使得list在插入和删除操作(特别是在序列中间进行操作)时具有较高的效率,因为不需要移动大量元素来腾出或填充空间。List的头文件是<list>,在使用list之前需要包含这个头文件。
- 由于std::list是链表结构,元素在内存中不是像vector那样连续存储的。这意味着它不会像vector一样,因为内存重新分配而导致元素的大量复制。
- 例如,当向一个vector中插入大量元素,导致其内存空间不足时,vector会重新分配一块更大的内存空间,并将原有的元素复制到新的空间中。而std::list则不存在这个问题,它可以很灵活地在链表的任何位置插入或删除节点,而不需要移动其他节点。
2.List的使用
定义和初始化:
定义一个空的List:
#include <list>
int main() {
std::list<int> myList;
return 0;
}
用已有元素初始化List:
std::list<int> myList1 = {1, 2, 3};
std::list<int> myList2(myList1);
添加元素
在尾部添加元素用push_back()函数,头部添加用push_front()函数,例如:
//尾部添加元素
std::list<int> myList;
myList.push_back(1);
myList.push_back(2);
myList.push_back(3);
//头部添加元素
std::list<int> myList;
myList.push_front(3);
myList.push_front(2);
myList.push_front(1);
在指定位置插入元素:使用insert()函数。例如:在某个迭代器位置插入元素
std::list<int> myList = {2, 3};
auto it = myList.begin();
myList.insert(it, 1);
这里begin()是指向List第一个元素的迭代器,插入操作是在这个迭代器指向的元素之前插入新元素
访问元素
使用迭代器访问元素:List通常用迭代器来访问元素,例如:
std::list<int> myList = {1, 2, 3};
std::list<int>::iterator it;
for (it = myList.begin(); it!= myList.end(); ++it)
{
std::cout << *it << " ";
}
其中begin()是指向第一个元素的迭代器,end()是指向最后一个元素的迭代器
但是list中,不能像数组一样直接使用下标访问.因为List元素在内存中不是连续的,所以不能像vector一样直接使用[]操作符来进行访问元素.
删除元素
**删除头部元素和删除尾部元素:**删除头部元素使用pop_front()函数,尾部元素使用pop_back()函数,例如
//删除头部元素
std::list<int> myList = {1, 2, 3};
myList.pop_front();
//删除尾部元素
std::list<int> myList = {1, 2, 3};
myList.pop_back();
**删除指定位置的元素:**使用erase()函数,例如,删除某个迭代器指向的元素:
std::list<int> myList = {1, 2, 3};
auto it = myList.begin();
++it;
myList.erase(it);
**获取大小:**可以使用size()函数来获取List中元素的个数:
std::list<int> myList = {1, 2, 3};
std::cout << "Size of the list: " << myList.size() << std::endl;
List的实际应用场景:
- 频繁插入和删除操作的场景:例如,在一个文本编辑器中,用于存储文本行,当用户插入或删除行时,list的高效插入和删除特性就可以发挥作用。
- 需要保持元素插入顺序的场景:比如记录用户操作的历史记录,每个操作作为一个元素插入到list中,按照操作的先后顺序存储。
3List和Vector的比较
内存布局
vector:
- vector的元素在内存中是连续存储的。就像是一个数组,这种连续存储的方式使得它可以通过指针运算来实现快速的随机访问。例如,对于一个vector<int> v,如果知道第一个元素的地址(&v[0]),那么可以通过简单的偏移量计算来访问其他元素,如v[2]的地址就是&v[0]+ 2*sizeof(int)。
- 由于内存连续,vector在存储大量元素时,如果需要重新分配内存(例如,当添加元素导致当前内存空间不足时),会把所有元素复制到新的内存空间中。这可能会导致性能开销,特别是在元素数量很大的情况下。
List
- list是双向链表结构,元素在内存中不是连续存储的。每个元素(节点)都包含指向前一个节点和后一个节点的指针。这种存储方式使得它在插入和删除元素时不需要移动其他元素,只需要调整节点之间的指针关系。
- 例如,在list中插入一个新元素,只需要修改新元素前后节点的指针,使其正确地链接到新元素即可。这种内存布局使得list在频繁插入和删除操作时比vector更具优势。
访问元素
vector
- vector支持快速的随机访问。可以使用下标操作符[]或者at函数来访问元素。例如,对于vector<int> v = {1,2,3},访问v[1]的时间复杂度是,这意味着无论vector中有多少个元素,访问任意一个元素的时间基本是固定的。
- 不过,使用[]操作符时要注意边界检查,因为如果下标超出范围,可能会导致程序出现未定义行为。at函数则会进行边界检查,当越界时会抛出std::out_of_range异常。
List
- list不支持像vector那样高效的随机访问。如果要访问list中的一个特定元素,需要从链表的头部或者尾部开始遍历。例如,对于std::list<int> l = {1,2,3},如果要访问第二个元素,通常需要使用迭代器从头部开始遍历,时间复杂度是,其中是链表中元素的个数。
- 虽然list也有迭代器可以用于遍历元素,但它的迭代器不能像vector的下标那样进行简单的算术运算来快速定位元素。
插入和删除操作
vector
- 在vector的末尾插入元素是很快的,时间复杂度为。例如,使用push_back函数向vector的末尾添加元素,只需要在当前已分配的内存空间的末尾添加新元素即可。
- 但是,在vector的中间或者开头插入元素就比较复杂。因为元素是连续存储的,插入一个元素可能需要将插入位置之后的所有元素都向后移动一个位置,以腾出空间给新元素.
list
- list在任意位置插入和删除元素的时间复杂度都是。无论是在头部、中间还是尾部插入或删除元素,只需要调整相关节点的指针即可。例如,使用insert函数在list的指定位置插入元素,或者使用erase函数删除指定位置的元素,操作相对简单且高效。
今天就分享到这里了😁😁😁