本文代码为学习总结,如果错误敬请指正!感谢各位大佬 😃
文章目录
循环单链表
循环单链表和单链表的区别在于,表中最后一个结点的指针不是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:2
第1个结点为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!