赞
踩
目录
list链表的模拟实现,与前面的string和vector相比,略复杂一点:
(1)由于链表节点本身就是一个结构体,包含数据域、指针域。 将节点的相关操作放入节点类进行处理,逻辑更清晰
(2)string和vector的元素在空间上是连续分布的,迭代器++就能指向下一个元素,但list的迭代器不行,它的每个元素在空间上都不连续,要访问下一个节点必须找到当前节点的next,因此list的迭代器必须重写
链表主要模拟实现以下功能:
为了和库里面的list区分开,使用命名空间delia将 list类和库里list隔离开
- namespace delia
- {
- }
节点类的成员变量有3个:
(1)节点值_val
(2)指向前一个节点的指针_prev
(3)指向后一个节点的指针_next
节点无需拷贝构造、赋值运算符重载,由于没有额外申请空间,因此也不需要析构
- template<class T>
- struct _list_node
- {
- //3个成员变量
- T _val;
- _list_node<T>* _next;
- _list_node<T>* _prev;
-
- //构造函数
- _list_node(const T& val = T())
- :_val(val)
- , _prev(nullptr)
- , _next(nullptr)
- {};
- };
由于节点的指针原生行为不满足迭代器定义,在这里,迭代器通过类来封装节点的指针重载运算符,如operator*、operator->、operator++等。用迭代器封装节点指针的思路不关心容器底层结构是数组、链表还是树等数据结构,都封装隐藏了底层细节,可以用一种统一的方式去访问、修改容器。
迭代器分为普通迭代器和const迭代器,对于_list_iterator类要实现两个版本,一个是普通的_list_iterator,另一个是const版本的_list_const_iterator,区别在于:对于两个类中的部分函数有普通函数和const函数之分(如begin( )和end( )),其他并无区别。因为这两个类的大部分代码相似,会造成代码冗余,如何解决呢?
对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。使用
template<class T,class Ref,class Ptr>
类模板就会实例化出来两个类,一个是普通的不带const的T,T&, T*,另一个是带const的T,const T&, const T*,其中Ref是引用,Ptr是指针,该类模板实例化了以下这两个类模板:
- template _list_iterator<T, T&, T*> _iterator;
- template _list_iterator<T, const T&, const T*> const_iterator;
这就解决了需要写两个类的问题。
迭代链表的节点,只需要一个成员变量即节点
- template<class T,class Ref,class Ptr>
- struct _list_iterator//使用_list_iterator类来封装node*
- {
- typedef _list_node<T> node;
- typedef _list_iterator<T,Ref,Ptr> self;
-
- node* _pnode;//成员变量
- }
只需要初始化节点即可
- //构造函数
- _list_iterator(node* pnode)
- :_pnode(pnode)
- {}
拷贝构造、赋值运算符重载、析构函数,编译器默认生成即可
- //解引用,返回左值,是拷贝,因此要用传引用返回
- Ref operator*()
- {
- return _pnode->_val;
- }
- //结构体指针解引用,返回节点值的指针
- Ptr operator->()
- {
- return &_pnode->_val;
- }
- //!=重载
- bool operator!=(const self& s) const
- {
- return _pnode != s._pnode;
- }
- //==重载
- bool operator==(const self& s) const
- {
- return _pnode == s._pnode;
- }
让迭代器走到下一个节点
- //前置++ it.operator(&it)
- self& operator++()
- {
- _pnode = _pnode->next;
- return *this;
- }
- //后置++ 返回++之前的值 it.operator(&it,0)
- self operator++(int)//需构造临时对象,临时对象不能用引用返回,所以self没有加&
- {
- self tmp(*this);
- _pnode = _pnode->_next;
- return tmp;
- }
让迭代器走到前一个节点
- //前置-- it.operator(&it)
- self& operator--()
- {
- _pnode = _pnode->prev;
- return *this;
- }
- //后置-- 返回--之前的值 it.operator(&it,0)
- self operator--(int)//需构造临时对象,临时对象不能用引用返回,所以self没有加&
- {
- self tmp(*this);
- _pnode = _pnode->_prev;
- return tmp;
- }
list的成员只需要一个头节点,通过迭代器访问其他节点元素
- 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;//重命名const迭代器
- private:
- node* _head;
- };
- list()
- {
- _head = new node;//会调_list_node的构造函数
- _head->_next = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
- _head->_prev = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
- }
创建一个空链表,再把参数链表节点的值一个一个尾插进去
- //拷贝构造 lt1(lt)
- list(const list<T>& lt)
- {
- //1.先构造一个只有头节点的空list:创建头节点,头节点的前一个是自己,头节点的后一个是自己
- _head = new node;
- _head->_next = _head;
- _head->_prev = _head;
-
- //2.将lt的元素全部尾插到新链表
- for (const auto& e : lt)
- {
- push_back(e);
- }
- }
- //赋值运算符重载 lt1 = lt 传统写法
- list<T> operator=(const list<T>& lt)
- {
- //链表已存在,只需将节点尾插进去即可
- if(this != lt)
- {
- for (auto& e : lt)
- {
- push_back(e);
- }
- }
- }
- list<T>& operator=(list<T>& lt)
- {
- swap(_head, lt->_head);
- return *this;
- }
其中,swap( )函数的执行过程如下:
- template <class T> void swap ( T& a, T& b )
- {
- T c(a); a=b; b=c;
- }
在执行赋值运算符重载时,会调用拷贝构造函数执行深拷贝,然后再交换两个链表头节点的指针,把this要释放的资源交给临时对象,临时对象出了swap的函数作用域就被this要释放的资源也会被同时释放。
(1)清除所有节点内容
(2)删除头节点,链表就销毁了
- //析构
- ~list()
- {
- clear();//先清除所有节点内容
- delete _head;//再删除头节点
- _head = nullptr;
- }
(1)普通迭代器
- iterator begin()
- {
- return iterator(_head->_next);//头节点不存数据
- }
-
- iterator end()
- {
- return iterator(_head);//尾节点的下一个节点位置即头节点
- }
(2)const迭代器
- const_iterator begin() const
- {
- return const_iterator(_head->_next);//头节点不存数据
- }
-
- const_iterator end() const
- {
- return const_iterator(_head);//尾节点的下一个节点位置即头节点
- }
(1)构造节点
(2)双向带头循环链表插入节点
- //插入节点
- void insert(iterator pos, const T& x)
- {
- assert(pos._pnode);
- node* newnode = new node(x);//构造节点
-
- node* prev = pos._pnode->_prev;//为方便后续操作,给插入位置的前一个节点起了个名字,pos是迭代器对象
-
- //插入节点
- newnode->_next = pos._pnode;
- pos._pnode->_prev = newnode;
- prev->_next = newnode;
- newnode->_prev = prev;
-
- }
(1)删除节点
(2)更新指针指向
- //删除节点
- iterator erase(iterator pos)
- {
- assert(pos._pnode);//判断该位置节点是否存在
- assert(pos != end());//end()是最后一个节点的下一个节点位置,也就是头节点,头节点不能删,需要断言
-
- node* prev = pos._pnode->_prev;//pos位置节点的前一个节点
- node* next = pos._pnode->_next;//pos位置节点的后一个节点
-
- //删除节点
- delete pos._pnode;
- prev->_next = next;
- next->_prev = prev;
-
- return iterator(next);//删除之后pos失效,把下一个位置的迭代器给它
- }
只清除所有节点内容,不能删除头节点,如果删除头节点那么链表就不存在了,是链表的析构函数需要完成的操作
- void clear()
- {
- iterator it = begin();
- while (it != end())
- {
- erase(it++);//erase需要传参迭代器,即位置
- }
- }
头插
- //头插
- void push_front(const T& x)
- {
- insert(begin(), x);
- }
尾插
- //尾插
- void push_back(const T& x)
- {
- insert(end()--, x);
- }
头删
- //头删
- void pop_front()
- {
- erase(begin());
- }
尾删
- //尾删
- void pop_back()
- {
- erase(--end());
- }
判空
- //判空
- bool empty()
- {
- return end() == begin();
- }
求节点个数
- //求节点个数
- size_t size()
- {
- iterator it = begin();
- size_t sz = 0;
- while (it != end())//时间复杂度O(N)
- {
- it++;
- sz++;
- }
-
- return sz;
- }
016-list.h
- #pragma once
- #include<iostream>
- #include<assert.h>
- using namespace std;
-
- namespace delia
- {
- template<class T>
- struct _list_node
- {
- T _val;
- _list_node<T>* _prev;
- _list_node<T>* _next;
-
- _list_node(const T& val = T())
- :_val(val)
- , _prev(nullptr)
- , _next(nullptr)
- {};
- };
-
-
- //节点的指针原生行为不满足迭代器定义,在这里,迭代器通过类来封装节点的指针重载运算符来控制
-
- //对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。
- //使用template<class T,class Ref,class Ptr>类模板就会实例化出来两个类,一个是T,T&, T*的,一个是T,const T&, const T*的
- //template _list_iterator<T, T&, T*> _iterator;
- //template _list_iterator<T, const T&, const T*> const_iterator;
-
- //这样就不需要再去定义一个如下const的迭代器类:
- //template<class T>
- //struct _list_const_iterartor
- //{
- // typedef _list_node<T> node;
- // typedef _list_const_iterartor<T> self;
-
- // node* _pnode;
- //}
- //
-
- template<class T,class Ref,class Ptr>
- struct _list_iterator//使用_list_iterator类来封装node*
- {
- typedef _list_node<T> node;
- typedef _list_iterator<T,Ref,Ptr> self;
-
- node* _pnode;
-
- //构造函数
- _list_iterator(node* pnode)
- :_pnode(pnode)
- {}
-
- //拷贝构造、赋值运算符重载、析构函数,编译器默认生成即可
-
- //解引用,返回左值,是拷贝,因此要用引用返回
- Ref operator*()
- {
- return _pnode->_val;
- }
-
- //结构体指针解引用,返回节点值的引用
- Ptr operator->()
- {
- return &_pnode->_val;
- }
-
- //!=重载
- bool operator!=(const self& s) const
- {
- return _pnode != s._pnode;
- }
-
- //==重载
- bool operator==(const self& s) const
- {
- return _pnode == s._pnode;
- }
-
- //前置++ it.operator(&it)
- self& operator++()
- {
- _pnode = _pnode->_next;
- return *this;
- }
-
- //后置++ 返回++之前的值 it.operator(&it,0)
- self operator++(int)
- {
- self tmp(*this);
- _pnode = _pnode->_next;
- return tmp;
- }
-
- //前置-- it.operator(&it)
- self& operator--()
- {
- _pnode = _pnode->prev;
- return *this;
- }
-
- //后置++ 返回++之前的值 it.operator(&it,0)
- self operator--(int)//临时对象不能用引用返回,所以self没有加&
- {
- self tmp(*this);
- _pnode = _pnode->_prev;
- return tmp;
- }
- };
-
- 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;//重命名const迭代器
-
- //构造函数
- list()
- {
- _head = new node;//会调_list_node的构造函数
- _head->_next = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
- _head->_prev = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
- }
-
- //拷贝构造 lt1(lt)
- list(const list<T>& lt)
- {
- //1.先构造一个只有头节点的空list:创建头节点,头节点的前一个是自己,头节点的后一个是自己
- _head = new node;
- _head->_next = _head;
- _head->_prev = _head;
-
- //2.将lt的元素全部尾插到新链表
- for (const auto& e : lt)
- {
- push_back(e);
- }
- }
-
- 赋值运算符重载 lt1 = lt 传统写法
- //list<T>& operator=(list<T>& lt)
- //{
- // if(this != lt)
- // {
- // for (auto& e : lt)
- // {
- // push_back(e);
- // }
- // }
- //}
-
- //赋值运算符重载 lt1 = lt 现代写法
- list<T>& operator=(list<T>& lt)
- {
- swap(_head, lt._head);
- return *this;
- }
-
- //析构
- ~list()
- {
- clear();
- delete _head;
- _head = nullptr;
- }
-
- iterator begin()
- {
- return iterator(_head->_next);
- }
-
- iterator end()
- {
- return iterator(_head);//尾节点的下一个节点位置即头节点
- }
-
- const_iterator begin() const
- {
- return const_iterator(_head->_next);
- }
-
- const_iterator end() const
- {
- return const_iterator(_head);//尾节点的下一个节点位置即头节点
- }
-
- //插入节点
- void insert(iterator pos, const T& x)
- {
- assert(pos._pnode);
- node* newnode = new node(x);//构造节点
-
- node* prev = pos._pnode->_prev;//为方便后续操作,给插入位置的前一个节点起了个名字,pos是迭代器对象
-
- //插入节点
- newnode->_next = pos._pnode;
- pos._pnode->_prev = newnode;
- prev->_next = newnode;
- newnode->_prev = prev;
-
- }
-
- //删除节点
- iterator erase(iterator pos)
- {
- assert(pos._pnode);//判断该位置节点是否存在
- assert(pos != end());//end()是最后一个节点的下一个节点位置,也就是头节点,头节点不能删,需要断言
-
- node* prev = pos._pnode->_prev;//pos位置节点的前一个节点
- node* next = pos._pnode->_next;//pos位置节点的后一个节点
-
- //删除节点
- delete pos._pnode;
- prev->_next = next;
- next->_prev = prev;
-
- return iterator(next);//删除之后pos失效,把下一个位置的迭代器给它
- }
-
- void clear()
- {
- iterator it = begin();
- while (it != end())
- {
- erase(it++);
- }
- }
-
- //头插
- void push_front(const T& x)
- {
- insert(begin(), x);
- }
-
- //尾插
- void push_back(const T& x)
- {
- insert(end()--, x);
- }
-
- //头删
- void pop_front()
- {
- erase(begin());
- }
-
- //尾删
- void pop_back()
- {
- erase(--end());
- }
-
- //判空
- bool empty()
- {
- return _head->_next == _head;
- }
-
- //求节点个数
- size_t size()
- {
- iterator it = begin();
- size_t sz = 0;
- while (it != end())//时间复杂度O(N)
- {
- it++;
- sz++;
- }
-
- return sz;
- }
- private:
- node* _head;
- };
-
- void PrintList(const list<int>& lt)
- {
- list<int>::const_iterator it = lt.begin();
- while (it != lt.end())
- {
- cout << it._pnode->_val << " ";
- it++;
- }
- cout << endl;
- }
-
- void test_list1()
- {
- list<int> l1;
- l1.push_back(5);
- l1.push_back(8);
- l1.push_back(20);
- l1.push_back(9);
-
- PrintList(l1);
-
- list<int> l2;
- l2 = l1;
- PrintList(l2);
-
- cout << endl;
- }
- }
016-test.cpp
- #include "016-list.h"
- #include<list>
- using namespace std;
-
- int main()
- {
- delia::test_list1();
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。