目录
带头双向循环链表是一种相对复杂的链表,但在存储数据方面也有一定的优势。本文就简单地介绍下如何用c语言实现——增删查改这基本功能。
基本结构
如图就是基本结构,可以看出每个节点都会存储一个数据两个指针,其中head节点为“哨兵”节点不存储数据。我们把前后两个指针分别命名为prev和next,可以看出头结点的prev指向尾结点,尾结点的next指向头结点,这样形成环结构。
认识了基本结构后,我们就开始实现相关功能。
增删查改
首先创建结构体变量:
typedef struct doublelist
{
struct doublelist* prev;
int val;
struct doublelist* next;
}dlist;
如果命名太长可进行一定的缩写,如上图缩写为dlist。由上方的基本结构可知我们需要一个“哨兵位”,当哨兵位的指针均指向自己时,则为空链表,我们先要进行初始化:
dlist* dlistinit()
{
dlist* guard = (dlist*)malloc(sizeof(dlist));
if (guard == NULL)
{
perror("malloc fail");
exit(-1);
}
guard->next = guard;
guard->prev = guard;
return guard;
}
接着我们开始实现增这个功能,可分为尾增、头增、任意位置增。我们先从尾增开始。由基本结构我们可以看出头结点的prev就是指向的尾结点,而我们要做的就是创建一个新结点使它成为新的尾结点。我们先实现创建新结点的函数:
dlist* makenewnode(x)
{
dlist* newnode = (dlist*)malloc(sizeof(dlist));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
然后根据逻辑关系:
void dlistbackpush(dlist* head, int x)
{
assert(head);//判断head是否为空指针
dlist* newnode = makenewnode(x);
dlist* tail = head->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = head;
head->prev = newnode;
}
完成尾增后另外两个的实现也就相对简单:
void dlistfrontpush(dlist* head, int x)
{
assert(head);
dlist* newnode = makenewnode(x);
dlist* next = head->next;
next->prev = newnode;
newnode->next = next;
newnode->prev = head;
head->next = newnode;
}
在进行任意位置的增删时,我们要实现一个查找位置的函数:
dlist* dlistfind(dlist* head, int x)
{
assert(head);
dlist* cur = head->next;
while (cur != head)
{
if (cur->val == x)
return cur;
cur = cur->next;
}
return NULL;
}
这样就可以找出相应位置进行一些列操作了。
void dlistinsert(dlist* pos, int x)
{
assert(pos);
dlist* newnode = makenewnode(x);
dlist* prev = pos->prev;
newnode->next = pos;
newnode->prev = prev;
pos->prev = newnode;
prev->next = newnode;
}
接下来就是删除方面,逻辑也很简单,就是将指针的方向重置,并且释放空间。要注意的一点是要确保不为空链表,所以我们就需要一个函数:
bool ifdlistempty(dlist* head)
{
return (head->next == head);//以bool类型来进行逻辑判断
}
随后就开始:尾删、头删、任意位置删:
void dlistbackpop(dlist* head)
{
assert(head);
assert(!(ifdlistempty(head)));
dlist* tail = head->prev;
dlist* prev = tail->prev;
prev->next = head;
head->prev = prev;
free(tail);
tail = NULL;
}
void dlistfrontpop(dlist* head)
{
assert(head);
assert(!ifdlistempty(head));
dlist* cur = head->next;
dlist* newcur = cur->next;
head->next = newcur;
newcur->prev = head;
free(cur);
cur = NULL;
}
void dlisterase(dlist* pos)
{
assert(pos);
dlist* next = pos->next;
dlist* prev = pos->prev;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}
在实现完成后我们可以打印,需要一个打印函数:
void dlistprint(dlist* head)
{
assert(head);
printf("Guard<-->");
dlist* cur = head->next;
while (cur != head)
{
printf("%d<-->", cur->val);
cur = cur->next;
}
printf("\n");
}
最后也别忘了销毁链表:
void dlistdertory(dlist* head)
{
assert(head);
dlist* cur = head->next;
while (cur != head)
{
dlist* next = cur->next;
free(cur);
cur = next;
}
free(head);
head = NULL;
}
测试用例
void test()
{
dlist* head = dlistinit();
dlistbackpush(head, 1);
dlistbackpush(head, 2);
dlistbackpush(head, 3);
dlistbackpush(head, 7);
dlistbackpush(head, 8);
dlistprint(head);
dlistfrontpush(head, 4);
dlistfrontpush(head, 5);
dlistprint(head);
dlistbackpop(head);
dlistbackpop(head);
dlistprint(head);
dlistfrontpop(head);
dlistfrontpop(head);
dlistprint(head);
dlist* pos = dlistfind(head, 2);
dlistinsert(pos, 5);
dlistinsert(pos, 6);
dlistprint(head);
dlisterase(pos);
dlistprint(head);
dlistdertory(head);
}
int main()
{
test();
return 0;
}