带头结点的循环链表实现
先上代码,循环链表的数据结构定义和单链表是相同的。
#include <iostream>
#include <string>
using namespace std;
typedef struct{
int num;
string name;
}ElemType;
typedef struct Node{
ElemType data;
Node* next;
}Node,*CLList;
struct CircleLinkList{
CLList CLL;
int length;
};
下面对上述的数据结构定义进行解释:
- ElemType是根据你所需要的数据类型去进行定义,此处主要是存储学生的学号和姓名信息。你也可以加上char sex,int score等等。
- Node和 ∗ * ∗CLList是对结点进行定义,一个节点包括数据和它指向的下一个节点。Node是一个结点本身,而 ∗ * ∗CLList则是指向结点的指针。如果在此处定义了 ∗ * ∗Node,那么它和 ∗ * ∗CLList的作用是一样的。千万不要因为加了一个 ∗ * ∗就把两个弄混淆。
- CircleLinkList顾名思义就是对循环链表进行定义,包括指向链表的指针和链表的长度。如果前面没有 ∗ * ∗CLList,你也可以定义为Node ∗ * ∗CLL,定义*CLList我个人觉得是为了看起来更直观,一看就知道是链表,而不是结点。
接下来的代码是循环链表的一些功能实现。
- CL:表示循环链表整体(包括链表及其长度)
- CL.CLL:表示循环链表本身(仅包括链表)
- CL.length:表示循环链表长度(仅包括长度)
- CL.CLL->next:表示循环链表中某结点下一个结点,这种形式在下面代码中表示头结点的下一个结点。
- CL.CLL->data.num:表示循环链表中某结点的数据中的学号,这种形式在下面代码中表示头结点的数据中的学号。但我们一般不给头结点的数据赋值。
- CL.CLL->data.name:表示循环链表中某结点的数据中的姓名,这种形式在下面代码中表示头结点的数据中的姓名。但我们一般不给头结点的数据赋值。
void InitCircleLinkList(CircleLinkList &CL){ //初始化
CL.CLL = new Node();
CL.CLL->next = CL.CLL;
CL.length = 0;
}
int CircleLinkList_Length(CircleLinkList CL){ //获取链表长度
return CL.length;
}
int CreateCircleLinkList_head(CircleLinkList &CL,int n){ //头插法创建循环链表
if(n > 0){
cout << "请输入学生的学号和姓名:" << endl;
for(int i = 0;i < n;i++){
Node* p = new Node();
cin >> p->data.num >> p->data.name;
p->next = CL.CLL->next;
CL.CLL->next = p;
CL.length++;
}
return 1;
}
return 0;
}
int CreateCircleLinkList_tail(CircleLinkList &CL,int n){ //尾插法创建循环链表
if(n > 0){
cout << "请输入学生的学号和姓名:" << endl;
Node* temp = CL.CLL;
for(int i = 0;i < n;i++){
Node* p = new Node();
cin >> p->data.num >> p->data.name;
p->next = CL.CLL;
temp->next = p;
temp = p;
CL.length++;
}
return 1;
}
return 0;
}
void Insert_CircleLinkList(CircleLinkList &CL,int i){ 在i位置插入数据项
if(i > 0 && i <= CL.length+1){
Node* p = CL.CLL->next;
if(i == 1){
Node* q = new Node();
cout << "请输入学生的学号和姓名:" << endl;
cin >> q->data.num >> q->data.name;
q->next = CL.CLL->next;
CL.CLL->next = q;
CL.length++;
cout << "信息插入成功!" << endl;
}
else{
int j = 1;
while(j < i-1){
p = p->next;
j++;
}
Node* q = new Node();
cout << "请输入学生的学号和姓名:" << endl;
cin >> q->data.num >> q->data.name;
q->next = p->next;
p->next = q;
CL.length++;
cout << "信息插入成功!" << endl;
}
}
else
cout << "信息插入失败!" << endl;
}
void Delete_CircleLinkList(CircleLinkList &CL,int i){ //在i位置删除数据项
if(i > 0 && i <= CL.length){
if(i == 1){
Node *q = CL.CLL->next;
CL.CLL->next = q->next;
delete q;
CL.length--;
cout << "信息删除成功!" << endl;
}
else{
Node* p = CL.CLL->next;
int j = 1;
while(j < i-1){
p = p->next;
j++;
}
Node* q = p->next;
p->next = q->next;
delete q;
CL.length--;
cout << "信息删除成功!" << endl;
}
}
else
cout << "信息删除失败!" << endl;
}
void Print_CircleLinkList(CircleLinkList CL){ //遍历循环链表
Node* p = CL.CLL->next;
int i = 1;
while(p!=CL.CLL){
cout << "第" << i << "名学生信息如下:" << endl;
cout << "学号为:" << p->data.num << endl;
cout << "姓名为:" << p->data.name << endl;
p = p->next;
i++;
}
}
void Clear_CircleLinkList(CircleLinkList &CL){ //清空链表
CL.CLL->next = CL.CLL;
CL.length = 0;
cout << "链表已清空!" << endl;
}
int main(){
CircleLinkList CL;
InitCircleLinkList(CL);
if(CreateCircleLinkList_head(CL,2)){
cout << "链表(头插法)创建成功!" << endl;
}
Print_CircleLinkList(CL);
cout << "链表长度为:" << CircleLinkList_Length(CL) << endl;
Clear_CircleLinkList(CL);
cout << "链表长度为:" << CircleLinkList_Length(CL) << endl;
cout << "-----------------------------------------------------------------" << endl;
if(CreateCircleLinkList_tail(CL,2)){
cout << "链表(尾插法)创建成功!" << endl;
}
Print_CircleLinkList(CL);
cout << "链表长度为:" << CircleLinkList_Length(CL) << endl;
cout << "-----------------------------------------------------------------" << endl;
Insert_CircleLinkList(CL,3);
Print_CircleLinkList(CL);
cout << "链表长度为:" << CircleLinkList_Length(CL) << endl;
cout << "-----------------------------------------------------------------" << endl;
Delete_CircleLinkList(CL,3);
Print_CircleLinkList(CL);
cout << "链表长度为:" << CircleLinkList_Length(CL) << endl;
system("pause");
return 0;
}
为了保证代码的完整性,我就先放代码再给大家进行解释了。刚开始写,很多格式上,逻辑上都有问题,欢迎小伙伴们指正~一起进步噢。大家可以根据代码去看下面的图,希望能够帮助大家更好的理解。
循环链表的变化主要是尾结点的next不是NULL,而是指向头结点。初始化的定义可根据下图来理解。
头插法图解。此处分为3个步骤。
-
首先是创建要插入的结点;
-
然后将新节点的next指向头结点CL的next,注-意时CL->next,而不是CL噢;
-
最后再将CL->next指向新的节点;
尾插法图解:针对尾插法,需要增加一个指针temp使其每次都指向链表的最后一个结点。解决方法就是每次增加一个结点,都让其指向它。分为四个步骤: -
首先是创建新节点,同时创建一个指针temp,让其指向头结点CL;
-
然后将新节点的next域指向temp的next域;
-
接下来将temp的next域指向新节点;
-
最后将temp指向新节点。
在第i个位置进行插入:分为两种情况,一种是在第1个位置进行插入,另一种为其余所有情况。这种情况是根据循环判断的写法决定的。其他所有情况的解决思想是,找到第i-1个结点,然后在第i个位置进行插入。
- 在第一个位置插入(采用头插法),可以翻上去看头插法的图,是一样的操作。
- 其余所有情况。下面举例在第3个位置插入的情况。所下图所示。话就不多说了,思想和头插法是一样的。
删除第i个位置结点:同插入一样也是分为两种情况。一种是删除第1个位置的结点,另一种是其他所有情况。
- 删除第1个位置的情况。如下图。
- 其他所有情况。展示删除第二个位置结点的例子,如下图。
至于遍历和清空链表的思想比较简单,这里就不画图给大家啦!看着图多会有点复杂,谁当初开始都是这样的,加油!最后再附上程序的输入输出图。