1.单链表
在顺序结构中,每个数据元素只需要存储数据元素信息即可。在链式存储中,除了要存储数据元素信息之外,还要存储它的后继元素的地址。
对于数据元素
a
i
a_{i}
ai来说,它需要存储数据元素信息(数据域)还要存储,后继位置信息(指针域),指针域中存储的信息称为指针或链。这两部分信息组成数据元素
a
i
a_i
ai的存储映像,称为结点(Node).
n个节点链接成一个链表,即为线性表的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。
顺序表
- 优点:可随机存取,存储密度高
- 缺点:要求连续的存储空间,扩容不方便,插入和删除元素需要移动其它的元素
单链表:利用链式存储方式
- 优点:不要求连续的存储空间,扩容方便,插入和删除元素不需要移动其它元素
- 缺点:不能随机存取,指针需要消耗存储空间
对于链表来说,第一个节点的存储位置称为头指针。有时候我们为了方便,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储任何信息。
1.1 头指针和头结点的异同
头指针:
- 指向链表第一个结点的指针,若链表有投机欸但,则时指向头结点的指针
- 头指针具有标志作用,所以常用头指针冠以链表的名字
- 无论链表是否为空,头指针均不为空。头指针是链表的必要元素
头结点: - 头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义
- 有了头结点,对在第一个元素结点前插入和删除第一结点,其操作与其他节点的操作就是统一的了
- 头结点不一定是链表的必需元素
2. 单链表的实现
2.1 初始化
利用typedef关键字,将LNode重命名为*LinkList类型,此时利用就能方便使用 LinkList 来表示整个链表的头指针,从而操作整个链表。
typedef int Elemtype;
struct LNode
{
Elemtype data; // 数据域
LNode* next; // 指针域
};
typedef LNode *LinkList; // 指向结点的指针,利用这个指针串联整个链表,故而头指针也可以是整个链表
void InitLinkList(LinkList* L)
{
L->data = NULL;
L->next = nullptr;
}
2.2 向链表中插入元素
方法:从头结点开始,找到第i-1个元素的位置,将第i-1个元素的指针域指向新元素,将新元素的指针域指向之前的第i个元素,此时新元素就成为了第i个元素
// 向第i个位置插入元素e
bool ListInsert(LinkList* L, int i, Elemtype e)
{
if (L==nullptr) // 链表不存在
{
return false;
}
LNode* tmp_ptr = *L; // 从头结点开始
for (int k = 1; k < i && tmp_ptr; k++)
{
tmp_ptr = tmp_ptr->next;
}
// 找到第i-1个结点
if (tmp_ptr==nullptr) // 第i-1个结点不存在,位置非法
{
return false;
}
// 插入数据
// 1.准备数据
LNode* new_ptr = new LNode;
new_ptr->data = e;
// 2. 插入
new_ptr->next = tmp_ptr->next;
tmp_ptr->next = new_ptr;
return true;
}
2.3 删除链表元素
方法:找到第i-1个元素,将第i-1个元素的指针域指向第i+1个元素,清空第i个元素
/ 删除第i个位置的元素,返回给e
bool ListDelete(LinkList* L, int i, Elemtype* e)
{
if (IsEmpty(*L)) // 链表不存在或为空
{
return false;
}
LNode* tmp_ptr = *L; // 从头结点开始
for (int k = 1; k < i && tmp_ptr; k++)
{
tmp_ptr = tmp_ptr->next;
}
// 找到第i-1个结点
if (tmp_ptr == nullptr) // 第i-1个结点不存在,位置非法
{
return false;
}
// 删除结点
// 1. 保存数据
LNode* node_ptr = tmp_ptr->next; // 第i个元素的数据,包括指针域
*e = node_ptr->data;
// 2. 删除
tmp_ptr->next = node_ptr->next; // 第i-1个元素指针域指向第i+1个元素
delete node_ptr;
return true;
}
3. 完整实现
#include <iostream>
using namespace std;
// 单链表
typedef int Elemtype;
struct LNode
{
Elemtype data; // 数据域
LNode* next; // 指针域
};
typedef LNode *LinkList; // 指向结点的指针,利用这个指针串联整个链表,故而头指针也可以是整个链表
void InitLinkList(LinkList* L)
{
(*L)->data = NULL;
(*L)->next = nullptr;
}
bool IsEmpty(LinkList L)
{
if (L == nullptr) // 链表不存在
{
return true;
}
return L->next == nullptr; // 链表为空true
}
// 获取链表第i个元素,返回给e
bool GetElem(LinkList L, int i, Elemtype* e)
{
if (IsEmpty(L)) // 链表不存在或为空
{
return false;
}
LNode* tmp_ptr = L->next; // 指向第一个结点
for (int k = 1; k < i && tmp_ptr; k++)
{
tmp_ptr = tmp_ptr->next;
}
if (tmp_ptr == nullptr)// 找不到第i个结点
{
return false;
}
*e = tmp_ptr->data;
return true;
}
// 向第i个位置插入元素e
bool ListInsert(LinkList* L, int i, Elemtype e)
{
if (L==nullptr) // 链表不存在
{
return false;
}
LNode* tmp_ptr = *L; // 从头结点开始
for (int k = 1; k < i && tmp_ptr; k++)
{
tmp_ptr = tmp_ptr->next;
}
// 找到第i-1个结点
if (tmp_ptr==nullptr) // 第i-1个结点不存在,位置非法
{
return false;
}
// 插入数据
// 1.准备数据
LNode* new_ptr = new LNode;
new_ptr->data = e;
// 2. 插入
new_ptr->next = tmp_ptr->next;
tmp_ptr->next = new_ptr;
return true;
}
// 删除第i个位置的元素,返回给e
bool ListDelete(LinkList* L, int i, Elemtype* e)
{
if (IsEmpty(*L)) // 链表不存在或为空
{
return false;
}
LNode* tmp_ptr = *L; // 从头结点开始
for (int k = 1; k < i && tmp_ptr; k++)
{
tmp_ptr = tmp_ptr->next;
}
// 找到第i-1个结点
if (tmp_ptr == nullptr) // 第i-1个结点不存在,位置非法
{
return false;
}
// 删除结点
// 1. 保存数据
LNode* node_ptr = tmp_ptr->next;
*e = node_ptr->data;
// 2. 删除
tmp_ptr->next = node_ptr->next;
delete node_ptr;
return true;
}
// 清空链表(除头结点外的所有结点)
bool ClearList(LinkList* L)
{
if (L==nullptr)// 链表不存在
{
return false;
}
if (IsEmpty(*L))
{
return true;
}
LNode* tmp_ptr = (*L)->next; // 指向第一个结点
LNode* next_ptr; // 指向下一个结点
while (tmp_ptr)
{
next_ptr = tmp_ptr->next; // 记录下一个结点位置
delete tmp_ptr; // 删除当前结点
tmp_ptr = next_ptr; // 跳转
}
(*L)->next = nullptr; // 最后把头结点断开
return true;
}
bool DestroyList(LinkList* L)
{
if (*L == nullptr)// 链表不存在
{
return true;
}
LNode* tmp_ptr = *L; // 指向头结点
LNode* next_ptr; // 指向下一个结点
while (tmp_ptr)
{
next_ptr = tmp_ptr->next; // 记录下一个结点
delete tmp_ptr; // 删除当前结点
tmp_ptr = next_ptr; // 跳转至下一个结点
}
*L = nullptr; // 头结点置为空
return true;
}
void ListShow(LinkList* L)
{
if (IsEmpty(*L)) // 链表不存在或为空
{
cout << "链表为空" << endl;
return;
}
LNode* tmp_ptr = (*L)->next; // 指向第一个结点
while (tmp_ptr)
{
cout << tmp_ptr->data << " ";
tmp_ptr = tmp_ptr->next;
}
cout << endl;
}
int main(void)
{
LinkList L = new LNode;
InitLinkList(&L); // 初始化链表
Elemtype e;
e = 1; ListInsert(&L, 1, e);
e = 2; ListInsert(&L, 2, e);
e = 3; ListInsert(&L, 3, e);
e = 4; ListInsert(&L, 4, e);
e = 0; ListInsert(&L, 1, e);
ListShow(&L);
ListDelete(&L, 3, &e);
ListShow(&L);
ClearList(&L);
ListShow(&L);
cout <<"L的地址:"<< L << endl;
DestroyList(&L);
cout << "L的地址:" << L << endl;
return 0;
}