【数据结构】1.4线性表的链式表示(循环单链表)

本文代码为学习总结,如果错误敬请指正!感谢各位大佬 😃

1.1线性表的顺序表示(顺序表)

1.2线性表的链式表示(单链表)

1.3线性表的链式表示(双链表)

循环单链表

循环单链表和单链表的区别在于,表中最后一个结点的指针不是NULL,而改为指向头结点,从而整个链表形成一个环

在这里插入图片描述

结点定义

//结点定义 
#define MaxSize 50//线性表最大长度 

/*使用 typedef,之后可用于直接定义参数
	如LNode *GetElem (LinkList L)
	  LNode强调返回的是一个节点
	  LinkList强调L是一个单链表 
*/
typedef struct LNode{
	int data; 
	struct LNode *next;
}LNode,*LinkList;

初始化

//(带头结点的空表) 初始化
bool InitList(LinkList &L){
	L=(LinkList)malloc(sizeof(LNode));//动态分配内存 
	if(L==NULL){//内存分配失败 
		return false;
	}
	//不管带不带头结点,头指针始终指向链表的第一个结点
	//而头结点是带头结点的链表中第一个结点,结点内通常不存储信息 
	L->next=L; //头结点next指向头结点 
	//L->next=NULL;//单链表带头结点(若不带头结点,则可表示为L==NULL) 
	return true;
} 

求长度

//求长度 (带头结点 ) 
int Length(LinkList L){
	int len=0;
	LNode *p,*head;
	head=L;
	p=L;
	while(p->next!=head){
		p=p->next;
		len++;
	}
	return len;
}

判断是否为空

//判断循环单链表是否为空
bool Empty(LinkList L){
	if(L->next==L){
		printf("循环单链表为NULL!\n");
		return true;
	}else{
		printf("循环单链表不为NULL!\n");
		return false;
	}
}

判断是否为尾结点

//判断p是否为表尾结点 
bool inTail(LinkList L,LNode *p){
	if(p->next==L){
		return true;
	}else{
		return false;
	}
}

构建链表

头插法(反向)

//头插构建 (反向) 
//头插法的重要作用:链表的逆置 
LinkList List_HeadInsert(LinkList &L){
	printf("头插法构建链表:(以空格间隔、9999结束)\n"); 
	LNode *s;
	int x;
	L=(LinkList)malloc(sizeof(LNode));//创建头结点 
	L->next=L;//初始为空链表(!!!!!!!循环链表一开始指向自己) 
	scanf("%d",&x);//输入结点的值 
	while(x!=9999){//输入9999 表示结束 
		s=(LNode*)malloc(sizeof(LNode));//创建新结点(由系统生成一个LNode型的结点,同时将该节点的起始位置赋给指针变量s) 
		s->data=x;
		s->next=L->next;
		L->next=s;//L为头指针 
		scanf("%d",&x);
	}
	return L;
}

尾插法(正向)

//尾插构建,正向 
LinkList List_TailInsert(LinkList &L){
	printf("尾插法构建链表:(以空格间隔、9999结束)\n"); 
	int x;
	L=(LinkList)malloc(sizeof(LNode));//创建头结点 
	LNode *s,*r=L;//r为表尾指针 
	L->next=L;//初始为空链表((!!!!!!!循环链表一开始指向自己) 
	scanf("%d",&x);//输入结点的值 
	while(x!=9999){//输入9999 表示结束 
		s=(LNode*)malloc(sizeof(LNode));//创建新结点(由系统生成一个LNode型的结点,同时将该节点的起始位置赋给指针变量s) 
		s->data=x;
		r->next=s;
		r=s;//r指向新的表尾结点 
		scanf("%d",&x);
	}
	r->next=L;//尾结点指针指向头结点,即为最后结点
	return L;
}

查找

按值查找

//按值查找(带头结点,头结点可以看为i=0) 
LNode *LocateElem(LinkList L,int e){
	LNode *p,*head;
	head=L;//头结点 
	p=L->next;//指向第一个结点 
	while(p!=head && p->data!=e){//查找数据域为e的结点 
		p=p->next;
	}
	return p; //找到后返回该指针,否则返回NULL 
} 

按位序查找

