【陈越 数据结构】第二讲 线性结构1-线性链表

2.1线性表及其实现
2.1.1引子:多项式表示

例:一元多项式及其运算
一元多项式: f(x) = a0 + a1x + a2x^2 + ……+ a(n-1) x ^ (n-1) + an x^n
主要运算:多项式相加、相减、相乘等
如何表示多项式?
多项式的关键数据:多项式项数n,各项系数ai及指数i
方法1:顺序存储结构直接表示
数组各分量对应多项式各项
例如:f(x) = 4x^5 - 3x^2 +1 表示成:
a[0] = 1,a[1] = 0,a[2] = -3,a[3] = 0,a[4] = 0 ,a[5] = 4
a[i]每个分量表示系数,i表示x的指数。
即用数组的6个分量来表示多项式的3个项。
在这里插入图片描述

两个多项式相加:两个数组对应分量相加
问题:如何表示多项式x+3*x^2000?
则至少需要大小为2001的数组,并且只有两项是非零的,造成空间的巨大浪费。遍历的话也是很麻烦的。
因此:有没有办法只表示非零项呢?
方法2:顺序存储结构表示非零项
每个非零项涉及两个信息:系数ai,指数i,可以把多项式看成是一个(ai,i)二元组的集合
用结构数组表示:数组分量是由系数ai,指数i组成的结构,对应一个非零项。按指数大小有序储存
在这里插入图片描述
两个多项式相加过程:
把指数大的排在前面,指数小的排在后面,从头开始,比较两个多项式当前对应项的指数,指数大的那项则直接输出,指数相同则对应系数相加后输出。
相加过程实例:
在这里插入图片描述
但是我们不一定非要存储在数组里,因此:
方法3:链表结构存储非零项
链表中每个结点存储多项式中的一个非零项,包括系数和指数两个数据域以及一个指针域
在这里插入图片描述


typedef struct PolyNode* Polynomial;
struct PolyNode{
	int coef;	//系数 
	int expon;	//指数 
	Polynomial link;
}; 

同样需要将指数项从大到小排列,相加过程跟数组一样。

2.1.2线性表及顺序存储

多项式表示问题的启示:
1.同一个问题可以有不同的表示(存储)方法
2.有一类共性问题:有序线性序列的组织和管理
什么是线性表?
线性表(Linear List):由同类型数据元素构成有序序列数据结构。表中元素个数称为线性表的长度;线性表没有元素时称为空表;表起始位置称表头,表结束位置称表尾
在这里插入图片描述

在这里插入图片描述
线性表的顺序存储实现
在这里插入图片描述
利用数组的连续存储空间顺序存放线性表的各元素

typedef struct LNode* List;
struct LNode{
	ElementType Data[MAXSIZE];	//存储元素的数组 
	int last;	//最后一个元素的位置 
};
struct LNode L;
List Ptrl;

访问下标为i的元素:L.Data[i]或Ptrl->Data[i]
线性表的长度:L.Last + 1或Ptrl->Last + 1

2.1.3 线性表主要操作的实现

线性表的数组实现:

1.初始化(建立空的顺序表)

List makeEmpty()
{
	List Ptrl;
	Ptrl = (List) malloc(sizeof(struct LNode));
	Ptrl->last = -1; 
	return Ptrl;
 } 

2.查找
给定一个线性表List Ptrl和要查找的元素ElementType x:

int find(ElementType x,List Ptrl)
{
	int i = 0;
	while(i <= Ptrl->last && Ptrl->Data[i] != x)
		i++; //循环遍历每个元素,看是否有与x相等的
	if(i > Ptrl->last)	
		return -1;	//如果没找到,返回-1 
	else
		return i;	//找到后返回的是存储位置 
}

查找成功的平均次数为(n+1)/2(运气好,第1次就找到了,运气不好,最后一次n次才找到,平均一下就是(n+1)/2),平均实现性能为O(n)。
3.顺序存储的插入
在这里插入图片描述


void insert(ElementType x,int i,List Ptrl)
{
	int j;
	if(Ptrl->last == MAXSIZE - 1)	//表空间已满,不能插入 
	{
		printf("表满\n");
		return;	
	}	
	if(i < 1 || i > Ptrl->last + 2)	//检查插入位置的合法性
	{
		printf("位置不合法\n");
		return;
	 } 
	 for(j = Ptrl->last;j >= i - 1; j--)
		Ptrl->Data[j+1] = Ptrl->Data[j];	//将ai~an倒序向后移动
		//从最后一个元素开始,每次都将Data[j]移动到Data[j+1]
	Ptrl->Data[j] = x;	//将新元素插入 
	Ptrl->last++;		//Last仍指向最后元素,因为多了一个元素,所以+1 
	return;
 }

平均移动次数为n/2,平均时间性能为O(n)。
在这里插入图片描述

void delete(int i,List Ptrl)
 {
 	int j;
 	if(i < 1 || i > Ptrl->last + 1)	//检查空表及删除位置的合法性 
 	{
 		printf("不存在第%d个元素",i);
 		return;
	 }
	 for(j = i;j < Ptrl->last;j++)
		Ptrl->Data[j-1] = Ptrl->Data[j];	//将ai+1~an顺序向前移动,把Data[j-1]每次向前移动一个得到Data[j]
	Ptrl->last--;	//last仍指向最后元素,因为少了一个元素,所以-1
	return;
  }
2.1.4 线性表的链式存储实现

不要求逻辑上相邻的两个元素物理上也相邻;通过“链”建立起数据元素之间的逻辑关系
插入、删除不需要移动数据元素,只需要修改“链”
在这里插入图片描述

typedef struct LNode* List;
struct LNode{
	ElementType Data; //已经每个块里的数据结构
	List Next;//以及下一个指向
}; 
struct LNode L;
List Ptrl;

