(文章只是为了作者自己可以复习,很少考虑读者能读懂,但还是想发出来分享,见谅)
循环链表 循环链表是另一种形式的链式存储结构。
它的特点是表中最后一个结点的 指针 域指向 头结点 ,整个链表形成一个环。
好的 以下介绍带有尾指针的循环链表 即 操作链表是从链表的最后一个开始操作的。
循环链表的各部分讲解
链表的结点的基本内容
typedef struct ListNode{
ElemType date;//ElemType是double类型
ListNode *next;
}ListNode,*LinkList;
首先是循环链表的建立
其实跟单链表一样 但是是让头结点指向自己。 结构:尾指针->末尾结点->头结点->首元结点
//创建循环链表
Status InitLinkList(LinkList *L){//这个L就是尾指针
*L=(LinkList)malloc(sizeof(Lnode));
if(!(*L)) return ERROR;//ERROR是0 表示建立失败
(*L)->next = (*L); //这个就是让 头结点先指向自己
return OK;
}
得到链表的末尾结点
接着就是得到链表的末尾结点
需要一个结点L_rear 来接受这个尾结点的地址
Status GetRearNode(LinkList *RearNode,LinkList *L){
*RearNode = (*L);
while((*RearNode)->next != (*L)) *RearNode = (*RearNode)->next;
return OK;
}
没啥好解释的 就是循环遍历一遍到最后看能不能找到下一个结点就是头结点的结点,然后把这个结点赋值给RearNode也就是L_rear.
利用头插法在带有尾结点的链表插入结点
void InsertNodeByHead_RearNode(LinkList *L_rear,int n){//利用头插法在带有尾结点的循环链表进行插入
LinkList headnode = (*L_rear)->next;
while(n--){ //这个不用考虑链表是否为空
LinkList p=(LinkList)malloc(sizeof(Lnode));
p->next = (headnode)->next;
(headnode)->next = p;
scanf("%lf",&(p->date));
}
GetRearNode(L_rear,&headnode);}//最后还要让尾结点指向 末尾指针
这个时间的复杂度可以是O(n+m)
利用尾插法在带有尾指针的链表插入结点
//时间复杂度是O(n) //可以看的出 如果用带尾指针的循环链表那就最好用尾插法
void InsertNodeByRear_RearNode(LinkList *L_rear,int n){//利用尾插法在带有尾结点的循环链表进行插入
LinkList Curnode = *L_rear,p;
while(n--){
p=(LinkList)malloc(sizeof(Lnode));
scanf("%lf",&(p->date));
p->next = Curnode->next;
Curnode->next = p;
Curnode = p;
}
*L_rear = Curnode;//这里不要忘记把尾指针再指向末尾结点
}
这个的时间复杂度就是O(n)其实对于带有尾指针的循环链表来说 能用尾插法更好一点!
链表结点的插入(插入指定位置的结点)
//循环链表的插入
//需要判断链表是否为空!
Status ListInsert(LinkList *L,int place,ElemType e){
LinkList Curnode=NULL;
LinkList Tempnode=NULL;
if(place==1){//要对插入在链表的第一个位置特判
for(Curnode=(*L);Curnode->next!=(*L);Curnode=Curnode->next);
//这里不需要判断链表是否是空的
Tempnode=(LinkList)malloc(sizeof(Lnode));
Tempnode->date = e;
Tempnode->next = Curnode->next;
Curnode->next = Tempnode;
*L = Tempnode;
}
else{
int i=1;//如果place大于表的结点数 就在表的尾部插入
for(Curnode=(*L);Curnode->next!=(*L)&&i<place-1;i++,Curnode=Curnode->next){
Tempnode=(LinkList)malloc(sizeof(Lnode));
Tempnode->date = e;
Tempnode->next = Curnode->next;
Curnode->next = Tempnode;
}
}
return OK;
}
遍历
//带有尾指针的循环链表的遍历 自动屏蔽掉了头指针的内容
Status ListTraverse_rearnode(LinkList L_rear){
for(LinkList Curnode=L_rear->next->next;Curnode!=L_rear->next;Curnode=Curnode->next){
printf("%.2lf\n",Curnode->date);
}
return OK;
}
链表的合并
Status Connect(LinkList *Ta,LinkList *Tb){//b接到a的后面
//假设Ta 和 Tb都是非空的单循环链表 且Ta和Tb都是链表的尾指针
LinkList Curnode = (*Ta)->next;
(*Ta)->next = (*Tb)->next->next;
free((*Tb)->next);
(*Tb)->next = Curnode;
*Ta = *Tb;
return OK;
}
最后最后总的代码(包括测试代码)
typedef struct ListNode{
ElemType date;
ListNode *next;
}ListNode,*LinkList;
//创建循环链表
Status InitLinkList(LinkList *L){
*L=(LinkList)malloc(sizeof(Lnode));
if(!(*L)) return ERROR;
(*L)->next = (*L);
return OK;
}
//判断链表为空
Status ListEmpty(LinkList L){
if(L->next==L) return OK;
else return ERROR;
}
Status GetRearNode(LinkList *RearNode,LinkList *L){
*RearNode = (*L); //写这个的时候不禁会产生疑问 什么时候是要用到形参的二级指针?
while((*RearNode)->next != (*L)) *RearNode = (*RearNode)->next;
return OK;
}
//时间复杂度是 O(m+n)
void InsertNodeByHead_RearNode(LinkList *L_rear,int n){//利用头插法在带有尾结点的循环链表进行插入
LinkList headnode = (*L_rear)->next; //这个不太好写!!!!
while(n--){ //这个不用考虑链表是否为空
LinkList p=(LinkList)malloc(sizeof(Lnode));
p->next = (headnode)->next;
(headnode)->next = p;
scanf("%lf",&(p->date));
}
GetRearNode(L_rear,&headnode);
}
//时间复杂度是O(n) //可以看的出 如果用带尾指针的循环链表那就最好用尾插法
void InsertNodeByRear_RearNode(LinkList *L_rear,int n){//利用尾插法在带有尾结点的循环链表进行插入
LinkList Curnode = *L_rear,p;
while(n--){
p=(LinkList)malloc(sizeof(Lnode));
scanf("%lf",&(p->date));
p->next = Curnode->next;
Curnode->next = p;
Curnode = p;
}
*L_rear = Curnode;
}
//循环链表的插入
//需要判断链表是否为空!
Status ListInsert(LinkList *L,int place,ElemType e){
LinkList Curnode=NULL;
LinkList Tempnode=NULL;
if(place==1){//要对插入在链表的第一个位置特判
for(Curnode=(*L);Curnode->next!=(*L);Curnode=Curnode->next);
//这里不需要判断链表是否是空的
Tempnode=(LinkList)malloc(sizeof(Lnode));
Tempnode->date = e;
Tempnode->next = Curnode->next;
Curnode->next = Tempnode;
*L = Tempnode;
}
else{
int i=1;//如果place大于表的结点数 就在表的尾部插入
for(Curnode=(*L);Curnode->next!=(*L)&&i<place-1;i++,Curnode=Curnode->next){
Tempnode=(LinkList)malloc(sizeof(Lnode));
Tempnode->date = e;
Tempnode->next = Curnode->next;
Curnode->next = Tempnode;
}
}
return OK;
}
//带有尾指针的循环链表的遍历 自动屏蔽掉了头指针的内容
Status ListTraverse_rearnode(LinkList L_rear){
for(LinkList Curnode=L_rear->next->next;Curnode!=L_rear->next;Curnode=Curnode->next){
printf("%.2lf\n",Curnode->date);
}
return OK;
}
Status Connect(LinkList *Ta,LinkList *Tb){//b接到a的后面
//假设Ta 和 Tb都是非空的单循环链表 且Ta和Tb都是链表的尾指针
LinkList Curnode = (*Ta)->next;
(*Ta)->next = (*Tb)->next->next;
free((*Tb)->next);
(*Tb)->next = Curnode;
*Ta = *Tb;
return OK;
}
int main()
{
LinkList L,L_rear;
LinkList B,B_rear;
InitLinkList(&L);
GetRearNode(&L_rear,&L);
InsertNodeByRear_RearNode(&L_rear,2);
InitLinkList(&B);
GetRearNode(&B_rear,&B);
InsertNodeByRear_RearNode(&B_rear,3);
Connect(&L_rear,&B_rear);
ListTraverse_rearnode(L_rear);
return 0;
}