大家好,我是小峰,今天给大家讲解的是双向链表,我们先来看看链表的结构。
链表分类
这里带头是哨兵位的头节点
3. 循环与非循环
我们排列一些就可以看出链表的结构种类
这些链表的操作大都差不多
我们今天讲解的是双向带头循环链表
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了,后面我们代码实现了就知道了。
首先是链表基础结构
![](https://i-blog.csdnimg.cn/blog_migrate/5670b8a1be91ae6596f017144010f77e.png)
创建返回链表的头结点
//创建并返回链表表头
listnode* listcj() {
listnode* ps = (listnode*)malloc(sizeof(CMMlet) + 2*sizeof(listnode*));
ps->next = ps;
ps->prev = ps;
return ps;
}
这是链表的头节点
那么我们接下来实现一个尾插
尾插数据
void listwc(listnode* pead) {
listnode* cur=listcj();
listnode* ps = pead->next;
//连接
ps->next = cur;
cur->prev = ps;
cur->next = pead;
pead->prev = cur;
}
我们在写个打印函数
链表打印
//链表打印
void listdy(listnode* pead) {
listnode* cur = pead->next;
printf("哨兵位");
while (cur != pead) {
printf("<=>%d", cur->SZ);
cur = cur->next;
}
printf("\n");
}
那接下来我们就可以测试一下我们写的尾插数据
老规矩我们分装一个函数来测试
头插数据
//头插数据
void listtc(listnode* pead, CMMlet n) {
listnode* ps = listcj();
listnode* cur = pead->next;
pead->next = ps;
ps->prev = pead;
cur->prev = ps;
ps->next = cur;
ps->SZ = n;
}
测试结果
头删数据
//头删数据
void listts(listnode* pead) {
listnode* pt = pead->next;
listnode* ps = pead->next->next;
free(pt);
ps->prev = pead;
pead->next = ps;
}
这个代码是有点小问题的大家可以想想如果参数只有头节点呢?
最后我们是不是会出现空指针问题?
这时我们就要用到断言了,但我们发现断言的条件也不好写,这时我们的bool类型就要大展身手了
//bool判断
bool listpd(listnode* pead) {
if (pead->next == pead && pead->prev == pead) {
return false;
}
else {
return true;
}
}
修改后的代码如下
//头删数据
void listts(listnode* pead) {
assert(pead);
assert(listpd(pead));
listnode* pt = pead->next;
listnode* ps = pead->next->next;
free(pt);
ps->prev = pead;
pead->next = ps;
}
我们测试一下
当我们再删除时
它就会报错,并提示我们在哪一行。
尾删数据
//尾删数据
void listws(listnode* pead) {
assert(pead);
assert(listpd(pead));
listnode* ps = pead->prev;
listnode* pt = ps->prev;
free(ps);
pt->next = pead;
pead->prev = pt;
}
查找数据
与单链表类似我们来试试
//查找数据
listnode* listcz(listnode* pead, CMMlet n) {
assert(pead);
listnode* ps = pead->next;
while (ps != pead) {
if (ps->SZ == n) {
return ps;
}
else {
ps = ps->next;
}
}
return NULL;
}
我们测试一下
在pos的前面插入数据
//在pos前面插入数据
void listposcr(listnode* pos, CMMlet n) {
listnode* ps = pos->prev;
listnode*pt=listcj();
pt->next = pos;
pos->prev = pt;
pt->prev = ps;
ps->next = pt;
pt->SZ = n;
}
测试一下
删除pos位置的数据
//删除pos位置的数据
void listpossc(listnode* pos) {
assert(pos);
assert(listpd(pos));
listnode* prev = pos->prev;
listnode* next = pos->next;
free(pos);
prev->next = next;
next->prev = prev;
}
测试一下
销毁链表
//销毁链表
void listxh(listnode* pead) {
listnode* cur = pead->next;
listnode* ps = cur->next;
while (cur != pead) {
free(cur);
cur = ps;
ps = ps->next;
}
pead->next = pead;
pead->prev = pead;
}
测试一下
这就是双向带头循环链表的所有操作了
下面就是本节的所有代码了
# include<stdio.h>
# include<stdlib.h>
# include<stdbool.h>
# include<assert.h>
typedef int CMMlet;
typedef struct listnode listnode;
struct listnode {
CMMlet SZ;
//上一个节点
listnode* prev;
//下一个节点
listnode* next;
};
//创建并返回链表表头
listnode* listcj() {
listnode* ps = (listnode*)malloc(sizeof(CMMlet) + 2*sizeof(listnode*));
ps->next = ps;
ps->prev = ps;
return ps;
}
//尾插数据
void listwc(listnode* pead,CMMlet n) {
listnode* cur=listcj();
listnode* ps = pead->prev;
//连接
ps->next = cur;
cur->prev = ps;
cur->next = pead;
pead->prev = cur;
cur->SZ = n;
}
//链表打印
void listdy(listnode* pead) {
listnode* cur = pead->next;
printf("哨兵位");
while (cur != pead) {
printf("<=>%d", cur->SZ);
cur = cur->next;
}
printf("\n");
}
//头插数据
void listtc(listnode* pead, CMMlet n) {
listnode* ps = listcj();
listnode* cur = pead->next;
pead->next = ps;
ps->prev = pead;
cur->prev = ps;
ps->next = cur;
ps->SZ = n;
}
//bool判断
bool listpd(listnode* pead) {
if (pead->next == pead && pead->prev == pead) {
return false;
}
else {
return true;
}
}
//头删数据
void listts(listnode* pead) {
assert(pead);
assert(listpd(pead));
listnode* pt = pead->next;
listnode* ps = pead->next->next;
free(pt);
ps->prev = pead;
pead->next = ps;
}
//尾删数据
void listws(listnode* pead) {
assert(pead);
assert(listpd(pead));
listnode* ps = pead->prev;
listnode* pt = ps->prev;
free(ps);
pt->next = pead;
pead->prev = pt;
}
//查找数据
listnode* listcz(listnode* pead, CMMlet n) {
assert(pead);
listnode* ps = pead->next;
while (ps != pead) {
if (ps->SZ == n) {
return ps;
}
else {
ps = ps->next;
}
}
return NULL;
}
//在pos前面插入数据
void listposcr(listnode* pos, CMMlet n) {
listnode* ps = pos->prev;
listnode*pt=listcj();
pt->next = pos;
pos->prev = pt;
pt->prev = ps;
ps->next = pt;
pt->SZ = n;
}
//删除pos位置的数据
void listpossc(listnode* pos) {
assert(pos);
assert(listpd(pos));
listnode* prev = pos->prev;
listnode* next = pos->next;
free(pos);
prev->next = next;
next->prev = prev;
}
//销毁链表
void listxh(listnode* pead) {
listnode* cur = pead->next;
listnode* ps = cur->next;
while (cur != pead) {
free(cur);
cur = ps;
ps = ps->next;
}
pead->next = pead;
pead->prev = pead;
}
void listcheshi() {
listnode* pead = listcj();
listtc(pead, 1);
listtc(pead, 2);
listtc(pead, 3);
listtc(pead, 4);
listtc(pead, 5);
listdy(pead);
listxh(pead);
listdy(pead);
}
int main() {
//测试函数
listcheshi();
return 0;
}
以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。