双向链表List类模板的实现
双向链表List类模板的实现
1.考虑设计时需提供的类
- List类本身,它包含连接到表两端的链、表的大小,以及一些方法。
- Node类,可能是一个私有的内嵌类。一个节点包含数据和指向前后两个节点的两个指针,以及一些适当的构造函数。
- const_iterator类,它抽象了位置的概念,是一个公有的内嵌类。该const_iterator存储一个指向“当前”节点的指针,并提供基本迭代器操作的实现,所有的操作,像=、==、!= 和 ++ 等,均以重载运算符的形式出现。
- iterator类,它抽象了位置的概念,是一个公有的内嵌类。该iterator有着与const_iterator相同的功能,但operator* 返回的是所指项的引用,而不是对该项的常量引用。一个重要的技术问题是,iterator可以用在任何需要const_iterator的例程中,但反之不真。换句话说,iterator是一个const_iterator。
2.List中的附加节点设计
头节点(header node): 在表的前端创建一个附加节点,逻辑上代表开始标志。
尾节点(tail node): 在表的终端创建一个终端标记(endmarker)。
使用这些附加节点的好处在于,它们通过排除一些特殊情况大大地简化了编码过程。例如,如果我们不使用头节点,那么删除第一个节点就变成了一种特殊情形,因为我们在删除期间必须重新安置链表对第一个节点的连接,还因为删除算法一般需要访问正在被删除的节点前面的节点(而没有头节点,则第一个节点就没有在它前面的节点)。
#pragma once #include<algorithm> template<typename Object> class List { private: struct Node { Object element; Node* pre; Node* next; Node(const Object& e=Object{ }, Node* p = nullptr, Node* n = nullptr) :element{e},pre{p},next{n}{} Node(Object&& e, Node*& p = nullptr, Node*& n = nullptr) :element{ std::move(e)}, pre{p}, next{n}{} }; int theSize; Node* head; Node* tail; void init() { theSize = 0; head = new Node; tail = new Node; head->next = tail; tail->pre = head; } public: //List类的内嵌const_iterator类 class const_iterator { protected: Node* current; Object& retrieve()const { return current->element; }; const_iterator(Node*p):current{p}{} friend class List<Object>; //赋予List类访问const_iterator的非公有成员的权力 public: const_iterator():current{nullptr}{} const Object& operator*()const { return retrieve(); } const_iterator& operator++() { //重载前缀自增运算符 current = current->next; return *this; } /** * C++规定,通过为前缀形式指定空参数表而为后缀形式指定(匿名的)单参数int来赋予他它们不同的 * 特征。即,++itr调用零参数的operator++,而itr++调用单参数operator++。可以看出,在存在选用 * 前缀或后缀operator++的许多场合下,前缀形式要比后缀形式更快。 */ const_iterator& operator++(int) { const_iterator old = *this; ++(*this); return old; } bool operator==(const const_iterator& rhs)const { return current == rhs.current; } bool operator!=(const const_iterator& rhs)const { return !(*this == rhs); } }; //List类的嵌套iterator类 class iterator :public const_iterator { protected: iterator(Node*p):const_iterator{p}{} friend class List<Object>; public: iterator(){} Object& operator*() { return const_iterator::retrieve(); } const Object& operator*()const { return const_iterator::operator*(); } iterator& operator++() { this->current = this->current->next; return *this; } iterator& operator++(int) { iterator old = *this; ++(*this); return old; } }; public: List() { init();} List(const List& rhs) { init(); for (auto& x : rhs) push_back(x); } List(List&& rhs) :theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail } { rhs.theSize = 0; rhs.head = rhs.tail = nullptr; } ~List() { clear(); delete head; delete tail; } List& operator=(const List& rhs) { List copy = rhs; std::swap(*this, copy); return *this; } List& operator=(List&& rhs) { std::swap(theSize, rhs.theSize); std::swap(head, rhs.head); std::swap(tail, rhs.tail); return *this; } iterator begin() { return { head->next }; } const_iterator begin()const { return { head->next }; } iterator end() { return tail; } const_iterator end()const { return tail; } int size()const { return theSize;} bool empty()const { return theSize == 0;} void clear() { while (!empty()) pop_front(); } Object& front() { return *begin();} const Object& front()const { return *begin();} Object& back() { return *--end();} const Object& back()const { return *--end();} void push_front(const Object& x) { insert(begin(), x); } void push_front(Object&& x) { insert(begin(), std::move(x)); } void push_back(const Object& x) { insert(end(), x); } void push_back(Object&& x) { insert(end(), std::move(x)); } void pop_front() { erase(begin()); } void pop_back() { erase(--end()); } //获取一个新节点,按顺序修改指针再将新节点插入双向链表 //在itr前插入x iterator insert(iterator itr, const Object& x) { Node* p = itr.current; theSize++; return { p->pre = p->pre->next = new Node{x,p->pre,p} }; } iterator insert(iterator itr, Object&& x) { Node* p = itr.current; theSize++; return { p->pre = p->pre->next = new Node{std::move(x),p->pre,p}}; } //从双向链表中删除由itr处的项 iterator erase(iterator itr) { Node* p = itr.current; iterator retVal{ p->next }; //返回删除后指向该位置的迭代器,原来的itr失效 p->pre->next = p->next; p->next->pre = p->pre; delete p; theSize--; return retVal; } iterator erase(iterator from, iterator to) { for (iterator itr = from; itr != to;) itr = erase(itr); return to; } };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!