//按位序查找(带头结点,结点可以看为i=0) 
LNode *GetElem(LinkList L,int i){
	LNode *p,*head;//指针p指向当前扫描到的结点 
	head=L;//头结点 
	printf("head::::::%d\n",head);
	p=L->next;//L指向第一个结点 
	if(p!=head && i<0){
		return NULL;
	}
	if(i==0){
		return L;//头结点 
	} 
	int j=1;//当前p指向的是第几个结点 
	while(p!=head && j<i){//循环找到第i个结点 
		p=p->next;
		j++;
	}
	return p;//返回第i个头结点的指针,若大于表长,则返回NULL 
} 

插入

按结点插入

在结点p后插
//后插,在p结点后插入元素e 
bool InsertNextNode(LNode *p,int e){
	if(p==NULL){//考虑到健壮性 
		return false;
	}
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if(s==NULL){//内存分配失败 
		return false;
	}
	s->data=e;
	s->next=p->next;
	p->next=s;//将s连到p之后 
	return true;
} 
在结点p前插
//前插,在p结点前插入元素e 
bool InsertPriorNode(LNode *p,int e){
	if(p==NULL){
		return false;
	}
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if(s==NULL){
		return false;
	}
	s->next=p->next;
	p->next=s;
	s->data=p->data;
	p->data=e; 
	return true;
} 

按位序插入

//按位序插入,在第i个位置插入元素e(带头结点)
bool ListInsert1(LinkList &L,int i,int e){
	if(i<1 || i>Length(L)+1){
		return false;
	}
	LNode *p=GetElem(L,i-1);//找到第i-1个结点

	return InsertNextNode(p,e); 
}

删除

删除指定位序

//(带头结点)按位序删除
bool ListDelete(LinkList &L,int i,int &e){
	if(i<1 || i>Length(L)){
		return false;
	}
	LNode *p=GetElem(L,i-1);//找到第i-1个结点 
	printf("第%d个结点为%d:",i-1,p->data); 
	if(p==NULL){
		return false;
	}
	if(p->next==L){
		return false;
	}
	LNode *q=p->next;
	e=q->data;
	p->next=q->next;
	free(q);
	return true;
} 

删除指定结点

有bug,当结点为最后一个时,由于交换数据域后删除,释放的是头结点,导致程序出错,欢迎指正

 //删除指定结点p
bool DeleteNode(LinkList L,LNode *p){
	LNode *head=L;
	if(p==NULL){
		return false;
	}
	if(p==head){
		return false;
	}
	LNode *q=p->next;//q指向*p的后继节点 
	p->data=p->next->data;//和后继节点交换数据域 
	p->next=q->next;//将*q结点从链中断开 
	free(q);//释放内存 
	return true;
} 

删除整个链表

//删除整个链表
/*初始条件:表已经存在*/
/*操作结果:删除所有数据元素,将表重置为空表*/
void ClearList(LinkList &L)
{
	LNode *p,*q,*head;
	head=L; //记下头结点位置
	p=L->next;  //从第一个结点开始
	while(p!=head) //当p等于头结点说明已经清空完毕
	{
		q=p;
		p=p->next;
		free(q);
	}
	L=head; //更新尾指针
	L->next=head; //尾指针的后继指针指向头结点
}

打印

打印整个链表

//打印输出链表 
void PrintList(LinkList L){
	LNode *p,*head;
	head=L;
	p=L->next;//指向第一个结点 
	if(p==head){
		printf("这是空链表!\n");
	}
	printf("表长为:%d\n",Length(L)); 
	printf("[head]%d\n",head);
	while(p!=head){
		printf("[%d]%d\n",p,p->data);
		p=p->next;
	} 
} 

打印单个结点

//打印输出结点
void PrintNode(LNode *s){
	printf("查找的结点为:%d\n",s->data);
} 

完整代码及运行结果

代码

#include<stdlib.h>
#include<stdio.h>

//结点定义 
#define MaxSize 50//线性表最大长度 

/*使用 typedef,之后可用于直接定义参数
	如LNode *GetElem (LinkList L)
	  LNode强调返回的是一个节点
	  LinkList强调L是一个单链表 
*/
typedef struct LNode{
	int data; 
	struct LNode *next;
}LNode,*LinkList;

