C++初阶—list类

embedded/2025/2/28 11:18:18/

第一章:list的介绍及使用

1.1 list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素

1.2 list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。

1.2.1 list的构造

构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

1.2.2 list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

函数声明接口说明
begin + end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置

【注意】

  1. beginend为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

1.2.3 list capacity

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

  

1.2.4 list element access

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

1.2.5 list modifiers

函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

1.2.6 list的迭代器失效

前面说过,此处可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

Use of List.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>using namespace std;void test_list1() {list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator it = lt.begin();while (it != lt.end()) { //遍历cout << *it << " ";++it;}cout << endl;for (auto e : lt) //范围forcout << e << " ";cout << endl;
}void test_list2() {list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.reverse();//逆置for (auto e : lt)cout << e << " ";//5 4 3 2 1cout << endl;//sort(lt.begin(), lt.end());//报错(从访问使用的角度划分)迭代器功能上分类:正向/反向 及 正向/反向 + const(从容器底层实现的角度划分)迭代器性质上分类:单向 ++		    单链表/哈希表双向 ++/--		双向链表/红黑树(map和set)随机 ++/--/+/-  vector/string/deque算法库的sort要求传随机迭代器,而list的是双向迭代器。所以list有自己的sort//less<int> less_lt;//升序//lt.sort(less_lt);lt.sort();//sort默认升序for (auto e : lt)cout << e << " ";cout << endl;//降序//greater<int> gt;//lt.sort(gt);lt.sort(greater<int>());//实际中这种传匿名对象的方式更常用//数据量小可以用这个sort,数据量大效率不高。	
}void test_list3() { //验证list模板中sort的效率vecor排序比list快,数据量越大,两者差距越大//srand((int)time(0));//const int N = 1000000;//vector<int> v;//v.reserve(N);//list<int> lt1;//for (int i = 0; i < N; ++i) {//	auto e = rand();//	lt1.push_back(e);//	v.push_back(e);//}排序//int begin1 = clock();//sort(v.begin(), v.end());//int end1 = clock();////int begin2 = clock();//lt1.sort();//int end2 = clock();//printf("vector sort:%d\n", end1 - begin1);//printf("list sort:%d\n", end2 - begin2);//比较:lt1直接排序,lt2数据拷贝到vector在排序srand((int)time(0));const int N = 1000000;list<int> lt1;list<int> lt2;for (int i = 0; i < N; ++i) {auto e = rand();lt1.push_back(e);lt2.push_back(e);}// 排序int begin1 = clock();vector<int> v(lt2.begin(), lt2.end());//将lt2的数据拷贝到vector再排序sort(v.begin(), v.end());lt2.assign(v.begin(), v.end());//将vector的数据再拷贝回lt2int end1 = clock();int begin2 = clock();lt1.sort();int end2 = clock();printf("list2 sort:%d\n", end1 - begin1);printf("list1 sort:%d\n", end2 - begin2);
}void test_list4() {//merge() 归并两个链表,取小的尾插。前提是两个链表有序//去重演示,需要保证有序list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(3);lt.push_back(3);lt.push_back(3);lt.push_back(5);lt.push_back(5);lt.push_back(3);for (auto e : lt)cout << e << " ";//1 2 3 4 3 3 3 5 5 3cout << endl;lt.sort();lt.unique();for (auto e : lt)cout << e << " ";//1 2 3 4 5cout << endl;lt.remove(3);//删除全部3//lt.remove(30);//删除的数据不存在,什么都不做for (auto e : lt)cout << e << " ";//1 2 4 5cout << endl;
}void test_list5() {list<int> lt1, lt2;for (int i = 1; i <= 4; ++i)lt1.push_back(i);// lt1: 1 2 3 4for (int i = 1; i <= 3; ++i)lt2.push_back(i * 10);// lt2: 10 20 30list<int>::iterator it = lt1.begin();++it;//it指向2//lt1.splice(it, lt2);//1 10 20 30 2 3 4  //将整个lt2转移到lt1 第二节点的前面lt1:1 10 20 30 2 3 4   lt2为空,it依旧指向2//lt1.splice(it, lt2, ++lt2.begin());//1 20 2 3 4  //将lt2的第二节点转移到lt1的第二节点前面lt1.splice(it, lt2, ++lt2.begin(), lt2.end());//1 20 30 2 3 4  //将lt2从第二节点至末尾转移到lt1的第二节点前面for (auto e : lt1)cout << e << " ";cout << endl;
}int main() {//test_list1();test_list2();//test_list3();//test_list4();//test_list5();return 0;
}

第二章:list的模拟实现

2.1 模拟实现list

List Class.h

#pragma oncenamespace bit {//封装目的: 封装成 list_node 结构体是为了将链表节点与具体的操作逻辑分离,保持代码的清晰性和模块化。//在链表实现中多次使用这个结构体来管理节点,而不仅仅是在 list 类里,因此单独封装是合理的做法。template<class T>//(如果使用class封装就会变成私有,后期可能需要友元)struct list_node { //节点结构体。T _data;list_node<T>* _next;list_node<T>* _prev;list_node(const T& x = T())//如果T是自定义类型,则x会是该类型的默认构造对象,取决于该类型的默认构造函数如何定义。:_data(x), _next(nullptr), _prev(nullptr) {}};vector中原生指针符合迭代器需要的场景。但list不行,对list指针解引用得到的是一个节点结构体,而不是该节点储存的数据;且单纯的++也不支持这种操作所以需要对list的迭代器进行封装+运算符重载(即封装节点指针,可以参考日期类的++)将迭代器封装成一个独立的类,使得链表的遍历更加简洁和灵活。迭代器是 STL 容器设计中的重要概念,单独封装可以提高代码的复用性和可维护性。//template<class T>//struct __list_iterator { //迭代器//	typedef list_node<T> Node;//	typedef __list_iterator<T> self;//	Node* _node;//	__list_iterator(Node* node)//		:_node(node) {//	}//	//迭代器不需要析构函数,因为不需要释放资源(即节点)//	//迭代器的赋值和拷贝构造需不需要深拷贝?//	//不需要。迭代器通常只是一个轻量级的指针包装类,它指向的资源归容器管理,迭代器不负责资源的分配或释放。//	//深拷贝的必要性:深拷贝(Deep Copy)通常用于动态分配资源的类,如 new 申请的内存。//	//目的是避免多个对象共享同一块动态内存,导致释放时的重复释放或悬空指针问题。//	self& operator++() { //前置++//		_node = _node->_next;//		return *this;//	}//	self& operator--() { //前置--//		_node = _node->_prev;//		return *this;//	}//	self operator++(int) { //后置++//		self tmp(*this);//		_node = _node->_next;//		return tmp;//	}//	self operator--(int) { //后置--//		self tmp(*this);//		_node = _node->_prev;//		return tmp;//	}//	T& operator*() { return _node->_data; }//	T* operator->() { return &_node->_data; }//提供给自定义类型解引用。自定义类型解引用是 指针->形式,所以返回指针//	bool operator!= (const self& s) { return _node != s._node; }//	bool operator== (const self& s) { return _node == s._node; }//};const迭代器非const迭代器是可读可写,const迭代器是只读const T*(指向的内容不能修改)、T* const(指针本身不能修改,即不能指向其他内容)const迭代器依然要实现++等(即迭代器指针要改变指向),所以模拟的是第一种const迭代器不是非const迭代器加const修饰,那样会造成迭代器本身不能修改。const迭代器是一种单独的类型  //template<class T>//struct __list_const_iterator { //迭代器//	typedef list_node<T> Node;//	typedef __list_const_iterator<T> self;//	Node* _node;//	__list_const_iterator(Node* node)//		:_node(node) {//	}//	//self& operator++() const该写法不对,这里的const修饰指的是this指向的成员不能修改,//	//那么_node就不能被重新赋值,无法向下一节点移动//	self& operator++() { //前置++//		_node = _node->_next;//		return *this;//	}//	self& operator--() { //前置--//		_node = _node->_prev;//		return *this;//	}//	self operator++(int) { //后置++//		self tmp(*this);//		_node = _node->_next;//		return tmp;//	}//	self operator--(int) { //后置--//		self tmp(*this);//		_node = _node->_prev;//		return tmp;//	}//	//const迭代器不能修改指向的内容,但本身可以移动。所以不能改为const T& operator*() const { return _node->_data; }//	const T& operator*() { return _node->_data; }//	const T* operator->() { return &_node->_data; }//  //迭代器通过上面两个运算符重载修改指向内容,所以const迭代器要对他们返回值进行const修饰防止修改//	bool operator!= (const self& s) { return _node != s._node; }//	bool operator== (const self& s) { return _node == s._node; }//};//非const迭代器和const迭代器仅仅只是operator*()和operator->()返回值不同,//又因为同一个类模板,实例化参数不同,就是完全不同的类型。所以两个迭代器通过模板参数列表可以合并为一个。//将非const和const迭代器合并为一个类模板//vector中原生指针符合迭代器需要的场景。//但list不行,对list指针解引用得到的是一个节点结构体,而不是该节点储存的数据//所以需要对list的迭代器进行封装+运算符重载(即封装节点指针,可以参考日期类的//将迭代器封装成一个独立的类,使得链表的遍历更加简洁和灵活。//迭代器是 STL 容器设计中的重要概念,单独封装可以提高代码的复用性和可维护性。template<class T, class Ref, class Ptr>//迭代器是模拟指针的行为struct __list_iterator { //迭代器typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;//迭代器可以理解为节点指针__list_iterator(Node* node)//单参数构造函数支持隐式类型转换,也就是说节点指针也可以隐式类型转换为迭代器:_node(node) {}//迭代器不需要析构函数,因为不需要释放资源(即节点)//迭代器的赋值和拷贝构造需不需要深拷贝?//不需要。迭代器通常只是一个轻量级的指针包装类,它指向的资源归容器管理,迭代器不负责资源的分配或释放。//深拷贝的必要性:深拷贝(Deep Copy)通常用于动态分配资源的类,如 new 申请的内存。//目的是避免多个对象共享同一块动态内存,导致释放时的重复释放或悬空指针问题。self& operator++() { //前置++_node = _node->_next;return *this;}self& operator--() { //前置--_node = _node->_prev;return *this;}self operator++(int) { //后置++self tmp(*this);_node = _node->_next;return tmp;}self operator--(int) { //后置--self tmp(*this);_node = _node->_prev;return tmp;}Ref operator*() { return _node->_data; }Ptr operator->() { return &_node->_data; }//提供给自定义类型解引用。自定义类型解引用是 指针->形式,所以返回指针//非const和const迭代器中只有上面这两个运算符重载的返回值不同bool operator!= (const self& s) { return _node != s._node; }bool operator== (const self& s) { return _node == s._node; }};template<class T>class list { //链表模板typedef list_node<T> Node;public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;//上方并没有实例化迭代器,而是根据list<T>模板的不同类型创建了适用于该类型的迭代器类型别名。这些别名只是便于使用而已。//对迭代器模板的重命名:因为迭代器模板内部因为需要同时兼容非const和const迭代器,所以参数表达更宽泛。//而list类模板内部知道需要使用哪几种迭代器,所以对类模板的参数进行了进一步的限定表达。//typedef __list_const_iterator<T> const_iterator;//非const和const迭代器合并为一个类模板,所以不需要这个//const_iterator begin() const { return const_iterator(_head->_next); }const_iterator begin() const { return _head->_next; }//支持隐式类型转换const_iterator end() const { return _head; }//支持隐式类型转换iterator begin() {//在return iterator(_head->_next); 这一行,_head->_next是list_node<T>*类型的指针,代表链表中第一个有效节点。//iterator(_head->_next) 会通过 __list_iterator 构造函数将这个指针传递给 _node 成员。//然后返回一个 iterator 对象,该对象持有一个指向节点的指针。//return iterator(_head->_next);return _head->_next;}iterator end() { return _head; }void empty_init() {_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list() { empty_init(); } //构造函数本质是创建一个哨兵位节点//lt2(lt1)list(const list<T>& lt) { //lt是const对象,需要const迭代器empty_init();//创建(lt2的)哨兵位for (auto e : lt)//遍历lt1每个节点赋值给epush_back(e);//lt2尾插每个e节点}传统写法 lt3 = lt1 思路:释放lt3,将lt1的节点尾插到lt3//list<T>& operator=(const list<T>& lt) {//	if (this != &lt) {//		clear();//		for (auto e : lt)//			push_back(e);//	}//	return *this;//}//现代写法void swap(list<T>& lt) {std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt) {swap(lt);return *this;}~list() {clear();delete _head;//清除哨兵位_head = nullptr;}void clear() {iterator it = begin();//begin指向哨兵位的下一节点,即有效数据的头节点while (it != end())it = erase(it);}//void push_back(const T& x) {//	Node* newnode = new Node(x);//	Node* tail = _head->_prev;//	tail->_next = newnode;//	newnode->_prev = tail;//	newnode->_next = _head;//	_head->_prev = newnode;//}//优化版本 - 复用insertvoid push_back(const T& x) { insert(end(), x); }//end指向哨兵位,在哨兵位之前插入void push_front(const T& x) { insert(begin(), x); }void pop_back() { erase(--end()); }//哨兵位前一节点就是尾结点void pop_front() { erase(begin()); }//list中insert之后迭代器理论上不会失效。因为不涉及扩容,且迭代器依旧指向原先位置。//但库里面有返回值,所以和库里保持一致。//pos是迭代器对象,现在需要使用的是节点指针,所以需要通过迭代器对象来访它自己的成员变量来获取节点指针。iterator insert(iterator pos, const T& x) { Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;//prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;++_size;return iterator(newnode);}iterator erase(iterator pos) { //erase后迭代器失效,因为迭代器指向的节点被释放了,所以返回下一个节点的位置Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;delete cur;prev->_next = next;next->_prev = prev;--_size;return iterator(next);}size_t size() { return _size; }private:Node* _head;size_t _size;//增加该成员避免每次获取size都要遍历一遍链表};void test_list1() {list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);//封装屏蔽底层差异和实现细节//提供统一的访问修改方式list<int>::iterator it = lt.begin();while (it != lt.end()) {cout << *it << " ";++it;}cout << endl;for (auto e : lt)cout << e << " ";cout << endl;}void test_list2() {list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);for (auto e : lt)cout << e << " ";cout << endl;list<int> lt1(lt);for (auto e : lt1)cout << e << " ";cout << endl;list<int> lt2;lt2.push_back(10);lt2.push_back(20);lt2.push_back(30);lt2.push_back(40);lt2.push_back(50);lt1 = lt2;for (auto e : lt1)cout << e << " ";cout << endl;for (auto e : lt2)cout << e << " ";cout << endl;}struct AA {AA(int a1 = 0, int a2 = 0):_a1(a1), _a2(a2) {}int _a1;int _a2;};void test_list3() {list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));lt.push_back(AA(3, 3));list<AA>::iterator it = lt.begin();while (it != lt.end()) {//cout << *it << " ";//报错。//该*it(AA)是自定义对象,不支持流插入//第一种方式让AA支持流插入,但这种方式怎么分隔两个值有歧义,并且提供这种方式的流插入就不能变了(写死了)//cout << (*it)._a1 << " " << (*it)._a2 << endl;//_a1和_a2是公有的,可以直接访问cout << it->_a1 << " " << it->_a2 << endl;//迭代器模拟的是指针的行为,所以自定义类型指针解引用就是->cout << it.operator->()->_a1 << " " << it.operator->()->_a2 << endl;//根据上面显示调用可知,本应该是it->->_a1。第一个箭头是运算符重载,返回一个指针,第二个箭头才是解引用。//但是这种写法可读性不好,所以编译器特殊处理,省略了一个箭头++it;}cout << endl;}该版本只能打印list<int>//void print_list(const list<int>& lt) {//	list<int>::const_iterator it = lt.begin();//	while (it != lt.end()) {//		cout << *it << " ";//		++it;//	}//	cout << endl;//	for (auto e : lt)//		cout << e << " ";//	cout << endl;//}该版本只能打印listtemplate<class T>//template<typename T>//void print_list(const list<T>& lt) {//	//下方list<T>还没有实例化(list<int>才是实例化)//	//编译器不确定list<T>::const_iterator是静态成员变量还是内嵌类型//	//所以加typename告诉编译器这是一个类型,等类模板(list<T>)实例化再去类里面取//	typename list<T>::const_iterator it = lt.begin();//	while (it != lt.end()) {//		cout << *it << " ";//		++it;//	}//	cout << endl;//}该版本只能打印list//template<typename T>//void print_container(const list<T>& lt) {//	//下方list<T>还没有实例化(list<int>才是实例化)//	//编译器不确定list<T>::const_iterator是静态成员变量还是内嵌类型//	//所以加typename告诉编译器这是一个类型,等类模板(list<T>)实例化再去类里面取//	typename list<T>::const_iterator it = lt.begin();//	while (it != lt.end()) {//		cout << *it << " ";//		++it;//	}//	cout << endl;//}template<typename Container> //Container是一个自定义模板参数void print_container(const Container& con) {typename Container::const_iterator it = con.begin();//Container可以是list<T>、vector<T>while (it != con.end()) {cout << *it << " ";++it;}cout << endl;}void test_list4() {list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);print_container(lt);//vector的push_back涉及深拷贝问题,因为vector需要扩容,//当vector中元素拷贝到新空间时,vector中元素指向的内容也需要拷贝。但list不存在扩容问题。list<string> lt1;lt1.push_back("1111111111111");lt1.push_back("1111111111111");lt1.push_back("1111111111111");lt1.push_back("1111111111111");lt1.push_back("1111111111111");print_container(lt1);vector<string> v;v.push_back("2222222222222");v.push_back("2222222222222");v.push_back("2222222222222");v.push_back("2222222222222");print_container(v);}
}

