一、双向链表
1.定义
typedef int datatype;
typedef struct node
{
datatype data; //存放数据空间
struct node *ppre; //存放前一个节点地址
struct node *pnext; //存放下一个节点地址
}linknode;
2.创建
申请节点空间,对pnext和ppre赋值为NULL,返回空白节点地址
linknode *create_empty_linklist(void)
{
linknode *ptmpnode = NULL;
ptmpnode = malloc(sizeof(linknode));
if (NULL == ptmpnode)
{
perror("fail to malloc");
return NULL;
}
ptmpnode->ppre = NULL;
ptmpnode->pnext = NULL;
return ptmpnode;
}
3.头插法
①申请节点
②存放数据
③pnext赋值为phead->pnext
④ppre赋值为phead的地址
⑤phead->pnext赋值为新申请节点地址
⑥如果有后一个节点,需要让后一个节点的ppre指向该节点
int insert_head_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;ptmpnode = malloc(sizeof(linknode));
if (NULL == ptmpnode)
{
perror("fail to malloc");
return -1;
}
ptmpnode->data = tmpdata;
ptmpnode->pnext = phead->pnext;
ptmpnode->ppre = phead;
phead->pnext = ptmpnode;
if (ptmpnode->pnext != NULL)
{
ptmpnode->pnext->ppre = ptmpnode;
}
return 0;
}
4.遍历
具体查看上次单向链表遍历
int show_linklist(linknode *phead)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext;
while (ptmpnode != NULL)
{
printf("%d ", ptmpnode->data);
ptmpnode = ptmpnode->pnext;
}
printf("\n");
return 0;
}
5、查找
具体查看上次单向链表查找
linknode *find_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnex;while (ptmpnode != NULL)
{
if (ptmpnode->data == tmpdata)
{
return ptmpnode;
}
ptmpnode = ptmpnode->pnext;
}
return NULL;
}
6. 修改
具体查看上次单向链表修改
int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext;
while (ptmpnode != NULL)
{
if (ptmpnode->data == olddata)
{
ptmpnode->data = newdata;
}
ptmpnode = ptmpnode->pnext;
}
return 0;
}
7.删除
①找到要删除的节点
②让删除节点的前一个节点的pnext指向要删除节点的后一个节点
③让删除节点的后一个节点的ppre指向要删除节点的前一个节点
④将要删除的节点释放掉
int delete_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
linknode *pfreenode = NULL; //需要定义两个变量,因为如果只有一个变量被释放 后,无法继续遍历
ptmpnode = phead->pnext;
while (ptmpnode != NULL)
{
if (ptmpnode->data == tmpdata)
{
ptmpnode->ppre->pnext = ptmpnode->pnext;
if (ptmpnode->pnext != NULL) //判断要删除的节点是否为最后一个
{
ptmpnode->pnext->ppre = ptmpnode->ppre;
}
pfreenode = ptmpnode;
ptmpnode = ptmpnode->pnext;
free(pfreenode);
}
else //如果不加else会向后推进两个节点,导致无法全部遍历
{
ptmpnode = ptmpnode->pnext;
}
}
return 0;
}
8. 销毁
具体查看上次单向链表销毁
int destroy_linklist(linknode **pphead)
{
linknode *ptmpnode = NULL;
linknode *pfreenode = NULL;
ptmpnode = *pphead;
pfreenode = ptmpnode;
while (ptmpnode != NULL)
{
ptmpnode = ptmpnode->pnext;
free(pfreenode);
pfreenode = ptmpnode;
}
*pphead = NULL;
return 0;
}
9.尾插法
①申请节点
②将节点的pnext赋值为NULL
③找到链表最后一个节点
④将节点的ppre赋值为最后一个节点地址
⑤将最后一个节点的pnext赋值为新申请节点
int insert_tail_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
linknode *plastnode = NULL;
ptmpnode = malloc(sizeof(linknode));
if (NULL == ptmpnode)
{
perror("fail to malloc");
return -1;
}
plastnode = phead;
while (plastnode->pnext != NULL)
{
plastnode = plastnode->pnext;
}
ptmpnode->data = tmpdata;
ptmpnode->pnext = NULL;
ptmpnode->ppre = plastnode;
plastnode->pnext = ptmpnode;
return 0;
}
二、循环列表
1. 头结点的ppre指向链表最后一个节点,最后节点的pnext指向第一个节点,可以通过头节点或尾节点定位到头节点和尾节点
2.定义
typedef int datatype;
typedef struct node
{
datatype data; //存放数据空间
struct node *ppre; //存放前一个节点地址
struct node *pnext; //存放下一个节点地址
}linknode;
3.创建
申请节点,pnext和ppre都指向自己
linknode *create_empty_linklist(void)
{
linknode *ptmpnode = NULL;
ptmpnode = malloc(sizeof(linknode));
if (NULL == ptmpnode)
{
perror("fail to malloc");
return NULL;
}
ptmpnode->pnext = ptmpnode->ppre = ptmpnode;
return ptmpnode;
}
4.头插法
①申请节点空间
②存放数据
③将pnext指向空白节点的pnext
④将ppre指向空白节点
⑤将空白节点的pnext指向新申请节点
⑥将后续节点的ppre指向新申请节点
int insert_head_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
ptmpnode = malloc(sizeof(linknode));
if (NULL == ptmpnode)
{
perror("fail to malloc");
return -1;}
ptmpnode->data = tmpdata;
ptmpnode->pnext = phead->pnext;
ptmpnode->ppre = phead;
ptmpnode->pnext->ppre = ptmpnode;
ptmpnode->ppre->pnext = ptmpnode;
return 0;
}
5.尾插法
①申请节点空间
②将数据存放到节点中
③将pnext赋值为空白节点
④将ppre赋值为之前的最后一个节点地址
⑤将之前最后一个节点的pnext指向新申请节点
⑥将空白节点的ppre指向新申请节点
int insert_tail_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
ptmpnode = malloc(sizeof(linknode));
if (NULL == ptmpnode)
{
perror("fail to malloc");
return -1;
}
ptmpnode->data = tmpdata;
ptmpnode->pnext = phead;
ptmpnode->ppre = phead->ppre;
ptmpnode->pnext->ppre = ptmpnode;
ptmpnode->ppre->pnext = ptmpnode;
return 0;
}
6.遍历
其他与双向链表无差别,唯一的差别是循环链表最终循环条件由 != NULL修改为 != phead
int show_linklist(linknode *phead)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext;
while (ptmpnode != phead)
{
printf("%d ", ptmpnode->data);
ptmpnode = ptmpnode->pnext;
}
printf("\n");
return 0;
}
7.查找
具体与双向链表的查找方法一致
linknode *find_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext;
while (ptmpnode != phead)
{
if (ptmpnode->data == tmpdata)
{
return ptmpnode;
}
ptmpnode = ptmpnode->pnext;
}
return NULL;
}
8. 修改
具体与双向链表的修改方法一致
int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{
linknode *ptmpnode = NULL;
ptmpnode = phead->pnext;
while (ptmpnode != phead)
{
if (ptmpnode->data == olddata)
{
ptmpnode->data = newdata;
}
ptmpnode = ptmpnode->pnext;
}
return 0;
}
9.删除
具体与双向链表的删除方法一致,但是无需判断是否有pnext,因为循环链表永远有pnext
int delete_linklist(linknode *phead, datatype tmpdata)
{
linknode *ptmpnode = NULL;
linknode *pfreenode = NULL;
ptmpnode = phead->pnext;
while (ptmpnode != phead)
{
if (ptmpnode->data == tmpdata)
{
ptmpnode->ppre->pnext = ptmpnode->pnext;
ptmpnode->pnext->ppre = ptmpnode->ppre;
pfreenode = ptmpnode;
ptmpnode = ptmpnode->pnext;
free(pfreenode);
}
else
{
ptmpnode = ptmpnode->pnext;
}}
return 0;
}
10.销毁
具体与双向链表的销毁方法一致,但是需要从第一个非空节点开始销毁,因为判断条件是!=*pphead,如果销毁会导致无法进入循环;循环结束后要单独释放*pphead
int destroy_linklist(linknode **pphead)
{
linknode *ptmpnode = NULL;
linknode *pfreenode = NULL;
ptmpnode = (*pphead)->pnext;
pfreenode = ptmpnode;
while (ptmpnode != *pphead)
{
ptmpnode = ptmpnode->pnext;
free(pfreenode);
pfreenode = ptmpnode;
}
free(*pphead);
*pphead = NULL;
return 0;
}
三、 内核链表
1.是Linux内核中所使用到的一种链表结构,使用同一种结构可以存储不同类型的链表;普通链表是链表节点中包含数据,而内核链表是数据中包含链表节点。
2.结构描述

创建:创建空白节点即可(只需给ppre和pnext初始化)
插入:申请节点,并按照之前学习的循环链表插入即可(插入的链表节点首地址即数据节点首地
址)
访问节点:通过遍历找到每个节点的首地址,强制类型转换即可获得对应数据空间首地
2509

被折叠的 条评论
为什么被折叠?



