List数据结构的实现

来源:互联网 发布:古筝曲谱制作软件 编辑:程序博客网 时间:2024/06/08 10:06

这几天在学习数据结构(C++描述)的知识,为了把基础打的牢固一些,想想把一种数据结构自己动手写代码实现以下,感觉是个不错的方法。那样就不会只停留在会用的程度。首先整理一下要实现的山寨list要提供的接口。

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的基石--dnodednode<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,元素个数为0miniList<T>::miniList(){header=new dnode<T>;listSize=0;}template <typename T>//构造函数,生个n个item元素的listminiList<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中只有一个headerheader->next=temp;temp->next=header;header->prev=temp;temp->prev=header;}else if(listSize>0){//元素数不为0,在末端插入值其实就是在header前面插入值,因为list是串起来的环,先找到header的前驱curr=header->prev;//打开header的前驱和header,在中间插入新生成的tempcurr->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当中则返回NULLdnode<T>* miniList<T>::find(const T& item){dnode<T> *temp=header->next;//查找元素,直到找到该元素或者遍历完整个listwhile(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的全部元素,测试结果:

按照预定的方式运行了,由于想让代码看起来简洁,没有进行异常的捕捉,也假设测试输入都是正确的。






0 0
原创粉丝点击