//(带头结点的空表) 初始化
bool InitList(LinkList &L){
	L=(LinkList)malloc(sizeof(LNode));//动态分配内存 
	if(L==NULL){//内存分配失败 
		return false;
	}
	//不管带不带头结点,头指针始终指向链表的第一个结点
	//而头结点是带头结点的链表中第一个结点,结点内通常不存储信息 
	L->next=L; //头结点next指向头结点 
	//L->next=NULL;//单链表带头结点(若不带头结点,则可表示为L==NULL) 
	return true;
} 
 

//求长度 (带头结点 ) 
int Length(LinkList L){
	int len=0;
	LNode *p,*head;
	head=L;
	p=L;
	while(p->next!=head){
		p=p->next;
		len++;
	}
	return len;
}

//判断循环单链表是否为空
bool Empty(LinkList L){
	if(L->next==L){
		printf("循环单链表为NULL!\n");
		return true;
	}else{
		printf("循环单链表不为NULL!\n");
		return false;
	}
}


//判断p是否为表尾结点 
bool inTail(LinkList L,LNode *p){
	if(p->next==L){
		return true;
	}else{
		return false;
	}
}

//头插构建 (反向) 
//头插法的重要作用:链表的逆置 
LinkList List_HeadInsert(LinkList &L){
	printf("头插法构建链表:(以空格间隔、9999结束)\n"); 
	LNode *s;
	int x;
	L=(LinkList)malloc(sizeof(LNode));//创建头结点 
	L->next=L;//初始为空链表(!!!!!!!循环链表一开始指向自己) 
	scanf("%d",&x);//输入结点的值 
	while(x!=9999){//输入9999 表示结束 
		s=(LNode*)malloc(sizeof(LNode));//创建新结点(由系统生成一个LNode型的结点,同时将该节点的起始位置赋给指针变量s) 
		s->data=x;
		s->next=L->next;
		L->next=s;//L为头指针 
		scanf("%d",&x);
	}
	return L;
}

//尾插构建,正向 
LinkList List_TailInsert(LinkList &L){
	printf("尾插法构建链表:(以空格间隔、9999结束)\n"); 
	int x;
	L=(LinkList)malloc(sizeof(LNode));//创建头结点 
	LNode *s,*r=L;//r为表尾指针 
	L->next=L;//初始为空链表((!!!!!!!循环链表一开始指向自己) 
	scanf("%d",&x);//输入结点的值 
	while(x!=9999){//输入9999 表示结束 
		s=(LNode*)malloc(sizeof(LNode));//创建新结点(由系统生成一个LNode型的结点,同时将该节点的起始位置赋给指针变量s) 
		s->data=x;
		r->next=s;
		r=s;//r指向新的表尾结点 
		scanf("%d",&x);
	}
	r->next=L;//尾结点指针指向头结点,即为最后结点
	return L;
}
 
 
//按值查找(带头结点,头结点可以看为i=0) 
LNode *LocateElem(LinkList L,int e){
	LNode *p,*head;
	head=L;//头结点 
	p=L->next;//指向第一个结点 
	while(p!=head && p->data!=e){//查找数据域为e的结点 
		p=p->next;
	}
	return p; //找到后返回该指针,否则返回NULL 
} 

//按位序查找(带头结点,结点可以看为i=0) 
LNode *GetElem(LinkList L,int i){
	LNode *p,*head;//指针p指向当前扫描到的结点 
	head=L;//头结点 
	printf("head::::::%d\n",head);
	p=L->next;//L指向第一个结点 
	if(p!=head && i<0){
		return NULL;
	}
	if(i==0){
		return L;//头结点 
	} 
	int j=1;//当前p指向的是第几个结点 
	while(p!=head && j<i){//循环找到第i个结点 
		p=p->next;
		j++;
	}
	return p;//返回第i个头结点的指针,若大于表长,则返回NULL 
} 


//后插,在p结点后插入元素e 
bool InsertNextNode(LNode *p,int e){
	if(p==NULL){//考虑到健壮性 
		return false;
	}
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if(s==NULL){//内存分配失败 
		return false;
	}
	s->data=e;
	s->next=p->next;
	p->next=s;//将s连到p之后 
	return true;
} 

