【数据结构】双链表
双链表
结构描述:
#include <iostream> #include <cstdlib> using namespace std; typedef int DataType; //链表节点 typedef struct Node { DataType A; struct Node * Prev; struct Node * Next; }Node; class DoubleLinkedList { private: Node * Head; public: //尾插 void PushBack(DataType X); //头插 void PushFront(DataType X); //在第Pos个位置插入,若不存在该位置报错 void InsertThisPos(int Pos, DataType X); //尾删 void PopBack(); //头删 void PopFront(); //删除第Pos位元素,若无该位置,则返回错误 void DeleteThisPos(int Pos); // 把链表置为空表 void MakeEmpty(); //查找值为 Target的元素,若不存在,则返回空指针,存在则返回节点指针 Node * SearchByValue(DataType Target); void ModifyByValue(DataType Value, DataType NewValue); //创建一个空表 void Init(); //判空,若空,则返回true,反之返回false bool IsEmpty(); //打印链表 void Print(); //分配空间,根据参数X分配空间,分配成功则返回该节点的指针 Node * BuyNode(DataType X); };
初始化
把 头节点 Head
指向空
void DoubleLinkedList::Init() { Head = nullptr; }
判空:
Head
指向空,即链表为空。
bool DoubleLinkedList::IsEmpty() { return Head == nullptr; }
分配节点
使用 malloc()
函数分配空间,为节点的各个域赋值。
Node * DoubleLinkedList::BuyNode(DataType X) { Node * NewNode = (Node *)malloc(sizeof (Node)); if (NewNode == nullptr) { cout << "Malloc Failed!\n"; exit(-1); } NewNode->A = X; NewNode->Prev = nullptr; NewNode->Next = nullptr; return NewNode; }
头插
方法原型: void PushFront(DataType X)
功能:根据 X
分配节点,并把该节点设置为链表的首个元素
- 若表空:直接头指针
Head
指向新节点NewNode
- 表非空:
- 把
NewNode
的Next
指针域指向头指针Head
; - 把
Head
的Prev
指针域指向NewNode
; - 把
Head
指向NewNode
。
- 把
图示:
void DoubleLinkedList::PushFront(DataType X) { //表空 if (IsEmpty()) { Node * NewNode = BuyNode(X); Head = NewNode; } //表非空 else { Node * NewNode = BuyNode(X); NewNode->Next = Head; Head->Prev = NewNode; Head = NewNode; } }
头删
-
表空:报错
-
非空:删除第一个元素
-
当链表只有一个元素时:
- 直接释放
-
当链表有多个元素时:
- 用临时变量
Tmp
保存头指针,头指针向后移动一位Head = Head->Next
; - 把当前头指针指向的节点的
Pre
域置空; - 释放
Tmp
- 用临时变量
-
void DoubleLinkedList::PopFront() { if (IsEmpty()) { cout << "List Is Empty!\n"; exit(-1); } //非空 //只有一个元素时 if (Head->Next == nullptr) { free(Head); Head = nullptr; } //有多个元素时 else { //临时变量保存 Node * Tmp = Head; //头指针后移一位 Head = Head->Next; //当前头指针的 Prev域置空 Head->Prev = nullptr; //释放原来的头指针指向的节点 free(Tmp); Tmp = nullptr; } }
插入
方法:void InsertThisPos(int Pos, DataType X);
功能:在第 Pos (Pos >= 0)
个位置插入元素(从0
开始计数)。
-
若
Pos == 0
直接调用头插方法 -
若
Pos
不为0,判断Pos
是否存在: -
存在:插入元素
-
否则:返回错误
利用双指针,让控制循环的变量 i
和 PreCur
一起向后走,直到 i == Pos
或者 PreCur->Next == nullptr
停下。此时若 i == Pos && PreCur->Next != nullptr
,由说明该位置可以插入元素,否则不可插入。
其中,表尾是一个特殊的情况,一般表中间插入会将插入节点 NewNode
的后继节点的 Prev
指针指向 NewNode
,但是在表尾插入,NewNode
的下一位是空指针,无法进行这种操作。
void DoubleLinkedList::InsertThisPos(int Pos, DataType X) { if (Pos < 0) { cout << Pos << " Posiont is not exist!\n"; exit(-1); } //表头插入 if (Pos == 0) { PushFront(X); } else { int i; //要插入位置的前一位 Node * PreCur; for (i = 1, PreCur = Head; i < Pos && PreCur != nullptr; i++, PreCur = PreCur->Next) { ; } //表尾插入 if (i == Pos && PreCur->Next == nullptr) { Node * NewNode = BuyNode(X); //设置新节点的指针域 NewNode->Next = PreCur->Next; NewNode->Prev = PreCur; //修改节点之间的关系 NewNode->Prev->Next = NewNode; //这一步在表尾插入时无法使用,对一个空指针取值无意义。 //// NewNode->Next->Prev = NewNode; } //表中间插入 else if (Pos == i && PreCur != nullptr) { Node * NewNode = BuyNode(X); //设置新节点的指针域 NewNode->Next = PreCur->Next; NewNode->Prev = PreCur; //修改节点之间的关系 NewNode->Prev->Next = NewNode; NewNode->Next->Prev = NewNode; } else { cout << Pos << " Posiont is not exist!\n"; exit(-1); } } }
删除
- 表空:报错
- 非空:
- 头
- 中
- 尾
void DoubleLinkedList::DeleteThisPos(int Pos, DataType X) { if (Pos < 0) { cout << Pos << " Posiont is not exist!\n"; exit(-1); } //删除首个节点 if (Pos == 0) { PopFront(X); } else { int i; //要删除的节点 Node * Cur; //Cur->Next == nullptr则说明Cur是末尾元素 for (i = 1, Cur = Head->Next; Cur->Next != nullptr && i < Pos; i++, Cur = Cur->Next) { ; } //是最后一位元素 if (i == Pos && Cur->Next == nullptr) { Node * Tmp = Cur; //把倒数第二位元素向空指针,即把倒数第二位元素变成末尾元素 Cur->Prev->Next = Cur->Next; //释放 free(Tmp); Tmp = nullptr; } //非末尾元素 else if (i == Pos && Cur->Next != nullptr) { Node * Tmp = Cur; //设置节点关系 Cur->Prev->Next = Cur->Next; //非末尾元素多这一步,把后继的Prev指针重新设置位置 Cur->Next->Pre = Cur->Pre; //释放 free(Tmp); Tmp = nullptr; } else { cout << Pos << " Posiont is not exist!\n"; exit(-1); } } }
尾插
- 表空:直接把头指针指向新节点
NewNode
- 非空:遍历至表尾,在末尾接入
NewNode
,并设置好其指针域
void DoubleLinkedList::PushBack(DataType X) { if (IsEmpty()) { Node * NewNode = BuyNode(X); Head = NewNode; } else { Node * NewNode = BuyNode(X); Node * Cur = Head; //Cur->Next == nullptr时退出循环,即Cur是末尾节点时退出循环 while (Cur->Next != nullptr) { Cur = Cur->Next; } //设置新节点的指针域 NewNode->Next = Cur->Next; NewNode->Prev = Cur; //在末尾插入该节点 Cur->Next = NewNode; } }
尾删
-
表空:报错
-
非空:
-
只有一个元素:直接释放
-
有多个元素时:遍历至表尾,释放
-
void DoubleLinkedList::PopBack() { if (IsEmpty()) { cout << "List Is Empty!\n"; exit(-1); } //只有一个元素时 if (Head->Next == nullptr) { free(Head); Head = nullptr; } else { //有多个元素时,遍历到末尾,删除末尾元素。 Node * Cur = Head; while (Cur->Next != nullptr) { Cur = Cur->Next; } Cur->Prev->Next = Cur->Next; free(Cur); Cur = nullptr; } }
根据值查找元素
- 表空:返回错误
- 非空:遍历链表,逐一比较,直到第一个与目标值相等的元素时,返回该节点的指针。若直至表尾仍未发现该元素,则返回一个空指针。
Node * DoubleLinkedList::SearchByValue(DataType Target) { if (IsEmpty()) { cout << "List Is Empty!\n"; exit(-1); } Node * Cur = Head; while (Cur != nullptr) { if (Cur->A == Target) { return Cur; } Cur = Cur->Next; } return nullptr; }
修改链表元素值
- 查找:
- 表空:返回错误
- 非空:找到返回,找不到返回空
- 修改:根据查找到的结果修改;
- 查找返回空:给出提示消息,并退出程序
- 查找返回元素节点指针:修改该元素的值
void DoubleLinkedList::ModifyByValue(DataType Target, DataType NewValue) { Node * Ret = SearchByValue(Target); if (Ret == nullptr) { cout << "The Target Is Not Exist!\n"; exit(-1); } Ret->A = NewValue; }
置空
- 表空:报错
- 非空:一直头删,直至表空
void DoubleLinkedList::MakeEmpty() { if (IsEmpty()) { cout << "List Is Empty!\n"; exit(-1); } while (!IsEmpty()) { cout << Head->A << " "; PopFront(); cout << "Has Been Poped!\n"; } cout << "Make Empty Successfully!\n"; }
踩坑
双链表插入操作应该先考虑头和尾。不考虑尾节点的话,该节点为尾节点,但是却要进行NewNode->Next->Pre = NewNode
的操作,这样对空指针来说是不被允许的。
考虑插入删除第 Pos
个位置时,一定保证 Pos
是非负数。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架