单向循环链表的定义
循环链表与单向链表的结构比较类似,不同的地方在于其最后一个节点的指针域并不指向NULL,而是指向了首节点first,这样就构成了一个循环,其结构如下:
从上面也可以看出,其和单向链表的区别就在于最后一个节点的指针域的值不同,并且其还有一个节点作为表头节点,这个节点是的数据是空的。所以,对于单项循环链表的描述和单向链表是一致的,不过在插入等操作上会有不同,下面是其描述代码:
template<class T>//声明链表类
class Link;
template<class T>//创建结点类
class Node{
friend class Link<T>;
private:
Node<T>* next;//指针域
T data;//数据域
Node(T newdata){data=newdata;}//构造方法1
Node(){}//这个构造方法用来创建第一个空节点,即表头节点,这是
}
template<class T>//实现链表类
class Link{
public:
Link();//构造方法
Insert(const int k,const T& x);//向k处插入数据x,k最小值为1
void Delete(T& x);//删除节点x
void Browser();//浏览链表中的所有数据
int Length();//获取链表长度
void Reserve();//反转链表
private:
Node<T>* first=NULL;
}
构造方法的实现
与单向链表的构造方法不同,因为单向链表可以不需要表头,所以其构造方法可以什么都不做;但是循环单向链表必须要有表头,所以在其构造方法中,必须创建一个表头节点,这也是两者在构造方法上的不同。
具体的代码实现如下:
template<class T>
Link<T>::Link(){
Node<T>* head = new Node<T>(); //创建表头
first=head; //让first指向表头
head->next=first; //让表头指向first
}
如果用上述构造方法创建一个链表,那么其初始的结构如下所示:
上述结构也表明链表是一个空链表,但是要注意,data域中没有数据。
插入方法的实现
与单向链表的插入不同,循环单链表插入时只能往表头节点的后面插入,由初始结构可知,想完成插入操作,必须先找到要插入位置的前一个节点,然后修改相应指针即可完成操作。图解如下:
下面是代码实现示例:
template<class T>
void Link<T>::Insert(const int k,const T& x){
if(k<1 || k>Length()+2){ //插入位置非法
cerr<<"the value of k is too big"<<endl;
throw this;
}
//满足条件,执行插入操作
Node<T>* pt = first;
for(int i=1;i<k;i++){ //将pt移动至插入位置的前一个元素
pt = pt->next;
}
Node<T>* temp = new Node<T>(x); //封装要插入的数据
Node<T>* ptn = pt->next; //保存上一个结点的下一个结点信息
pt->next=temp; //让上一个的下一个结点改为要插入的节点
temp->next=ptn; //让插入的节点的下一个结点为其上一个结点原来的恶下一个结点
}
获取循环链表长度的方法
template<class T>
int Link<T>::Length(){
int length=0;
Node<T>* p = first;
while(p->next!=first){ //如果其下一个结点不是first,长度+1
length++;
p=p->next;
}
return length;
}
浏览链表数据方法的实现
template<class T>
void Link<T>::Browser(){
Node<T>* p = first->next; //指向第一个元素
while(p!=first){ //如果第一个元素有数据,输出其,然后移动p指针
cout<<p->data;
p = p->next;
if(p!=first){
cout<<" -> ";//如果p不是最后一个元素,添加“->”符号
}
}
cout<<endl;
}
删除操作的实现
以下面一个循环链表为例,删除“2”节点的操作:
template<class T>
void Link<T>::Delete(const T& x){
Node<T>* pr=first;
Node<T>* middle=NULL;
Node<T>* right=NULL;
while(pr->next!=first){
middle=pr->next;//让当前结点为pr的下一个
if(middle->data==x){ //找到,删除
right = middle->next;
pr->next=right;
delete middle;
middle = NULL;
break;
}
pr=pr->next;
}
}
对循环链表制作简易迭代器,代码如下:
//获取下一个值
template<class T>
class Iterator{
public:
T Next();
Iterator(Link<T> x);
private:
Node<T>* p=NULL;
Node<T>* temp=NULL;
};
//构造方法
template<class T>
Iterator<T>::Iterator(Link<T> x){
p = x.getFirst();
temp=x.getFirst();
}
//Next方法
template<class T>
T Iterator<T>::Next(){
if(p==temp){
p=p->next;
}
T data = p->data;
p = p->next;
return data;
}
注: 需要在最前面声明Iterator类,然后再将其声明为Node类的友元类。
翻转循环链表的实现
反转循环链表和反转单向链表很相似,可以先将循环链表从表头节点的下一个结点处断开,这样循环链表就成为了单向链表,然后运用反转单链表的方式即可,最后设置其表头节点的下一个结点为最后一个节点即q
template<class T>
void Link<T>::Reserve(){
Node<T>* p = first->next;//指向表头节点的下一个节点
Node<T>* q = first; //即将q节点设为first,这样q会指向最后一个节点,即表头节点
while(p!=first){
Node<T>* temp = q;
q = p;
p = p->next;
q->next = temp;
}
first->next = q;//让表头结点的下一个结点为最后一个节点
}
以上所有方法的测试程序
int main(){
//创建一个构造函数
Link<int> Link1 = Link<int>();
//创建两个变量,用于数据的插入,其中k表示插入的位置,而x表示插入的值
int k=1;
int x=5;
Link1.Insert(k,x); //插入数据
Link1.Browser(); //浏览数据
int length = Link1.Length(); //获取当前长度
k = 2;x=12;
Link1.Insert(k,x);
k = 3;x=15;
Link1.Insert(k,x);
k = 2;x=17;
Link1.Insert(k,x);
Link1.Browser();
cout<<"the Length is "<<Link1.Length()<<endl;
//下一轮测试开始
Link1.Delete(15);
Link1.Browser();
cout<<"Length is "<<length<<endl;
//测试Next方法
Iterator<int>* It1 = new Iterator<int>(Link1);
int data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
//测试Reverse方法
Link1.Reserve();
Link1.Browser();
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
data = It1->Next();
cout<<"the data is "<<data<<endl;
cout<<"Execute Successful!";
return 0;
}