一、结构体定义
typedef int LTDataType;
typedef struct ListNode {
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
}LTNode;
与单链表相比,多了一个前驱指针,看似结构更加复杂,但实际的操作却更加简单
二、增删查改
1.链表初始化
//创建节点
LTNode* BuyLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc");
return NULL;
}
newnode->data = x;
newnode->next = newnode->prev = NULL;
return newnode;
}
//初始化
LTNode* InitList()
{
LTNode* phead = BuyLTNode(-1);//头节点里面存啥不重要
//因为是循环链表,所以phead的前后指针都指向自己
phead->next = phead;
phead->prev = phead;
return phead;
}
2.头插+尾插
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyLTNode(x);
LTNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = BuyLTNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
3.头删+尾删
//头删
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));//链表不能为空,LTEmpty是自定义函数
LTNode* first = phead->next;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
}
//尾删
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));//链表不能为空,LTEmpty是自定义函数
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
tailPrev->next = phead;
phead->prev = tailPrev;
}
4.任意位置的插入删除
//插入
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* newnode = BuyLTNode(x);
//posPrev newnode pos
newnode->prev = posPrev;
posPrev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
//删除
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
5.查询和销毁链表
//查找
LTNode* Find(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
//销毁
void LTDestroy(LTNode* phead)
{
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
//链表置空要在外面指空,与free函数类似
写完上面的函数,其实你会发现函数的逻辑就是指针之间的相互赋值问题,只要将逻辑图画出来,实际的实现其实并不会复杂,而这都是我们通过指针找到节点的前驱和后继的结构优势导致的,当然画图解决逻辑问题这也是很关键的,希望读者可以好好体会一下上面说的两点。