第二章 线性表(2.5.1-2.5.2)

2.5 线性表的链式表示和实现

2.5.1单链表的定义的表示

线性表链式存储结构的特点是:用一组任意的存储单元存储存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素ai与其后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素ai的存储映像,称为结点(node)。它包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存位置的域称为指针域。指针域中存储的信息称作指针或链。n个结点链结成一个链表,即为线性表的链式存储结构。又由于此链表的每个结点中只包含一个指针域,故又称线性链表或单链表。

1、非顺序映像、链式映像:

用单链表表示线性表时,数据元素之间的逻辑关系是由结点中的指针指示的。换句话说,指针为数据元素之间的逻辑关系的映像,则逻辑上相邻的两个数据元素其存储的物理位置不要求紧邻,由此,这种存储结构为非顺序映像链式映像

通常将链表画成下图形式,这是因为在使用链表时,关心的只是它所表示的线性表中数据元素之间的逻辑顺序,而不是每个数据元素在存储器中的实际位置。

2.首元结点、头结点、头指针的区分:

(1)首元结点是指链表中存储第一个数据元素ai的结点。如上两图中的“ZHOU”。
(2)头结点是在首元结点之前附设的一个结点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可以存储与数据元素类型相同的其他附加信息。例如,当数据元素为整数型时,头结点的数据域中可存放该线性表的长度。
(3)头指针是指向链表中第一个结点的指针。若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。

3、链表增加头结点的作用:

(1)便于首元结点的处理
增加了头结点后,首元结点的地址保存在头结点的指针域中。则对链表的第一个数据元素的操作与其他数据元素相同,无需进行特俗处理
(2)便于空表和非空表的统一处理
当链表不设头结点时,假设L为单链表的头指针,他应该指向首元结点,则当单链表为长度n为0的空表时,L指针为空(L==NULL)。

4、总结:

在顺序表中,由于逻辑上相邻的两个元素在物理位置上紧邻,则每个元素的存储位置都可从线性表的起始位置计算得到。而在单链表中,各个元素的存储位置都是随意的。然而,每个元素的存储位置都包含在其直接前驱结点的信息之中。假设p是指向单链表中第i个数据元素的指针,则p->next是指向第i+1个个数据元素的指针。换句话说,若p->date=ai.则p->next->date=ai+1。由此,单链表是非随机存取的存储结构,要取得第i个数据元素必须从头指针出发顺链进行寻找。也称为顺序存取的存取结构。因此,其基本操作的实现不同于顺序表。

2.5.2 单链表基本操作的实现

1.初始化

1.1算法步骤

生成新的结点作为头结点,用头指针L指向头结点

头结点的指针域置空

1.2算法描述
Status InitList(LinkList L)
 {
 	L=(LinkList)malloc(sizeof(Node));
 	L-next=NULL;
 } 

2、取值

2.1算法步骤

用指针p指向首元结点,用 j 做计数器初值赋为1。
从首元结点开始依次顺着链域next向下访问,只要指向当前结点的指针 p 不为空(NULL),并且没有达到序号为i的结点,则循环执行一下操作:

1)p指向下一个结点
2)计数器 j++

3}退出循环时,如果指针p为空,或者计数器 j 大于 i ,说明指定的序号 i 值不合法(i大于表长n 或 i 小于等于0),取值失败返回ERROR;否则取值成功,此时 j=i时,p所指的结点就是要找到第 i 个结点,用参数e保存当前结点的数据域,返回OK.

2.2算法描述
Status GetElem(LinkList L,int i,Elemtpye &e)
{//在带头结点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
	LNode *p=L->next;	//初始化,p指向首元结点
	int j=1;		//计数器初值赋为1
	while(p&&j<i)		//当p不为空,j<i时顺链域向后扫描
	{
		p=p->next;	//p指向下一个结点
		j++;		//计数器加一
	}
	if(!p||j>i)		//i值不合法i大于表长n或i<=0
		return ERROR;
	e=p->data;		//取第i个结点的数据域	
	return OK;
}

3、查找

3.1算法步骤

1、用指针p指向首元结点。

2、从首元结点开始依次顺着链域next向下查找,只要指向当前结点的指针p不为空,并且p所指结点的数据域不等于给定值e,则循环执行以下操作:p指向下一个结点

3、返回p。若查找成功,p此时即为结点的地址值,若查找失败,p的值即为NULL。

3.2算法描述
LNode *LocateElem(LinkList L,ElemType e)
{//在带头结点的单链表L中查找值为e的元素
	LNode *p=L->next;		//初始化,p指向首元结点
	while(p && p->data != e)	//顺链域向后扫描,直到p为空或者p所指结点的数据域等于e
	{
		p=p->next;
	return p;			//查找成功返回值为e的结点地址p,查找失败p为NULL
	}
}

4、插入

4.1算法步骤

