文件分类
测试模块:Test.c文件
功能声明和实现模块:List.c和List.h文件
链表的定义
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。
示意图:
链表节点结构体声明:
typedef int LTDataType;
//使用typedef便于后面改动单链表中储存的数据类型
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
双向链表各种功能的实现
动态申请一个节点
由于每次插入数据都需要动态增加一个节点,所以把申请节点写成一个函数,便于每次插入时调用该函数。
LTNode* BuyNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail");
return NULL;
}
node->data = x;
node->next = NULL;
node->prev = NULL;
return node;
}
链表初始化
创建双向链表的哨兵位,并返回该节点。
LTNode* LTInIt()
{
LTNode* phead = BuyNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
双向链表打印
void LTPrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
printf("\n");
}
判断双向链表是否为空
当哨兵位的下一个节点还是自己时,则说明该双向链表为空,返回true,否则返回false。
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
双向链表尾插
双向链表由于有哨兵位节点,尾插不需要分空链表和非空链表两种情况。因此只需要传一级指针作为参数。
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyNode(x);
phead->prev->next = newnode;
newnode->prev = phead->prev;
phead->prev = newnode;
newnode->next = phead;
}
双向链表尾删
删除前需要检查是否为空,不为空时,改变要删除节点前面指针的next指向为后面的指针,后面的指针的prev指向为前面的指针,然后把删除的节点使用free释放掉。具体代码如下:
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* ptail = phead->prev;
phead->prev = ptail->prev;
ptail->prev->next = phead;
free(ptail);
ptail = NULL;
}
双向链表头插
头插直接插在哨兵位的后面,将原本哨兵位后面的节点的prev指针指向要插入的节点,哨兵位的next指针指向要插入的节点,要插入的节点的prev指针指向哨兵位节点,插入的节点的next节点指向本哨兵位后面的节点。
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyNode(x);
phead->next->prev = newnode;
newnode->next = phead->next;
phead->next = newnode;
newnode->prev = phead;
}
双向链表头删
删除前需要检查是否为空,不为空时,直接改变哨兵位节点的next指向为第一个节点的下一个节点,然后将第一个节点的下一个节点的prev指针指向哨兵位节点,然后使用free释放掉要删除的节点。代码如下:
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* front = phead->next;
front->next->prev = phead;
phead->next = front->next;
free(front);
front = NULL;
}
双向链表的查找
首先判断是否为空,不为空时,遍历一遍链表查找是否有等于x的值,如果有,返回该节点,如果没有,返回NULL。
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
双向链表在pos位置前面插入
找到pos位置之前的节点front,然后改变pos和front的指向,具体代码如下:
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyNode(x);
LTNode* front = pos->prev;
pos->prev = newnode;
newnode->next = pos;
front->next = newnode;
newnode->prev = front;
}
双向链表删除pos位置的节点
找到pos位置前后的节点,front和next,将front节点的next指针指向next节点,将next节点的prev指针指向front节点,然后使用free释放掉pos节点。
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* front = pos->prev;
LTNode* next = pos->next;
front->next = next;
next->prev = front;
free(pos);
pos = NULL;
}
双向链表的销毁
将双向链表依次遍历并释放,最后将哨兵位节点指向NULL。
void LTDestory(LTNode* phead)
{
assert(phead);
LTNode* cur = phead;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
完整代码
List.h文件完整代码
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
// 创建返回链表的头结点.
LTNode* LTInIt();
// 双向链表销毁
void LTDestory(LTNode* phead);
// 双向链表打印
void LTPrint(LTNode* phead);
bool LTEmpty(LTNode* phead);
// 双向链表尾插
void LTPushBack(LTNode* phead, LTDataType x);
// 双向链表尾删
void LTPopBack(LTNode* phead);
// 双向链表头插
void LTPushFront(LTNode* phead, LTDataType x);
// 双向链表头删
void LTPopFront(LTNode* phead);
// 双向链表查找
LTNode* LTFind(LTNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void LTInsert(LTNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void LTErase(LTNode* pos);
List.c文件完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
//动态申请一个节点
LTNode* BuyNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail");
return NULL;
}
node->data = x;
node->next = NULL;
node->prev = NULL;
return node;
}
// 创建返回链表的头结点.
LTNode* LTInIt()
{
LTNode* phead = BuyNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
// 双向链表销毁
void LTDestory(LTNode* phead)
{
assert(phead);
LTNode* cur = phead;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
// 双向链表打印
void LTPrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
printf("\n");
}
//判断是否为空
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
// 双向链表尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead, x);
/*LTNode* newnode = BuyNode(x);
phead->prev->next = newnode;
newnode->prev = phead->prev;
phead->prev = newnode;
newnode->next = phead;*/
}
// 双向链表尾删
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTErase(phead->prev);
/*LTNode* ptail = phead->prev;
phead->prev = ptail->prev;
ptail->prev->next = phead;
free(ptail);
ptail = NULL;*/
}
// 双向链表头插
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead->next, x);
/*LTNode* newnode = BuyNode(x);
phead->next->prev = newnode;
newnode->next = phead->next;
phead->next = newnode;
newnode->prev = phead;*/
}
// 双向链表头删
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTErase(phead->next);
/*LTNode* front = phead->next;
front->next->prev = phead;
phead->next = front->next;
free(front);
front = NULL;*/
}
// 双向链表查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 双向链表在pos的前面进行插入
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyNode(x);
LTNode* front = pos->prev;
pos->prev = newnode;
newnode->next = pos;
front->next = newnode;
newnode->prev = front;
}
// 双向链表删除pos位置的节点
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* front = pos->prev;
LTNode* next = pos->next;
front->next = next;
next->prev = front;
free(pos);
pos = NULL;
}
test.c文件完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
void test()
{
LTNode* plist = LTInIt();
LTPushBack(plist, 0);
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 4);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
LTPushFront(plist, -1);
LTPushFront(plist, -2);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTNode* retnode = LTFind(plist, 3);
LTPrint(retnode);
LTInsert(retnode, 8);
LTPrint(plist);
LTDestory(plist);
}
int main()
{
test();
return 0;
}