链表可以分为三大类:循环与不循环,双向和单向,带头与不带头。
上期更新了不循环不带头的单向链表,这期为循环带头的双向链表。
1.头文件包含
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
2.链表的定义
typedef strcut ListNode{
LTdataType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
3.链表初始化
LTNode* ListInit()
{
//创建一个虚拟头节点
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->next = phead;
phead->prev = phead;
return phead;
}
该链表循环,所以在初始化的时候(只有虚拟头节点这一个节点),这个虚拟头节点的next指向自己,prev也指向自己。
4.节点的创建
LTNode* CreateListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
5.链表打印
void ListPoint(LTNode* phead)//phead为虚拟头节点
{
assert(phead);
LTNode* cur = phead->next;
while(cur != phead)
{
printf("%d",cur->data);
cur = cur->next;
}
printf("\n");
}
循环链表打印的结束标志不像单向链表(NULL),它的结束标志是不等于虚拟头节点。
6.尾插
void ListPushBack(LTNode* phead, LTNodeDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = CreateLTNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//ListInsert(phead,x);后面的插入函数
}
7.尾删
void ListPopBack(LTNode* Phead)
{
assert(phead);
aseert(phead->next != phead);
LTNode* tail_pre = phead->prev->prev;
free(phead->prev);
tail_pre->next = phead;
phead->pre = tail_pre;
}
注意必须不能少于两个节点
8.头插
void ListPushFront(LTNode* phead, LTNodeDataType x)
{
assert(phead);
//ListInsert(phead->next, x);
LTNode* newnode = CreateNode(x);
LTNode* next = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = next;
next->prev = newnode;
}
9.头删
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
//ListErase(phead->next);
LTNode* next = phead->next;
phead->next = next->next;
next->next->prev = phead;
free(next);
}
10.遍历寻找
LTNode* ListFind(LTNode* phead, LTNodeDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while(cur != phead)
{
if(cur->data = x)
return cur;
cur = cur->next;
}
return NULL;
}
11.插入节点
void ListNodeInsert(LTNode* pos, LTDataType x)
{
//在pos之前插入x
assert(pos);
LTNode* pos_prev = pos->prev;
LTNode* newnode = CreateListNode(x);
pos_prev->next = newnode;
newnode->prev = pos_prev;
newnode->next = pos;
pos->prev = newnode;
}
在pos位置之前插入,首先pos不能为空(不然不知在哪插入元素),否则报错(使用断言能更快发现错误)。
创建一个元素为x的新节点,并把该节点与pos和pos->prev相连接
12.删除节点
void ListErase(LTNode* pos)
{
//删除pos位置的节点
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos->prev = NULL;
pos->next = NULL;
}
注意需要释放pos,并将前后指针置空,避免出现使用野指针的情况。