【数据结构】第五章 数组与广义表
1.基本概念
数组和广义表,可看成是一种扩展的线性数据结构。
(1)数组
1从逻辑结构上看,数组可以看成是一般线性表的扩充。一维数组即为线性表,二维数组可定义为“其数据元素为一维数组(线性表)”的线性表。
2数组是一组有固定个数的元素的集合。对数组的操作不像对线性表的操作那样可以在表中任意一个合法的位置插入或删除一个元素。对于数组的操作一般只有两类:1.获得特定位置的元素值;2.修改特定位置的元素值。其本质上是地址计算问题。
3数组的顺序存储结构有两种:一种是按行序存储;一种是按列序存储
4根据数组的下标,可以计算出其在存储器中的位置。因此,数组的顺序存储是一种随机存取的结构。
5地址计算
(1)一维数组的地址计算
Loc(A[i])=Loc(A[1])+(i-1)size
(2)二维数组的地址计算
Loc(A[i][j])=Loc(A[1][1])+(n(i-1)+j-1)size
(3)三维数组的地址计算
Loc(A[i][j][k])=Loc(A[1][1][1])+(mn*(i-1)+n*(j-1)+(k-1))*size
<1>规律分布特殊矩阵的压缩存储
- 三角矩阵
分为上三角、下三角和对称矩阵。三角矩阵A的元素个数为1+2+3…+n=n(n+1)/2
下三角矩阵压缩形式:
Loc[i,j] = Loc[1,1]+(i*(i-1)/2+j-1)
将n²个元素压缩到n(n+1)/2个空间
A[i][j]存储到B[k]中对应关系: - 带状矩阵
带状矩阵中,除了第一行和最后一行只有2个非零元素外,其余各行均有3个非零元素。所需一维向量空间的大小为2+2+3(n-2)=3n-2,
Loc[i,j] = Loc[1,1]+(3(i-1)-1+j-i+1)*size = Loc[1,1]+(2(i-1)+j-1)
<2>稀疏矩阵的压缩存储
- 三元组表示法
存储非零元素值的同时,存储该非零元素在矩阵中的行号列号
三元组按“行序为主序”用一维数组进行存放,即将j矩阵的任何一行的全部非零元素的三元组按列号递增存放。 - 十字链表
但是在进行矩阵加法、减法和乘法等运算时,有时矩阵中的非零元素的位置和个数会发生很大的变化。稀疏矩阵的链式存储法——十字链表,它能够灵活地插入、删除实现矩阵的各种运算。
right:用于链接同一行中的下一个非零元素;
down:用于链接同一列中的下一个非零元素。
(2)广义表
在第2章中,线性表被定义为一个有限的序列(a1,a2,as,…,an),其中a被限定为是单个数据元素。广义表也是n个数据元素(d1,da,d,…,d。)的有限序列,但不同的是,广义表中的d既可以是单个元素,还可以是一个广义表,通常记作:GL=(di,da,da,…,dn)。GL是广义表的名字,通常广义表的名字用大写字母表示。n是广义表的长度。
(1)广义表的元素可以是子表,而子表还可以是子表,因此,广义表是一个多层的结构。
(2)广义表可以被其它广义表共享。在表B中不必列出表A的内容,只要通过子表的名称就可以引用该表。
(3)广义表具有递归性,如广义表C C=(a,C)长度为2递归定义的广义表,C相当于无穷表C=(a,(a,(a,(…))))。
广义表的表尾一定是一个表。
广义表的存储:难以用顺序存储结构来表示,通常用链式存储结构表示
广义表中有两类结点:一类是单个元素结点;另一类是子表结点。一对确定的表头和表尾可以唯一地确定一个广义表。
2.算法
(1)稀疏矩阵
<1>三元组表
三元组表类型定义:
#define MAXSIZE 1000
typedef struct
{
int row, col;
ElementType e;
}Triple;
typedef struct
{
Triple data[MAXSIZE+1];
int m,n,len;
}TSMatrix;
- 矩阵转置运算
算法思路:把位于(row, col)位置上的元素换到(col, row)位置上,即把元素的行列互换。
'''source 和dest 分别为被转置的矩阵和转置以后的矩阵(二维数组表示)'''
void TransMatrix(ElementType source[m][n],ElementType dest[n][m])
{
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
dest[j][i]=source[i][j];
}
- 三元组表转置
实现转置方法如下:1.原表A行列互换得到转置B 。2.B按B的行下标以递增顺序重新排序
a.列序递增转置法
算法思想:1.找第一行全部元素:第一遍从头至尾扫描三元组表A 找出所有col 为1的三元组,转置后按顺序送到三元组中。以此类推,找到K行(1<=k<=A.n)
为实现处理设置存放当前位置计数器j,用于指向当前转置后元素应放入三元组表B中的位置下标,j 初值为1,处理完一个元素后j+1.
void TransposeTSMatrix(TSMatrix A, TSMatrix *B)
{
int i,j,k;
B->m=A.n; B->n=A.m; B->len=A.len;
if(B->len>0)
{
j=1;
for(k=1;k<A.n;k++)
{
for(i=1;i<A.len;i++)
{
if(A.data[i].col==k)
{
B->data[j].row=A.data[i].col;
B->data[j].col=A.data[i].row;
B->data[j].e=A.data[i].e;
j++;
}
}
}
}
}
算法的时间耗费主要是在双重循环中,其时间复杂度为O(A.n×A.len),最坏情况下,当A.len=A.m×A.n时,时间复杂度为O(A.m×A.n2)。采用正常方式实现矩阵转置的算法时间复杂度为O(A.m×An)。因此,三元组表示法仅使用与存储稀疏矩阵
b.一次定位快速转置法
算法思想:设两个数组num[]和position[],其中num[col]用来存放三元组表A中第col列中非零元素个数(三元组表B中第col行非零元素的个数),position[col]用来存放转置前三元组表A中第col列(转置后三元组表B中第col行)中第一个非零元素在三元组表B中的正确位置。
num[col]的计算方法,将三元组表A扫描一遍,对于其中列号为col的元素,给相应的num数组中col的元素加1;
position[col]的计算方法:position[1]=1表示三元组表A中列值为1的第一个元素在三元组表B中的下标值,position[col]=position[col-1]+num[col-1],其中2<=col<=A.n
position[col]的初值为三元组表A中第col列中第一个非0元素的正确位置,当三元组表A中第col列有一个元素加入到三元组表B时,position[col]=position[col]+1 即令position[col]始终指向三元组表A中第col列中下一个非0元素在三元组表B中的正确存放位置。
void FastTransposeTSMatrix(TSMatrix A, TSMatrix *B)
{
int col,t,p,q
int num[MAXSIZE],position[MAXSIZE];
B->n=A.m; B->m=A.n; B->len=A.len;
if(B->len>0)
{
for(col=1;col<=A.n;col++) num[col]=0;
for(t=1;t<=A.len;t++) num[A.data[t].col]++;
position[1]=1;
for(col=2;col<=A.n;col++) position[col]=position[col-1]+num[col-1];
for(p=1;p<=A.len;p++)
{
col=A.data[p].col; q=position[col];
B->data[q].row=A.data[p].col;
B->data[q].col=A.data[p].row;
B->date[q].e=A.data[p].e;
position[col]++;
}
}
}
时间复杂度为O(A.len+A.n)