【 声明:版权所有,转载请标明出处,请勿用于商业用途。 联系信箱:libin493073668@sina.com】
前言:
前面我们已经学会了单链表的动态与静态操作,我们知道了单链表与顺序表各自的优点与缺点,但是单链表还是有缺点的,由于其存储方式的随机性,那么如果我仅仅知道单链表的其中一个结点的地址,那么我该如何寻找它前面那些结点的位置呢?在单链表中我们仅仅只保存了每个结点的下一个结点的指针域,那么一旦我们找到最后的结点了,我们却并不能返回头结点。怎么解决这个问题呢?接下来我们就将要学习循环链表。
注:
本文仅代表博主本人的一些浅显的见解,欢迎大家评论学习,共同创造出一个良好的环境
对于一些问题,博主会尽量为大家解答,但是如果有疑问没有及时回答的,也希望其他热心人心帮忙解决,鄙人不胜感激
循环单链表
1.存储结构
单链的循环链表存储结构和单链表的存储结构是一样的。
所不同的是,最后一个结点的next指向头结点,而不是“空”。
struct LNode
{
ElemType data;
struct LNode *next;
};
typedef struct LNode *LinkList;
2.相关操作
由于循环单链表是根据单链表而来的,相关的一些操作基本也差不多,我们知道循环链表中,最后一个结点的next指向头结点,因此由表尾找到表头很简单,但是若果链表很长,则由表头找到表尾却很费事,因此单循环链表一般设立尾指针而不是头指针,如图所示:
Status InitList_CL(LinkList *L)
{
//操作结果:构造一个空的线性表L
(*L) = (LinkList)malloc(sizeof(struct LNode)); //产生头结点,并使L指向此头结点
if(!(*L)) exit(OVERFLOW); //存储分配失败
(*L)->next = (*L); //指针域指向头结点
return OK;
}
Status DestroyList_CL(LinkList *L)
{
//操作结果:销毁线性表L
LinkList q,p = (*L)->next; //p指向头结点
while(p!=(*L)) //没到表尾
{
q = p->next;
free(p);
p = q;
}
free(*L);
(*L) = NULL;
return OK;
}
Status ClearList_CL(LinkList *L)
{
//初始条件:线性表L已存在。操作结果:将L重置为空表
LinkList p,q;
(*L) = (*L)->next; //L指向头结点
p = (*L)->next; //p指向第一个结点
while(p!=(*L)) //没到表尾
{
q = p->next;
free(p);
p = q;
}
(*L)->next = (*L); //头结点指针域指向自身
return OK;
}
Status ListEmpty_CL(LinkList L)
{
//初始条件:线性表L已存在。
//操作结果:若L为空表,则返回TRUE,否则返回FALSE
return L->next == L;
}
int ListLength_CL(LinkList L)
{
//初始条件:L已存在。
//操作结果:返回L中数据元素个数
int i = 0;
LinkList p = L->next; //p指向头结点
while(p!=L) //没到表尾
{
i++;
p = p->next;
}
return i;
}
Status GetElem_CL(LinkList L,int i,ElemType *e)
{
//操作结果:当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
int j = 1; //初始化,j为计数器
LinkList p = L->next->next; //p指向第一个结点
if(i<=0 || i>ListLength_CL(L)) return ERROR; //第i个元素不存在
while(j<i)
{
//顺指针向后查找,直到p指向第i个元素
p = p->next;
j++;
}
*e = p->data; //取第i个元素
return OK;
}
int LocateElem_CL(LinkList L,ElemType e,Status (*compare)(ElemType,ElemType))
{
//初始条件:线性表L已存在,compare()是数据元素判定函数
//操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。若这样的数据元素不存在,则返回值为0
int i = 0;
LinkList p = L->next->next; //p指向第一个结点
while(p!=L->next)
{
i++;
if(compare(p->data,e)) return i; //满足关系
p = p->next;
}
return 0;
}
Status PriorElem_CL(LinkList L,ElemType cur_e,ElemType *pre_e)
{
//初始条件:线性表L已存在
//操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义 */
LinkList q,p = L->next->next; //p指向第一个结点
q = p->next;
while(q!=L->next) //p没到表尾
{
if(q->data==cur_e)
{
*pre_e = p->data;
return TRUE;
}
p = q;
q = q->next;
}
return FALSE;
}
Status NextElem_CL(LinkList L,ElemType cur_e,ElemType *next_e)
{
//初始条件:线性表L已存在
//操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义 */
LinkList p=L->next->next; //p指向第一个结点
while(p!=L) //p没到表尾
{
if(p->data==cur_e)
{
*next_e = p->next->data;
return TRUE;
}
p = p->next;
}
return FALSE;
}
Status ListInsert_CL(LinkList *L,int i,ElemType e)
{
//操作结果:在L的第i个位置之前插入元素e
LinkList p = (*L)->next,s; //p指向头结点
int j = 0;
if(i<=0 || i>ListLength_CL(*L)+1) return ERROR; //无法在第i个元素之前插入
while(j<i-1) //寻找第i-1个结点
{
p = p->next;
j++;
}
s = (LinkList)malloc(sizeof(struct LNode)); //生成新结点
s->data = e; //插入L中
s->next = p->next;
p->next = s;
if(p==(*L)) (*L) = s; //改变尾结点
return OK;
}
Status ListDelete_CL(LinkList *L,int i,ElemType *e)
{
//操作结果:删除L的第i个元素,并由e返回其值
LinkList p = (*L)->next,q; //p指向头结点
int j = 0;
if(i<=0 || i>ListLength_CL(*L)) return ERROR; //第i个元素不存在
while(j<i-1) //寻找第i-1个结点
{
p = p->next;
j++;
}
q = p->next; //q指向待删除结点
p->next = q->next;
*e = q->data;
if((*L)==q) (*L) = p; //删除的是表尾元素
free(q); //释放待删除结点
return OK;
}
Status ListTraverse_CL(LinkList L,void (*vi)(ElemType))
{
//初始条件:L已存在。
//操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败
LinkList p = L->next->next;
while(p!=L->next)
{
vi(p->data);
p = p->next;
}
printf("\n");
return OK;
}
3.合并操作
循环链表的合并操作比较简单,只需要将一个表的表尾和另一个表的表头相接即可,如图:
void MergeList_CL(LinkList *La,LinkList Lb)
{
//操作结果:将Lb表合并到La表后面,并返回新形成的La表
LinkList p = Lb->next;
Lb->next = (*La)->next; //Lb的尾结点指向La头结点
(*La)->next = p->next; //La的尾结点指向Lb的头结点
free(p);
*La = Lb; //La的尾指针地址变为Lb的尾指针地址
}
4.具体测试
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <algorithm>
#include <climits>
#include <ctype.h>
#include <malloc.h>
#include <limits.h>
#include <stdlib.h>
#include <io.h>
#include <process.h>
using namespace std;
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
typedef int ElemType;
typedef int Status;
struct LNode
{
ElemType data;
struct LNode *next;
};
typedef struct LNode *LinkList;
Status InitList_CL(LinkList *L)
{
//操作结果:构造一个空的线性表L
(*L) = (LinkList)malloc(sizeof(struct LNode)); //产生头结点,并使L指向此头结点
if(!(*L)) exit(OVERFLOW); //存储分配失败
(*L)->next = (*L); //指针域指向头结点
return OK;
}
Status DestroyList_CL(LinkList *L)
{
//操作结果:销毁线性表L
LinkList q,p = (*L)->next; //p指向头结点
while(p!=(*L)) //没到表尾
{
q = p->next;
free(p);
p = q;
}
free(*L);
(*L) = NULL;
return OK;
}
Status ClearList_CL(LinkList *L)
{
//初始条件:线性表L已存在。操作结果:将L重置为空表
LinkList p,q;
(*L) = (*L)->next; //L指向头结点
p = (*L)->next; //p指向第一个结点
while(p!=(*L)) //没到表尾
{
q = p->next;
free(p);
p = q;
}
(*L)->next = (*L); //头结点指针域指向自身
return OK;
}
Status ListEmpty_CL(LinkList L)
{
//初始条件:线性表L已存在。
//操作结果:若L为空表,则返回TRUE,否则返回FALSE
return L->next == L;
}
int ListLength_CL(LinkList L)
{
//初始条件:L已存在。
//操作结果:返回L中数据元素个数
int i = 0;
LinkList p = L->next; //p指向头结点
while(p!=L) //没到表尾
{
i++;
p = p->next;
}
return i;
}
Status GetElem_CL(LinkList L,int i,ElemType *e)
{
//操作结果:当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
int j = 1; //初始化,j为计数器
LinkList p = L->next->next; //p指向第一个结点
if(i<=0 || i>ListLength_CL(L)) return ERROR; //第i个元素不存在
while(j<i)
{
//顺指针向后查找,直到p指向第i个元素
p = p->next;
j++;
}
*e = p->data; //取第i个元素
return OK;
}
int LocateElem_CL(LinkList L,ElemType e,Status (*compare)(ElemType,ElemType))
{
//初始条件:线性表L已存在,compare()是数据元素判定函数
//操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。若这样的数据元素不存在,则返回值为0
int i = 0;
LinkList p = L->next->next; //p指向第一个结点
while(p!=L->next)
{
i++;
if(compare(p->data,e)) return i; //满足关系
p = p->next;
}
return 0;
}
Status PriorElem_CL(LinkList L,ElemType cur_e,ElemType *pre_e)
{
//初始条件:线性表L已存在
//操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义 */
LinkList q,p = L->next->next; //p指向第一个结点
q = p->next;
while(q!=L->next) //p没到表尾
{
if(q->data==cur_e)
{
*pre_e = p->data;
return TRUE;
}
p = q;
q = q->next;
}
return FALSE;
}
Status NextElem_CL(LinkList L,ElemType cur_e,ElemType *next_e)
{
//初始条件:线性表L已存在
//操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义 */
LinkList p=L->next->next; //p指向第一个结点
while(p!=L) //p没到表尾
{
if(p->data==cur_e)
{
*next_e = p->next->data;
return TRUE;
}
p = p->next;
}
return FALSE;
}
Status ListInsert_CL(LinkList *L,int i,ElemType e)
{
//操作结果:在L的第i个位置之前插入元素e
LinkList p = (*L)->next,s; //p指向头结点
int j = 0;
if(i<=0 || i>ListLength_CL(*L)+1) return ERROR; //无法在第i个元素之前插入
while(j<i-1) //寻找第i-1个结点
{
p = p->next;
j++;
}
s = (LinkList)malloc(sizeof(struct LNode)); //生成新结点
s->data = e; //插入L中
s->next = p->next;
p->next = s;
if(p==(*L)) (*L) = s; //改变尾结点
return OK;
}
Status ListDelete_CL(LinkList *L,int i,ElemType *e)
{
//操作结果:删除L的第i个元素,并由e返回其值
LinkList p = (*L)->next,q; //p指向头结点
int j = 0;
if(i<=0 || i>ListLength_CL(*L)) return ERROR; //第i个元素不存在
while(j<i-1) //寻找第i-1个结点
{
p = p->next;
j++;
}
q = p->next; //q指向待删除结点
p->next = q->next;
*e = q->data;
if((*L)==q) (*L) = p; //删除的是表尾元素
free(q); //释放待删除结点
return OK;
}
Status ListTraverse_CL(LinkList L,void (*vi)(ElemType))
{
//初始条件:L已存在。
//操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败
LinkList p = L->next->next;
while(p!=L->next)
{
vi(p->data);
p = p->next;
}
printf("\n");
return OK;
}
void MergeList_CL(LinkList *La,LinkList Lb)
{
//操作结果:将Lb表合并到La表后面,并返回新形成的La表
LinkList p = Lb->next;
Lb->next = (*La)->next; //Lb的尾结点指向La头结点
(*La)->next = p->next; //La的尾结点指向Lb的头结点
free(p);
*La = Lb; //La的尾指针地址变为Lb的尾指针地址
}
Status compare(ElemType c1,ElemType c2)
{
return c1==c2;
}
void visit(ElemType c)
{
printf("%d ",c);
}
int main()
{
LinkList L;
ElemType e;
int j;
Status i;
i=InitList_CL(&L); //初始化单循环链表L
printf("初始化单循环链表L i=%d (1:初始化成功)\n",i);
i=ListEmpty_CL(L);
printf("L是否空 i=%d(1:空 0:否)\n",i);
ListInsert_CL(&L,1,3); //在L中依次插入3,5
ListInsert_CL(&L,2,5);
i=GetElem_CL(L,1,&e);
j=ListLength_CL(L);
printf("L中数据元素个数=%d,第1个元素的值为%d。\n",j,e);
printf("L中的数据元素依次为:");
ListTraverse_CL(L,visit);
PriorElem_CL(L,5,&e); //求元素5的前驱
printf("5前面的元素的值为%d。\n",e);
NextElem_CL(L,3,&e); //求元素3的后继
printf("3后面的元素的值为%d。\n",e);
printf("L是否空 %d(1:空 0:否)\n",ListEmpty_CL(L));
j=LocateElem_CL(L,5,compare);
if(j)
printf("L的第%d个元素为5。\n",j);
else
printf("不存在值为5的元素\n");
i=ListDelete_CL(&L,2,&e);
printf("删除L的第2个元素:\n");
if(i)
{
printf("删除的元素值为%d,现在L中的数据元素依次为:",e);
ListTraverse_CL(L,visit);
}
else
printf("删除不成功!\n");
printf("清空L:%d(1: 成功)\n",ClearList_CL(&L));
printf("清空L后,L是否空:%d(1:空 0:否)\n",ListEmpty_CL(L));
printf("销毁L:%d(1: 成功)\n",DestroyList_CL(&L));
//合并
int n=5,k;
LinkList La,Lb;
InitList_CL(&La);
for(k=1; k<=n; k++)
ListInsert_CL(&La,k,k);
printf("La="); //输出链表La的内容
ListTraverse_CL(La,visit);
InitList_CL(&Lb);
for(k=1; k<=n; k++)
ListInsert_CL(&Lb,1,k*2);
printf("Lb="); //输出链表Lb的内容
ListTraverse_CL(Lb,visit);
MergeList_CL(&La,Lb);
printf("La+Lb="); //输出合并后的链表的内容
ListTraverse_CL(La,visit);
return 0;
}
总结:
循环链表貌似并没有说什么新的东西,就连书本也就是一笔带过,不知道大家是否学到了一点东西呢?
目前我们一直讲的都只是单向的链表,有的同学可能早就有想法了,难道我就不能再在结构体内添加一个指向前一个元素的指针域吗?
答案当然是可以的,这就是我们下一次将要说的双向链表的内容了。