双向循环链表接口
1.链表定义
双向链表就在这里可以体现,也就是双指针,next和prev
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
2.链表初始化
链表初始化,这里可以体现循环这一特点以及头结点的建立。
ListNode* ListInit()
{
ListNode* phead = BuyListNode(-1);//头结点
phead->next = phead;//循环
phead->prev = phead;
return phead;
}
3.链表的销毁
这里对于链表销毁,无非就是链表遍历,但主要是我们要明白如何遍历:
这里我们会选择从头结点的下一个结点(cur)开始遍历,直到回到头结点,完成一次遍历,每遍历一个元素,就释放一个元素,所以我们为了方便一般好会有个(next)指针指向cur下一个位置
void ListDestroy(ListNode* phead)
{
assert(phead);//什么时候断言:当phead不可能为空的时候断言
ListNode* cur = phead->next;
ListNode* next = cur->next;
while (cur != phead)
{
free(cur);//销毁cur指向的结点
cur = next;
next = cur->next;
}
free(phead);
phead = NULL;
}
4.链表的打印
同样的道理,遍历打印即可。
void ListPrint(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
printf("phead<=>");
while (cur!=phead)
{
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("\n");
}
5.链表的查找
遍历对比即可。
ListNode* ListFind(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
if (x == cur->data)
{
return cur;
}
}
return NULL;
}
6.链表的插入
链表任意位置插入,需要结合上述接口,链表的查找,找到pos位置,然后在pos前后插入,这里是pos前插入。
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* node = BuyListNode(x);
ListNode* cur = pos->prev;//优化插入过程
//pos前结点与新节点建立联系
cur->next = node;
node->prev = cur;
//新节点与pos建立联系
pos->prev = node;
node->next = pos;
}
我们来思考一下,如果我们没有ListNode* cur = pos->prev;//优化插入过程
这串代码,会有什么麻烦。
- 麻烦在于我们必须考虑连接的先后顺序,不可以先改变pos位置指针与前一个结点的关系,不然会增大找到上一个结点的难度(只能遍历寻找)。
- 只能先建立前一个结点与newnode结点的关系 。
7.链表的删除
这里思路也更加简单,记录pos位置前后,然后释放pos,在直接用指针链接前后关系。
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}
8.判断链表是否为空
注意带库声明,其余都比较简单
bool ListEmpty(ListNode* phead)
{
assert(phead);
return phead->next == phead;
}
9.获取链表元素个数
遍历并且带着一个count记录即可。
int ListsSize(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
int count = 0;
while (cur != phead)
{
count++;
cur = cur->next;
}
return count;
}
2.总结:
带头双向循环链表在我们学习了单链表以后显得十分简单,因为带头结点,插入也不需要二级指针。具体原因如下:
插入过程中并没有改变头节点的位置,因此无需通过二级指针来更新头节点的指向。只需要通过头指针即可访问到头节点,并进行相应的操作。phead并未改变。