首页 > 其他分享 >【数据结构】第二章——线性表(4)

【数据结构】第二章——线性表(4)

时间:2023-12-26 19:33:33浏览次数:42  
标签:初始化 结点 单链 线性表 LNode 链表 第二章 数据结构 指针

线性表的链式表示

【数据结构】第二章——线性表(4)_头结点

导言

大家好,很高兴又和大家见面啦!!! 在前面的内容中我们介绍了线性表的第一种存储方式——顺序存储,相信大家经过前面的学习应该已经掌握了对顺序表的一些基本操作了。今天,我们将开始介绍线性表的第二种存储方式——链式存储。

一、链式存储

线性表中的数据元素在存储时,其逻辑顺序与物理位置都相邻的存储方式,我们称其为顺序存储,又称为顺序表;

当线性表中的数据元素在存储时,只满足逻辑上相邻,但是物理位置上可以不相邻时,我们称其为链式存储,又称为链表。如下图所示:

【数据结构】第二章——线性表(4)_头结点_02

顺序存储的优点是可以做到顺序表中的数据元素可以进行随机存储,所以它又是一种随机存取的存储结构;但是它的缺点是需要再内存中申请一块连续的存储空间,而且在进行空间大小的修改时不方便,并且在插入和删除元素时需要进行元素的移动。

链式存储则优化了这个缺点,链表中的数据元素并不需要物理位置上相邻,所以在内存中不需要通过申请连续的空间进行存放,而且不需要在插入或删除元素时调整元素的物理位置;但是它的缺点是需要消耗额外的空间来单独存放指向其它元素的指针。

链表根据存放的指针数量不同又分为单链表与双链表,下面我们就来介绍一下单链表的基本概念与基础操作。

二、单链表

1.1 单链表的定义

线性表的链式存储又称为单链表。它是指通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据元素之间的线性关系,对链表的每个结点,除存放元素自身的信息外,还需要存放一个指向其后继结点的指针。

1.2 单链表节点的创建

链表在内存中是通过一个个结点构成的,单链表的结点分为两部分:

  • date——数据域,存放数据元素;
  • next——指针域,存放其后继结点的地址;

结构如下图所示:

【数据结构】第二章——线性表(4)_数据结构_03

下面我们就来通过C语言来描述一下单链表的结点:

//单链表结点的C语言描述
typedef struct LNode//定义单链表的结点类型
{
	ElemType data;//数据域
	struct LNode* next;//指针域
}LNode,*LinkList;//将结点类型重命名为LNode,将单链表类型重命名为LinkList
//LNode——强调的是结点
//LinkList——强调的是链表

注:由于链表中的结点数据类型相同,所以结点的数据类型也就代表着链表的数据类型,在后续的基本操作中,我们为了更好的区分此时引用的是整个链表还是单个结点,这里我们将单链表的结点类型重命名为LNode,将其指针类型重命名为LinkList,这样我们在后续的操作中就可以通过LNodeLinkList这两个名字来更好的区分结点与链表。

因为单链表的各个元素时离散的分布在内存中,所以单链表不能像顺序表一样做到随机存取,因此单链表是一个非随机存取的存储结构,即不能直接找到表中某个特定的节点。在查找某个特定的结点时,需要从表头开始遍历,依次查找。

1.3 单链表的头指针与头结点

因为单链表都是由一个个结点组成,所以我们通常将指向单链表的指针称为头指针,头指针指向的是单链表中的第一个结点,当头指针为空指针NULL时,表示的是一个空表。

此外,为了操作上的方便,我们还可以在单链表的第一个结点之前附加一个不需要存储任何数据的结点,这个结点我们将它称为头结点。头结点的数据域可以存储表长等信息,也可以不存储任何信息,头结点的指针域指向的是链表中的第一个元素结点。如下图所示:

【数据结构】第二章——线性表(4)_结点_04

1.3.1 头指针与头结点的区别

  1. 性质不同
  • 头指针指向链表第一个结点的指针
  • 头结点是链表的第一个结点
  1. 存储内容不同
  • 不管链表带不带头结点,头指针存储的始终是链表第一个结点的地址
  • 带头结点的链表中,头结点的数据域可以存储表长等信息,也可以不存储,指针域存储的是下一个结点的地址,即链表中第一个元素的地址

1.3.2 头结点的优点

