【数据结构】02线性表-单链表

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;

}
  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值