大佬教程收集整理的这篇文章主要介绍了带头双向循环链表的实现@线性表,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
🍓本文出现如此多草莓的原因是c;我昨天晚上吃了好多草莓c;并且现在还想吃。 🍓接下来要更新链表这儿的题解咯~
正文开始@边通书
上文提到c;带头双向循环链表 ——
代码实现的简单归功于它复杂的结构c;以下这几点都是在写的过程中感受到的 ——
🍓 带头
🍓 双向
方便找上一个下一个
🍓 循环
使找尾变得简单
🍓 对于pList即这个链表地址的分配我们完全可以直接写c;但还是推荐写成接口函数
🍓 初始化函数c; 对于pList这个实参的改变c;我们同样可以采取以往的办法传二级指针(即pList的地址类型c;为LTNode**
)
LTNode* LisTinit()
{
//哨兵位的头结点
LTNode* phead = (LTNode*)@H_43_173@malloc(sizeof(LTNode));
if (phead == NULL)
{
printf("malloc failedn");
exit(-1);
}
phead->prev = phead;
phead->next = phead;
return phead;
}
test.c
中是这样调用的
LTNode* pList = LisTinit();
void ListDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* curNext = cur->next;
free(cur);
cur = curNext;
}
free(phead);
//phead = NULL;
}
注意这里的phead = NULL;
很有必要c;但这样写没有发挥实际作用(所以我也把它注释掉了)c;因为phead是形参c;形参改变不会影响实参c;也就是pList不会改变。
这就导致了野指针的问题 —— 虽然pList仍保持着原来的地址c;但是phead已经被释放了c;此时pList就是一个野指针。(可以传二级c;实际上传二级就不会有这些问题c;但为了保持接口一致性用一级比较好)
因此c;我们需要在外面c;即test.c
文件中把它置空 ——
ListDestroy(pList);
pList = NULL;//外面置空c;就跟free一样
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)@H_43_173@malloc(sizeof(LTNode));
if (newnode == NULL)
{
printf("malloc failedn");
exit(-1);
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("n");
}
🍓 相比于单链表
void ListPushBACk(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
🍓 与单链表相比
void ListPopBACk(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//1.删空了
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
//链接
phead->prev = tailPrev;
tailPrev->next = phead;
}
🍓 与单链表相比
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* headNext = phead->next;
//链接
phead->next = newnode;
newnode->prev = phead;
newnode->next = headNext;
headNext->prev = newnode;
}
🍓 与单链表相比
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//删空时断言
LTNode* next = phead->next;
LTNode* nextNext = next->next;
free(next);
//链接
phead->next = nextNext;
nextNext->prev = phead;
}
和打印类似
LTNode* ListFind(LTNode* phead, LTDataType x)
{
assert(phead);
//空链表
if (phead->next == phead)
{
return NULL;
}
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void LisTinsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* posPrev = pos->prev;
//链接
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
void ListErase(LTNode* pos)
{
//按理说应该断言一下pos是pList的情况c;但是为了这个再增加一个参数c;得不偿失
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* posNext = pos->next;
free(pos);
//链接
posPrev->next = posNext;
posNext->prev = posPrev;
}
有了上面的任意位置插入删除c;就可以快速写出头插头删、尾插尾删。
//尾插
void ListPushBACk(LTNode* phead, LTDataType x)
{
assert(phead);
LisTinsert(phead, x);
}
//尾删
void ListPopBACk(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//1.删空了
ListErase(phead->prev);
}
//头插
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LisTinsert(phead->next, x);
}
//头删
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//删空时断言
ListErase(phead->next);
}
这两个结构各有优势c;很难说谁更优c;它们是相辅相成的结构。
顺序表 | 链表(带头双向循环链表) | |
---|---|---|
优点 | 1. 支持随机访问c;需要随机访问支持的结构可以很好地适用 | 1. 任意位置插入删除效率高O(1) |
2. CPU高速缓存命中率高 | 2. 按需申请释放空间 | |
缺点 | 1. 头部、中间插入效率低 | 1. 不支持随机访问(即下标访问c;如要访问第5个) |
2. 连续的物理空间c;空间不够了需要增容 | 2. 链表存储一个值c;同时要存储两个指针c;有一定消耗 | |
a. 增容有一定程度的消耗 | 3. CPU高速缓存命中率低 | |
b. 为了避免频繁增容c;一般按倍数去增c;用不完存在一定空间浪费 |
关于CPU高速缓存命中率c;请阅读陈皓的文章与程序员相关的CPU缓存知识 | 酷 壳 - CoolSHellc;我顺便也读了c;没法完全的懂c;另外我还被他的其他文章拽走了c;太有意思了c;但还是解释一下CPU高速缓存命中率。
List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
//带头双向循环---非常棒的结构
typedef struct ListNode
{
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
}LTNode;
//初始化
LTNode* LisTinit();
//链表打印
void ListPrint(LTNode* phead);
//销毁
void ListDestroy(LTNode* phead);
//尾插
void ListPushBACk(LTNode* phead, LTDataType x);
//尾删
void ListPopBACk(LTNode* phead);
//头插
void ListPushFront(LTNode* phead, LTDataType x);
//头删
void ListPopFront(LTNode* phead);
//查找
LTNode* ListFind(LTNode* phead, LTDataType x);
//任意位置插入
void LisTinsert(LTNode* pos, LTDataType x);
//任意位置删除
void ListErase(LTNode* pos);
List.c
#define _CRT_SECURE_NO_WARNINGS 1
#include@R_874_7468@ist.h"
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)@H_43_173@malloc(sizeof(LTNode));
if (newnode == NULL)
{
printf("malloc failedn");
exit(-1);
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
LTNode* LisTinit()
{
//哨兵位的头结点
LTNode* phead = (LTNode*)@H_43_173@malloc(sizeof(LTNode));
if (phead == NULL)
{
printf("malloc failedn");
exit(-1);
}
phead->prev = phead;
phead->next = phead;
return phead;
}
void ListDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* curNext = cur->next;
free(cur);
cur = curNext;
}
free(phead);
//phead = NULL;
}
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("n");
}
void ListPushBACk(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
//链接
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//LisTinsert(phead, X);
}
void ListPopBACk(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//1.删空了
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
//链接
phead->prev = tailPrev;
tailPrev->next = phead;
//ListErase(phead->prev);
}
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* headNext = phead->next;
//链接
phead->next = newnode;
newnode->prev = phead;
newnode->next = headNext;
headNext->prev = newnode;
//LisTinsert(phead->next, X);
}
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//删空时断言
LTNode* next = phead->next;
LTNode* nextNext = next->next;
free(next);
//链接
phead->next = nextNext;
nextNext->prev = phead;
//ListErase(phead->next);
}
LTNode* ListFind(LTNode* phead, LTDataType x)
{
assert(phead);
//空链表
if (phead->next == phead)
{
return NULL;
}
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void LisTinsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* posPrev = pos->prev;
//链接
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
void ListErase(LTNode* pos)
{
//按理说应该断言一下pos是pList的情况c;但是为了这个再增加一个参数c;得不偿失
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* posNext = pos->next;
free(pos);
//链接
posPrev->next = posNext;
posNext->prev = posPrev;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include@R_874_7468@ist.h"
//测试尾插尾删
void testList1()
{
LTNode* pList = LisTinit();
ListPushBACk(pList, 1);
ListPushBACk(pList, 2);
ListPushBACk(pList, 3);
ListPrint(pList);
ListPopBACk(pList);
//ListPopBACk(pList);
/*ListPopBACk(pList);
ListPopBACk(pList);*/
ListPrint(pList);
ListDestroy(pList);
pList = NULL;
}
//测试头插头删
void testList2()
{
LTNode* pList = LisTinit();
ListPushFront(pList, 1);
ListPushFront(pList, 2);
ListPushFront(pList, 3);
ListPrint(pList);
ListPopFront(pList);
/*ListPopFront(pList);
ListPopFront(pList);
ListPopFront(pList);*/
ListPrint(pList);
ListDestroy(pList);
pList = NULL;
}
//测试查找、任意位置插入、任意位置插入
void testList3()
{
LTNode* pList = LisTinit();
ListPushBACk(pList, 1);
ListPushBACk(pList, 2);
ListPushBACk(pList, 4);
ListPushBACk(pList, 5);
ListPrint(pList);
LTNode* ret = ListFind(pList, 4);
if (ret == NULL)
{
printf("not foundn");
}
else
{
printf("%dn", ret->data);
}
LisTinsert(ret,3);
ListPrint(pList);
ListErase(ret);
ListPrint(pList);
ListDestroy(pList);
pList = NULL;
}
int @H_43_173@main()
{
//testList1();
testList2();
//testList3();
return 0;
}
本文完@边通书
以上是大佬教程为你收集整理的带头双向循环链表的实现@线性表全部内容,希望文章能够帮你解决带头双向循环链表的实现@线性表所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。