引入头结点后,可以带来两个优点:

  1. 由于第一个数据节点的位置被存放在头结点的指针域中,因此在链表的第一个位置上的操作和在表的其它位置上的操作一致,无需进行特殊处理;
  2. 无论链表是否为空,其头指针都是指向头结点的非空指针,因此空表和非空表的处理也就得到了统一。

注:当链表为空表时,头指针指向的是头结点,而头结点的指针域为空指针。

接下来我们来看一下带头结点的单链表与不带头结点的单链表它们的初始化有什么区别。

1.4 单链表的初始化

1.4.1 不带头结点的初始化

对于不带头结点的单链表来说,我们在进行初始化时,头指针指向的是NULL即空指针,如下所示:

//不带头结点的单链表
typedef struct LNode//定义单链表的数据类型
{
	int data;//单链表的数据域
	struct LNode* next;//单链表的指针域
}LNode, * LinkList;
void InitList(LinkList* L)//通过二级指针接收头指针L的地址
{
	*L = NULL;//通过解引用将头指针初始化为空指针,防止头指针变成野指针
	//此时的链表为空表
}
int main()
{
	LinkList L;//定义指向单链表的指针L
	//这里使用LinkedList强调的是定义的内容为指向链表的指针
	//初始化单链表L
	InitList(&L);//通过传址传参来初始化单链表
	return 0;
}

当然,我们也可以在初始化完了之后对这个链表进行判断,看看它是否为空表,如下所示:

【数据结构】第二章——线性表(4)_数据结构_05

为了提高代码的健壮性,我们还可以在初始化之后给函数一个返回值,用来告知现在已经完成了初始化,如下所示:

【数据结构】第二章——线性表(4)_链表_06

像这样修改后,我们就能更加清楚的知道此时是否成功进行了初始化。

1.4.2 带头结点的初始化

当我们需要创建一个带头结点的单链表时,我们就不能直接将头指针初始化为空指针,而是应该先让头指针指向头结点,再将头结点的指针域初始化为空指针。如下所示:

//带头结点的单链表
typedef struct LNode//定义单链表的数据类型
{
	int data;//单链表的数据域
	struct LNode* next;//单链表的指针域
}LNode, * LinkList;
bool InitList(LinkList* L)//通过二级指针接收头指针L的地址
{
	//通过calloc向内存申请一块空间
	*L = (LNode*)calloc(1, sizeof(LNode));
	//这里不能直接通过L来接收申请的空间的起始地址,L此时是一个二级指针,我们需要先对其解引用
	//这里使用LNode*来表示此时申请的是结点的空间
	if (!(*L))//通过逻辑反操作,判断空间是否申请失败
   //!的优先级高于*,此时我们需要先通过括号让*与二级指针结合,再对其进行逻辑反操作
		return false;//当空间申请失败时,头指针为空指针,此时返回false
	(*L)->next = NULL;//当空间申请成功时,将头结点的指针域初始化为空指针
	//->的优先级高于*,此时我们需要通过括号先让*与二级指针L结合,再对其进行指向结构体成员
	return true;
}
int main()
{
	LinkList L;//定义指向单链表的指针L
	//这里使用LinkedList强调的是定义的内容为指向链表的指针
	//初始化单链表L
	InitList(&L);//通过传址传参来初始化单链表
	return 0;
}

现在我们就能很直观的感受到使用LinkListLNode这两个名字的区别了:

  • LinkList强调的是整个链表;
  • LNode强调的是单个结点;

注:这里一定要注意我们通过LinkList定义的L是一个指针类型,我们在对L进行初始化时,需要通过传址传参,函数需要使用二级指针来接收,所以是LinkList*。这时我们需要通过解引用才能对L进行初始化; 我们需要通过L来访问结构体成员时,也需要对其进行解引用。

我们如果相对带头结点的链表进行判空操作的话,就不是直接对头指针L进行判空操作,而是对头结点的指针域进行判空操作,如下所示:

//判断链表是否为空表
bool Empty(LinkList L)
{
	return (L->next == NULL);
	//这里同样可以通过逻辑反操作来进行判空
	return (!L->next);
	//根据操作符的优先级->的优先级高于!,所以不需要使用括号
}
int main()
{
	LinkList L;//定义指向单链表的指针L
	//这里使用LinkedList强调的是定义的内容为指向链表的指针
	//初始化单链表L
	if (InitList(&L))//通过传址传参来初始化单链表
	{
		//判断链表是否为空
		if (Empty(L))
		{
			printf("L此时为空表\n");
		}
		else
		{
			printf("L此时不为空表\n");
		}
	}
	return 0;
}

