List数据结构的实现
Posted Sunnix
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了List数据结构的实现相关的知识,希望对你有一定的参考价值。
这几天在学习数据结构(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的基石--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的全部元素,测试结果:
按照预定的方式运行了,由于想让代码看起来简洁,没有进行异常的捕捉,也假设测试输入都是正确的。
以上是关于List数据结构的实现的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode203. Remove Linked List Elements
数据结构 已知P指向双向循环链表中的一个结点,其结点结构为datapriornext三个域,写出算法change(p),交换 p所指向的结点和它的前缀结点的顺序。