1、list的介绍及使用
template < class t, class alloc = allocator<t> > class list;
list的底层是带头双向链表结构,双向链表中每个元素存储在独立节点中,在节点中通过指针指向其前一个元素和后一个元素。list与forward_list非常相似,最主要的不同在于forward_list是无头单向链表。
与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;此外list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)。
1.1、构造及赋值重载
explicit list (const allocator_type& alloc = allocator_type()); // 默认构造 explicit list (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type()); // 构造n个val值 template <class inputiterator> list (inputiterator first, inputiterator last, const allocator_type& alloc = allocator_type()); // 迭代器区间构造 list (const list& x); // 拷贝构造 list& operator= (const list& x); // 赋值重载
1.2、迭代器
iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; reverse_iterator rbegin(); reverse_iterator rend(); const_reverse_iterator rbegin() const; const_reverse_iterator rend() const;
例如:
int main() { list<int> lt1; list<int> lt2(10, 2); list<int> lt3(lt2.begin(), lt2.end()); list<int> lt4(lt3); lt1 = lt4; list<int>::iterator it1 = lt1.begin(); while (it1 != lt1.end()) { cout << *it1 << ' '; ++it1; } cout << endl; for (auto e : lt4) { cout << e << ' '; } cout << endl; return 0; }
1.3、空间
bool empty() const; // 判断是否为空 size_type size() const; // 元素个数
例如:
int main() { list<int> lt1; list<int> lt2(10, 2); cout << lt1.empty() << endl; cout << lt2.empty() << endl; cout << lt1.size() << endl; cout << lt2.size() << endl; return 0; }
1.4、访问
reference front(); // 起始元素 const_reference front() const; reference back(); // 末尾元素 const_reference back() const;
例如:
int main() { list<int> lt1(10, 2); lt1.front()++; lt1.back()--; cout << lt1.front() << endl; cout << lt1.back() << endl; return 0; }
1.5、修改
void push_front (const value_type& val); // 头插 void pop_front(); // 头删 void push_back (const value_type& val); // 尾插 void pop_back(); // 尾删 iterator insert (iterator position, const value_type& val); // 插入 iterator erase (iterator position); // 删除 void swap (list& x); // 交换 void resize (size_type n, value_type val = value_type()); // 改变元素的个数 void clear(); // 清空
例如:
int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_back(4); lt.push_front(5); lt.push_front(6); for (auto e : lt) { cout << e << ' '; } cout << endl; lt.resize(10, 9); lt.insert(lt.begin(), 7); for (auto e : lt) { cout << e << ' '; } cout << endl; list<int> lt2(lt); lt.clear(); for (auto e : lt2) { cout << e << ' '; } cout << endl; lt.swap(lt2); for (auto e : lt) { cout << e << ' '; } cout << endl; lt.pop_front(); lt.pop_back(); for (auto e : lt) { cout << e << ' '; } cout << endl; return 0; }
1.6、操作
void sort(); // 排序,默认是升序 template <class compare> void sort (compare comp); // 关于仿函数,后面再说 void reverse(); // 逆置
例如:
int main() { list<int> lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_back(4); lt.push_front(5); lt.push_front(6); lt.reverse(); list<int>::iterator it = lt.begin(); while (it != lt.end()) { cout << *it << ' '; ++it; } cout << endl; list<int> lt2(lt); lt.sort(); for (auto e : lt) { cout << e << ' '; } cout << endl; greater<int> gt; lt2.sort(gt); for (auto e : lt2) { cout << e << ' '; } cout << endl; return 0; }
2、迭代器失效
前面说过,迭代器失效即迭代器所指向的节点的无效。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。例如:
int main() { int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; list<int> lt(array, array + sizeof(array) / sizeof(array[0])); auto it = lt.begin(); while (it != lt.end()) { // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值 lt.erase(it); ++it; } return 0; }
当运行到++it时就会报错。 改为如下即可:
int main() { int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; list<int> lt(array, array + sizeof(array) / sizeof(array[0])); auto it = lt.begin(); while (it != lt.end()) { lt.erase(it++); //或者也可以写成: //it = lt.erase(it); } for (auto e : lt) { cout << e << ' '; } cout << endl; return 0; }
3、list的模拟实现
template<class t> struct list_node { list_node<t>* _prev; list_node<t>* _next; t _data; list_node(const t& x = t()) :_prev(nullptr) ,_next(nullptr) ,_data(x) {} }; // /////////////////////////////////////////////////////////// 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) {} 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; } bool operator!=(const self& s) { return _node != s._node; } bool operator==(const self& s) { return _node == s._node; } }; // ///////////////////////////////////////// template<class t> class list { public: typedef list_node<t> node; // 正向迭代器 typedef __list_iterator<t,t&,t*> iterator; typedef __list_iterator<t,const t&,const t*> const_iterator; // ////////////////////////////////////////////////////// iterator begin() { return _head->_next;//这里也可以写为:iterator(_head->_next); } iterator end() { return _head;//这里也可以写为:iterator(_head); } const_iterator begin() const { return _head->_next; } const_iterator end() const { return _head; } // /////////////////////////////////////////////////////////// void empty_init() { _head = new node; _head->_next = _head; _head->_prev = _head; _size = 0; } list() { empty_init(); } list(const list<t>& lt) { empty_init(); const_iterator it = lt.begin(); while (it != lt.end()) { push_back(*it); ++it; } //像下面这样写也是可以的 /*for (auto e : lt) { push_back(e); }*/ } /*list<t>& operator=(const list<t>& lt) // 传统写法 { if (this != <) { 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; } iterator insert(iterator pos, const t& x) { node* newnode = new node(x); node* cur = pos._node; node* prev = cur->_prev; prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode; ++_size; return iterator(newnode); } void push_front(const t& x) { insert(begin(), x); } void pop_front() { erase(begin()); } void pop_back() { erase(--end()); } ~list() { clear(); delete _head; _head = nullptr; } void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } } iterator erase(iterator pos) { node* cur = pos._node; node* prev = cur->_prev; node* next = cur->_next; delete cur; prev->_next = next; next->_prev = prev; --_size; return iterator(next); } void push_back(const t& x) { node* newnode = new node(x); node* end = _head->_prev; end->_next = newnode; newnode->_prev = end; newnode->_next = _head; _head->_prev = newnode; _size++; } size_t size() { return _size; } private: node* _head; size_t _size; };
4、forward_list介绍与使用
template < class t, class alloc = allocator<t> > class forward_list;
forward_list的底层结构是无头单向链表。
4.1、构造及赋值重载
//默认构造 explicit forward_list (const allocator_type& alloc = allocator_type()); //构造n个val explicit forward_list (size_type n, const value_type& val, const allocator_type& alloc = allocator_type()); //用迭代区间构造 template <class inputiterator> forward_list (inputiterator first, inputiterator last, const allocator_type& alloc = allocator_type()); //拷贝构造 forward_list (const forward_list& fwdlst); //赋值重载 forward_list& operator= (const forward_list& fwdlst);
4.2、迭代器
iterator before_begin() noexcept; // 返回容器第一个元素之前的位置 const_iterator before_begin() const noexcept; iterator begin() noexcept; // 返回第一个元素的位置 const_iterator begin() const noexcept; iterator end() noexcept; // 返回最后一个元素之后的位置 const_iterator end() const noexcept;
例如:
int main() { forward_list<int> f1; forward_list<int> f2(5, 3); forward_list<int> f3(f2.begin(), f2.end()); forward_list<int> f4(f3); f1 = f4; forward_list<int>::iterator it1 = f2.begin(); while (it1 != f2.end()) { cout << *it1 << ' '; ++it1; } cout << endl; for (auto& e : f3) { cout << ++e << ' '; } cout << endl; return 0; }
4.3、容量
bool empty() const noexcept; // 判断是否为空
4.4、访问
reference front(); // 返回第一个元素 const_reference front() const;
4.5、修改
void push_front (const value_type& val); //头插 void pop_front(); // 头删 iterator insert_after ( const_iterator position, const value_type& val ); // 之后插入 iterator erase_after (const_iterator position); // 之后删除 void swap (forward_list& fwdlst); // 交换 void resize (size_type n); // 增大元素个数 void resize (size_type n, const value_type& val); void clear() noexcept; // 清空
例如:
int main() { forward_list<int> f1; f1.push_front(1); f1.push_front(2); f1.push_front(3); f1.push_front(4); f1.push_front(5); cout << f1.empty() << endl; cout << f1.front() << endl; f1.insert_after(f1.before_begin(), 6); forward_list<int>::iterator it1 = f1.begin(); while (it1 != f1.end()) { cout << *it1 << ' '; ++it1; } cout << endl; forward_list<int> f2; f2.resize(10, 5); f1.swap(f2); f2.clear(); for (auto e : f1) { cout << e << ' '; } cout << endl; return 0; }
4.6、操作
void sort(); // 排序,默认为升序 template <class compare> void sort (compare comp); void reverse() noexcept; // 逆置
例如:
int main() { forward_list<int> f1; f1.push_front(5); f1.push_front(4); f1.push_front(3); f1.push_front(5); f1.push_front(2); f1.sort(); for (auto e : f1) { cout << e << ' '; } cout << endl; f1.reverse(); for (auto e : f1) { cout << e << ' '; } cout << endl; return 0; }
5、迭代器的分类
5.1、按功能分类
迭代器按功能分类可以分为正向迭代器和反向迭代器。关于反向迭代器,会在模板进阶部分进行模拟实现。
5.2、按性质分类
迭代器按性质(由容器的底层实现决定的)分类可以分为单向迭代器、双向迭代器以及随机迭代器。
单向迭代器:只支持++,不支持--,例如:forward_list(单链表)。
双向迭代器:支持++,也支持--,例如:list(双向链表)
随机迭代器:支持++,也支持--,还支持+以及-,例如:vector(顺序表)、string(顺序表)以及deque(后面讲)。
例如:算法库中有一个sort模板函数,用来进行排序
template <class randomaccessiterator> void sort (randomaccessiterator first, randomaccessiterator last); template <class randomaccessiterator, class compare> void sort (randomaccessiterator first, randomaccessiterator last, compare comp);
但是该模板函数不能用来对list以及forward_list进行排序,因为该模板函数要求的是传随机迭代器,这也就是为什么,明明算法库中有sort模板函数,但是forward_list以及list中也实现了sort函数的原因。
6、list与vector的对比
vector | list | |
底层结构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
随机访问 | 支持随机访问,访问某个元素效率o(1) | 不支持随机访问,访问某个元素效率为o(n) |
插入和删除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂度为o(n),插入时有可能需要增容,增容会开辟新空间、拷贝元素以及释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不需要搬移元素,时间复杂度为 o(1) |
空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高。 | 底层节点动态开辟,节点容易造成内存碎片,空间利用率低, 缓存利用率低。 |
迭代器 | 原生态指针 | 对原生态指针进行封装 |
迭代器失效 | 在插入元素后,要给迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效。删除后,迭代器需要重新赋值否则会失效。 | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代器失效,其他迭代器不受影响。 |
使用场景 | 需要高效存储,随机访问,不关心插入删除效率。 | 大量插入和删除操作,不关心随机访问。 |
到此这篇关于c++中的list与forward_list介绍与使用的文章就介绍到这了,更多相关c++ list与forward_list内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论