通过这两种链表初始化的对比,我们可以更直观的看到带头结点与不带头结点的区别。因此为了方便我们后面的操作,在后续的介绍中,我都会以带头结点的链表来进行介绍,同时也希望大家能够多使用带头结点的链表。

结语

今天的内容到这里就结束了,我们今天重点介绍了带头结点的单链表与不带头结点的单链表之间的区别。希望大家在阅读完这篇内容后,能够更好的理解这两种形式的单链表。在下一篇内容中,我会继续给大家介绍单链表的一些基本操作,大家记得关注哦!

最后,感谢大家的翻阅,咱们下一篇再见!!!

标签:初始化,结点,单链,线性表,LNode,链表,第二章,数据结构,指针
From: https://blog.51cto.com/u_16231477/8986656

相关文章

  • MySQL-索引数据结构
    BTreeB-树即B树。指的是BalanceTree,也就是平衡树,平衡树是一颗查找树,并且所有叶子节点位于同一层。每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点。所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中。B+Tree是B树的一种变形,它是基于B......
  • 数据结构
    数据结构概念a,分析问题,将实际问题抽象出一个人数学模型b,设计一个解决问题的模型c,编写代码,调试,测试得到最后结果什么是数据结构数据 客观事物用符号表示结构· 数据之间的存在的关系 数据结构:计算机存储和组织数据符号,相互之间的关系集合数据结构分类:逻辑结构:数据之间的关系(......
  • 数据结构之<堆>的介绍
    1.简介堆是一种特殊的数据结构,通常用于实现优先队列。堆是一个可以被看作近似完全二叉树的结构,并且具有一些特殊的性质,根据这些性质,堆被分为最大堆(或者大根堆,大顶堆)和最小堆两种。2.基本性质完全二叉树结构:堆必须是一棵完全二叉树,即除了最底层,其他层都是满的,而且最底层的节点都尽量......
  • 数据结构习题24/12/24
    这道题目可以考虑,如果前缀是一样的长度,那么只需要两个链表同时向后检索,直到找到一样的元素为止。所以应该先找到两个链表的长度,然后将较长的一个链表的多出来的前缀部分删掉,也就不去看这一部分。因为后缀都是一样的,所以长度的差异只可能来自前缀。解决代码:typedefstructNode{......
  • C++简单实现list链表数据结构(一)
    链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。链表的组成:链表由一系列结点组成结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域C++STL中的链表是一个双向循环链表由于链表的存储方式并不是连续的内存空......
  • 2-1张量数据结构
    0.配置Pytorch的基本数据结构是张量Tensor。张量及多维数组。Pytorch的张量和numpy中的array很类似。本节我们主要介绍张量的数据类型、张量的维度、张量的尺寸、张量和numpy数组等基本概念。importtorchprint('torch.__version__='+torch.__version__)"""torch.__ve......
  • [2024深圳市考][计算机素质测试考纲](二)算法和数据结构
    前言因篇幅有限,本文仅对考纲中的考点做基本介绍。更详细的内容请自行学习:【双语字幕】CS61B数据结构|整合版|UCBDataStructureSpring2021【中英双字】普林斯顿大学-算法分析AlgorithmAnalysis2015COS423一、基本概念二、数组三、链表四、栈和队列五、递......
  • Week1——STL 与基础数据结构专题训练
    https://blog.csdn.net/qq_46025844/article/details/127948957 实训概要实训专题STL与基础数据结构专题训练实训目的掌握STL常用的算法、容器、容器适配器的使用方法。能够利用STL的算法、容器、容器适配器求解问题。题目列表A:摘苹果B:立方和C:计算个数D:后缀表达式的值E:做蛋糕......
  • 【数据结构】第二章——线性表(2)
    线性表的顺序表示导言大家好,很高兴又和各位见面啦!!!在上一个篇章中,我们简单了解了一下线性表的基础知识以及一下重要的术语。在今天的篇章中我们将来开始正式介绍线性表的顺序存储——又称顺序表。我们将会在本章介绍什么是顺序表,对于顺序表的操作我们又应该如何实现。接下来,我们就来......
  • 【算法】【线性表】移除元素
    1 题目给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。示例1:输......