数据结构期末复习(第五章 数组和广义表)

数据结构期末复习(第五章 数组和广义表)

我裂开了 怎么这么多东西啊,我以为没多少东西(叹气) 代码这也。。没什么可敲的,稀疏矩阵敲一下吧。。 广义表人工智能领域用的广泛,咱们现在也敲不出来,知道这个东西就行了 一定!多刷题!多刷题!多刷题! 这一单元的题很多都很阴间!

Part 1、知识点总结

数组是一种人们非常熟悉的数据结构,几乎所有的程序设计语言都支持这种数据结构或将这种数据结构设定为语言的固有类型。数组这种数据结构可以看成是线性表的推广。
科学计算中涉及到大量的矩阵问题,在程序设计语言中一般都采用数组来存储,被描述成一个二维数组。但当矩阵规模很大且具有特殊结构(对角矩阵、三角矩阵、对称矩阵、稀疏矩阵等),为减少程序的时间和空间需求,采用自定义的描述方式。
广义表是另一种推广形式的线性表,是一种灵活的数据结构,在许多方面有广泛的应用。

1.1 数组的定义

数组是一组偶对(下标值,数据元素值)的集合。在数组中,对于一组有意义的下标,都存在一个与其对应的值。一维数组对应着一个下标值,二维数组对应着两个下标值,如此类推。
数组是由n(n>1)个具有相同数据类型的数据元素a1,a2,…,an组成的有序序列,且该序列必须存储在一块地址连续的存储单元中。
◆ 数组中的数据元素具有相同数据类型。
◆ 数组是一种随机存取结构,给定一组下标,就可以访问与其对应的数据元素。
◆ 数组中的数据元素个数是固定的。
◆ 数组元素的下标具有上下界的约束且下标有序

1.1.1 抽象数据类型定义

ADT Array{
数据对象:ji= 0,1,…,bi-1 , 1,2, …,n ;
D = { aj1j2…jn | n>0称为数组的维数,bi是数组第i维的长度,ji是数组元素第i维的下标,aj1j2…jn∈ElemSet }
数据关系:R = {R1, R2, …, Rn}
Ri={<aj1j2 …ji…jn , aj1j2 …ji+1…jn>|0≦jk≦bk-1 , 1≦k≦n且k≠i,0≦ji≦bi-2, aj1j2 …ji+1…jn∈D }
基本操作: ……
} ADT Array
◆ 由上述定义知,n维数组中有b1b2  …  bn个数据元素,每个数据元素都受到n维关系的约束。

1.1.2 直观的n维数组

以二维数组为例讨论。将二维数组看成是一个定长的线性表,其每个元素又是一个定长的线性表。
在这里插入图片描述

1.2 数组的顺序表示和实现

数组一般不做插入和删除操作,也就是说,数组一旦建立,结构中的元素个数和元素间的关系就不再发生变化。因此,一般都是采用顺序存储的方法来表示数组。
计算机的内存结构是一维(线性)地址结构,对于多维数组,将其存放(映射)到内存一维结构时,有个次序约定问题。即必须按某种次序将数组元素排成一列序列,然后将这个线性序列存放到内存中。
二维数组是最简单的多维数组,以此为例说明多维数组存放(映射)到内存一维结构时的次序约定问题。
通常有两种顺序存储方式
⑴ 行优先顺序(Row Major Order) :将数组元素按行排列,第i+1个行向量紧接在第i个行向量后面。对二维数组,按行优先顺序存储的线性序列为:
a11,a12,…,a1n, a21,a22,…a2n ,……, am1,am2,…,amn
PASCAL、C是按行优先顺序存储的,如图5-2(b)示。
⑵ 列优先顺序(Column Major Order) :将数组元素按列向量排列,第j+1个列向量紧接在第j个列向量之后,对二维数组,按列优先顺序存储的线性序列为:
a11,a21,…,am1, a12,a22,…am2, ……, an1,an2,…,anm
FORTRAN是按列优先顺序存储的,如图5-2©。
这边。。。下标显示会有问题,我就直接放图片了在这里插入图片描述
设有二维数组A=(aij)mxn,若每个元素占用的存储单元数为l(个),LOC[a11]表示元素a11的首地址,即数组的首地址。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总而言之,LOC[i,j]是元素a[i][j]的存储地址,每个数组占用了L个存储字

1.3 矩阵的压缩存储

