目录
1.认识双向带头循环链表:
从结构可以看出,这种链表两个指针,”前驱后继“,而head补位的指针只是一个“哨兵:,里面不用存放有效数据。
与单向链表相比,这种链表可以不用传二级指针,遍历更少,前插后插更加方便,更加适合于存放数据。
2.预处理:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DListDataType;
typedef struct DListNode
{
DListDataType Data;
struct DListNode* prev;
struct DListNode* next;
}DLN;
3.初始化,前插后插:
为了减少二级指针的使用,我们可以直接返回一个创建好的哨兵头节点即可,
DLN* CreatNewNode(DListDataType x)
{
DLN* newnode = (DLN*)malloc(sizeof(DLN));
if (newnode == NULL)
{
printf("malloc failed!\n");
return NULL;
}
newnode->Data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
DLN* DLSTInit()
{
DLN* newnode = CreatNewNode(0);
newnode->next = newnode;
newnode->prev = newnode;
return newnode;
}
DLN* Plist = NULL;
Plist = DLSTInit();
这样我们的头节点就创建好了。
前插:
void DLSTPushFront(DLN* Phead, DListDataType x)
{
assert(Phead);
DLN* first = Phead->next;
DLN* newnode = CreatNewNode(x);
Phead->next = newnode;
newnode->prev = Phead;
newnode->next = first;
first->prev = newnode;
}
这点我们可以画图理解:
后插:
void DLSTPushBack(DLN* Phead, DListDataType x)
{
assert(Phead);
DLN* newnode = CreatNewNode(x);
DLN* tail = Phead->prev;
tail->next = newnode;
newnode->prev = tail;
Phead->prev = newnode;
newnode->next = Phead;
}
图:
有了双向循环链表,后插显得简单了很多。
4.查找删除插入:
与单向链表类似,这三点比较简单,注意每个数据点之间的链接即可:
but,
这里少了前删后删:因为可以直接复用删除即可,这里我是分开写的:
void DLSTPopFront(DLN* Phead)
{
assert(Phead);
assert(Phead->next!=Phead);
DLN* first = Phead->next;
DLN* second = first->next;
Phead->next = second;
second->prev = Phead;
free(first);
first = NULL;
}
void DLSTPopBack(DLN* Phead)
{
assert(Phead);
assert(Phead->next != Phead);
DLN* tail = Phead->prev;
DLN* previous = tail->prev;
free(tail);
tail = NULL;
previous->next = Phead;
Phead->prev = previous;
}
DLN* DLSTDataFind(DLN* Phead, DListDataType x)
{
assert(Phead);
DLN* cur = Phead->next;
while (cur != Phead)
{
if (cur->Data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void DLSTInsert(DLN* Phead, DLN* pos, DListDataType x)
{
assert(Phead);
DLN* newnode = CreatNewNode(x);
DLN* prev = pos->prev;
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
void DLSTErase(DLN* Phead, DLN* pos)
{
assert(Phead);
DLN* prev = pos->prev;
DLN* next = pos->next;
free(pos);
pos = NULL;
prev->next = next;
next->prev = prev;
}
5.其他:
1.为什么Plist被修改了?
其实并没有被修改,Plist只是一个头节点,是他指向的空间内容被修改了,这里就是带头和不带头的区别了,明显的减少了二级指针的使用。
2.可以简化上述代码:
把前后删除统统都可以放在删除里面,只需要函数回调即可。