链表有很多分类,比如单向还是双向,循环还是不循环等等。根据这些分类,链表有8种分类。双向链表是其中最复杂的链表。学会了本篇文章的双向链表和昨天发的单向链表,链表的基本知识就学完了,我们也可以应用到实战去了。
(一)双向链表的定义和初始化
双向链表是指链表头尾相连,结点相互指向的链表。所以一个结点中需要保存两个指针,分别指向上一个结点和下一个结点。
//双向链表的定义
#define ListDataType int
typedef struct ListNode
{
ListDataType data;
struct ListNode* prev;
struct ListNode* next;
}LtNode;
这里将双向链表的数据类型重命名为LtNode。里面保存了指向上一个结点的指针prev,和指向下一个结点的指针next。这里我们先写一个创建双向链表结点的函数。这里我们把哨兵岗位初始化为-1,这个哨兵岗位就是用来放哨的,占个位置,方便我们之后的操作。
//创建链表结点
LtNode* BuyNewNode(ListDataType val)
{
LtNode* NewNode = (LtNode*)malloc(sizeof(LtNode));
if (NewNode == NULL)
{
perror("malloc failed\n");
exit(1);
}
NewNode->data = val;
NewNode->prev = NewNode->next = NewNode;
return NewNode;
}
//链表的初始化
void InitList(LtNode** pphead)
{
//创建哨兵结点
assert(pphead);
*phead = BuyNewNode(-1);
}
需要注意的是,在初始化头结点时,它的前后指针都得指向头结点本身。
(二)双向链表尾插,头插
//双向链表的尾插
void ListPushBack(LtNode* phead, ListDataType x)
{
assert(phead);
//我们可以通过头结点找到尾结点
LtNode* newnode = BuyNewNode(x);
// phead ->phead->prev ->newnode
newnode->next = phead;
newnode->prev = phead->prev;
phead->prev->next = newnode;
phead->prev = newnode;
}
//双向链表的头插
void ListPushFront(LtNode* phead, ListDataType x)
{
assert(phead);
LtNode* newnode = BuyNewNode(x);
// phead -> newnode-> phead->next
newnode->next = phead->next;
newnode->prev = phead;
phead->next->prev = newnode;
phead->next = newnode;
}
(三)双向链表的尾删,头删,检查链表是否为空
//双向链表的尾删
void ListPopBack(LtNode* phead)
{
assert(!ListEmpty(phead));
LtNode* del = phead->prev;
// phead phead->prev->prev phead->prev
del->prev->next = phead;
phead->prev = del->prev;
free(del);
del = NULL;
}
//检查链表是否为空
bool ListEmpty(LtNode* phead)
{
assert(phead);
return phead->next == phead;
}
//双向链表的头删
void ListPopFront(LtNode* phead)
{
assert(phead);
LtNode* del = phead->next;
// phead del del->next
del->next->prv = phead;
phead->next = del->next;
free(del);
del = NULL;
}
(四)双向链表的查找,指定位置插入,销毁
//查找某值
LtNode* ListFind(LtNode* phead. ListDataType x)
{
assert(phead);
LtNode* pcur = phead->next;
while (pcur != phead)
{
if (pcur->val == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL
}
//双向链表的指定位置插入
void ListInsert(LtNode* pos, ListDataType x)
{
assert(phead);
LtNode* pcur = phead->next;
// pos newnode pos->next
LtNode* newnode = BuyNewNode(x);
newnode->next = pos->next;
newnode->prev = pos;
pos->next->prev = newnode;
pos->next = newnode;
}
//双向链表的销毁
void ListDestroy(LtNode** pphead)
{
assert(pphead && * pphead);
LtNode* pcur = *pphead->next;;
while (pcur != phead)
{
LtNode* ptem = pcur->next;
free(pcur);
pcur = ptem;
}
free(*pphead);
*pphead = NULL;
}