宇宙第一小正太\ (o)/~萌量爆表求带飞=≡Σ((( つo)つ~ dalao们点个关注呗~
大二上网课,老师要求写一个链表数据结构。
小熊虽然很不情愿,但是还是硬着头皮干完啦!!!
本数据链表的核心功能:
void Push_Back(T Value); //在链表的末尾插入一个元素
void Pop_Back() { Erase(Length - 1); } //弹出末尾最后一个元素
void Insert(int Index, T Value); //在Index之后,添加一个元素
void Erase(int Index); //删除在链表位于Index的元素
void Clear(); //清空链表
int GetLength(); //获得链表的长度
T &operator[](int Index); //或取变量或者设置变量
另外本链表还有一个功能是,实现了一个简单的迭代器:
typedef Node_Iterator iterator; //现在通过tyoedef,iterator就是一种类型
感觉自己又又又水了一次作业QAQ
链表结构设计:
实际上,就算本链表的Size=0,依旧存在两个永远不会被删除的头部标记数据元,和尾部标记数据元。
本链表的结构设计是:PowerBearList是一个链表。在PowerBearList的内部封装了cell数据元,我们通过PowerBearList来操作Cell数据元以此来修改链表的数据。
上文所说的头部标记数据元和尾部标记数据元正是:*cell_Front = new Cell(), *cell_Back = new Cell();
位于第21行。
实际上,链表本生真的没有什么难的,很多老师喜欢把简单的问题搞得复杂化,小熊建议您,您只需要知道链表是个数据结构就可以啦,什么约瑟夫环的问题还是什么环的问题,那个属于算法,饭要一口一口吃,咱们先慢慢来。
核心代码完全降解:
定位链表index:
下标从0开始。
在本程序中,您将会看见很多如下这样的代码(在各种函数中它都是高调的出现):
Cell *ptr = this->cell_Front->Next;
for (int i = 0; i < Index; i++) {
ptr = ptr->Next;
}
其中,第一行的*ptr
是一个指针,它进行初始化就在第一行,首先定位到调用函数的对象this,然后再从this的头部标记的Next,找到了真正的第一个在列表中的对象。其中对应的是i=0,然后 ptr = ptr->Next
则会自己往下运行查找下一个。
Insert插入cell函数:
哇塞
将这种链表的插入网上已经烂大街了,笔者就不逼逼叨叨了,下面直接贴出关键代码,关键代码并没有绕弯子,读者可以拿出纸和笔花几十秒跟着代码模拟一遍,然后就什么都懂啦。
Cell *leftL, *rightL;
leftL = ptr;
rightL = ptr->Next;
Cell *Middle = new Cell();
Middle->Value = Value;
Middle->Pre = leftL;
Middle->Next = rightL;
leftL->Next = Middle;
rightL->Pre = Middle;
this->Length++;
迭代器的实现:
其实就是在PowerBearList里面又封装了一个Node_Iterator
只不过在类里面封装的是private。然后我们又在PowerBearList的public中使用typedef Node_Iterator iterator;
来将iterator变为了一种类型为Node_Iterator的类型。
具体实现在代码中已经给出了注释,在这里小熊不再多言。
下面直接贴出这看起来很长实际很简单的代码吧!
代码一览:
// 环境 LLVM Clang++ MacOS
// C++ 标准 C++11
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;
template <typename T> class PowerBearList {
private:
int Length;
class Cell {
public:
T Value;
Cell *Pre, *Next;
Cell(T Value) { this->Value = Value; }
// Cell(T Value) : Value(Value) {}
// https://stackoverflow.com/questions/19576458/constructor-for-must-explicitly-initialize-the-reference-member
Cell() : Value() {}
//下面的则是对一些基本的运算符号进行重载
} *cell_Front = new Cell(), *cell_Back = new Cell();
//设置标志物,一个是链表头标记物,一个是链表尾的标记物,新增入的Cell会被夹在中间,这两个标记物是永久存在的
public:
PowerBearList();
PowerBearList(T Value);
void Push_Back(T Value); //在链表的末尾插入一个元素
void Pop_Back() { Erase(Length - 1); } //弹出末尾最后一个元素
void Insert(int Index, T Value); //在Index之后,添加一个元素
void Erase(int Index); //删除在链表位于Index的元素
void Clear(); //清空链表
int GetLength(); //获得链表的长度
T &operator[](int Index); //或取变量或者设置变量
private:
class Node_Iterator { //迭代器的实现,引用文章如下
// https://www.cnblogs.com/wengle520/archive/2020/03/14/12492708.html
private:
Cell *ptr; //指向容器中的某个元素
public:
Node_Iterator(Cell *p = nullptr) : ptr(p) {}
//对*进行重载,为了后面的*ite做准备,使用引用是为了方便*ite=1这样的修改
T &operator*() const { return ptr->Value; }
//对迭代器的++进行重载
Node_Iterator &operator++() {
ptr = ptr->Next;
return *this;
}
Node_Iterator operator++(int) {
Cell *tmp = ptr;
// this 是指向Node_Iterator的常量指针,因此*this就是ta的对象,前置++已经被重载过
++(*this);
return Node_Iterator(tmp);
}
bool operator==(const Node_Iterator &t) const { return t.ptr == this->ptr; }
bool operator!=(const Node_Iterator &t) const { return t.ptr != this->ptr; }
};
public:
typedef Node_Iterator iterator; //现在通过tyoedef,iterator就是一种类型
iterator begin() const { return Node_Iterator(this->cell_Front->Next); }
iterator end() const { return Node_Iterator(this->cell_Back); }
};
template <typename T> void PowerBearList<T>::Erase(int Index) {
if (Index < 0 || Index > this->Length - 1 || this->Length == 0) {
cout << "ERROR:删除元素失败,下标越界,或因为链表数据为空";
exit(0);
}
Cell *ptr = this->cell_Front->Next;
for (int i = 0; i < Index; i++) {
ptr = ptr->Next;
}
Cell *leftL, *rightL, *middleL;
leftL = ptr->Pre;
rightL = ptr->Next;
middleL = ptr;
leftL->Next = rightL;
rightL->Pre = leftL;
delete[] middleL;
this->Length--;
}
template <typename T> void PowerBearList<T>::Insert(int Index, T Value) {
if (this->Length == 0 && Index == 0)
Push_Back(Value);
try {
if (Index < 0 || Index > this->Length - 1) {
throw "ERROR:访问下标越届,程序停止运行。";
}
} catch (const char *msg) {
std::cout << msg << " "
<< "实例地址定位:" << this << " "
<< "使用的方法:void Insert(int Index, T Value);";
exit(0);
}
Cell *ptr = this->cell_Front->Next;
for (int i = 0; i < Index; i++) {
ptr = ptr->Next;
}
Cell *leftL, *rightL;
leftL = ptr;
rightL = ptr->Next;
Cell *Middle = new Cell();
Middle->Value = Value;
Middle->Pre = leftL;
Middle->Next = rightL;
leftL->Next = Middle;
rightL->Pre = Middle;
this->Length++;
}
template <typename T> PowerBearList<T>::PowerBearList() { this->Length = 0; }
template <typename T> PowerBearList<T>::PowerBearList(T Value) {
this->Length = 0;
this->Push_Back(Value);
}
template <typename T> T &PowerBearList<T>::operator[](int Index) {
Cell *ptr = this->cell_Front->Next;
try {
if (Index < 0 || Index > this->Length - 1) {
throw "ERROR:访问下标越届,程序停止运行。";
}
for (int i = 0; i < Index; i++) {
ptr = ptr->Next;
}
return ptr->Value;
} catch (const char *msg) {
std::cout << msg << " "
<< "实例地址定位:" << this << " "
<< "使用的方法:T &operator[](int Index)";
exit(0);
}
}
template <typename T> void PowerBearList<T>::Clear() {
if (this->Length == 0)
return;
Cell *ptr = this->cell_Front;
//开始释放内存
for (int i = 0; i < this->Length; i++) {
ptr = ptr->Next;
delete[] ptr;
}
this->Length = 0;
}
template <typename T> void PowerBearList<T>::Push_Back(T Value) {
if (this->Length == 0) {
Cell *a = new Cell();
a->Value = Value;
a->Pre = this->cell_Front;
a->Next = this->cell_Back;
cell_Front->Next = a;
cell_Back->Pre = a;
this->Length++;
} else {
Cell *a = new Cell();
a->Value = Value;
a->Next = cell_Back;
a->Pre = cell_Back->Pre;
cell_Back->Pre->Next = a;
cell_Back->Pre = a;
this->Length++;
}
}
template <typename T> int PowerBearList<T>::GetLength() { return this->Length; }
struct A {
int a, b;
A(int A, int B) : a(A), b(B) {}
A() {} //这里一定要存在一个无参数的构造函数,要不然会有问题。
};
int main() {
#define AllowDebug
#pragma region //宏定义的测试代码
#ifdef AllowDebug
PowerBearList<int> Seq;
Seq.Push_Back(1);
Seq.Push_Back(2);
Seq.Push_Back(3);
for (auto ite = Seq.begin(); ite != Seq.end(); ite++) {
cout << *ite << endl;
}
Seq.Pop_Back();
cout << "----------------" << endl;
for (auto ite = Seq.begin(); ite != Seq.end(); ite++) {
cout << *ite << endl;
}
Seq.Insert(1, 4);
cout << "----------------" << endl;
for (auto ite = Seq.begin(); ite != Seq.end(); ite++) {
cout << *ite << endl;
}
#endif
#pragma endregion //折叠一下,要不然不美观。
return 0;
}