提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
``
链表有着不同的种类,分别有《带头or不带头》,《单向or双向》,《循环or不循环》222结合起来就有8种,而相对与双向链表则是《带头》《双向》《循环》的链表
``
一、双向链表是什么?
带头:指的是链表中有哨兵位节点,该哨兵位节点即头节点(而在单链表中,口头提到的头节点实际上指的是第一个有效节点,这不是正确的称呼)
双向:
带头 | 双向 |
---|---|
有头节点(哨兵位) | 可以从俩个方向遍历 |
3:循环或不循环
二、代码实现
1.双链表结构
代码如下(示例):
typedef struct S
{
int data;//数据
struct S* next;//指向后面的地址
struct S* prve;//指向前面的地址
}S1;
2.代码功能实现
代码如下(示例):初始化
void chushihua(S1** ps);//初始化
void chushihua(S1** ps)
{
//给双向链表创建一个哨兵位
*ps = Buynode(-1);//即使有值我们也不会修改
}
尾插
void SLpushback(S1* ps,cwz x);//尾插
S1* Buynode(cwz x)
{
//申请节点
S1* node = (S1*)malloc(sizeof(S1));
if (node == NULL)
{
perror("malloc");
exit(1);
}
node->data = x;
node->prve = node->next = node;
return node;
}
void SLpushback(S1* ps, cwz x)
{
assert(ps);
S1* newnode = Buynode(x);
S1* next = ps;
newnode->prve = ps->prve;
ps->prve->next = newnode;
newnode->next = ps;
ps->prve = newnode;
}
每做完一个函数就要测试
头插
void SLpushfront(S1* ps, cwz x);
S1* Buynode(cwz x)
{
//申请节点
S1* node = (S1*)malloc(sizeof(S1));
if (node == NULL)
{
perror("malloc");
exit(1);
}
node->data = x;
node->prve = node->next = node;
return node;
}
void SLpushfront(S1* ps, cwz x)//头插
{
assert(ps);
S1* p = ps;
S1* newnode = Buynode(x);
newnode->prve = ps;
newnode->next = ps->next;
ps->next->prve = newnode;
ps->next = newnode;
}
测试
其他功能我就不一一介绍了
//打印
void SLprint(S1* ps)
{
assert(ps);
S1* p = ps->next;
while (p != ps)
{
printf("%d ", p->data);
p = p->next;
}
//走到哨兵位
printf("\n");
}
void LTPopnback(S1* ps)//尾删
{
//链表必须有效且链表不能为空
assert(ps && ps->next != ps);
S1* p = ps->prve;
ps->prve->prve->next = ps;
ps->prve = ps->prve->prve;
free(ps->prve);
ps->prve = NULL;
}
void LTPopnfront(S1* ps)//头删
{
assert(ps && ps->next != ps);
S1* p = ps->next;
ps->next = p->next;
p->next->prve = ps;
free(p);
p = NULL;
}
S1* find1(S1* ps, cwz x)//查找元素
{
S1* p = ps->next;
while (p != ps)
{
if (p->data == x)
{
return p;
}
p = p->next;
}
//没有找到
return NULL;
}
void insertback(S1* pos, cwz x)//指定位置插入元素
{
S1* newnode = Buynode(x);
newnode->prve = pos;
newnode->next = pos->next;
pos->next->prve = newnode;
pos->next = newnode;
}
void deletepos(S1* pos)//指定位置删除元素
{
//pos理论上来说是不能为哨兵位的,但是没有参数ps,无法增加校验
assert(pos);
S1* p = pos;
p->prve->next = p->next;
p->next->prve = p->prve;
free(p);
p = NULL;
}
void ruin(S1* ps)//函数销毁(这里传的是一级指针并不会影响实参所以要手动置空)
{
assert(ps);
S1* pcur = ps->next;
while (pcur->next != ps)
{
S1* p = pcur->next;
free(pcur);
}
//哨兵位
free(ps);
ps = NULL;
}
//头文件
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//双向链表
//节点->数据+指向下一个节点的指针+指向前一个节点的指针
typedef int cwz;//这里是可以不int类型转换为其他类
typedef struct S
{
int data;
struct S* next;
struct S* prve;
}S1;
//声明函数
/*void chushihua(S1** ps);*///初始化
//情况二
S1* chushihua();
void SLpushback(S1* ps, cwz x);//尾插
void SLprint(S1* ps);//打印
void SLpushfront(S1* ps, cwz x);//头插
void LTPopnback(S1* ps);//尾删
void LTPopnfront(S1* ps);//头删
S1* find1(S1* ps, cwz x);//查找元素
void insertback(S1* pos, cwz x);//在指定位置插入数据
void deletepos(S1* pos);//指定位置删除
void ruin(S1* ps);
//传一级指针是为了保存接口一致性
//测试文件
#include"标头.h"
void ceshi()
{
//S1* ps = NULL;
S1* ps = chushihua();
//printf("\n");
//SLpushback(ps, 1);//尾插1
//SLprint(ps);
//SLpushback(ps, 2);//尾插2
//SLprint(ps);
//SLpushback(ps, 3);//尾插3
//SLprint(ps);
//SLpushback(ps, 4);//尾插4
//SLprint(ps);
//SLpushback(ps, 5);//尾插5
//SLprint(ps);
SLpushfront(ps, 1);
SLprint(ps);
SLpushfront(ps, 2);
SLprint(ps);
SLpushfront(ps, 3);
SLprint(ps);
SLpushfront(ps, 4);
SLprint(ps);
SLpushfront(ps, 5);
SLprint(ps);
//printf("\n");
//S1* find = find1(ps, 3);//查找函数
//if (find == NULL)
//{
// printf("找不到");
//}
//else
//{
// printf("找到了");
//}
//insertback(find, 22);//插入函数
//printf("\n");
//SLprint(ps);
//deletepos(find);
//SLprint(ps);
//ruin(ps);
手动传为空是因为保证接口一致性采用以一级才传的
传一级的问题是形参被修改位NULL,但是实参也没有置空,所以要手动置空
//ps = NULL;
//LTPopnback(ps);//尾删
//SLprint(ps);
//LTPopnback(ps);
//SLprint(ps);
//LTPopnback(ps);
//SLprint(ps);
//LTPopnfront(ps);//头删
//SLprint(ps);
//LTPopnfront(ps);
//SLprint(ps);
//LTPopnfront(ps);
//SLprint(ps);
//LTPopnfront(ps);
//SLprint(ps);
}
int main()
{
ceshi();
return 0;
}
#/ 总结
双向链表是比单向链表方便
例如:1:双向链表有着前后指针指向自己前后位置使得操作者更具有灵活性
2:双向链表具有哨兵位可以保证了指针不为空的情况
3:双向链表的循环性大大提高了我们的做题效率和思路