通过上一章对于顺序表的学习,我们了解到顺序表的优点是:以物理位置相邻表示逻辑关系,任一元素均可随机存储。但同时它也存在着缺陷:进行插入和删除操作时,需要移动大量的元素,存储空间不灵活。这里,我们给大家引入链表,该结构不要求逻辑上相邻的数据元素物理上一定相邻,且插入和删除元素时不需要移动数据元素,只需要修改指针值。
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
在链式存储结构中,为了能正确表示结点间的逻辑关系,每个存储结点不仅要包含数据元素本身的信息(称为
数据域
),还必须存储数据元素之间的逻辑关系的信息,即指示其后继结点的地址(或位置)信息,这个信息称为指针
。一般的,每个结点有一个或多个指针域。若一个结点中的某个指针域不需要指向任何其他结点时,则它的值置为空,通常用常量NULL
表示。
与链式存储有关的术语:
单链表
或者线性链表。双链表
。循环链表
。单链表与双向链表:
如果每个结点除数据域外,仅设置一个指向后继的指针域,则这样构成的链表称为线性单向链表,称为
单链表
。在单链表中,由于每个结点只包含一个指向后继结点的指针,所以当访问一个结点后,只能依次访问其后继结点,但无法访问其前驱结点。
为此,引入了双向链表,该链表中每个结点除数据域外,设置两个指针域,一个指向前驱结点,一个指向后继结点,这样构成的链表称为线性双向链表,简称双向链表
。该链表当访问一个结点后,既可以依次向后访问后继结点,也可以依次向前访问前驱结点。
下面给出图示:
对于单链表,又给出了两种分类标准:
在单链表中,假定每个结点的类型用LNode表示,它包括存储数据类型元素的数据域(data)和存储后继结点位置的指针域(next)。其数据类型定义如下:
typedef struct LNode//定义单链表结点类型
{
ElemType data;//存放数据元素信息
LNode* next;//存放后继结点的地址信息
}LNode;
在双向链表中,假定每个结点的类型用LNode表示,它包括存储数据类型元素的数据域(data)、存储前驱结点位置的指针域(prior)和存储后继结点位置的指针域(next)。其数据类型定义如下:
typedef struct DNode//定义双向链表结点类型
{
ElemType data;//存放数据元素信息
DNode* next;//存放后继结点的地址信息
DNode* prior;//存放前驱结点的地址信息
}DNode;
typedef int ElemType;
typedef struct LNode//定义单链表结点类型
{
ElemType data;//存放数据元素信息
LNode* next;//存放后继结点的地址信息
}LNode;
class LinkList
{
private:
LNode* head;
public:
LinkList();//构造函数,构造一个空表
~LinkList();//析构函数,销毁函数
void CreatList_h(int n);//头插法创建具有n个数据元素的线性链表
void CreatList_t(int n);//尾插法创建具有n个数据元素的线性链表
void InsertList(int i, ElemType e);//在表中第i个位置插入数据元素
void DeleteList(int i, ElemType& e);//删除表中第i个元素的数据元素
int GetElem(int i, ElemType& e);//获取第i个数据元素
int LocateElem(ElemType e);//在链表中查找是否存在数据元素e,若存在,则返回1,否则返回0
int ListLength();//计算表长
};
该操作由构造函数实现,申请一个头结点并置指针域为
NULL
。
LinkList::LinkList()
{
head = new LNode;
head->next = NULL;
}
该操作由析构函数实现,主要工作是依次释放链表中结点的存储空间。
LinkList::~LinkList()
{
LNode* p = head;
while (p)
{
head = head->next;
delete p;
p = head;
}
}
该函数是从一个空表开始(初始化),依次读取数据,生成新结点,将读入的数据元素存放到新结点的数据域中,然后将新结点插入到当前链表的头结点后,直至读入所有数据为止。头插法建立单链表的示意图如下:
void LinkList::CreatList_h(int n)
{
LNode* s;
int i = 0;
for (i = 0; i < n; i++)
{
s = new LNode;//建立新结点
cin >> s->data;//读入数据元素
s->next = head->next;//新结点插入头结点之后
head->next = s;
}
}
该算法的时间复杂度为
O(n)
,其中n为单链表中数据结点的个数。
尾插法每次将新生成的结点插入到当前链表的表尾上。为此,需要增加一个尾指针rear,rear始终指向当前链表的尾结点。尾插法建立单链表的示意图如下:
void LinkList::CreatList_t(int n)
{
LNode* rear, * s;
int i = 0;
for (i = 0; i < n; i++)
{
s = new LNode;//建立新结点
cin >> s->data;//读入数据
rear->next = s;//新结点插在表尾
rear = s;//尾指针指向新结点
}
s->next = NULL;//置最后结点的指针域为NULL
}
该算法的时间复杂度为
O(n)
,其中n为单链表中数据结点的个数。
void LinkList::InsertList(int i, ElemType e)
{
LNode* p, * s;
p = head;
int j = 0;
while (p && j < i - 1)//查找第i-1个结点
{
p = p->next;
j++;
}
if (!p || j > i - 1)
return;
else//插入新结点
{
s = new LNode;
s->data = e;
s->next = p->next;
p->next = s;
}
}
void LinkList::DeleteList(int i, ElemType& e)
{
LNode* p = head;
int j = 0;
while (p && j < i - 1)//查找第i-1个结点
{
p = p->next;
j++;
}
if (!(p->next) || j > i - 1)
return;
else
{
LNode* q = p->next;
e = q->data;//保存被删除结点的数据元素
p->next = q->next;//删除被删结点
delete q;//释放空间
}
}
int LinkList::GetElem(int i, ElemType& e)
{
LNode* p = head;
int j = 0;
while (j < i && p)
{
p = p->next;
j++;
}
if (!p || j > i)//不存在第i个数据元素,返回0
return 0;
else
{//存在第i个元素,返回1
e = p->data;
return 1;
}
}
该操作是在单链表中查找第一个值为e的结点,若存在这样的结点,则返回1,否则返回0.
int LinkList::LocateElem(ElemType e)
{
LNode* p = head->next;
while (p && p->data != e)
{
p = p->next;
}
if (p)//存在元素值为e的结点,返回1
return 1;
else//不存在,返回0
return 0;
}
✅如果我们这里添加一条,如果找到了返回元素的位置,该怎么改这段代码呢?
int LinkList::LocateElem(ElemType e)
{
LNode* p = head->next;
int j = 1;
while (p && p->data != e)
{
p = p->next;
j++;
}
if (p)//存在元素值为e的结点,返回位置
return j;
else//不存在,返回0
return 0;
}
ListLength()函数从单链表的第一个结点开始依次遍历整个链表,每遍历一个结点,计数器+1,最后返回结点数量。
int LinkList::ListLength()
{
LNode* p = head->next;
int count = 0;
while (p)
{
count++;//计数器+1
p = p->next;
}
return count;//返回结点个数
}
void LinkList::print()
{
LNode* p = head->next;
while (p)
{
cout << p->data << " ";
p = p->next;
}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;
typedef int ElemType;
typedef struct LNode//定义单链表结点类型
{
ElemType data;//存放数据元素信息
LNode* next;//存放后继结点的地址信息
}LNode;
class LinkList
{
private:
LNode* head;
public:
LinkList();//构造函数,构造一个空表
~LinkList();//析构函数,销毁函数
void CreatList_h(int n);//头插法创建具有n个数据元素的线性链表
void CreatList_t(int n);//尾插法创建具有n个数据元素的线性链表
void InsertList(int i, ElemType e);//在表中第i个位置插入数据元素
void DeleteList(int i);//删除表中第i个元素的数据元素
void GetElem(int i);//获取第i个数据元素
void LocateElem(ElemType e);//在链表中查找是否存在数据元素e,若存在,则返回1,否则返回0
void ListLength();//计算表长
void print();//打印链表
};
LinkList::LinkList()
{
head = new LNode;
head->next = NULL;
}
LinkList::~LinkList()
{
LNode* p = head;
while (p)
{
head = head->next;
delete p;
p = head;
}
}
void LinkList::CreatList_h(int n)
{
LNode* s;
int i = 0;
for (i = 0; i < n; i++)
{
s = new LNode;//建立新结点
cin >> s->data;//读入数据元素
s->next = head->next;//新结点插入头结点之后
head->next = s;
}
}
void LinkList::CreatList_t(int n)
{
LNode* rear = NULL, * s = NULL;
int i = 0;
for (i = 0; i < n; i++)
{
s = new LNode;//建立新结点
cin >> s->data;//读入数据
rear->next = s;//新结点插在表尾
rear = s;//尾指针指向新结点
}
s->next = NULL;//置最后结点的指针域为NULL
}
void LinkList::InsertList(int i, ElemType e)
{
LNode* p, * s;
p = head;
int j = 0;
while (p && j < i - 1)//查找第i-1个结点
{
p = p->next;
j++;
}
if (!p || j > i - 1)
return;
else//插入新结点
{
s = new LNode;
s->data = e;
s->next = p->next;
p->next = s;
}
}
void LinkList::DeleteList(int i)
{
ElemType e;
LNode* p = head;
int j = 0;
while (p && j < i - 1)//查找第i-1个结点
{
p = p->next;
j++;
}
if (!(p->next) || j > i - 1)
return;
else
{
LNode* q = p->next;
e = q->data;//保存被删除结点的数据元素
p->next = q->next;//删除被删结点
delete q;//释放空间
}
cout << "删除了单链表上的第" << i << "个元素,值为" << e << endl;
}
void LinkList::GetElem(int i)
{
ElemType e;
LNode* p = head;
int j = 0;
while (j < i && p)
{
p = p->next;
j++;
}
if (!p || j > i)//不存在第i个元素
cout << "不存在第i个数据元素"<<endl;
else
{//存在第i个元素
e = p->data;
cout << "第" << i << "个元素的值为" << e << endl;
}
}
void LinkList::LocateElem(ElemType e)
{
LNode* p = head->next;
int j = 1;
while (p && p->data != e)
{
p = p->next;
j++;
}
if (p)//存在元素值为e的结点,返回位置
cout << e << "所在的位置为:" << j << endl;
else//不存在,返回0
cout << "不存在该数据元素" << endl;
}
void LinkList::ListLength()
{
LNode* p = head->next;
int count = 0;
while (p)
{
count++;//计数器+1
p = p->next;
}
cout << "表的长度为:" << count << endl;//返回结点个数
}
void LinkList::print()
{
LNode* p = head->next;
while (p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
int main()
{
LinkList L;
L.CreatList_h(10);
L.print();
L.InsertList(3, 6);
cout << "在第3个位置插入6后单链表为:";
L.print();
L.LocateElem(5);
L.DeleteList(8);
L.print();
L.GetElem(3);
L.ListLength();
return 0;
}
好啦,关于单链表的知识点到这里就结束啦,后期会继续更新数据结构与算法的相关知识,欢迎大家持续关注、点赞和评论!❤️❤️❤️