1.基本概念
1.前言
上一份笔记复习了顺序表,说明了顺序表的缺点,为了弥补缺点,便有了链表。
2.链表的类型、结构
链表结构:在物理上不连续,在逻辑上连续。⼤⼩不固定。 链式存储结构是基于指针实现的。我们把⼀个数据元素和⼀个指针称之为节点。
数据域 | 指针域 |
数据域:存数据元素的区域
指针域:存储直接后继位置的区域。
链式存储,其实就是⽤指针将相互关联的结点链接起来。
链式存储根据链表的构造不同,可以分成:单向链表,单向循环链表,双向链表,双向循环链 表。再根据有无头结点组合上述分类,总共有8种结构
何谓头指针、首元结点和头结点?
头指针 : 指向链表中第一个结点(为头结点或首元结点)的指针
首元结点 :是指链表中存储第一个数据元素a1的结点
头结点 : 是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息。 不能计入链表长度值
2.链表的代码实现
对链表的基本操作还是有增,删,改,查四个操作。因为是复习笔记使用,为了节省篇幅,以含头结点的双向链表展示操作。
双向链表结构示意:
1.双向链表的结点结构
//带头结点的双链表的节点结构
typedef struct node{
int data;//数据域
struct node *next; //指针域->存下一个结点的地址
struct node *pr; //指针域 ---》存上一个节点地址
}Node,*linklist;
//等同于: typedef struct NodeList* linklist;
//linklist与node*等价,都是用来声明结构体指针变量的
//linklist强调该指针标记标记了一个单链表
//node* 强调该指针标记了一个结点
2.初始化链表
//初始化:
linklist initlsit()
{
linklist l=(Node*)malloc(sizeof(Node));
if(l==NULL)
{
printf("内存分配失败\n");
return NULL;
}
else
{
l->next=NULL;
l->pr=NULL;
}
return l;
}
3.插入数据(增)
//头插法 把数据x插入到链表l中
void head_insert(int x,linklist l)
{
//x保存在一个节点中 s
Node* s=(Node*)malloc(sizeof(Node));
s->data=x;
s->next=l->next;//1
s->pr=l;//2
l->next=s;//3
if(s->next!=NULL)//判断s的下一个节点是否存在
{
s->next->pr=s;//4
}
}
//尾插法 把数据x插入到链表l中
void rear_insert(int x,linklist l)
{
//x保存在一个节点中 s
Node* s=(Node*)malloc(sizeof(Node));
s->data=x;
s->next=NULL;
//遍历找尾节点
Node* p=l;
while(p->next!=NULL)
{
p=p->next;
}
p->next=s;
s->pr=p;
}
4.删除结点
void delete_node(linklist l,int k)
{
if(l->next==NULL)
{
printf("空链表\n");
return;
}
Node *p=l->next;//首元
while(p!=NULL&&p->data!=k)
{
p=p->next;
}
if(p==NULL)
{
printf("删除的数据不存在\n");
}
else
{
/*Node *pre=p->pr;
Node *ne=p->next;
pre->next=ne;
if(ne!=NULL)//判断p的下一个节点是否存在
ne->pr=pre;*/
p->pr->next=p->next;
if(p->next!=NULL)判断p的下一个节点是否存在
{
p->next->pr=p->pr;
}
free(p);
p=NULL;
}
}
5.查找,更改结点数据
删除操作的基础就是查找,这里不再赘述
void delete_node(linklist l,int k,int m)
{
if(l->next==NULL)
{
printf("空链表\n");
return;
}
Node *p=l->next;//首元
while(p!=NULL&&p->data!=k)
{
p=p->next;
}
if(p==NULL)
{
printf("删除的数据不存在\n");
}
else
{
p->data = m;
}
}