在科学与工程计算问题中,矩阵是一种常用的数学对象,在高级语言编程时,通常将一个矩阵描述为一个二维数组。这样,可以对其元素进行随机存取,各种矩阵运算也非常简单。
对于高阶矩阵,若其中非零元素呈某种规律分布或者矩阵中有大量的零元素,若仍然用常规方法存储,可能存储重复的非零元素或零元素,将造成存储空间的大量浪费。对这类矩阵进行压缩存储:
◆ 多个相同的非零元素只分配一个存储空间;
◆ 零元素不分配空间。
这个时候,就用到了特殊矩阵:
特殊矩阵:是指非零元素或零元素的分布有一定规律的矩阵。

1.3.1 对称矩阵

若一个n阶方阵A=(aij)nxn中的元素满足性质:
aij=aji 1≦i,j≦n且i≠j,则称A为对称矩阵
在这里插入图片描述
对称矩阵中的元素关于主对角线对称,因此,让每一对对称元素aij和aji(i≠j)分配一个存储空间,则n2个元素压缩存储到n(n+1)/2个存储空间,能节约近一半的存储空间。
不失一般性,假设按“行优先顺序”存储下三角形(包括对角线)中的元素。
设用一维数组(向量)sa[0…n(n+1)/2]存储n阶对称矩阵,如图5-4所示。为了便于访问,必须找出矩阵A中的元素的下标值(i,j)和向量sa[k]的下标值k之间的对应关系。
在这里插入图片描述
在这里插入图片描述

1.3.2 三角矩阵

上三角矩阵的下三角(不包括主对角线)中的元素均为常数c(一般为0)。下三角矩阵正好相反,它的主对角线上方均为常数。
在这里插入图片描述
三角矩阵中的重复元素c可共享一个存储空间,其余的元素正好有n(n+1)/2个,因此,三角矩阵可压缩存储到向量sa[0…n(n+1)/2]中,其中c存放在向量的第1个分量中。
上三角矩阵元素ai j保存在向量sa中时的下标值k与(i,j)之间的对应关系是:
在这里插入图片描述

1.3.3 对角矩阵

矩阵中,除了主对角线和主对角线上或下方若干条对角线上的元素之外,其余元素皆为零。即所有的非零元素集中在以主对角线为了中心的带状区域中。
在这里插入图片描述
对角矩阵可按行优先顺序或对角线顺序,将其压缩存储到一个向量中,并且也能找到每个非零元素和向量下标的对应关系。
仍然以三对角矩阵为例讨论。
当i=1,j=1、2,或i=n, j=n-1、n或
1<i<n-1,j=i-1、i、i+1的元素aij外,其余元素都是0。
对这种矩阵,当以按“行优先顺序”存储时, 第1行和第n行是2个非零元素,其余每行的非零元素都要是3个,则需存储的元素个数为3n-2。
在这里插入图片描述
在这里插入图片描述

1.3.4 稀疏矩阵

稀疏矩阵(Sparse Matrix):对于稀疏矩阵,目前还没有一个确切的定义。设矩阵A是一个nm的矩阵中有s个非零元素,设 δ=s/(n·m),称δ为稀疏因子,如果某一矩阵的稀疏因子δ满足δ≦0.05时称为稀疏矩阵。
在这里插入图片描述

1.3.4.1 稀疏矩阵的压缩存储

对于稀疏矩阵,采用压缩存储方法时,只存储非0元素。必须存储非0元素的行下标值、列下标值、元素值。因此,一个三元组(i, j, aij)唯一确定稀疏矩阵的一个非零元素。
如图5-8的稀疏矩阵A的三元组线性表为:
( (1,2,12), (1,3,9), (3,1,-3), (3,8,4), (4,3,24), (5,2,18), (6,7,-7), (7,4,-6) )

1.3.4.1.1 三元组顺序表

若以行序为主序,稀疏矩阵中所有非0元素的三元组,就可以得构成该稀疏矩阵的一个三元组顺序表。
若以行序为主序,稀疏矩阵中所有非0元素的三元组,就可以得构成该稀疏矩阵的一个三元组顺序表。相应的数据结构定义如下:
⑴ 三元组结点定义

#define MAX_SIZE 101
typedef int elemtype ;
typedef struct
{   int   row ;     /*  行下标  */
int  col ;        /*  列下标  */
elemtype value;      /*  元素值  */
}Triple ;

⑵ 三元组顺序表定义

typedef struct 
{   int  rn ;         /*   行数   */
int  cn ;         /*   列数   */
int  tn ;         /*    非0元素个数   */
Triple   data[MAX_SIZE] ; 
}TMatrix ; 

在这里插入图片描述
矩阵的运算包括矩阵的转置、矩阵求逆、矩阵的加减、矩阵的乘除等。在此,先讨论在这种压缩存储结构下的求矩阵的转置的运算。

1.3.4.1.2 矩阵转置