//前插,在p结点前插入元素e 
bool InsertPriorNode(LNode *p,int e){
	if(p==NULL){
		return false;
	}
	LNode *s = (LNode *)malloc(sizeof(LNode));
	if(s==NULL){
		return false;
	}
	s->next=p->next;
	p->next=s;
	s->data=p->data;
	p->data=e; 
	return true;
} 

//按位序插入,在第i个位置插入元素e(带头结点)
bool ListInsert1(LinkList &L,int i,int e){
	if(i<1 || i>Length(L)+1){
		return false;
	}
	LNode *p=GetElem(L,i-1);//找到第i-1个结点

	return InsertNextNode(p,e); 
}

//(带头结点)按位序删除
bool ListDelete(LinkList &L,int i,int &e){
	if(i<1 || i>Length(L)){
		return false;
	}
	LNode *p=GetElem(L,i-1);//找到第i-1个结点 
	printf("第%d个结点为%d:",i-1,p->data); 
	if(p==NULL){
		return false;
	}
	if(p->next==L){
		return false;
	}
	LNode *q=p->next;
	e=q->data;
	p->next=q->next;
	free(q);
	return true;
} 
 
 //删除指定结点p
bool DeleteNode(LinkList L,LNode *p){
	LNode *head=L;
	if(p==NULL){
		return false;
	}
	if(p==head){
		return false;
	}
	LNode *q=p->next;//q指向*p的后继节点 
	p->data=p->next->data;//和后继节点交换数据域 
	p->next=q->next;//将*q结点从链中断开 
	free(q);//释放内存 
	return true;
} 

//删除整个链表
/*初始条件:表已经存在*/
/*操作结果:删除所有数据元素,将表重置为空表*/
void ClearList(LinkList &L)
{
	LNode *p,*q,*head;
	head=L; //记下头结点位置
	p=L->next;  //从第一个结点开始
	while(p!=head) //当p等于头结点说明已经清空完毕
	{
		q=p;
		p=p->next;
		free(q);
	}
	L=head; //更新尾指针
	L->next=head; //尾指针的后继指针指向头结点
}
 
//打印输出链表 
void PrintList(LinkList L){
	LNode *p,*head;
	head=L;
	p=L->next;//指向第一个结点 
	if(p==head){
		printf("这是空链表!\n");
	}
	printf("表长为:%d\n",Length(L)); 
	printf("[head]%d\n",head);
	while(p!=head){
		printf("[%d]%d\n",p,p->data);
		p=p->next;
	} 
} 

//打印输出结点
void PrintNode(LNode *s){
	printf("查找的结点为:%d\n",s->data);
} 

int main(){
	int i,e;
	bool flag;
	LNode *s;
	LinkList L1,L2,L3;
	InitList(L1);//初始化链表(带头结点) 
	InitList(L2);//初始化链表(带头结点)
	InitList(L3);//初始化链表(带头结点)
	PrintList(L1);//打印输出链表 
	PrintList(L2);//打印输出链表 
	PrintList(L3);//打印输出链表 
	printf("--------------------------------------------------------\n");
	List_HeadInsert(L1);
	Length(L1);
	PrintList(L1);//打印输出链表 
	printf("【按位序插入】\n请输入要插入的位序i:");
	scanf("%d",&i);
	printf("请输入要插入的值e:");
	scanf("%d",&e);
	flag=ListInsert1(L1,i,e);//按位序插入,成功true,失败false 
	if(flag){
		printf("插入成功,插入结点后的链表:\n");
		PrintList(L1);
	}else{
		printf("插入失败!\n");
	}
	printf("--------------------------------------------------------\n");
	List_TailInsert(L2);
	PrintList(L2);//打印输出链表
	printf("长度为:%d\n",Length(L2));
	printf("【按位序删除】\n请输入要删除的位序i:");
	scanf("%d",&i);
	flag=ListDelete(L2,i,e);
	if(flag){
		printf("删除成功,删除的值为:%d\n",e);
		printf("删除结点后的链表:\n");
		PrintList(L2);
	}else{
		printf("删除失败!\n");
	}
	printf("【按值删除】\n请输入要删除的值e:");
	scanf("%d",&e);
	s=LocateElem(L2,e);
	flag=DeleteNode(L2,s);
	if(flag){
		printf("删除成功,删除的值为:%d\n",e);
		printf("删除结点后的链表:\n");
		PrintList(L2);
	}else{
		printf("删除失败!\n");
	}
	PrintList(L2);
	printf("--------------------------------------------------------\n");
	List_TailInsert(L3);//尾插构建链表(顺序) 
	Length(L3);
	PrintList(L3);//打印输出链表 
	printf("【按值查找结点】请输入要查找的值e:");
	scanf("%d",&e);
	s=LocateElem(L3,e);//按值查找,有则返回结点,无则返回NULL
	if(s==NULL || s==L3){
		printf("查找的结点为NULL!\n");
	}else{
		PrintNode(s);//输出该结点的查找结果 
	}
	printf("【按位序查找结点】请输入位序i:");
	scanf("%d",&i);
	s=GetElem(L3,i); //按位序查找 ,有则返回结点,无则返回NULL
	if(s==NULL || s==L3){
		printf("查找的结点为NULL!\n");
	}else{
		PrintNode(s);//输出该结点的查找结果 
	}
}

