List数据结构的实现
2015-08-01 10:46
531 查看
这几天在学习数据结构(C++描述)的知识,为了把基础打的牢固一些,想想把一种数据结构自己动手写代码实现以下,感觉是个不错的方法。那样就不会只停留在会用的程度。首先整理一下要实现的山寨list要提供的接口。
push_back() 末端插入元素
back() 返回末端元素的值
push_front() 顶端插入元素
front() 返回顶端元素的值
find() 查找特定元素的地址
insert() 特定的位置插入元素
size() 返回list当中元素的个数
begin() 返回一个游标,指向顶端元素
end() 返回一个游标,指向末端元素
实现山寨list,底层的数据结构肯定选双向链表,在任何位置插入元素的复杂度都为o(1),双向链表是由一个个节点链接起来的,节点包括三个成员:指向后继节点的指针、指向前驱节点的指针、当前节点的节点值。先实现这个类:
然后将一个个节点串接起来就形成了双向链表,将双向链表的首位相接,就得到一个list,就像用一个个珠子串起来串成项链一样。实现miniList这个类:
测试一下能不能正常的工作:
按照预定的方式运行了,由于想让代码看起来简洁,没有进行异常的捕捉,也假设测试输入都是正确的。
push_back() 末端插入元素
back() 返回末端元素的值
push_front() 顶端插入元素
front() 返回顶端元素的值
find() 查找特定元素的地址
insert() 特定的位置插入元素
size() 返回list当中元素的个数
begin() 返回一个游标,指向顶端元素
end() 返回一个游标,指向末端元素
实现山寨list,底层的数据结构肯定选双向链表,在任何位置插入元素的复杂度都为o(1),双向链表是由一个个节点链接起来的,节点包括三个成员:指向后继节点的指针、指向前驱节点的指针、当前节点的节点值。先实现这个类:
template <typename T> class dnode{ public: T nodeValue;//节点值 dnode<T> *next;//后继节点 dnode<T> *prev;//前驱节点 dnode(const T& value=T()){ nodeValue=value; next=NULL; prev=NULL; };//构造函数,初始化节点值、前驱和后继节点 };
然后将一个个节点串接起来就形成了双向链表,将双向链表的首位相接,就得到一个list,就像用一个个珠子串起来串成项链一样。实现miniList这个类:
#include "dnode.h" #include <string> using namespace std; template <typename T> class miniList{ public: miniList();/*三个版本的构造函数 /第一个生成一个空list,第二个初始化list为n个item值的list 第三个用[first,last]初始化list*/ miniList(int n,const T& item=T()); miniList(T *first,T *last); ~miniList();//析构函数 /*游标的声明*/ class iterator{ public: iterator();//构造函数,生成一个空游标 iterator(dnode<T> *p);//构造函数,将dnode类型的指针转换为游标 /*重载"=="和"!="运算符,比较两个游标所指的值*/ bool operator ==(const iterator& rhs) const; bool operator !=(const iterator& rhs) const; /*重载"*"运算符,返回游标所指的值*/ T& operator *(); /*重载"++"和"--"运算符,因为这两个运算符有前缀和后缀的形式,所以后缀运算符多加一个 int参数,好让编译器判断用户使用的是哪种形式。游标的本质其实还是指针,所以++或者-- 只是让私有成员node成为自身的后继或者前驱*/ iterator& operator ++(){ node=node->next; return *this; }; iterator& operator ++(int){ iterator temp=*this; node=node->next; return temp; }; iterator& operator --(){ node=node->prev; return *this; }; iterator& operator --(int){ iterator temp=*this; node=node->prev; return temp; }; private: //游标的本质还是指针,指针的类型就是实现list的基石--dnode dnode<T> *node; }; void push_back(const T& item);//在list末端插入值 T& back();//返回list末端的值 void push_front(const T& item);//在list顶端插入值 T& front();//返回list顶端的值 /*在固定的位置插入值,返回新的新节点的地址,因为dnode指针是对外不可见的,所以要和find函数搭配使用*/ dnode<T>* insert(const T& item,dnode<T> *curr); dnode<T>* find(const T& item);//查找一个元素的位置,返回这个元素的指针 int size();//返回当前list的元素个数 typename miniList<T>::iterator begin();//返回一个游标对象,指向list的顶端值 typename miniList<T>::iterator end();//返回一个游标对象,指向list的末端值 private: dnode<T> *header;//头结点 int listSize;//元素个数 }; template <typename T>//构造函数,生成一个空list,元素个数为0 miniList<T>::miniList(){ header=new dnode<T>; listSize=0; } template <typename T>//构造函数,生个n个item元素的list miniList<T>::miniList(int n,const T& item=T()){ header=new dnode<T>(item); dnode<T> *temp;//存储n个item值的动态节点 dnode<T> *curr;//当前节点 curr=header; unsigned int i; for(i=0;i<n;i++){ temp=new dnode<T>(item);//生成一个节点 curr->next=temp;//当前节点的后继节点就是这个新生成的节点 temp->prev=curr;//新节点的前驱节点为当前节点 curr=temp;//更新当前节点,继续生成下一个节点 } curr->next=header;//结束之后将当前节点的后继节点更新为头结点 header->prev=curr;//头结点的前驱节点为当前这个节点,这样整个list就串起来 listSize=n;//元素个数更新为n } template <typename T> miniList<T>::miniList(T *first,T *last){ /*先计算出first到last这段地址的元素数,其他的与上面类似, 区别只是生成新节点时的节点值为当前地址中保存的值*/ int n=last-first; header=new dnode<T>; dnode<T> *temp; dnode<T> *curr; curr=header; unsigned int i; for(i=0;i<n;i++){ temp=new dnode<T>(*first); curr->next=temp; temp->prev=curr; curr=temp; first++; } curr->next=header; header->prev=curr; listSize=n; } template <typename T>//析构函数,遍历整个list,逐个释放内存 miniList<T>::~miniList(){ dnode<T> *curr; curr=header->next; while(curr!=header){ delete curr; curr=curr->next; } delete header; } template <typename T>//在list的末端插入值 void miniList<T>::push_back(const T& item){ dnode<T> *temp,*curr; temp=new dnode<T>(item); if(listSize==0){//表示当前list中只有一个header header->next=temp; temp->next=header; header->prev=temp; temp->prev=header; }else if(listSize>0){ //元素数不为0,在末端插入值其实就是在header前面插入值,因为list是串起来的环,先找到header的前驱 curr=header->prev; //打开header的前驱和header,在中间插入新生成的temp curr->next=temp; temp->next=header; header->prev=temp; temp->prev=curr; } listSize++; } template <typename T> T& miniList<T>::back(){ if(listSize>0)//元素个数不为0,返回末端元素的值,其实就是header前驱节点的值 return header->prev->nodeValue; } template <typename T>//在顶端和在末端插入元素的方法类似,区别只是在顶端插入值时,插入的位置为header和header后继节点的中间。 void miniList<T>::push_front(const T& item){ dnode<T> *curr,*temp; temp=new dnode<T>(item); if(listSize==0){ header->next=temp; temp->prev=header; temp->next=header; header->prev=temp; }else if(listSize>0){ curr=header->next; header->next=temp; temp->next=curr; temp->prev=header; curr->prev=temp; } listSize++; } template <typename T> T& miniList<T>::front(){ if(listSize>0)//元素个数不为0,返回顶端元素,其实就是header后继节点的节点值 return header->next->nodeValue; } template <typename T> int miniList<T>::size(){ return listSize;//返回当前元素的个数 } template <typename T>//返回一个游标,指向顶端元素, typename miniList<T>::iterator miniList<T>::begin(){ return iterator(header->next);//调用iterator的构造函数,将dnode类型的指针转换为游标 } template <typename T> typename miniList<T>::iterator miniList<T>::end(){ return iterator(header);//与begin函数类似,区别是end返回的游标指向list的header,而不是一个具体的元素 } template <typename T>//在固定的位置插入一个值 dnode<T>* miniList<T>::insert(const T& item,dnode<T> *curr){ dnode<T> *tempNode,*tempPrev; tempNode=new dnode<T>(item);//新生成一个节点 tempPrev=curr->prev;//找到插入位置的前驱节点 tempPrev->next=tempNode;//插入这个新节点 tempNode->next=curr; curr->prev=tempNode; tempNode->prev=tempPrev; listSize++; return tempNode; } template <typename T>//查找一个元素,返回它的地址,不在list当中则返回NULL dnode<T>* miniList<T>::find(const T& item){ dnode<T> *temp=header->next; //查找元素,直到找到该元素或者遍历完整个list while(temp->nodeValue!=item&&temp!=header){ temp=temp->next; } if(temp!=header)//找到的该元素,返回它的地址 return temp; return NULL;//遍历完整个list,说明不在list当中,返回NULL } template <typename T>//游标的两个构造函数,第一个生成一个空游标,第二个将dnode类型的指针转换为游标 miniList<T>::iterator::iterator(){ node=NULL; } template <typename T> miniList<T>::iterator::iterator(dnode<T> *p){ node=p; } template <typename T>//重载的运算符,比较两个游标所指元素的元素值 bool miniList<T>::iterator::operator==(const iterator& rhs)const{ return node->nodeValue==rhs.node->nodeValue; } template <typename T> bool miniList<T>::iterator::operator!=(const iterator& rhs)const{ return node->nodeValue!=rhs.node->nodeValue; } template <typename T>//重载的"*"运算符,返回游标所指元素的值 T& miniList<T>::iterator::operator*(){ return node->nodeValue; }
测试一下能不能正常的工作:
void main(){ unsigned int i; int a[]={1,6,2,5,6,7}; miniList<int> list1(a,a+6); miniList<int>::iterator it; dnode<int> *temp; temp=list1.find(2); list1.insert(3,temp); list1.push_back(9); it=list1.begin(); while(it!=list1.end()){ cout<<*it<<" "; it++; } while(1); }先用数组初始化一个list,生成一个空游标,并用list的end函数初始化,找到2的地址,在2前面插入3,在末端插入9,最后输出list的全部元素,测试结果:
按照预定的方式运行了,由于想让代码看起来简洁,没有进行异常的捕捉,也假设测试输入都是正确的。
相关文章推荐
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构揭秘一
- C#中Ilist与list的区别小结
- C#中IList<T>与List<T>的区别深入解析
- C#对list列表进行随机排序的方法
- 数据结构之Treap详解
- Vc++ 控件List Control用法总结
- C++实现的泛型List类分享
- C# Datagridview绑定List方法代码
- JavaScript数据结构和算法之图和图算法
- javascript radio list的实现细节(多浏览器兼容)
- c++ STL容器总结之:vertor与list的应用
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- 大家注意vector, list, set, map成员函数erase
- ASP.NET―001:GridView绑定List、页面返回值具体实现
- List all the Databases on a SQL Server
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