一个mxn的矩阵A,它的转置B是一个nxm的矩阵,且b[i][j]=a[j][i],0≦i≦n,0≦j≦m,即B的行是A的列,B的列是A的行。
设稀疏矩阵A是按行优先顺序压缩存储在三元组表a.data中,若仅仅是简单地交换a.data中i和j的内容,得到三元组表b.data,b.data将是一个按列优先顺序存储的稀疏矩阵B,要得到按行优先顺序存储的b.data,就必须重新排列三元组表b.data中元素的顺序。
求转置矩阵的基本算法思想是:
① 将矩阵的行、列下标值交换。即将三元组表中的行、列位置值i 、j相互交换;
② 重排三元组表中元素的顺序。即交换后仍然是按行优先顺序排序的。

1.3.5 十字链表

    对于稀疏矩阵,当非0元素的个数和位置在操作过程中变化较大时,采用链式存储结构表示比三元组的线性表更方便。
   矩阵中非0元素的结点所含的域有:行、列、值、行指针(指向同一行的下一个非0元)、列指针(指向同一列的下一个非0元)。其次,十字交叉链表还有一个头结点,结点的结构如图5-10所示。

在这里插入图片描述
由定义知,稀疏矩阵中同一行的非0元素的由right指针域链接成一个行链表, 由down指针域链接成一个列链表。则每个非0元素既是某个行链表中的一个结点,同时又是某个列链表中的一个结点,所有的非0元素构成一个十字交叉的链表。称为十字链表。
此外,还可用两个一维数组分别存储行链表的头指针和列链表的头指针。
结点的描述如下:

typedef struct  Clnode  
{   int  row , col ;   /*  行号和列号  */     
elemtype value ;    /*  元素值  */
struct  Clnode  *down , *right ;
}OLNode ;   /*  非0元素结点  */
typedef struct  Clnode  
{   int   rn;        /*  矩阵的行数  */     
int   cn;        /*  矩阵的列数  */
int   tn;        /*  非0元素总数  */
OLNode *rhead ;  
OLNode *chead ; 
} CrossList ;

1.4 广义表

广义表是线性表的推广和扩充,在人工智能领域中应用十分广泛。
在第2章中,我们把线性表定义为n(n≧0 )个元素a1, a2 ,…, an的有穷序列,该序列中的所有元素具有相同的数据类型且只能是原子项(Atom)。所谓原子项可以是一个数或一个结构,是指结构上不可再分的。若放松对元素的这种限制,容许它们具有其自身结构,就产生了广义表的概念。
广义表(Lists,又称为列表 ):是由n(n ≧0)个元素组成的有穷序列: LS=(a1,a2,…,an)
习惯上:原子用小写字母,子表用大写字母。
若广义表LS非空时:
◆ a1(表中第一个元素)称为表头;
◆ 其余元素组成的子表称为表尾;(a2,a3,…,an)
◆ 广义表中所包含的元素(包括原子和子表)的个数称为表的长 度。
◆ 广义表中括号的最大层数称为表深 (度)。
广义表的重要结论:
⑴ 广义表的元素可以是原子,也可以是子表,子表的元素又可以是子表, …。即广义表是一个多层次的结构。
表5-2中的广义表D的图形表示如图5-12所示。
(2) 广义表可以被其它广义表所共享,也可以共享其它广义表。广义表共享其它广义表时通过表名引用。
(3) 广义表本身可以是一个递归表。
(4) 根据对表头、表尾的定义,任何一个非空广义表的表头可以是原子,也可以是子表, 而表尾必定是广义表。
广义表的存储结构
由于广义表中的数据元素具有不同的结构,通常用链式存储结构表示,每个数据元素用一个结点表示。因此,广义表中就有两类结点:
◆ 一类是表结点,用来表示广义表项,由标志域,表头指针域,表尾指针域组成;
◆ 另一类是原子结点,用来表示原子项,由标志域,原子的值域组成。如图5-13所示。
只要广义表非空,都是由表头和表尾组成。即一个确定的表头和表尾就唯一确定一个广义表。
在这里插入图片描述
相应的数据结构定义如下:

typedef struct GLNode
{  int   tag ;     /*  标志域,为1:表结点;为0 :原子结点  */
union
{  elemtype value;     /* 原子结点的值域  */
struct
    {  struct GLNode  *hp , *tp ;
     }ptr ;   /*  ptr和atom两成员共用  */
}Gdata ; 
} GLNode ;      /* 广义表结点类型  */

