目录:
1.单链表;
2.双链表;
3.循环链表;
一:单链表:
目录:
(1).单链表的分类;
(2).单链表的操作;
(3).代码详解;
(4).完整代码展示;
1:单链表的分类
总体上来说,单链表可以分为两大类,一种是带头结点的,另一种是不带头结点的(由于带头结点的链表实用性更强,所以以下讲解和代码均为带头结点的链表);如果分细来讲,链表的插入可以分为两种:头插法和尾插法。
2:单链表的操作
1.单链表的初始化;
2.单链表的头插法;
3.单链表的尾插法;
4.删除元素;
5.遍历链表;
3:代码详解
1.首先应该列出需要用的头文件:
#include<stdio.h>
#include<stdlib.h> //可以用#include<malloc.h>代替
因为需要分配动态存储空间,所以要用到stdlib函数;
2.定义结构体;
typedef struct Node
{
int data;
struct Node *next;
}node;
data为数据域,next为指针域。
3.初始化链表:
node *listcreat()
{
node *list=(node *)malloc(sizeof(node));
list->data=0; //头结点的数据域存储的是该链表中的结点的个数
list->next=NULL;
return list;
}
建立一个返回值为node类型的指针函数,定义一个node类型的list指针并分配内存空间,
list的数据域为该链表的结点数,使头结点的next指向空,并返回该头结点的地址。
4头插法增加一个结点:
void addlist(node *list,int data)
{
node *p=(node *)malloc(sizeof(node));
p->data=data;
p->next=list->next;
list->next=p;
list->data++;
}
建立一个新的结点p,使p的next指向原来list的next指向的位置,并使list的next指向p,list的data加一。
5.尾插法增加一个结点:
void tailadd(node *list,int data)
{
node *p=list->next;
while(p->next!=NULL)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=data;
q->next=p->next;
p->next=q;
list->data++;
}
先使p指向该链表的最后一个结点,增加一个结点,使p的next指向新的结点,list的data加一。
6.删除一个结点:
void deletee(node *list,int n)
{
int flag=1;
node *p=list,*q;
while(p->next!=NULL)
{
q=p;
p=p->next;
if(n==p->data)
{
q->next=p->next;
free(p);
list->data--;
flag=0;
break;
}
}
if(flag)
printf("未找到该数据,请重新输入.\n");
}
这一步的难点为while循环中的q指针和p指针的纠缠,总之,q一直指向p的上一个结点,这样就好理解了。
7.遍历链表:
void print(node *list)
{
node *p=list;
while(p!=NULL)
{
printf("%d->",p->data);
p=p->next;
}
printf("NULL");
}
遍历链表就是输出链表中的所有结点的数据,只需使p指向list然后while循环让p指向链表的下一个节点就OK了。注:输出的第一个数是list->data,也就是链表结点的个数。
8.main函数:
int main()
{
node *list=listcreat();
addlist(list,3);
addlist(list,2);
addlist(list,1);
tailadd(list,4);
tailadd(list,5);
tailadd(list,6);
deletee(list,4);
print(list);
return 0;
}
main函数中对所有函数功能都检查一遍,确保没有错误。
4:完整代码展示:
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}node;
node *listcreat()
{
node *list=(node *)malloc(sizeof(node));
list->data=0; //头结点的数据域存储的是该链表中的结点的个数
list->next=NULL;
return list;
}
void addlist(node *list,int data)
{
node *p=(node *)malloc(sizeof(node));
p->data=data;
p->next=list->next;
list->next=p;
list->data++;
}
void tailadd(node *list,int data)
{
node *p=list->next;
while(p->next!=NULL)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=data;
q->next=p->next;
p->next=q;
list->data++;
}
void deletee(node *list,int n)
{
int flag=1;
node *p=list,*q;
while(p->next!=NULL)
{
q=p;
p=p->next;
if(n==p->data)
{
q->next=p->next;
free(p);
list->data--;
flag=0;
break;
}
}
if(flag)
printf("未找到该数据,请重新输入.\n");
}
void print(node *list)
{
node *p=list;
while(p!=NULL)
{
printf("%d->",p->data);
p=p->next;
}
printf("NULL");
}
int main()
{
node *list=listcreat();
addlist(list,3);
addlist(list,2);
addlist(list,1);
tailadd(list,4);
tailadd(list,5);
tailadd(list,6);
deletee(list,4);
print(list);
return 0;
}
二:双链表:
目录:
(1).双链表与单链表的区别;
(2).双链表的基本操作;
(3).代码详解;
(4).完整代码展示;
1:双链表与单链表的区别:
单链表的每一个结点中只有指向下一个结点的指针,不能进行回溯;
双链表的每一个结点中既有指向下一个结点的元素,也有指向上一个结点的指针,可以快速地找到当结点的前一个结点。
2:双链表的基本操作:
1.链表的初始化;
2.头插法加入一个元素;
3.尾插法加入一个元素;
4.删除一个元素;
5.求表长;
6.遍历链表;
*注:本篇讲解的双链表是有头结点的,无头结点的链表代码操作不同。
3:代码详解:
1.头文件:
#include<stdio.h>
#include<stdlib.h>
2.结构体的定义:
typedef struct Node
{
int data;
struct Node *next;
struct Node *pre;
}node;
next指针指向该节点的下一个结点,pre指针指向该节点的上一个结点。
3.初始化链表:
node *listcreat()
{
node *list=(node *)malloc(sizeof(node));
if(list==NULL)
printf("error\n");
list->data=0; //用来存储单链表的结点数
list->next=NULL;
list->pre=NULL;
return list;
}
初始化时将next和pre指针都指向NULL,并返回头结点。
4.头插法添加元素:
void addlist(node *list,int n)
{
node *p=(node *)malloc(sizeof(node));
p->data=n;
p->next=list->next;
p->pre=list;
list->next=p;
list->data++;
}
注意pre指针的操作。
5.尾插法添加元素:
void addtail(node *list,int n)
{
node *p=list->next;
while(p->next!=NULL)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=n;
q->next=p->next;
q->pre=p;
p->next=q;
list->data++;
}
定义node类型的p,并使p指向链表的最后一个节点,然后进行插入操作,注意pre指针的转换。
6.删除一个结点:
void deletee(Node *list,int n)
{
int flag=1;
Node *p=list,*q;
while(p!=NULL)
{
q=p;
p=p->next;
if(n==p->data){
if(p->next!=NULL)
{
q->next=p->next;
p->next->pre=q;
free(p);
flag=0;
list->data--;
printf("删除成功\n");
break;
}
else
{
free(p);
q->next=NULL;
flag=0;
list->data--;
printf("删除成功\n");
break;
}
}
}
if(flag)
{
printf("删除失败\n");
}
}
定义的p指针必须指向list用来删除第一个结点的数据,while中的需要有一个判断,判断要删除的元素是否在最后一个结点,分两种情况,以确保能删除最后一个结点。这里与单链表不同,因为单链表只需考虑next指针。而双链表不同,它需要考虑两个指针,当删除一个结点时,需要把下一个结点的pre指针指向前一个结点,因为NULL没有pre指针,所以需要分开考虑。
7.求表长:
void lengthlist(node *list)
{
printf("该双链表的长度为:%d\n",list->data);
}
8.遍历链表:
void print(node *list)
{
node *p=list;
while(p!=NULL)
{
printf("%d->",p->data);
p=p->next;
}
printf("NULL\n");
}
同样的,第一个数是链表的结点数,如果不需要,可以把list->next赋值给p,while中的p不能换成p->next,这样会导致最后一个节点的元素不能正常输出。
9.main函数:
int main(void)
{
node *list=listcreat();
addlist(list,3);
addlist(list,2);
addlist(list,1);
addtail(list,4);
addtail(list,5);
addtail(list,6);
deletee(list,1);
lengthlist(list);
print(list);
return 0;
}
main函数对所有功能检查。
4:完整代码展示:
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
struct Node *pre;
}node;
node *listcreat()//双链表的初始化
{
node *list=(node *)malloc(sizeof(node));
if(list==NULL)
printf("error\n");
list->data=0; //用来存储单链表的结点数
list->next=NULL;
list->pre=NULL;
return list;
}
void addlist(node *list,int n)//头插法
{
node *p=(node *)malloc(sizeof(node));
p->data=n;
p->next=list->next;
p->pre=list;
list->next=p;
list->data++;
}
void addtail(node *list,int n)//尾插法
{
node *p=list->next;
while(p->next!=NULL)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=n;
q->next=p->next;
q->pre=p;
p->next=q;
list->data++;
}
void deletee(Node *list,int n)//删除函数
{
int flag=1;
Node *p=list,*q;
while(p!=NULL)
{
q=p;
p=p->next;
if(n==p->data){
if(p->next!=NULL)
{
q->next=p->next;
p->next->pre=q;
free(p);
flag=0;
list->data--;
printf("删除成功\n");
break;
}
else
{
free(p);
q->next=NULL;
flag=0;
list->data--;
printf("删除成功\n");
break;
}
}
}
if(flag)
{
printf("删除失败\n");
}
}
void lengthlist(node *list)//求表长
{
printf("该双链表的长度为:%d\n",list->data);
}
void print(node *list)//遍历
{
node *p=list;
while(p!=NULL)
{
printf("%d->",p->data);
p=p->next;
}
printf("NULL\n");
}
int main(void)
{
node *list=listcreat();
addlist(list,3);
addlist(list,2);
addlist(list,1);
addtail(list,4);
addtail(list,5);
addtail(list,6);
deletee(list,1);
lengthlist(list);
print(list);
return 0;
}
三:循环链表:
目录:
(1).循环链表的定义;
(2).循环链表的分类;
(3).详细代码展示;
(4).完整代码展示;
1:循环链表的定义:
循环链表是另一种形式的链式存储结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。由此,可以从表中任一结点出发均可找到表中其他元素。
循环链表的操作和线性表基本一致,差别仅在于算法中的循环条件不是p或p->next是否指向空,而是它们是否指向头指针。
2:循环链表的分类:
循环链表可以分为单循环链表和双循环链表,接下来,将同时进行两种循环链表的代码详解,可以作比较进而更好的理解。
3:代码详解:
1.头文件:
#include<stdio.h>
#include<stdlib.h>
两种循环链表的头文件一样;
2.结构体定义:
typedef struct Node//单循环链表结构体定义
{
int data;
struct Node *next;
}node;
上方是单循环链表的结构体定义,可以看出结构体有两个成员,分别是data和next指针;
typedef struct node//双循环链表的结构体定义
{
int data;
struct Node *next;
struct Node *pre;
}node;
结构体有三个成员的是双循环链表;
3.初始化:
node *listcreat()//单
{
node *list=(node *)malloc(sizeof(node));
list->data=0;
list->next=list;
return list;
}
同样的定义list头结点,使它的data用来计算结点数,没有其他元素,所以list的next指针指向它本身。
node *listcreat()//双
{
node *list=(node *)malloc(sizeof(node));
list->data=0;
list->next=list;
list->pre=list;
return list;
}
与单循环链表不同,双循环链表多了一个pre指针,因为没有其他元素,使list的next和pre指针都指向本身。
4.头插法添加元素:
void addlist(node *list,int n)//单
{
node *p=(node *)malloc(sizeof(node));
p->data=n;
p->next=list->next;
list->next=p;
list->data++;
}
和单链表的操作一样,不需要考虑表中有没有元素。
void addlist(node *list,int n)//双
{
node *p=(node *)malloc(sizeof(node));
p->data=n;
p->next=list->next;
p->pre=list;
list->next=p;
list->data++;
}
相对于单循环,只是多了一个指针操作,其他原理一样。
5.尾插法添加元素:
void addtail(node *list,int n)//单
{
node *p=list->next;
while(p->next!=list)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=n;
q->next=list;
p->next=q;
list->data++;
}
因为循环,所以while中的条件不能是NULL了,尾插元素时,新插入的元素的next指针应该指向头结点。
void addtail(node *list,int n)//双
{
node *p=list->next;
while(p->next!=list)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=n;
q->next=list;
q->pre=p;
p->next=q;
list->pre=q;
list->data++;
}
双循环链表的尾插时容易忽略的一点是list的pre指针需要指向最后一个结点。
6.删除元素:
void deletee(node *list,int n)//单
{
node *p=list,*q;
while(p->next!=list)
{
q=p;
p=p->next;
if(n==p->data)
{
q->next=p->next;
free(p);
list->data--;
break;
}
}
}
while循环中的条件list。
void detelee(node *list,int n)//双
{
node *p=list,*q;
while(p->next!=list)
{
q=p;
p=p->next;
if(n==p->data)
{
q->next=p->next;
p->pre=q;
free(p);
list->data--;
break;
}
}
}
这里不用考虑需要删除的结点是否位于最后一个,因为最后一个节点的next指向list而不是NULL.
7.遍历链表:
void print(node *list)
{
node *p=list->next;
while(p!=list)
{
printf("%d->",p->data);
p=p->next;
}
printf("NULL\n");
}
单循环双循环的遍历操作都是一样的,注意这里输出的第一个元素并不是头结点的data,定义的p指针只能指向list的next,否则while中的循环无法进行。
8.main函数:
int main()
{
node *list=listcreat();
addlist(list,3);
addlist(list,2);
addlist(list,1);
addtail(list,4);
addtail(list,5);
addtail(list,6);
deletee(list,4);
print(list);
return 0;
}
用相同的main函数来检验操作是否正确。
4:完整代码展示:
单循环链表:
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}node;
node *listcreat()//单
{
node *list=(node *)malloc(sizeof(node));
list->data=0;
list->next=list;
return list;
}
void addlist(node *list,int n)
{
node *p=(node *)malloc(sizeof(node));
p->data=n;
p->next=list->next;
list->next=p;
list->data++;
}
void addtail(node *list,int n)
{
node *p=list->next;
while(p->next!=list)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=n;
q->next=list;
p->next=q;
list->data++;
}
void deletee(node *list,int n)
{
node *p=list,*q;
while(p->next!=list)
{
q=p;
p=p->next;
if(n==p->data)
{
q->next=p->next;
free(p);
list->data--;
break;
}
}
}
void print(node *list)
{
node *p=list->next;
while(p!=list)
{
printf("%d->",p->data);
p=p->next;
}
printf("NULL\n");
}
int main()
{
node *list=listcreat();
addlist(list,3);
addlist(list,2);
addlist(list,1);
addtail(list,4);
addtail(list,5);
addtail(list,6);
deletee(list,4);
print(list);
return 0;
}
双循环链表:
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
struct Node *pre;
}node;
node *listcreat()
{
node *list=(node *)malloc(sizeof(node));
list->data=0;
list->next=list;
list->pre=list;
return list;
}
void addlist(node *list,int n)
{
node *p=(node *)malloc(sizeof(node));
p->data=n;
p->next=list->next;
p->pre=list;
list->next=p;
list->data++;
}
void addtail(node *list,int n)
{
node *p=list->next;
while(p->next!=list)
{
p=p->next;
}
node *q=(node *)malloc(sizeof(node));
q->data=n;
q->next=list;
q->pre=p;
p->next=q;
list->pre=q;
list->data++;
}
void detelee(node *list,int n)
{
node *p=list,*q;
while(p->next!=list)
{
q=p;
p=p->next;
if(n==p->data)
{
q->next=p->next;
p->pre=q;
free(p);
list->data--;
break;
}
}
}
void print(node *list)
{
node *p=list->next;
while(p!=list)
{
printf("%d->",p->data);
p=p->next;
}
printf("NULL");
}
int main()
{
node *list=listcreat();
addlist(list,4);
addlist(list,3);
addlist(list,2);
addlist(list,1);
addtail(list,5);
addtail(list,6);
addtail(list,7);
addtail(list,8);
detelee(list,8);
print(list);
return 0;
}