双向循环链表的C++实现
双向循环链表与双向链表相比,多了一对指针,从链表尾的下一个结点指向首元素,以及从链表头的上一个结点指向链表尾。
双向循环链表的结构如图所示:
双向循环链表的结点定义,与双向链表的结点定义一致:
#ifndef SIMPLENODE_HPP
#define SIMPLENODE_HPP
template<typename T> //再定义一个模板 写双链表
class DoubleNode
{
public:
T element;
DoubleNode* prev;
DoubleNode* next;
DoubleNode(const T& theElement,DoubleNode* thePrev=NULL,DoubleNode* theNext=NULL)
:element(theElement),prev(thePrev),next(theNext) {}
//这里已经创建了一个双链表
};
#endif
在组成一个循环双向链表后,还需要补充一些功能,以便后续实现,模仿之前的双向链表:
1.定义这个双向链表类的构造函数及析构函数
2.清空链表内所有元素
3.给出元素位置再返回对应结点
4.返回链表内部元素个数
5.判断这个链表是否为空
6.返回链表首尾的元素值
7.查找元素是否在此链表内,如果在则返回所在位置
8.从首端到尾端输出链表上的各元素,以及反序输出链表上各元素
9.对链表插入元素以及删除元素
双向循环链表的类框架如下:
#ifndef CIRCLEDOUBLELINKLIST_HPP
#define CIRCLEDOUBLELINKLIST_HPP
#include<iostream>
#include"simplenode.hpp"
//这里默认位置pos的开始是从1开始,而不是从0开始
template<class T>
class CircleDoubleLinkList //双链表类的定义
{
private:
DoubleNode<T>* head; //链表头指针
DoubleNode<T>* tail; //链表尾指针
int size; //元素个数
DoubleNode<T>* GetPointAt(int pos)
{...}
public:
CircleDoubleLinkList():head(),tail(),size(0) {}
~CircleDoubleLinkList() {Clear();}
void Clear()
{...}
int Size() {...} //返回元素个数
bool isempty() {...} //返回链表是否为空
//-----------------------------------------------------
T GetHeadVal()
{...}
T GetTailVal()
{...}
int Find(T val)
{...}
void ShowAllVal()
{...}
void ReverseShowAllVal()
{...}
void AddBack(T val)
{
DoubleNode<T>* pNode=new DoubleNode<T>(val);
if (isempty())
{...}
else
{...}
size++;
}
void AddFront(T val)
{
DoubleNode<T>* pNode=new DoubleNode<T>(val);
if (isempty())
{...}
else
{...}
size++;
}
bool AddAt(T val,int pos)
{
DoubleNode<T>* pNode=NULL;
if (pos<=0 || pos>size)
{...}
if (pos==size)
AddBack(size);
else if (pos==1)
AddFront(1);
else
{...}
size++;
return true;
}
bool RemoveBack()
{
return RemoveAt(size);
}
bool RemoveFront()
{
return RemoveAt(1);
}
bool RemoveAt(int pos)
{
DoubleNode<T>* pNode=NULL;
if (isempty())
{...}
if (pos<=0 || pos>size)
{...}
if (size==1)
{
Clear();
}
if (pos==1)
{...}
else
{
DoubleNode<T>* pPreNode=GetPointAt(pos-1);
pNode=pPreNode->next;
if (pos==size)
{...}
else
{...}
delete pNode;
}
size--;
return true;
}
};
#endif
给出类框架后,就该一个个给出对应的实现了。除了部分细节需要修改,其他的与双向链表的实现毫无二致:
1.构造函数与析构函数:
CircleDoubleLinkList():head(),tail(),size(0) {}
~CircleDoubleLinkList() {Clear();}
2.清空链表内所有元素:
void Clear()
{
//从链表头到链表尾的方式逐个删除
const int nums=Size();
if (!isempty())
{
for (int k=1;k<=nums;++k)
{
DoubleNode<T>* temp=head->next;
delete head;
head=temp;
size--;
}
}
//如果链表本来就为空,就没必要再进for循环了
}
3.给出元素位置再返回对应结点:
DoubleNode<T>* GetPointAt(int pos)
{
DoubleNode<T>* pNode=NULL;
if (pos<=0 || pos>size)
std::cout<<"out of range."<<std::endl; //链表当前位置越界,异常
else
{
pNode=head; //当前位置满足条件,则一开始在链表头
for (int i=1;i<=pos-1;++i)
pNode=pNode->next;
}
return pNode;
}
要注意的是,遍历元素位置时避免pNode所获得的值越界,队首元素进不了for循环,改变不了pNode;队尾元素如果设置成i=1;i<=pos;++i,pNode会读取队尾的下一个值,那个值越界。最后就会返回一个未定义的元素。
4.返回首尾端元素、查找元素、正序反序输出链表所有元素、返回元素个数、判断链表是否为空:
T GetHeadVal()
{
if (isempty())
{
std::cout<<"the link list is empty"<<std::endl;
return NULL;
}
return head->element;
}
T GetTailVal()
{
if (isempty())
{
std::cout<<"the link list is empty"<<std::endl;
return NULL;
}
return tail->element;
}
int Find(T val)
{
int pos=1; //从1号位,也就是链表首开始
DoubleNode<T>* findNode=head;
do
{
if (findNode->element==val)
return pos;
findNode=findNode->next;
pos++;
}
while (findNode!=head);
std::cout<<"we can't find it.return -1"<<std::endl;
return -1;
}
void ShowAllVal()
{
DoubleNode<T>* findNode=head;
do
{
std::cout<<findNode->element<<" ";
findNode=findNode->next;
}
while (findNode!=head);
std::cout<<"最后一个元素的下一个元素是"<<findNode->element<<std::endl;
std::cout<<std::endl;
}
void ReverseShowAllVal()
{
DoubleNode<T>* findNode=tail;
do
{
std::cout<<findNode->element<<" ";
findNode=findNode->prev;
}
while (findNode!=tail);
std::cout<<"第一个元素的上一个元素是"<<findNode->element<<std::endl;
std::cout<<std::endl;
}
int Size() {return size;} //返回元素个数
bool isempty() {return size==0?true:false; } //返回链表是否为空
由于此时,链表已成环状,所以要用一个循环进行控制,如果满足条件或者已经遍历一圈,要注意跳出,以免陷入死循环。
5.链表中加入元素:
这里分四种情况讨论:
①插入元素位置越界
②在尾部插入元素
③在头部插入元素
④在其他位置插入元素
若插入元素成功,记得元素个数+1。这里用图示例空链表插入元素,其他情况读者们可以自己试着画图:
代码如下:
void AddBack(T val)
{
DoubleNode<T>* pNode=new DoubleNode<T>(val);
if (isempty())
{
head=pNode;
tail=pNode;
tail->next=head;
head->prev=tail;
}
else
{
tail->next=pNode;
pNode->prev=tail;
tail=pNode;
tail->next=head;
head->prev=tail;
}
size++;
}
void AddFront(T val)
{
DoubleNode<T>* pNode=new DoubleNode<T>(val);
if (isempty())
{
head=pNode;
tail=pNode;
tail->next=head;
head->prev=tail;
}
else
{
head->prev=pNode;
pNode->next=head;
head=pNode;
tail->next=head;
head->prev=tail;
}
size++;
}
bool AddAt(T val,int pos)
{
DoubleNode<T>* pNode=NULL;
if (pos<=0 || pos>size)
{
std::cout<<"out of range"<<std::endl;
return false;
}
if (pos==size)
AddBack(size);
else if (pos==1)
AddFront(1);
else
{
DoubleNode<T>* pPreNode=GetPointAt(pos-1);
pNode=new DoubleNode<T>(val);
pNode->next=pPreNode->next;
pNode->prev=pPreNode;
pPreNode->next->prev=pNode; //..
pPreNode->next=pNode;
}
size++;
return true;
}
6.链表中删除元素:
这里分五种情况讨论:
①链表为空
②删除元素位置越界
③在尾部删除元素
④在头部删除元素
⑤在其他位置删除元素
若删除元素成功,记得元素个数-1。这里用图示例非空链表首部删除元素,其他情况读者们可以自己试着画图:
代码如下:
bool RemoveBack()
{
return RemoveAt(size);
}
bool RemoveFront()
{
return RemoveAt(1);
}
bool RemoveAt(int pos)
{
DoubleNode<T>* pNode=NULL;
if (isempty())
{
std::cout<<"the link list is empty"<<std::endl;
return false;
}
if (pos<=0 || pos>size)
{
std::cout<<"out of range"<<std::endl;
return false;
}
if (size==1)
{
Clear();
}
if (pos==1)
{
pNode=head;
head=head->next;
head->prev=tail;
tail->next=head;
delete pNode;
}
else
{
DoubleNode<T>* pPreNode=GetPointAt(pos-1);
pNode=pPreNode->next;
if (pos==size)
{
pPreNode->next=head;
tail=pPreNode;
head->prev=tail;
tail->next=head;
}
else
{
pPreNode->next=pNode->next;
pNode->next->prev=pPreNode;
}
delete pNode;
}
size--;
return true;
}
将上述实现放入之前所说的类框架后,就形成了自制的简易双向循环链表。
当然了,还有更加具有技术含量的双向循环链表,那就是STL中的<list>。详情可坐等后续博文更新。
参考文献:
4.http://blog.csdn.net/mourinho_my_idol/article/details/12676549