标题本人小白一枚,最近学了数据结构的链表,索性将链表这部分的知识点总结一下分享给大家(大佬请绕路(嘿嘿))大家如果有更好的思路或者写法就写在评论区吧
一,单链表
要创建一个链表首先要创建结点(结点的实现用的是结构体)
废话不多说,上代码
结点的创建可以使用dypedef自定义
这样在后续的引用中就能少写一些代码
typedef struct Node{
Elemtype date;//数据域,存放数据(Elentype可以为任意的数据类型)
struct Node *next;//指针域。指向后继结点
}*Linklist,node;
当结点生成之后就可以生成链表了
单链表的生成有两种方法
1,头插法
2 ,尾插法
本人比较喜欢用尾插法
尾插代码如下:
// An highlighted block
#include<stdio.h>
#include<stdlib.h>
typedef struct link{
int date;
struct link *next;
}*Linklist,node; //这里给结构体起个别名
node *Create(int n){
printf("输入链表中的数据:\n");
int a;
Linklist head,t;//head为头结点,t为过渡结点
head=(Linklist)malloc(sizeof(Linklist));
t=(Linklist)malloc(sizeof(Linklist));
head->next=NULL;
t=head;
while(n--){
Linklist p;
p=(Linklist)malloc(sizeof(Linklist));
scanf("%d",&a);
p->date=a;//在数据域中存入数据
t->next=p;//把新的p结点连接到t结点后面
p->next=NULL;//这里,对于指针变量初始化为NULL的目的在于:区分垃圾数据!
t=p;
}
return head->next;//因为创建链表的过程中头节点并没有存入数据,所以返回存入数据的第一个结点
}
int main()
{
Linklist head;
int n,i;
printf("输入链表长度:\n");
scanf("%d",&n);
head=Create(n);
printf("链表O为:\n");
for(i=0;;i++){
printf("%d ",head->date);
if(head->next==NULL) break;//最后一个结点的指针域为NULL若读取到最后一位则退出
head=head->next;//每读取完一个节点时,指针便往后移动一位
}
return 0;
}
到此一个长度为n的链表便创建完成
顺便提一下头插法:
1,头插法与尾插法差别并不大,只是在指针赋值的时候把
t->next=p;
t=p;
改为
p->next=t;
t=p;//指针的后移
//这里新节点p的指针域已经存入了t的地址,不能赋NULL值
即可
二,双向链表
双向链表与单链表的差别就是在单链表结点的指针域中增加一个指向前驱结点的指针,
结点的创建为
typedef struct Node{
Elemtype date;//数据域
struct Node *next;//指针域,指向后继结点
struct Node *prior;//指针域,指向前驱结点
}
双向链表的创建和单链表相差并不大,代码如下:
// An highlighted block
#include<stdio.h>
#include<stdlib.h>
typedef struct link{
int date;
struct link *next;//指向下一个结点
struct link *prior;//指向上一个结点
}*Linklist,node;
node *Create(int n){
printf("输入链表中的数据:\n");
int a;
Linklist head,t;//head为头结点,t为过渡结点
head=(Linklist)malloc(sizeof(Linklist));
t=(Linklist)malloc(sizeof(Linklist));
head->next=NULL;
t=head;
while(n--){
Linklist p;
p=(Linklist)malloc(sizeof(Linklist));
scanf("%d",&a);
p->date=a;
t->next=p;
p->prior=t;//对比单链表,这里需要将prior赋值为上一个结点的地址
p->next=NULL;
t=p;
}
head->next->prior=NULL;//因为传出的头结点存入数据的结点,所以将第一个存入数据的结点的prior赋值为NULL
return head->next;//传出头节点
}
int main()
{
Linklist head;
int n,i;
printf("输入链表长度:\n");
scanf("%d",&n);
head=Create(n);
printf("链表为:\n");
for(i=0;;i++){
printf("%d ",head->date);
if(head->next==NULL) break;
head=head->next;
}//这里链表往后遍历打印数据
for(i=0;;i++){
printf("%d ",head->date);
if(head->prior==NULL) break;
head=head->prior;
}//这里链表往前遍历打印数据
return 0;
}
三,循环链表
循环链表与双向链表,单链表的区别不大主要在于链表生成之后把头尾结点链接到一块
这里我以单链表为例(读者可以自行试试双向链表):
在代码中添加一行便能使链表形成环
// An highlighted block
#include<stdio.h>
#include<stdlib.h>
typedef struct link{
int date;
struct link *next;
}*Linklist,node;
node *Create(int n){
printf("输入链表中的数据:\n");
int a;
Linklist head,t;//head为头结点,t为过渡结点
head=(Linklist)malloc(sizeof(Linklist));
t=(Linklist)malloc(sizeof(Linklist));
head->next=NULL;
t=head;
while(n--){
Linklist p;
p=(Linklist)malloc(sizeof(Linklist));
scanf("%d",&a);
p->date=a;
t->next=p;
p->next=NULL;
t=p;
}
t->next=head->next;//头尾相连操作,t为尾节点, 给尾结点的指针域赋予头结点的地址时链表形成一个环
return head->next;
}
int main()
{
Linklist head;
int n,i;
printf("输入链表长度:\n");
scanf("%d",&n);
head=Create(n);
printf("链表为:\n");
for(i=0;i<10;i++){//这里i为一个固定的数值是为了防止无限循环
printf("%d ",head->date);
head=head->next;
}
return 0;
}
这串代码和之前的单链表的创建几乎相同
只是增加了 t -> next = head -> next; 这样一串代码
最后来说说链表的插入删除操作吧
1,链表的插入
比如在第n个位置需要插入一个结点,我比较喜欢的方法是先遍历到第n-1个结点,再插入,代码如下
head结点为第n-1个结点,p为需要插入的结点
p->next=heda->next;
head->next=p;
这样插入操作就完成了
当然在遍历的时候一定不能超出链表长度(非循环链表),
循环判断可以用head -> next ! = NULL防止遍历超长
2,链表结点的删除
与插入相同,依然是先找到第n-1个结点
head -> next = head -> next -> next;
这样便把第n个结点从链表中删除了