运行结果1

这是空链表!
表长为:0
[head]136224
这是空链表!
表长为:0
[head]136256
这是空链表!
表长为:0
[head]136288
--------------------------------------------------------
头插法构建链表:(以空格间隔、9999结束)
1 2 3 4 5 9999
表长为:5
[head]159184
[159344]5
[159312]4
[159280]3
[159248]2
[159216]1
【按位序插入】
请输入要插入的位序i:4
请输入要插入的值e:888
插入成功,插入结点后的链表:
表长为:6
[head]159184
[159344]5
[159312]4
[159280]3
[159376]888
[159248]2
[159216]1
--------------------------------------------------------
尾插法构建链表:(以空格间隔、9999结束)
1 2 3 4 5 9999
表长为:5
[head]159408
[159440]1
[159472]2
[159504]3
[159536]4
[136976]5
长度为:5
【按位序删除】
请输入要删除的位序i:21个结点为1:删除成功,删除的值为:2
删除结点后的链表:
表长为:4
[head]159408
[159440]1
[159504]3
[159536]4
[136976]5
【按值删除】
请输入要删除的值e:3
删除成功,删除的值为:3
删除结点后的链表:
表长为:3
[head]159408
[159440]1
[159504]4
[136976]5
--------------------------------------------------------
尾插法构建链表:(以空格间隔、9999结束)
1 2 3 4 5 6 9999
表长为:6
[head]137264
[136912]1
[137232]2
[137104]3
[136432]4
[137136]5
[137040]6
【按值查找结点】请输入要查找的值e:2
查找的结点为:2
【按位序查找结点】请输入位序i:3
查找的结点为:3

运行结果2

这是空链表!
表长为:0
[head]1905696
这是空链表!
表长为:0
[head]1905728
这是空链表!
表长为:0
[head]1905760
--------------------------------------------------------
头插法构建链表:(以空格间隔、9999结束)
1 2 3 4 9999
表长为:4
[head]1928656
[1928784]4
[1928752]3
[1928720]2
[1928688]1
【按位序插入】
请输入要插入的位序i:0
请输入要插入的值e:0
插入失败!
--------------------------------------------------------
尾插法构建链表:(以空格间隔、9999结束)
1 2 3 4 5 9999
表长为:5
[head]1928816
[1928848]1
[1928880]2
[1928912]3
[1928944]4
[1928976]5
长度为:5
【按位序删除】
请输入要删除的位序i:7
删除失败!
【按值删除】
请输入要删除的值e:99
按值查找
删除失败!
--------------------------------------------------------
尾插法构建链表:(以空格间隔、9999结束)
1 2 3 4 5 9999
表长为:5
[head]1929008
[1906640]1
[1906064]2
[1906576]3
[1906096]4
[1906672]5
【按值查找结点】请输入要查找的值e:9
查找的结点为NULL!
【按位序查找结点】请输入位序i:8
查找的结点为NULL
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Selcouther

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值