赞
踩
链表是数据结构中十分重要的内容,我们知道链表的结构是非常多样的
当所有情况组合起来就有8种链表结构:
1.单向或者双向:
2.带头或者不带头:
3.循环或者非循环:
4.最常用的两种结构:
今天我们就来介绍优势更为明显的带头双向循环链表
在介绍带头双向循环链表之前呢,我们需要先来简单了解一下无头单向非循环链表,从图中我们也可以看出来,该结构较为简单。所以我们一般不会用其单独来存储数据。在实际应用中我们更多的是将其作为其他数据结构的子结构,如哈希桶、图的邻接表等等。在这里多提一句,这种结构在笔试面试中出现的概率是比较高的。
而我们今天要着重讲解的带头双向循环链表,它的结构相对来说是比较复杂的,一般用来单独存储数据。在实际使用链表数据结构,都是带头双向循环链表。虽然这个结构的结构较为复杂,但是在我们真正使用起来的时候,我们会发现它给我们带来了许多优势,反而实现起来更加简单了,这在后文讲解中会有所体现。
NOTE:老规矩,在这里我们先把所需要用的的库给到大家:
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
首先和单向链表一样,我们需要先定义一个节点,大致的实现都是相同的,但由于双向的结构,我们需要额外新增一个新的指针,因为我们需要一个指针指向前面一个指针指向后面,节点的定义具体如下:
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
LTNode* BuyListNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail!");
exit(-1);
}
node->next = NULL;
node->prev = NULL;
node->data = x;
return node;
}
LTNode* ListInit()
{
LTNode* guard = (LTNode*)malloc(sizeof(LTNode));
if (guard == NULL)
{
perror("malloc fail!");
exit(-1);
}
guard->next = guard;
guard->prev = guard;
return guard;
}
void ListDestory(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
void ListPrint(LTNode *phead)
{
assert(phead);
printf("phaed<=>");
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 ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
}
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(!ListEmpty(phead));
LTNode* tail = phead->prev;
LTNode* prev = tail->prev;
prev->next = phead;
phead->prev = prev;
free(tail);
tail = NULL;
}
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(!ListEmpty(phead));
LTNode* first = phead->prev;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
first = NULL;
}
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* newnode = BuyListNode(x);
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
LTNode* ListFind(LTNode* phead, LTDataType x) { assert(phead); size_t n = 0; LTNode* cur = phead->next; while (cur != phead) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
带头双向循环链表是一种较为复杂的就够,但由于其具有的循环特性,会使我们通过代码实现它时反而更为简单,双向链表这个结构的应用场景我们会在下一篇博客里通过oj题目为大家介绍,感谢大家的支持!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。