前言:
相较于单链表,有一种更优的链表,下面我来介绍一下双向链表的相关知识。
一、双向循环链表的实现
双向链表在创建时会建立两个指针,可以向前链接,也可以向后链接;
如图所示:
结构体的创建:
typedef int ListDataType;
typedef struct ListNode
{
ListDataType data;
struct ListNode* prev;
struct ListNode* next;
}ListNode;
创建新节点:
ListNode* BuyListNode(ListDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
链表的初始化:
void ListInit(ListNode* phead)
{
assert(phead);
phead->next = phead;
phead->prev = phead;
}
链表的初始化用的一级指针,为了和其他的函数统一用一级,所以我需要在main函数中提前
ListNode* phead = BuyListNode(-1);
链表的打印:
void ListPrint(ListNode* phead)
{
assert(phead);
printf("哨兵位<==>");
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>",cur->data);
cur = cur->next;
}
}
判断链表是否只有头节点:
bool ListEmpty(ListNode* phead)
{
assert(phead);
return phead->next == phead;
}
双向链表尾插:
void ListPushBack(ListNode* phead,ListDataType x)
{
assert(phead);
ListNode* newnode = BuyListNode(x);
ListNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
双向链表头插:
void ListPushFront(ListNode* phead, ListDataType x)
{
assert(phead);
ListNode* next = phead->next;
ListNode* newnode = BuyListNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = next;
next->prev = newnode;
}
链表查找:
ListNode* ListFind(ListNode* phead, ListDataType x)
{
assert(phead);
ListNode* cur = phead;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
头删:
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(!ListEmpty(phead));
ListNode* del = phead->next;
ListNode* next = del->next;
phead->next = next;
next->prev = phead;
free(del);
}
尾删:
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(!ListEmpty(phead));
ListNode* tail = phead->prev;
ListNode* tailPrev = tail->prev;
tailPrev->next = phead;
phead->prev = tailPrev;
free(tail);
}
在pos位置之后 插入:
void ListInsert(ListNode* pos, ListDataType x)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* newnode = BuyListNode(x);
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
删除pos位置:
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posPrev = pos->prev;
ListNode* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
二、顺序表和链表的区别
不同点 | 顺序表 | 链表 |
存储空间上 | 物理上一定连续 | 逻辑上连续,物理不一定 |
随机访问 | 支持 | 不支持 |
任意位置插入 或者删除元素 | 可能需要搬移元素,效率低 | 只需要修改指针指向 |
插入 | 动态顺序表,空间不够需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |
缓存利用率 | 高 | 低 |
总结:
这就是对双向链表的简要介绍,更新不易,希望各位小伙伴动动小手,三连走一走,你们的三连对我真的很重要!!!!!