对于上述存储结构,有如下几个特点:
(1) 若广义表为空,表头指针为空;否则,表头指针总是指向一个表结点,其中hp指向广义表的表头结点(或为原子结点,或为表结点) ,tp指向广义表的表尾(表尾为空时,指针为空,否则必为表结点)。
(2) 这种结构求广义表的长度、深度、表头、表尾的操作十分方便。
(3) 表结点太多,造成空间浪费。也可用图5-15所示的结点结构。
在这里插入图片描述

PPT课后题

⑴ 什么是广义表?请简述广义表与线性表的区别?
⑵ 一个广义表是(a, (a, b), d, e, (a, (i, j), k)) ,请画出该广义表的链式存储结构。
⑶ 设有二维数组a[6][8],每个元素占相邻的4个字节,存储器按字节编址,已知a的起始地址是1000,试计算:
① 数组a的最后一个元素a[5][7]起始地址;
② 按行序优先时,元素a[4][6]起始地址;
③ 按行序优先时,元素a[4][6]起始地址。
⑷ 设A和B是稀疏矩阵,都以三元组作为存储结构,请写出矩阵相加的算法,其结果存放在三元组表C中,并分析时间复杂度。
⑸ 设有稀疏矩阵B如下图所示,请画出该稀疏矩阵的三元组表和十字链表存储结构。
在这里插入图片描述

自己康的一点题

  • 若数组A[0…m][0…n]按列优先顺序存储,则aij地址为LOC(a00)+[j*n+i]。
  • 广义表A=((x,(a,B)),(x,(a,B),y)),则运算head(head(tail(A)))的结果为x

Part 2、代码

//稀疏矩阵
#include "stdio.h"
#include "stdlib.h"
typedef struct {
	int x;
	int y;
	int e;
}node;
typedef struct{
	int x;
	int y;
	int num;
	node data[100];
}list;
list* create()
{
	list *l;
	l = (list*)malloc(sizeof(list));
	int x,y,count=0;
	printf("input x,y,num:\n");
	scanf("%d %d %d",&x,&y,&l->num);
	l->x = x;
	l->y = y;
	int i,j;
	for(i=0;i<x;i++)
		for(j=0;j<y;j++)
		{
			l->data[count].e=0;
			count++;
		}
	count=0;
	printf("input x,y,e:\n");
	int e;
	scanf("%d %d %d",&x,&y,&e);
	l->data[count].x = x;
	l->data[count].y = y;
	l->data[count].e = e;
	for(i=1;i<l->num;i++)
	{
		if(count>=100)
		break;
		count++;
		scanf("%d %d %d",&x,&y,&e);
		l->data[count].x = x;
		l->data[count].y = y;
		l->data[count].e = e;
	}
	return l;
}
void print(list *l)
{
	int i,j,k;
	for(i=0;i<l->x;i++)
	{
		for(j=0;j<l->y;j++)
		{
			for(k=0;k<l->num;k++)
			{
				if(i==l->data[k].x&&j==l->data[k].y)
				{
					printf("%d ",l->data[k].e);
					break;
				}
				if(k==l->num-1)
				printf("0 ");
			}
		}
		printf("\n");
	}
}
void print2(list *l)
{
	int i;
	for(i=0;i<l->num;i++)
	{
		printf("[%d][%d]->%d\n",l->data[i].x,l->data[i].y,l->data[i].e);
	}
}
void search(list *l)
{
	int e;
	printf("please input e:\n");
	scanf("%d",&e);
	int i,j,k;
	for(k=0;k<l->num;k++)
	{
		if(e==l->data[k].e)
		{
			printf("the number's location is [%d][%d]\n",l->data[k].x,l->data[k].y);
			break;
		}
		if(k==l->num-1)
		printf("can't search!\n");
	}
}
int main()
{
	list *l;
	l = create();
	int num;
	printf("1->search\n");
	printf("2->print 矩阵\n");
	printf("3->print 三元组表\n");
	printf("0->end\n");
	printf("Please input n:\n");
	scanf("%d",&num);
	while(num!=0){
		if(num==0) break;
		else if(num == 1) search(l);
		else if (num==2) print(l);
		else if(num == 3) print2(l);
		else printf("ERROR!\n");
		printf("Please input n:\n");
		scanf("%d",&num);
	}
	printf("Thanks for using!");
	return 0;
}

Part 3、总结

艸!破防了!
这什么鬼东西都是!
注意的点就是
一个是要会求给定下标的数组元素地址
一个是要理解各种矩阵的本质
起码得知道这是啥
矩阵转置得会
十字链表后面搞图的时候会用到
广义表了解一下就彳亍,考不了多少基本上
由于这一章下标过于阴间
我这边很多地方都敲不出来,被迫截图PPT
应该没什么事情
这一章一定要多做题多见题!
要不然只看概念很多东西都是不会算的

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赤赤赤赤赤赤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值