对于双向链表来说,其实和单向链表差别不大,只要你了解单向链表的底层逻辑,实现双向链表其实很简单。
一、双向链表的结点/链表
typedef struct ListNode { // 双向链表的节点
int data; // 数据域
struct ListNode* next; // 后继指针
struct ListNode* prev; // 前驱指针
} ListNode;
typedef struct LinkedList { // 完整的双向链表
ListNode* head; // 头节点
int len; // 链表长度
} LinkedList;
二、初始化/创建一个双向链表
与单向链表一致
void Creat_LinkedList(LinkedList* list) // 初始化链表
{
list->head = NULL;
list->len = 0;
}
三、销毁链表
与单链表一致
void Destroy_LinkList(LinkedList* list) // 销毁链表
{
while (list->head)
{
ListNode* temp = list->head; // 缓存当前头节点
list->head = list->head->next; // 移动头指针
free(temp); // 释放当前节点
}
list->len = 0;
}
四、在双向链表中插入元素
4.1、核心代码实现
- 与单链表一样,分为头插法与尾插法。
- 与单链表相比,多了两条语句,即prev相关的语句,但难度不大
- 顺序:先把新结点的链表中的结点建立联系--->再把链表中的结点指向进行修改
// 在链表中插入元素
void Insert_LinkList(LinkedList* list, int i, int value)//第i个位置插入值为value的元素
{
if (i < 0 || i > list->len) // 越界检查
{
printf("插入失败\n");
return;
}
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
newNode->data = value;
if (i == 0) // 在头部插入
{
newNode->next = list->head; // 新节点指向原头节点
newNode->prev = NULL; // 新节点前驱为空
if (list->head) // 如果链表非空,更新原头节点的前驱指针
{
list->head->prev = newNode;
}
list->head = newNode; // 更新头节点
}
else // 插入到其他位置
{
ListNode* current = list->head;
for (int j = 0; j < i - 1; j++) // 找到插入位置的前一个节点
{
current = current->next;
}
newNode->next = current->next; // 新节点的后继为current的后继
newNode->prev = current; // 新节点的前驱为current
if (current->next) // 如果current后有节点,更新其前驱指针
{
current->next->prev = newNode;
}
current->next = newNode; // 更新current的后继为新节点
}
list->len++;
}
4.2、算法图解
4.2.1、当链表为空时
- 对单结点进行操作,prev与next都置空
- 头节点指向单节点
4.2.2、插入节点为头节点,但链表不为空时
- 先让新节点和链表节点搭上线(步骤1/2)
- 修改链表节点的指向(步骤3)
- 修改头指针的指向
4.2.3、插入的节点不为头节点时
- 定义迭代器,找到需要插入的节点位置的前一个结点
- 让我们需要插入的结点与链表搭上线(步骤3/4)
- 修改链表元素指向(5/6)
- 温馨提醒:如果插入为尾结点,则去掉步骤5,因为cur->next=NULL。而步骤3自动变成newNode->next=NULL
五、在双向链表中删除节点
5.1、核心代码实现:
- 需要一个变量toDelete用来最后的释放节点
- 当然,如果不为头节点时,同时也需要一个迭代器实现删除节点的查找
// 删除节点
void Delete_LinkList(LinkedList* list, int i)
{
if (i < 0 || i >= list->len) // 越界检查
{
printf("删除失败\n");
return;
}
ListNode* toDelete = list->head;
if (i == 0) // 删除头节点
{
list->head = toDelete->next; // 头节点后移
if (list->head) // 更新新头节点的前驱指针
{
list->head->prev = NULL;
}
free(toDelete); // 释放旧头节点
}
else // 删除其他节点
{
ListNode* current = list->head;
for (int j = 0; j < i - 1; j++) // 找到删除位置的前一个节点
{
current = current->next;
}
toDelete = current->next; // 要删除的节点
current->next = toDelete->next; // 前一个节点的后继指向要删除节点的后继
if (toDelete->next) // 如果删除节点的后继存在,更新其前驱
{
toDelete->next->prev = current;
}
free(toDelete); // 释放要删除的节点
}
list->len--;
}
5.2算法图解:
5.2.1、删除节点为头节点
- 定义一个toDelete指向头节点(步骤1,为释放做准备)
- 移动头节点指向(步骤2)
- 修改节点指向(步骤2就是为了这步做准备,不然访问不到这个节点)
- 释放toDelete节点(步骤4)
5.2.2、删除的节点不是头节点:
- 删除指向toDelete,迭代器cur(步骤1234)
- 修改cur的next节点,断开cur->next与toDelete的关系,建立起与toDelete->next的关系(步骤5)
- 修改toDelete->prev的节点指向,断开toDelete->prev与toDelete的关系,建立起与cur的关系(步骤6)
步骤5/6使得toDelete与链表完全断开联系
- 释放toDelete所指向的节点
六、通过元素来查找链表节点
与单链表没差别,不再赘述
// 通过元素查找节点
ListNode* Find_element_Linked(LinkedList* list, int value)
{
ListNode* current = list->head; // 迭代器从头开始
while (current) // 遍历链表
{
if (current->data == value) // 找到元素
{
return current;
}
current = current->next; // 继续下一个节点
}
return NULL; // 没有找到
}
七、通过下标来查找链表节点
与单链表没差别,不再赘述
// 通过下标查找节点
ListNode* Find_Index_Linked(LinkedList* list, int i)
{
if (i < 0 || i >= list->len) // 越界检查
{
printf("寻找错误\n");
return NULL;
}
ListNode* current = list->head;
for (int j = 0; j < i; j++) // 遍历到第i个节点
{
current = current->next;
}
return current;
}
八、更新链表元素
// 更新链表元素
void Update_LInked(LinkedList* list, int i, int value)
{
ListNode* temp = Find_Index_Linked(list, i);
if (temp) // 如果节点存在
{
temp->data = value; // 更新数据
}
}
九、打印链表
// 打印链表
void Print_Linked(LinkedList* list)
{
ListNode* current = list->head;
while (current)
{
printf("%d <-> ", current->data); // 打印当前节点
current = current->next; // 移动到下一个节点
}
printf("NULL\n");
}
十、完整的代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
typedef struct ListNode { // 双向链表的节点
int data; // 数据域
struct ListNode* next; // 后继指针
struct ListNode* prev; // 前驱指针
} ListNode;
typedef struct LinkedList { // 完整的双向链表
ListNode* head; // 头节点
int len; // 链表长度
} LinkedList;
void Creat_LinkedList(LinkedList* list) // 初始化链表
{
list->head = NULL;
list->len = 0;
}
void Destroy_LinkList(LinkedList* list) // 销毁链表
{
while (list->head)
{
ListNode* temp = list->head; // 缓存当前头节点
list->head = list->head->next; // 移动头指针
free(temp); // 释放当前节点
}
list->len = 0;
}
// 在链表中插入元素
void Insert_LinkList(LinkedList* list, int i, int value)//第i个位置插入值为value的元素
{
if (i < 0 || i > list->len) // 越界检查
{
printf("插入失败\n");
return;
}
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
newNode->data = value;
if (i == 0) // 在头部插入
{
newNode->next = list->head; // 新节点指向原头节点
newNode->prev = NULL; // 新节点前驱为空
if (list->head) // 如果链表非空,更新原头节点的前驱指针
{
list->head->prev = newNode;
}
list->head = newNode; // 更新头节点
}
else // 插入到其他位置
{
ListNode* current = list->head;
for (int j = 0; j < i - 1; j++) // 找到插入位置的前一个节点
{
current = current->next;
}
newNode->next = current->next; // 新节点的后继为current的后继
newNode->prev = current; // 新节点的前驱为current
if (current->next) // 如果current后有节点,更新其前驱指针
{
current->next->prev = newNode;
}
current->next = newNode; // 更新current的后继为新节点
}
list->len++;
}
// 删除节点
void Delete_LinkList(LinkedList* list, int i)
{
if (i < 0 || i >= list->len) // 越界检查
{
printf("删除失败\n");
return;
}
ListNode* toDelete = list->head;
if (i == 0) // 删除头节点
{
list->head = toDelete->next; // 头节点后移
if (list->head) // 更新新头节点的前驱指针
{
list->head->prev = NULL;
}
free(toDelete); // 释放旧头节点
}
else // 删除其他节点
{
ListNode* current = list->head;
for (int j = 0; j < i - 1; j++) // 找到删除位置的前一个节点
{
current = current->next;
}
toDelete = current->next; // 要删除的节点
current->next = toDelete->next; // 前一个节点的后继指向要删除节点的后继
if (toDelete->next) // 如果删除节点的后继存在,更新其前驱
{
toDelete->next->prev = current;
}
free(toDelete); // 释放要删除的节点
}
list->len--;
}
// 通过元素查找节点
ListNode* Find_element_Linked(LinkedList* list, int value)
{
ListNode* current = list->head; // 迭代器从头开始
while (current) // 遍历链表
{
if (current->data == value) // 找到元素
{
return current;
}
current = current->next; // 继续下一个节点
}
return NULL; // 没有找到
}
// 通过下标查找节点
ListNode* Find_Index_Linked(LinkedList* list, int i)
{
if (i < 0 || i >= list->len) // 越界检查
{
printf("寻找错误\n");
return NULL;
}
ListNode* current = list->head;
for (int j = 0; j < i; j++) // 遍历到第i个节点
{
current = current->next;
}
return current;
}
// 更新链表元素
void Update_LInked(LinkedList* list, int i, int value)
{
ListNode* temp = Find_Index_Linked(list, i);
if (temp) // 如果节点存在
{
temp->data = value; // 更新数据
}
}
// 打印链表
void Print_Linked(LinkedList* list)
{
ListNode* current = list->head;
while (current)
{
printf("%d <-> ", current->data); // 打印当前节点
current = current->next; // 移动到下一个节点
}
printf("NULL\n");
}
int main()
{
LinkedList list; // 定义链表
Creat_LinkedList(&list); // 创建链表
Insert_LinkList(&list, 0, 10); // 插入数据
Insert_LinkList(&list, 1, 20);
Insert_LinkList(&list, 2, 30);
Insert_LinkList(&list, 3, 40);
printf("Original List:\n");
Print_Linked(&list); // 打印链表
Delete_LinkList(&list, 2); // 删除节点
Update_LInked(&list, 1, 100); // 更新数据
printf("Change1 List:\n");
Print_Linked(&list); // 打印链表
ListNode* found = Find_element_Linked(&list, 30); // 查找元素
if (!found)
{
printf("此节点不存在\n");
}
Destroy_LinkList(&list); // 销毁链表
system("pause");
return 0;
}
纯私人撰写,欢迎大家私信勘误!!