Test.cpp

#define _CRT_SECURE_NO_WARNIN
#include <iostream>
#include <string>
#include <vector>using namespace std;
#include "List Class.h"int main() {//bit::test_list1();//bit::test_list2();//bit::test_list3();bit::test_list4();return 0;
}

第三章:list与vector的对比

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

vector
list
动态顺序表,一段连续空间
带头结点的双向循环链表
访
支持随机访问,访问某个元素效率 O(1)
不支持随机访问,访问某个元素效率O(N)
任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1)
底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
原生态指针
对原生态指针 ( 节点指针 ) 进行封装
在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使
需要高效存储,支持随机访问,不关心插入删除效率
大量插入和删除操作,不关心随机访问

作业

1. 以下程序输出结果为( )

int main() {int ar[] = { 0,1, 2, 3, 4,  5, 6, 7, 8, 9 };int n = sizeof(ar) / sizeof(int);list<int> mylist(ar, ar + n);list<int>::iterator pos = find(mylist.begin(), mylist.end(), 5);reverse(mylist.begin(), pos);reverse(pos, mylist.end());list<int>::const_reverse_iterator crit = mylist.crbegin();while (crit != mylist.crend()) {cout << *crit << " ";++crit;}cout << endl;
}

A.4 3 2 1 0 5 6 7 8 9
B.0 1 2 3 4 9 8 7 6 5
C.5 6 7 8 9 0 1 2 3 4
D.5 6 7 8 9 4 3 2 1 0