链表形式的求表长和查找都比数组形式的麻烦。

1.求表长

int length(List Ptrl)
{
	List p = Ptrl;	//p指向表的第一个结点
	int j = 0;
	while(p){  //开始从头到尾遍历这个链表
		p = p->Next; //让连边的指针加一位
		j++;	//当前p指向的是第j个结点 
	} 
	return j; 
}

时间性能为表的长度O(n)
2.查找
(1)按序号查找:findKth

List findKth(int K,List Ptrl)
{
	List p = Ptrl;
	int i = 1;
	while(p != NULL && i < K) //遍历
	{
		p = p->Next;
		i++;
	}
	if(i == K)
		return p;	//找到第K个,返回指针 
	else
		return NULL;	//否则返回空 
 } 

(2)按值查找:find

List find(ElementType x,List Ptrl)
{
	List p = Ptrl; //列表头
	while(p != NULL && p->Data != x) //没找到或者表不空
		p = p->Next;
	return p; 
}

平均时间性能O(n)。
3.插入
(在第i-1(1<=i<=n+1)个结点后插入一个值为X的新结点)
(1)构造一个新结点,用s指向
(2)再找到链表的第i-1个结点,用p指向
(3)然后修改指针,插入结点(p之后插入新结点是s)

在这里插入图片描述

List insert(ElementType x,int i,List Ptrl)
{
	List p,s;
	if(i == 1)	//新结点插在表头 
	{
		s = (List)malloc(sizeof(struct LNode));		//申请、填装结点
		s->Data = x;
		s->Next = Ptrl;
		return s; 
	}
	
	p = findKth(i - 1,Ptrl);	//查找第i-1个结点
	if(p == NULL)		//第i-1个不存在,不能插入 
	{
		printf("参数i错");
		return NULL;
	 } 
	 else
	 {
	 	s = (List)malloc(sizeof(struct LNode));		//申请、填装结点 
	 	s->Data = x;
	 	s->Next = p->Next;	//新结点插入在第i-1个结点的后面 
	 	p->Next = s;
	 	return Ptrl;
	 }
 } 

平均查找次数n/2
4.删除
(删除链表的第i(1<=i<=n)个位置上的结点)
(1)先找到链表的i-1个结点,用p指向
(2)再用指针s指向要被删除的结点(p的下一个结点)
(3)然后修改指针,删除s所指结点
(4)释放s所指结点的空间

在这里插入图片描述

在这里插入图片描述


List Delete(int i,List Ptrl)
 {
 	List p,s;
 	if(i == 1)	//若要删除的是表的第一个结点 
 	{
 		s = Ptrl;	//s指向第一个结点
		if(Ptrl != NULL)	//从链表中删除 
			Ptrl = Ptrl->Next; 
		else
			return NULL;
		free(s);		//释放被删除结点 
		return Ptrl;
	 }
	 
	p = findKth(i - 1,Ptrl);		//查找第i-1个结点
	if(p == NULL)
	{
	 	printf("第%d个结点不存在",i-1);
	 	return NULL;
	} 
	else if(p->Next == NULL)
	{
	  	printf("第%d个结点不存在",i);
	  	return NULL;
	}
	else
	{
		s = p->Next;	//s指向第i个结点 
		p->Next = s->Next;	//从链表中删除 
		free(s);		//释放被删除结点
		return Ptrl; 
	}
  }

平均查找次数n/2

2.1.6广义表与多重链表

在这里插入图片描述
广义表(Generalized List)是线性表的推广,对于线性表而言,n个元素都是基本的单元素,广义表中,这些元素不仅可以是单元素也可以是另一个广义表
那么怎么区分结点是一个单元素还是一个广义表呢?
可以用一个tag标志域来实现。
在这里插入图片描述

typedef struct GNode* GList;
struct GNode{
	int Tag;	//标志域,0表示结点是单元素,1表示结点是广义表
	union{		//子表指针域Sublist与单元素数据域Data复用,即共用存储空间 
		ElementType Data;
		GList SubList;
	}URegion; 
	GList Next;	//指向后继结点 
};

【emmm 这一部分看的不是很懂】

多重链表:链表中的结点可能同时隶属于多个链
多重链表中结点的指针域会有多个,如前面例子包含了Next和Sublist两个指针域,但包含两个指针域的链表不一定是多重链表,比如双向链表不是多重链表。
多重链表有广泛的用途:基本上如树、图这样相对复杂的数据结构都可以采用多重链表方式实现存储。
例:矩阵可以用二维数组表示,但二维数组表示有两个缺陷:
一是数组的大小需要事先确定
二是对于“稀疏数组”(零项很多,非零项很少),将造成大量的存储空间浪费
在这里插入图片描述
分析:采用一种典型的多重链表——十字链表来存储稀疏矩阵
只存储矩阵非零元素
结点的数据域:行坐标Row,列坐标Col,数值Value
每个结点通过两个指针域,把同行、同列串起来
行指针(或称为向右指针)Right
列指针(或称为向下指针)Down
在这里插入图片描述

term:属于某一行也属于某一列,形成一个十字形,因此叫做十字链表。(代表稀疏矩阵中的非零项)
在这里插入图片描述

head:作为行这个链表的头结点,也作为列这个链表的头结点在这里插入图片描述

最头上的这个term表示:这个矩阵总共有4行5列,非零项有7项。
是整个矩阵的入口结点
在这里插入图片描述

用一个标识符Tag来区分头结点和非零元素结点:头结点的标志值为Head,矩阵非零元素结点的标志值为Term。
在这里插入图片描述


本博客参考:https://blog.csdn.net/wtrnash/article/details/48339021


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值