【数据结构】双链表
双链表
结构描述:
#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;
}
}
头删
-
表空:报错
-
非空:删除第一个元素
-
当链表只有一个元素时:
- 直接释放
![img]()
-
当链表有多个元素时:
- 用临时变量
Tmp保存头指针,头指针向后移动一位Head = Head->Next; - 把当前头指针指向的节点的
Pre域置空; - 释放
Tmp
![img]()
- 用临时变量
-
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;
}
}
尾删
-
表空:报错
-
非空:
-
只有一个元素:直接释放
-
有多个元素时:遍历至表尾,释放
![img]()
-
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 是非负数。



浙公网安备 33010602011771号