大家好,我是小峰,今天给大家讲解的是双向链表,我们先来看看链表的结构。
链表分类
这里带头是哨兵位的头节点
3. 循环与非循环
我们排列一些就可以看出链表的结构种类
这些链表的操作大都差不多
我们今天讲解的是双向带头循环链表
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了,后面我们代码实现了就知道了。
首先是链表基础结构
创建返回链表的头结点
//创建并返回链表表头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;
}
以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。