线性表的链式存储结构
结点在存储器中的位置是任意的,在存储单元既可以是连续的,也可以是不连续的,元素的逻辑次序和物理次序不一定相同。
各结点由两个域组成
- 数据域:存储元素数值数据
- 指针域:存储直接后继结点的存储位置
头指针、头结点、首元结点
头指针:指向链表中第一个结点的指针
首元结点:链表中存储第一个数据元素的结点
头结点:链表的首元结点之前附设的一个结点
如何表示空表?
- 无头结点时,头指针为空时表示空表
- 有头结点时,头结点的指针域为空时表示空表
头结点的数据域
可以为空,也可以存放线性表长度等附加信息,但此结点不计入链表长度
单链表特点
访问时只能通过头指针进入链表,通过每个结点的指针域依次向后顺序扫描其余结点
单链表的定义和初始化
//链表的定义
typedef int ElemType;
typedef struct Lnode {
ElemType data; //结点的数据域
Lnode * next; //结点的指针域
}*Linklist;
//单链表的初始化
bool Initlist(Linklist& L)
{
L = new Lnode; //堆区开辟一个头结点,数据类型为Lnode
L->next = nullptr; //空表,头结点的指针域指向空
return true;
}
判断链表是否为空
bool Isempty(const Linklist& L)
{
if (L->next) {
return false;}
else { return true; }
}
单链表的销毁
从头指针开始,依次释放所有的结点
bool Destroylist(Linklist& L)
{
if (Isempty(L)) //判断链表是否为空
{ cout << "empty list!" << endl;
return false;
}
while (L) //链表未到底端,循环继续
{ //从头指针开始,将头指针指向下一个结点,依次释放所有结点,
Lnode *p = L;
L = L->next;
delete p;
}
}
单链表的清空
链表仍然存在,但是无元素,成为空链表
依次释放所有的结点,并将头结点指针域置为空
bool Clearlist(Linklist& L)
{
Lnode* p, * q; //定义新结点p和q
p = L->next; //定义p为首元结点
while (p) //没到表尾,头结点指针域不为空
{
q = p->next;
delete p;
p = q;
}
L->next = nullptr; //头结点指针域置为空
return true;
}
求单链表的表长
从首元结点开始,依次计数所有结点
//求单链表的表长 时间复杂度O(n)
int Getlength(const Linklist& L)
{
Lnode* p; //定义结点p
p = L->next; //p指向首元结点
int i = 0; //初始结点数即链表长度为0
while (p) //遍历单链表,统计结点数,直至p指向表尾为空停止
{ ++i;
p = p->next;
}
return i;
}
获取链表中第i个元素
从链表的头指针出发,顺着链域next逐个结点往下搜索,直到搜索到第i个结点。
- 从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点
- j作计数器,累计当前扫描过的结点数,j初值为1
- 当p扫描到下一结点时,j加1
- 当j==i时,p所指结点就是要找的第i个结点
bool Getelem(const Linklist& L, const int& i, ElemType& e)
{
if (i < 0)
{
cout << "不在链表范围内" << endl;
return false;
}
Lnode* p = L; //p指向头结点
for (int j = 1; j < i+1; ++j)
//j为计数器,累计当前扫描过的结点数,初值为1,当p扫描到下一个结点时,j加1
{
p = p->next; //指针p指向遍历中当前扫描到的结点
if (!p) //如果此时p为空,意味着已经到达链表末端,停止循环
{
cout << "out of range" << endl;
return false;
}
}
e = p->data; //第i个元素的数据域
return true;
}
按值查找链表
根据指定数据获取该数据所在的位置
- 从第一个结点开始,依次与e相比较
- 如果找到一个其值与e相等的数据元素,则返回其在链表中的位置
- 如果遍历整个链表都没有找到其值与e相等的元素,则返回0
int LocateElem(Linklist& L, ElemType& e)
{
Lnode* p = L->next; //p指向首元结点
int i = 1; //初始结点数为1
while (p) //遍历单链表,p为空时循环停止
{
if (p->data == e) //找到该数据,返回其结点数,即它在此链表当中的位置
{ return i; }
++i; //继续向后遍历,结点数加1
p = p->next; //p指向下一个结点
}
cout << "链表中没有此元素" << endl; //没有找到该元素,循环结束
return 0 ;
}
在第i个结点前插入值为e的新结点
- 首先找到a(i-1)的存储位置p
- 生成一个数据域为e的新结点s
- 插入新结点,新结点的指针域指向ai结点,结点a(i-1)的指针域指向新结点
//在第i个结点前插入值为e的新结点 时间复杂度为O(n)
bool Insertlist(Linklist& L, const int& i, const ElemType& e)
{
Lnode* p = L;
int j = 0;
while (p && j < i - 1) //寻找第i-1个结点
{
p = p->next; //从头结点开始遍历,直至p指向第i-1个结点
++j;
}
if (!p || i < 0) //异常插入情况判断
{
cout << "out of range" << endl;
return false;
}
Linklist s = new Lnode; //生成一个数据域为e的新结点s
s->data = e;
// 将新结点插入链表中的两大步骤
s->next = p->next; //新结点的指针域指向第i个结点
p->next = s; //结点i-1的指针域指向新结点s
return true;
}
删除第i个结点
bool Deletelist(Linklist& L, const int& i)
{
Lnode* p = L;
int j = 0;
while (p->next && j < i - 1) //寻找第i-1个结点
{
p = p->next; //从头结点开始遍历,直至p指向第i-1个结点
++j;
}
if (!(p->next) || i < 0) //异常删除条件判断
{
cout << "out of range" << endl;
return false;
}
Lnode* q = p->next; //临时保存被删除结点的地址以备释放
p->next = p->next->next; //第i-1个结点的指针域指向第i+1个结点
delete q; //释放删除结点的空间(如果要保存删除结点的数据域,可定义变量e来接收,e=q->data)
return true;
}
建立单链表–头插法
- 从一个空表开始,重复读入数据
- 生成新结点,将读入数据存放到新结点的数据域中
- 从最后一个结点开始,依次将各结点插入到链表的前端
#define n 6
void Creathead(Linklist& L)
{
cout << "请输入" << n << "个数据元素:" << endl;
for (int i = n; i >0; --i) {
Lnode* p = new Lnode;
cin >> p->data;
p->next = L->next;
L->next = p;
}
}
建立单链表–尾插法
- 从一个空表开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点
- 初始时,r和L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点
void Creatlist(Linklist& L)
{
cout << "请为单链表输入" << n << "个数据元素" << endl;
//L = new Lnode; //建立一个带头结点的单链表
//L->next = nullptr; //头结点的指针域置为空
Lnode* r = L; //尾指针r指向头结点
for (int i = 1; i <= n; i++) //正序输入
{
Lnode* p = new Lnode; //生成新的结点p
cin >> p->data; //输入新结点的数据域,即元素值
p->next = nullptr; //生成新结点的指针域置为空
r->next = p; //将新结点插入到列表尾部
r = p; //r指向新的尾结点
}
}时间复杂度为O(n)
单链表的输出以及测试
void PrintList(Linklist & L)
{
int i,m;
m = Getlength(L);
Lnode* p = L->next;
for (i =1; i <=m; i++) {
cout << p->data << ' ';
p = p->next;
}
}
//测试
void Print(Linklist& L)
{
cout << "单链表为:(尾插法)" << endl;
PrintList(L); //单链表元素的输出
Creathead(L);
cout << endl << "单链表为:(头插法)" << endl;
PrintList(L); //单链表元素的输出
//while (!Isempty(L)){
int a1, x1 = 0;
cout << endl << "要获取第几个元素" << endl;
cin >> x1;
Getelem(L, x1, a1);
cout << "第" << x1 << "个元素为:" << a1 << endl;
int a2;
cout << "请输入要查找的元素:" << endl;
cin >> a2;
cout << "查找的元素在第" << LocateElem(L, a2) << "个位置" << endl;
int a3, x2;
cout << "要插入的位置: ";
cin >> x2;
cout << "要插入的元素: ";
cin >> a3;
Insertlist(L, x2, a3);
cout << "单链表为:" << endl;
PrintList(L);
cout << endl << "此时单链表的长度为:" << Getlength(L) << endl;
int x3;
cout << endl << "要删除第几个元素?";
cin >> x3;
Deletelist(L, x3);
cout << "单链表为:" << endl;
PrintList(L);
cout << endl << "此时单链表的长度为:" << Getlength(L) << endl;
//}
}
主函数
void main()
{
Linklist L; //定义单链表L
Initlist(L); //初始化单链表L
Creatlist(L); //单链表元素的输入
Print(L);
system("pause");
return;
}
ps:注意头结点和首元结点的区别,以及结点的数据域和指针域