要实现:将值为e的新结点插入到表的第i个结点的位置上,即插入到结点ai-1与ai之间
1、声明一个结点p指向链表的第一个结点,初始化j=1
2、 当j<i时,遍历链表,p的指针向后移动,j++;
3、若链表末尾p为空时,则说明第i个元素不存在
4、 否则查找成功,在系统中生成一个空结点s
5、将数据元素e赋值给s->data
6、s->next=p->next; p->next=s;
7、返回成功

4.2算法步骤
Status ListInsert(LinkList &L,int i,ElemType e)
{//在带头结点的单链表L中第i个位置插入值为e的新结点
	LNode *p = L;
	int j=0;
	while(p&&(j<i-1))
	{
		p = p->next;	//查找第i-1个结点,p指向该结点
		j++;
	}
	if(!p||j>i-1)		//i>n+1也就是表长或者i<1
		reutrn ERROR;
	LNode *s=new LNode;	//生成新结点 *s
	s->data=e;		//将结点 *s的数据域置为e
	s->next=p->next;	//将结点 *s的指针域指向结点ai
	p->next = s;		//经结点 *p 的指针域指向结点 *s
	return OK;

}

5、删除

5.1算法步骤

1、声明结点p指向空结点,初始化计数器j=0;
2、当j<i时,遍历链表,让p的指针向后移动,不断指向下一个结点,j++;
3、若链表末尾为空,则说明第i个结点不存在返回ERROR
4、否则将待删除的结点地址赋给q,以备释放。
5、q=p->next;p->next=q->next;
6、释放q,返回OK.

5.2算法描述
Status ListDelete(LinkList &L,int i)
{//在带头结点的单链表L中,删除第i个元素
LNode *p = L;
int j = 0;
while(p->next && j<i-1)		//查找第i-1个结点,p指向该结点
{
	p=p->next;
	j++;
}
if(!(p->next)||j>i-1)		//i大于n或者i<1时,删除位置不合理
	return ERROR;
	LNode *q=p->next;	//临时保存被删除结点以备释放
	p->next=q->next;	//改变删除结点前驱结点的指针域
	delete q;		//释放被删除结点空间
	return OK;


}

6、创建单链表

6.1前插法创建单链表
6.1.1描述:

​ 前插法:将n个新结点逐个插入链表的头部(头结点结束之后),每次申请一个新结点,读入数据元素值,然后将新结点插入到头结点之后,例如,要插入a、b、c、d、e
,那么用前插法插入到链表中后,他们从头到尾的内容为:e、d、c、b、a,即输入顺序与线性表的逻辑顺序相反

6.1.2算法步骤:

1、创建一个只有头结点的空链表
2、根据待创建链表包括的元素个数n,循环n次执行以下操作:

1)生成一个新结点 p。
\2) 输入元素值赋给新结点
p的数据域。
3)将新结点*p插入到头结点之后。

6.1.3算法描述
void CreateList_H(LinkList &L,int n)
{//逆位序输入n个元素的值,建立带 表头结点 的单链表L
	L=new LNode		//分配空间,建立一个带头结点的空链表
	L->next = NULL;		
	for(i=0;i<n;i++)
	{
		LNode *p = new LNode;	//生成新结点*p
		cin>>p->data;		//输入元素值赋给新结点 *p 的数据域
		//将新结点 *p 插入到头结点之后
		p->next = L->next;	//将新结点*p的指针域指向下一结点
		L->next = p;		//将头结点的指针域指向新结点*p
	}
	
}
6.2后插法创建单链表
6.2.1描述

​ 后插法是通过将新结点逐个插入到链表尾部来创建链表,同前插法一样,每次申请一个新结点,读入数据元素值,不同的是,为了使新结点能插入到表尾,需增加一个尾指针r指向链表的尾结点。

6.2.2算法步骤

1、创建一个只有头结点的空链表
2、尾指针初始化,指向头结点。
3、根据创建链表包括的元素个数n,循环n次执行以下操作:

1)生成一个新结点p
2)输入元素值赋给新结点p的数据域。
3)将新结点 *p插入到尾结点 *r之后;
4)尾指针r指向新的尾结点 *p;

后插法的创建过程,读入数据的顺序和线性表中的逻辑顺序是相同的,读入1、2、3、4则输出顺序1、2、3、4。

6.2.2算法描述
void CreateList_R(LinkList &L,int n)
{//正位序输入n个元素的值,建立带表头结点的单链表L
	L=new LNode;
	L->next = NULL;		//先建立一个带头结点的空链表
	r=L;			//尾指针r指向头结点
	for(i = 0;i<n;i++)
	{
		LNode *p=new LNode;		//生成新结点
		cin>>p->data;			//输入元素值赋给新结点*p的数据域
		p->next = NULL;			
		r->next = p;			//将新结点*p插入尾结点*r之后
		r=p;				//r指向新的尾结点*p

}

}
posted @     阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示