数据结构-带头双向循环链表的基本实现(C语言,简单易懂,含全部代码)
链表的概念和结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
结构:实际中链表的结构非常多样,以下情况组合起来就有8种链表结构。
(1)单向、双向
(2)带头、不带头
(3)循环、非循环
本篇主要详解带头双向循环链表,结构如图:
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。
链表的实现
首先在头文件中定义结构体和提供接口:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}ListNode;
void ListPrint(ListNode* phead);//打印链表
ListNode* BuyListNode(LTDataType x);//开辟新节点
ListNode* ListInit();//初始化哨兵位
void ListPushBack(ListNode* phead, LTDataType x);//尾插,只要不改变头指针的内容,就不用传二级指针
void ListPushFront(ListNode* phead, LTDataType x);//头插
void ListPopBack(ListNode* phead);//尾删
void ListPopFront(ListNode* phead);//头删
ListNode* ListFind(ListNode* phead, LTDataType x);//查找
void ListInsert(ListNode* pos, LTDataType x);//pos位置前插
void ListErase(ListNode* pos);//pos位置删除
其次在源文件中实现接口功能:
(1)链表打印
void ListPrint(ListNode* phead)
{
ListNode* cur = phead;
while (cur != phead)
{
printf("%d", cur->data);
cur = cur->next;//找到下一个节点
}
printf("\n");
}
(2)开辟新节点
ListNode* BuyListNode(LTDataType x)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->next = NULL;
node->prev = NULL;
node->data = x;
return node;
}
(3)初始化哨兵位
ListNode* ListInit()
{
ListNode* phead = BuyListNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
(4)尾插
带头双向循环链表的头插尾插非常方便,只需要找到头(phaed->next)和尾(phead->prev)并链接
void ListPushBack(ListNode* phead, LTDataType x)
{
//无论是0个(只有哨兵位)还是多个节点都可以,与节点个数无关
assert(phead);
ListNode* tail = phead->prev;//找尾
ListNode* newnode = BuyListNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
(5)头插
void ListPushFront(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* first = phead->next;
ListNode* newnode = BuyListNode(x);
//phead newnode first
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
(6)尾删
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* tail = phead->prev;
ListNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;
}
(7)头删
void ListPopFront(ListNode* phead)
{
assert(phead);//没有节点
assert(phead->next != phead);//只有phead
ListNode* first = phead->next;
ListNode* second = first->next;
free(first);
phead->next = second;
second->prev = phead;
}
(8)查找x
ListNode* ListFind(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
(9)pos位置前插
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* newnode = BuyListNode(x);
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
(10)pos位置删除
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
主函数的设计大家可以自由发挥,做个简单的测试功能调用函数或是创建菜单栏实现交互都可以。我水平有限,请朋友们谅解!写的不好的地方还请大佬们指出。最后,如果这篇文章对你有帮助,就点个赞或者收藏评论一下吧,谢谢大家支持😁。
下期预告——栈和队列详解!