数据结构:链表List的实现与代码分析
跟Vector一样,我自己也写了一个简单的List来进行研究。
这里实现的是双向链表,因为双向链表包含了单向链表的所以功能,所以就没有单独写一个了。
这个双向链表的实现,在创建的时候,就已经有了两个节点,分别是head和tail。这是抽离出来,不存放数据的节点,是为了让程序简化,被称为哨兵节点。
假如不使用哨兵节点,在表头表尾操作的插入删除的时候,有很多要特殊处理的地方。
加了着两个节点,就可以大大简化这些特殊操作。数据上的表头不再试实际代码的表头。
// // List.h 双向链表的实现 // HelloWorld // csdn blog:http://blog.csdn.net/u012175089 // Created by feiyin001 on 17/1/7. // Copyright (c) 2017年 FableGame. All rights reserved. // #ifndef __HelloWorld__List__ #define __HelloWorld__List__ namespace Fable { //双向链表,使用模板 template<typename Object> class List { private: //节点,只在List内部使用,所有在私有区域内声明 struct Node { Object data;//链表内的有效数据 Node* prev;//指向前一个数据 Node* next;//指向下一个数据 Node(const Object& d = Object(), Node* p = nullptr, Node* n = nullptr): data(d),prev(p),next(n){} }; public: //const的迭代器 class const_iterator { public: //默认构造函数 const_iterator():current(nullptr){} //获得Object对象 const Object& operator*()const { return retrieve(); } //自增,前缀自增 const_iterator& operator++() { current = current->next; return *this; } //自增,后缀自增,这里的int只是作为一种标志来使用,并没有什么卵用 const_iterator operator++(int) { const_iterator old = *this;//原来的数据 ++(*this);//调用前缀版本的自增 return old;//返回原来的数据 } //自减,前缀自增 const_iterator& operator--() { current = current->prev; return *this; } //自减,后缀自减,这里的int只是作为一种标志来使用,并没有什么卵用 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); } protected: const List<Object> * theList;//把列表传了进来,作为检测的时候使用的 Node* current;//迭代器所指向的结点 //获得结点的数据,方便给子类使用 Object& retrieve()const { return current->data; } void assertIsValid()const//检测指针是否合法 { if (!theList || !current || current == theList->head)//检测列表为空,当前指针为空,当前在头结点 { throw ("IteratorOutOfBoundsException");//抛出异常,这里应该使用Exception相关类 } } //构造函数 const_iterator(const List<Object>& lst, Node* p):theList(&lst), current(p){} //友元类,方便在List中访问 friend class List<Object>; }; //迭代器 class iterator: public const_iterator { public: iterator(){}//默认构造函数 //*重载 Object& operator*() { //return retrieve();无法通过编译 return this->retrieve();//也可以用域作用符 } //*重载 const Object& operator*()const { return const_iterator::operator*();//调用父类函数 } //自增,前缀 iterator operator++() { //current = current->next;无法通过编译 this->current = this->current->next;//也可以用域作用符 return *this; } //自增,后缀 iterator operator++(int) { iterator old = *this; ++(*this); return old; } //自减,前缀 iterator operator--() { this->current = this->current->prev;//也可以用域作用符 return *this; } //自减,后缀 iterator operator--(int) { iterator old = *this; --(*this); return old; } //==重载 bool operator==(const iterator& rhs) const { return this->current == rhs.current; } //!=重载 bool operator!=(const iterator& rhs)const { return !(*this == rhs); } protected: //构造函数,list内部调用的 iterator(const List<Object>& lst, Node* p) :const_iterator (lst, p){} //友元类 friend class List<Object>; }; public: //默认构造函数 List() { init();//初始化 } //复制构造函数 List(const List& rhs) { init(); *this = rhs; } //析构函数 ~List() { clear();//清空链表 //释放掉两个指针的内存 delete head; delete tail; } //复制赋值运算符 const List& operator=(const List& rhs) { //如果是相同的地址,就不用复制了。 if (this == &rhs) { return *this; } clear();//清空当前的链表 //按顺序一个一个复制进链表中 for (const_iterator iter = rhs.begin(); iter != rhs.end(); ++iter) { push_back(*iter); } return *this; } //获取第一个迭代器 iterator begin() { return iterator(*this, head->next);//head是不存放数据的 } //获取第一个数据的迭代器 const_iterator begin() const { return const_iterator(*this, head->next);//head是不存放数据的 } //获取最后一个迭代器 iterator end() { return iterator(*this, tail);//在STL里面,end返回的是超界地址,而不是最后一个数据的地址,这里是tail } const_iterator end()const { return const_iterator(tail);//在STL里面,end返回的是超界地址,而不是最后一个数据的地址,这里是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());//end()返回的是超界迭代器,所以要自减才能获得最后一个数据 } //在表头插入数据 void push_front(const Object& x) { insert(begin(), x);//因为head和tail都是不放数据的,所以无论在哪里插入,都是指定表中的位置插入数据 } //在表尾插入收据 void push_back(const Object& x) { insert(end(), x); } //弹出最开始的数据 void pop_front() { erase(begin()); } //弹出最后的数据 void pop_back() { erase( --end() ); } //在迭代器的位置插入数据 iterator insert(iterator iter, const Object& x) { iter.assertIsValid();//检测迭代器是否合法 if (iter.theList != this)//检测是否同一个链表的 { throw ("IteratorMismatchException"); } Node* p = iter.current;//当前的指针,会被压后一个 theSize++;//链表大小+1 //p的前一个指针是新的节点,p的原来的前一个指针的后一个指针指向新的节点。 return iterator(*this, p->prev = p->prev->next = new Node(x, p->prev, p));//构建新的指针 } //erase函数,删除迭代器指向的节点,这个很重要,具体的实现,决定了for循环中删除数据的写法。 iterator erase(iterator iter) { Node* p = iter.current; iterator retVal(*this, p->next); p->prev->next = p->next; p->next->prev = p->prev; delete p;//这个写法里面,P指向的节点已经被删除了, theSize--;//减少一 return retVal;//返回的是下一个指针。 } //删除区间内的节点 iterator erase(iterator start, iterator end) { for (iterator iter = start ; iter!= end; ) { iter = erase(iter);//删除之后,返回的是下一个数据,所以这个for循环里面,是不需要iter++的。 } } private: int theSize; Node* head; Node* tail; //初始化,把构造函数里面的内容抽出来 void init() { //创建了两个节点,把节点指针互相指向,这是一个空链表 theSize = 0; head = new Node; tail = new Node; head->next = tail;//指向结尾 tail->prev = head; } }; } #endif /* defined(__HelloWorld__List__) */写的过程中,发现还是有好多好多细节要实现的,其实非常繁琐。
估计还有很多很多地方是没有实现的,例如异常检测,还是少了点。
不过可以大致看到原理。
再写个简单的测试程序:
// // List.cpp // HelloWorld // csdn blog:http://blog.csdn.net/u012175089 // Created by feiyin001 on 17/1/7. // Copyright (c) 2017年 FableGame. All rights reserved. // #include "List.h" #include <iostream> using namespace Fable; int main(int argc, char* argv[]) { List<int> li; for (int i = 0; i < 100; i++) { li.push_back(i); } for ( List<int>::iterator iter = li.begin(); iter != li.end(); ) { if (*iter % 3 == 0) { iter = li.erase(iter); } else { std::cout << *iter << std::endl; ++iter; } } return 0; }
人生如戏,还是戏如人生?微信公众号:传说之路
csdn博客 http://blog.csdn.net/u012175089/article/list/2