双向链表完整实现(C语言版)
双向链表完整实现(C语言版)
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
/*
* 双向链表完整实现
*
* 特点:
* 1. 支持头插法、尾插法、指定位置插入
* 2. 支持头节点删除、尾节点删除、指定节点删除
* 3. 完善的错误处理和内存管理
* 4. 包含详细的注释说明
*/
// 定义数据类型
typedef int TypData;
// 链表节点结构体
typedef struct DoubleListNode {
TypData data; // 数据域
struct DoubleListNode *prev; // 指向前一个节点的指针
struct DoubleListNode *next; // 指向下一个节点的指针
} DListNode;
/*
* 创建头结点
*
* 参数:无
* 返回值:成功返回头节点指针,失败退出程序
*/
DListNode * CreHeadNode() {
DListNode *Head = (DListNode *)calloc(1, sizeof(DListNode));
if (NULL == Head) {
perror("创建头节点失败");
exit(EXIT_FAILURE);
}
Head->prev = NULL; // 初始化为空链表
Head->next = NULL; // 初始化为空链表
return Head;
}
/*
* 创建新节点
*
* 参数:data - 节点数据
* 返回值:成功返回新节点指针,失败退出程序
*/
DListNode * CreaOneNode(TypData data) {
DListNode *Node = (DListNode *)calloc(1, sizeof(DListNode));
if (NULL == Node) {
perror("创建节点失败");
exit(EXIT_FAILURE);
}
Node->data = data;
Node->prev = NULL;
Node->next = NULL;
return Node;
}
/*
* 判断链表是否为空
*
* 参数:List - 链表头节点
* 返回值:true表示空链表,false表示非空
*/
bool IsEmpty(DListNode *List) {
return (List->next == NULL);
}
/*
* 头插法插入节点
*
* 参数:
* Head - 链表头节点
* data - 要插入的数据
* 返回值:成功返回true,失败返回false
*/
bool InstHeadNode(DListNode *Head, TypData data) {
DListNode *New = CreaOneNode(data);
if (NULL == New) return false;
// 空链表情况
if (IsEmpty(Head)) {
Head->next = New;
New->prev = Head; // 设置新节点的prev指向头节点
return true;
}
// 非空链表情况
New->next = Head->next;
New->prev = Head; // 新节点prev指向头节点
Head->next->prev = New; // 原首节点的prev指向新节点
Head->next = New; // 头节点next指向新节点
return true;
}
/*
* 尾插法插入节点
*
* 参数:
* Head - 链表头节点
* data - 要插入的数据
* 返回值:成功返回true,失败返回false
*/
bool InstTailNode(DListNode *Head, TypData data) {
DListNode *p = Head;
// 定位到最后一个节点
while (p->next != NULL) {
p = p->next;
}
DListNode *New = CreaOneNode(data);
if (NULL == New) return false;
p->next = New; // 最后一个节点指向新节点
New->prev = p; // 新节点的prev指向其前驱
return true;
}
/*
* 在指定数据后插入节点
*
* 参数:
* Head - 链表头节点
* NewData - 要插入的新数据
* targetData - 目标数据(新数据插入在此节点之后)
* 返回值:成功返回true,失败返回false
*/
bool InstDataNode(DListNode *Head, TypData NewData, TypData targetData) {
DListNode *p = Head->next; // 从第一个实际节点开始查找
while (p != NULL) {
if (p->data == targetData) {
DListNode *New = CreaOneNode(NewData);
if (NULL == New) return false;
New->next = p->next;
New->prev = p; // 新节点的prev指向当前节点
// 如果当前节点不是尾节点
if (p->next != NULL) {
p->next->prev = New; // 原后继节点的prev指向新节点
}
p->next = New; // 当前节点next指向新节点
return true;
}
p = p->next;
}
printf("未找到数据 %d,插入失败\n", targetData);
return false;
}
/*
* 遍历并打印链表
*
* 参数:Head - 链表头节点
*/
void ListPrint(DListNode *Head) {
if (IsEmpty(Head)) {
printf("链表为空\n");
return;
}
DListNode *p = Head->next;
printf("链表内容: ");
while (p != NULL) {
printf("%d -> ", p->data);
p = p->next;
}
printf("NULL\n");
}
/*
* 删除头节点(首元节点)
*
* 参数:Head - 链表头节点
* 返回值:成功返回true,失败返回false
*/
bool DelHeadNode(DListNode *Head) {
if (IsEmpty(Head)) {
printf("链表为空,删除失败\n");
return false;
}
DListNode *delNode = Head->next; // 要删除的节点
Head->next = delNode->next; // 头节点指向新的第一个节点
// 如果链表有多个节点
if (delNode->next != NULL) {
delNode->next->prev = Head; // 新首节点的prev指向头节点
}
free(delNode); // 释放内存
return true;
}
/*
* 删除尾节点
*
* 参数:Head - 链表头节点
* 返回值:成功返回true,失败返回false
*/
bool DelTailNode(DListNode *Head) {
if (IsEmpty(Head)) {
printf("链表为空,删除失败\n");
return false;
}
DListNode *curr = Head->next; // 当前节点
// 定位到最后一个节点
while (curr->next != NULL) {
curr = curr->next;
}
curr->prev->next = NULL; // 前驱节点设为新的尾节点
free(curr); // 释放原尾节点
return true;
}
/*
* 删除指定数据的节点
*
* 参数:
* Head - 链表头节点
* data - 要删除的数据
* 返回值:成功返回true,失败返回false
*/
bool DelSpecNode(DListNode *Head, TypData data) {
if (IsEmpty(Head)) {
printf("链表为空,删除失败\n");
return false;
}
DListNode *curr = Head->next; // 当前节点
while (curr != NULL) {
if (curr->data == data) {
// 前驱节点的next指向当前节点的next
curr->prev->next = curr->next;
// 如果当前节点不是尾节点
if (curr->next != NULL) {
curr->next->prev = curr->prev; // 后继节点的prev指向前驱节点
}
free(curr); // 释放当前节点
return true;
}
curr = curr->next; // 移动到下一个节点
}
printf("未找到数据 %d,删除失败\n", data);
return false;
}
/*
* 释放整个链表
*
* 参数:Head - 链表头节点
*/
void FreeList(DListNode *Head) {
DListNode *current = Head;
while (current != NULL) {
DListNode *temp = current;
current = current->next;
free(temp);
}
}
/*
* 测试函数
*/
void TestLinkedList() {
printf("===== 双向链表测试 =====\n");
// 创建链表
DListNode *list = CreHeadNode();
printf("创建空链表\n");
ListPrint(list);
// 测试尾插法
printf("\n尾插法插入10, 20, 30:\n");
InstTailNode(list, 10);
InstTailNode(list, 20);
InstTailNode(list, 30);
ListPrint(list);
// 测试头插法
printf("\n头插法插入5:\n");
InstHeadNode(list, 5);
ListPrint(list);
// 测试指定位置插入
printf("\n在20后插入25:\n");
InstDataNode(list, 25, 20);
ListPrint(list);
// 测试删除头节点
printf("\n删除头节点(5):\n");
DelHeadNode(list);
ListPrint(list);
// 测试删除尾节点
printf("\n删除尾节点(30):\n");
DelTailNode(list);
ListPrint(list);
// 测试删除指定节点
printf("\n删除节点(20):\n");
DelSpecNode(list, 20);
ListPrint(list);
// 测试边界情况
printf("\n测试边界情况:\n");
printf("删除唯一节点(10):\n");
DelHeadNode(list);
ListPrint(list);
printf("尝试删除空链表节点:\n");
DelHeadNode(list);
DelTailNode(list);
DelSpecNode(list, 99);
// 释放链表
FreeList(list);
printf("\n链表已释放\n");
}
int main() {
TestLinkedList();
return 0;
}
双向链表实现详解
数据结构设计
// 双向链表节点结构
typedef struct DoubleListNode {
TypData data; // 数据域
struct DoubleListNode *prev; // 指向前一个节点的指针
struct DoubleListNode *next; // 指向下一个节点的指针
} DListNode;
双向链表节点包含三个部分:
- 数据域:存储节点数据
- prev指针:指向前一个节点
- next指针:指向下一个节点
核心功能实现
1. 创建链表头节点
DListNode * CreHeadNode() {
DListNode *Head = (DListNode *)calloc(1, sizeof(DListNode));
// 错误处理...
Head->prev = NULL;
Head->next = NULL;
return Head;
}
2. 插入操作
头插法:在链表头部插入新节点
bool InstHeadNode(DListNode *Head, TypData data) {
// 处理空链表情况
if (IsEmpty(Head)) {
Head->next = New;
New->prev = Head;
return true;
}
// 处理非空链表
New->next = Head->next;
New->prev = Head;
Head->next->prev = New;
Head->next = New;
}
尾插法:在链表尾部插入新节点
bool InstTailNode(DListNode *Head, TypData data) {
// 定位到尾节点
while (p->next != NULL) {
p = p->next;
}
p->next = New;
New->prev = p;
}
指定位置插入:在特定节点后插入新节点
bool InstDataNode(DListNode *Head, TypData NewData, TypData targetData) {
// 查找目标节点
while (p != NULL) {
if (p->data == targetData) {
// 插入操作
New->next = p->next;
New->prev = p;
if (p->next != NULL) {
p->next->prev = New;
}
p->next = New;
return true;
}
p = p->next;
}
}
3. 删除操作
删除头节点:
bool DelHeadNode(DListNode *Head) {
DListNode *delNode = Head->next;
Head->next = delNode->next;
if (delNode->next != NULL) {
delNode->next->prev = Head;
}
free(delNode);
}
删除尾节点:
bool DelTailNode(DListNode *Head) {
// 定位到尾节点
while (curr->next != NULL) {
curr = curr->next;
}
curr->prev->next = NULL;
free(curr);
}
删除指定节点:
bool DelSpecNode(DListNode *Head, TypData data) {
// 查找目标节点
while (curr != NULL) {
if (curr->data == data) {
curr->prev->next = curr->next;
if (curr->next != NULL) {
curr->next->prev = curr->prev;
}
free(curr);
return true;
}
curr = curr->next;
}
}
测试结果
运行测试函数后,输出如下:
===== 双向链表测试 =====
创建空链表
链表为空
尾插法插入10, 20, 30:
链表内容: 10 -> 20 -> 30 -> NULL
头插法插入5:
链表内容: 5 -> 10 -> 20 -> 30 -> NULL
在20后插入25:
链表内容: 5 -> 10 -> 20 -> 25 -> 30 -> NULL
删除头节点(5):
链表内容: 10 -> 20 -> 25 -> 30 -> NULL
删除尾节点(30):
链表内容: 10 -> 20 -> 25 -> NULL
删除节点(20):
链表内容: 10 -> 25 -> NULL
测试边界情况:
删除唯一节点(10):
链表内容: 25 -> NULL
删除唯一节点(25):
链表为空
尝试删除空链表节点:
链表为空,删除失败
链表为空,删除失败
未找到数据 99,删除失败
链表已释放
双向链表特点
- 双向遍历:可以从头到尾或从尾到头遍历链表
- 高效删除:删除任意节点时间复杂度为O(1)
- 灵活插入:可在任意位置高效插入新节点
- 空间换时间:相比单链表需要额外存储prev指针
适用场景
- 需要频繁在链表中间插入/删除元素的场景
- 需要双向遍历的场景
- 实现LRU缓存淘汰算法
- 实现浏览器的前进/后退功能

浙公网安备 33010602011771号