本文中的所有代码均在github上的项目中:List_DataStructure
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
每个结点包括两个部分内容:一是存储数据元素的数据域,另一个是存储其他结点地址的指针域(不同种类的链表存储的结点地址不同)。
链表根据每个结点存储的指向下一结点的链接不同而分为几类
单链表:结构最简单的链表,每个结点中都有指向下一结点的地址
双链表:对单链表做了部分加强,每个结点中都有两个地址,分别指向上一结点和下一结点
环形链表:尾结点中存储有头结点的地址,整个链表形成了一个封闭的环
单链表又名单向链表,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取的方式,从头部开始遍历链表
单链表的开始是一个头指针,每个结点中都有指向下一结点的地址,尾结点中的地址为nullptr
template<typename Type> class SingleLinkedList;
template<typename Type>
class SingleLinkedListNode
{
private:
//将链表类声明为友元
friend typename SingleLinkedList;
//构造函数和析构函数
SingleLinkedListNode() :pnext(nullptr){}
SingleLinkedListNode(const Type item, SingleLinkedListNode * next = nullptr) :data(item),pnext(next){}
~SingleLinkedListNode() {
pnext = nullptr;
}
public:
//创建公共方法,用于获取结点中的数据
Type GetData();SingleLinkedListNode&);
private:
//结点类的私有属性,结点数据和下一结点的地址
Type data;
SingleLinkedListNode * pnext;
};
#include"SingleLinkedListNode.h"
template <typename Type>
class SingleLinkedList
{
public:
//构造函数和析构函数
SingleLinkedList() :head(new SingleLinkedListNode<Type>()){}
~SingleLinkedList() {
MakeEmpty();
delete head;
}
//对单链表的基本操作
void MakeEmpty();
void Insert(Type item, int i = 0);
void Remove(int i = 0);
void RemoveAll(Type item);
//返回单聊表的基本信息
int length();
Type Get(int i);
//查找和排序
SingleLinkedList<Type> * SequentialSearch(Type item);
//基础功能
void Print();
private:
//链表的头节点是私有的
SingleLinkedListNode<Type> *head;
};
SingleLinkedList<int> list;
根据空参构造方法,创建单链表对象时初始化了一个私有的,没有数据的头结点*head
单链表的一个可用的Insert( )方法版本:
该版本不用按照插入位置的不同(头尾体)而分情况实现,比较简洁
//向单链表中插入元素
template <typename Type>
void SingleLinkedList<Type>::Insert(Type item, int i = 0) {
if (i < 0){
cout << "请输入一个大于0的数" << endl;
}
SingleLinkedListNode<Type> *pmove = head;
SingleLinkedListNode<Type> *pnode = new SingleLinkedListNode<Type>(item);
//循环条件是j
for (int j = 0; j < i&&pmove; j++){
pmove = pmove->pnext;
}
//插入点越界
if (pmove == nullptr){
cout << "数据插入位置错误,单链表没那么长!" << endl;
}
pnode->pnext = pmove->pnext;
pmove->pnext = pnode;
}
以中间部分插入为主讨论:
1,首先创建两个结点类型指针pmove 和pnode
2,将pmove指针移动到插入点,如上图,插入位置为a2,a3之间,pmove指向a2
3,变更结点中存储的地址以实现结点插入
pnode->pnext = pmove->pnext; //第一步
pmove->pnext = pnode; //第二步
第一步:待插入节点pnode的下一结点地址pnext指向a3,a3的地址为a2结点的下一结点地址pnext,也是pmove的pnext,即pmove->pnext
第二步:更改a2结点的下一结点地址pnext,使其指向待插入节点pnode
如上图所示,s为待插入节点pnode,p为插入点位置pmove
int main() {
SingleLinkedList<int> list;
//向链表中插入20个数据
for (int i = 0; i < 20; i++)
{
list.Insert(i*4,i);
}
//在链表头部插入数据
list.Insert(10,0);
//在链表尾部插入数据
list.Insert(100,21);
list.Print();
cin.get();
return 0;
}
执行结果:
执行原理与添加数据相似,创建两个结点类型指针pdel和pmove
pdel指向待删除元素,pmove指向待删除结点的前一个结点
//删除单链表中的元素
template <typename Type>
void SingleLinkedList<Type>::Remove(int i = 0) {
if (i < 0){
cout << "请输入一个大于0的数" << endl;
}
SingleLinkedListNode<Type> *pmove = head, *pdel;
for (int j = 0; j < i&&pmove->pnext; j++){
pmove = pmove->pnext;
}
if (pmove->pnext == nullptr){
cout << "删除出错,链表没有那么长!" << endl;
}
pdel = pmove->pnext;
pmove->pnext = pdel->pnext;
delete pdel;
}
析构函数调用了此方法
//删除单链表
template <typename Type>
void SingleLinkedList<Type>::MakeEmpty() {
SingleLinkedListNode<Type> *pdel;
while (head->pnext != nullptr)
{
pdel = head->pnext;
head->pnext = pdel->pnext;
delete pdel;
}
}
//返回单链表长度
template<typename Type>
int SingleLinkedList<Type>::length() {
SingleLinkedListNode<Type> *pmove = head->pnext;
int count = 0;
while (pmove != nullptr) {
pmove = pmove->pnext;
count++;
}
return count;
}
//打印单链表中的所有数据
template<typename Type>
void SingleLinkedList<Type>::Print() {
SingleLinkedListNode<Type> *pmove = head->pnext;
cout << endl << "head";
while (pmove != nullptr)
{
cout << "->" << pmove->data;
pmove = pmove->pnext;
}
cout << "-over" << endl;
}