答案:C
分析 : list<int>::iterator pos = find(mylist.begin(), mylist.end(), 5); //找到5的位置
reverse(mylist.begin(), pos);//逆置0 1 2 3 4 为 4 3 2 1 0
reverse(pos, mylist.end()); //逆置5 6 7 8 9 为 9 8 7 6 5
逆置完成之后其数据为:4 3 2 1 0 9 8 7 6 5
list<int>::const_reverse_iterator crit = mylist.crbegin(); //反向迭代器进行反向访问
while (crit != mylist.crend()) {}
所以答案为:5 6 7 8 9 0 1 2 3 4

2. 以下代码实现了从表中删除重复项的功能,请选择其中空白行应填入的正确代码(   )

template<typename T>
void removeDuplicates(list<T>& aList) {T curValue;list<T>::iterator cur, p;cur = aList.begin();while (cur != aList.end()) {curValue = *cur;//空白行 1while (p != aList.end()) {if (*p == curValue)	//空白行 2			else			p++;			}}
}

A. p=cur+1;aList.erase(p++);
B.p=++cur; p == cur ? cur = p = aList.erase(p) : p = aList.erase(p);
C.p=cur+1;aList.erase(p);
D.p=++cur;aList.erase(p);

答案:B
分析:迭代p需要迭代不重复节点的下一节点,重要的是cur迭代器需要往下迭代,因此cur需要往前移动,二答案A C的cur都不会改变,空白行2是当需要找到重复值时进行节点删除,当删除时当前迭代器会失效,因此需要将迭代器p往后迭代,所以答案为 B

3. 对于list有迭代器it, 当erase(it)后,说法错误的是(    )

A.当前迭代器it失效
B.it前面的迭代器仍然有效
C.it后面的迭代器失效
D.it后面的迭代器仍然有效

答案:C
分析:删除节点后,只有指向当前节点的迭代器失效了,其前后的迭代器仍然有效,因为底层为不连续空间,只有被删除的节点才会失效, 所以答案为 C

4. 下面程序的输出结果正确的是( )

int main() {int array[] = { 1, 2, 3, 4, 0, 5, 6, 7, 8, 9 };int n = sizeof(array) / sizeof(int);list<int> mylist(array, array + n);auto it = mylist.begin();while (it != mylist.end()) {if (*it != 0)cout << *it << " ";elseit = mylist.erase(it);++it;}return 0;
}

A.1 2 3 4 5 6 7 8 9
B. 1 2 3 4 6 7 8 9
C.程序运行崩溃
D.1 2 3 4 0 5 6 7 8 9

答案:B
分析:程序在使用迭代器取值时,如果不等于0就进行打印,为0时不打印并删除当前节点,所以答案为 B

5. 下面有关vector和list的区别,描述正确的是(    )

A.两者在尾部插入的效率一样高
B.两者在头部插入的效率一样高
C.两者都提供了push_back和push_front方法
D.两者都提供了迭代器,且迭代器都支持随机访问

答案:A
A.vector在尾部插入数据不需要移动数据,list为双向循环链表也很容易找到尾部,因此两者在尾部插入数据效率相同
B.vector头部插入效率极其低,需要移动大量数据
C.vector由于在头部插入数据效率很低,所以没有提供push_front方法
D.list不支持随机访问

6. 下面有关vector和list的区别,描述错误的是(    )

A.vector拥有一段连续的内存空间,因此支持随机存取,如果需要高效的随机读取,应该使用vector
B.list拥有一段不连续的内存空间,如果需要大量的插入和删除,应该使用list
C.vector<int>::iterator支持“+”、“+=”、“<”等操作符
D.list<int>::iterator则不支持“+”、“+=”、“<”等操作符运算,但是支持了[]运算符

答案:D
A.如果想大量随机读取数据操作,vector是首选的容器
B.如果想大量的插入和删除数据,list效率较高,是首选
C.由于vector底层是连续空间,其迭代器就是相应类型的指针,所以支持对应的操作
D.list迭代器不支持[]运算符


http://www.ppmy.cn/embedded/167790.html

相关文章

性能测试丨App端WebView性能优化分析

在移动应用开发中&#xff0c;WebView 是一个常用的控件&#xff0c;用于在应用中嵌入网页内容。然而&#xff0c;WebView 的性能问题可能会影响用户体验。以下是对 App 端 WebView 控件性能分析的几个关键点&#xff1a; 1. 加载时间 首次加载时间&#xff1a;WebView 首次加…

DeepSeek 与网络安全:AI 在网络安全领域的应用与挑战

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 在当今数字化时代&#xff0c;网络安全已成为国家、企业和个人面临的重要挑战。从传统的病毒、木马攻击&#xff0c;到高…

PMP项目管理—整合管理篇—2.制定项目管理计划

文章目录 基本信息概述4W1HITTO输入工具与技术输出 项目管理计划分项管理计划其他组件项目基准 基本信息 概述 项目管理计划确定项目的执行、监控和收尾方式&#xff0c;其内容会因项目所在的应用领域和复杂程度而异。项目管理计划可以是概括或详细的。项目管理计划应足够强大…

指针解剖学:穿透C/C++内存操作的核心密码与避坑指南

一、指针的本质与内存模型 指针是C/C的核心特性&#xff0c;本质是内存地址的变量化表示。每个变量在内存中占据连续的字节空间&#xff0c;地址是内存单元的唯一编号&#xff08;如0x0028FF40&#xff09;。指针变量存储的是目标数据的首地址&#xff0c;通过地址间接操作数据…

【嵌入式Linux应用开发基础】网络编程(4):UDP协议

目录 一、UDP 协议概述 二、UDP 协议特点 三、UDP协议的字段格式 四、UDP协议的数据传输过程 五、嵌入式UDP编程核心API 六、UDP 在嵌入式 Linux 中的编程实现 6.1 UDP 服务器代码示例 6.2 UDP 客户端代码示例 七、UDP 协议的应用场景 八、UDP 协议的优缺点 8.1 优点…

【python随手记】——读取文本文件内容转换为json格式

文章目录 前言一、TXT文件转换为JSON数组1.txt文件内容2.python代码3.输出结果 二、TXT文件转换为JSON对象1.txt文件2.python代码3.输出结果 前言 场景&#xff1a;用于读取包含空格分隔数据的TXT文件&#xff0c;并将其转换为结构化JSON文件 一、TXT文件转换为JSON数组 1.tx…

AcWing 蓝桥杯集训·每日一题2025·密接牛追踪2

密接牛追踪2 农夫约翰有 N 头奶牛排成一排&#xff0c;从左到右依次编号为 1∼N。 不幸的是&#xff0c;有一种传染病正在蔓延。 最开始时&#xff0c;只有一部分奶牛受到感染。 每经过一个晚上&#xff0c;受感染的牛就会将病毒传染给它左右两侧的牛&#xff08;如果有的话…

(Qt) QThread 之 moveToThread

文章目录 &#x1f9f5;前言&#x1f9f5;QObject::moveToThread&#x1f5d2;️Code&#x1f5d2;️moveToThread 的基础使用&#x1f5d2;️注意点 &#x1f9f5;QThreadPool&#x1f5d2;️Code&#x1f5d2;️QThreadPool & QRunnable&#x1f5d2;️源码&#xff08;接…