一、 数组和广义表的定义
数组和广义表可以视为是线性表的扩展,即线性表中的数据元素本身也是一个数据结构。
1. 二维数组定义
a11 a12 … a1n
a21 a22 … a2n
An*n= ……
am1 am2 … amn
An*n 可定义为一个线性表:
(1) A=(a1,a2,…,an),其中:每个数据元素aj(j=1,2,…,n)又是一个列向量的线性表,即:aj=(a1j,a2j,…,amj),1≤j≤n
(2) A=(B1,B2,…,Bm),其中:每个数据元素Bj(j=1,2,…,m)又是一个行向量的线性表,即:Bj=(aj1,aj2,…,ajn),1≤j≤m
2. 广义表定义
定义:数据元素可以使表的线性表。记为:LS=(d1,d2,…,dn)LS为表名,di(i=1,2,…,n),可以使单元素(用小写字母表示);也可以是广义表(称为子表,用大写字母表示);
n 为表的长度,当长度为0时称为空表;
称非空表的第一个元素d1为表头,其余元素组成的表(d2,…,dn)称表尾。(表尾一定是表,即使表尾只有一个元素或者没有元素也是表)
二、 数组和广义表的基本运算
1. 数组的基本运算
(1) 给定下标,存取相应的数据元素
(2) 给定下标,修改相应的数据元素
2. 广义表的基本运算
(1) 取表头HEAD(LS);
(2) 取表尾TAIL(LS)。
三、 数组的存储结构
1. 数组的顺序存储结构
由于数组不作插入、删除操作,所以常采用顺序存储结构来实现数组,即用一组连续的存储单元,以行主(或列主)顺序依次存放数组的数据元素。
设每个数据元素占L个存储单元,di为第i维的下标上界,ci为第i维的下标下界,采用行主顺序存放,则二维数组中任一元素aij的存储地址为:
Loc[i,j]= Loc[c1,c2]+[(i-c1)*(d2-c2+1)+(j-c2)]*L
(Loc[c1,c2]逻辑上矩阵左上角的存放位置,也表示了整个数组存放的起始位置,c1为行下标下界,c2为列下标下界)
物理存放为一维向量顺序存储
Array:array[i]=itemi
不需要移动数据元素,我们可以
求长度,遍历数组,取数组元素,修改数组元素
不能进行的操作
Insertion,Deletion
2. 矩阵的压缩存储
(1) 对称矩阵
满足aij=aji,1≤i,j≤n
可以只存储n阶对称矩阵的上(或下)三角部分,将存储量从n2压缩到n(n+1)/2。
若用一维数组S(1……n(n+1)/2),以行主顺序存放下三角部分,则S[k]和矩阵元素aij 的对应关系为:
i(i+1)/2+j当i≥j
k=
j(j-1)/2+i当i<j
(2) 对角矩阵
所有非零元素集中在平行于主对角线的带状区域中。如下矩阵。
a11 a12
a21 a22a23
……
……
an-1n-1 an-1n-1
ann-1 ann
若将三对角矩阵的元素按行主顺序存放在数组B[1,…,3n-2]中,则B[k]和元素aij的对应关系为:k=2i+j-2
(3) 稀疏矩阵
非零元素较零元素少,且分布没有规律。对每一个非零元素aij可以用一个三元组(i,j,aij)来表示,其中i,j为aij的行列号,aij为元素的值。
1)三元组线性表的顺序存储结构称为三元组表
形式描述:
CONST maxnum=大于非零元素个数的常量;
maxmn=Max{m,n};{行列最大值}
TYPE tuple3tp=RECORD
i,j:integer;
v:elemtp
END; {定义三元组}
稀疏矩阵可用一个三元组线性表和行列数来描述
TYPE sparmattp=RECORD
mu,nu,tu:integer;{mu总行数,nu总列数,tu非零元素总个数}
data:ARRAY[1……maxnum] OF tuple3tp
END;
另一种表示方法:
a[0].row=矩阵的总行数;
a[0].col=矩阵的总列数;
a[0].value=矩阵中非零元的总个数;
a[1]—a[maxnum]存放非零元。
行主顺序存放算法比较简单
2)矩阵转置算法
B=AT <=> b[j][i]=a[i][j]
设A,B是sparmattp型变量,且互为转置矩阵
由A求其转置矩阵B,需做以下事情:
(1) 矩阵的行列值互换,即若A为m*n,则B为n*m;
(2) 每个三元组的行列号互换,即aij对应于bji;
(3) A中三元组以原阵的行序,而B中三元组是以原阵的列序。
算法一:
以原阵的列序依次产生b中各三元组的值;
PROC trans_sparmat(VAR b:sparmattp;a:sparmattp);
b.mu:=a.nu;b.nu:=a.mu;b.tu:=a.tu;
IF b.tu<>0 THEN
[q:=1;
FOR col:=1 TO a.nu DO
FOR p:=1 TO a.tu DO
IF a.data[p].j=col
THEN[b.data[q]:=a data[p];q=q+1]
]
ENDP;{trans_sparmat}
该方法实现直观、易理解,空间上节约,时间复杂度为O(a.nu*a.tu)。如果矩阵不是稀疏的,则a.tu=a.nu*a.mu,则时间复杂度为O(a.nu2*a.mu)
适用于矩阵稀疏的情况,不是稀疏矩阵时,此算法时间复杂度太高,不适合使用。
算法二:
快速矩阵转置算法
以原阵的行序产生转置后的三元组,并置入b中的恰当位置。
算法思想:
(1) 设置num[1……a.nu] 和cpot [1……a.nu]两个辅助向量;
(2) 在a.data中求矩阵M的每一列中非零元素个数,存于辅助向量num[ ]的对应列中;
(3) 用递推公式算出每一列第一个非零元素在b.data中的位置,存于辅助向量cpot[]的相应列中,递推公式为:
cpot[1]=1
cpot[col]=cpot[col-1]+num[col-1],col=2……a.nu
(4) 依次对a.data中的三元组进行转置,并按三元组的列号,从cpot的对应列取出该三元组应存放在b.data中的位置,然后将cpot该列中的值加1,使其记载该列下一非零元在b.data中的位置
(num的作用是初始化cpot)
算法实现:
PORC fast_transport(VAR b:=sparmattp; a:=sparmattp)
b.mu:=a.nu; b.nu:=a.mu; b.tu:=a.tu;
IF b.tu<>0 THEN
[q:=1;
FOR col:=1 TO a.nu DO num[col]:=0;
FOR t:=1 TO a.tu DO
Num[a.data[t].j]:=num[a.data[t].j]+1;{a.data[t]为当前扫描到的三元组}
cpot[1]:=1;
FOR col:=2 TO a.nu DO
cpot[col]:=cpot[col-1]+num[col-1];
FOR p:=1 TO a.tu DO
[col:=a.data[p].j; q:=cpot[col];
b.data[q].i:=a.data[p].j; b.data[q].j:=a.data[p].i;b.data[q].v:=a.data[p].v;
cpot[col]:=cpot[col]+1]
]
ENDP;{fast_transpos}
时间复杂度
Time complexity=O(a.nu + a.tu)
3. 三元组表线性表的链式存储结构称为十字链表
四、 广义表的存储结构
广义表中的数据元素可以是单元素,或是广义表,很难用顺序存储结构表示,常采用链式存储结构。
1. 表头表尾链存储结构
有两类结点:表结点和单元素结点。
Tag=1 表结点 |
Hp 表头指针域 |
Tp 表尾指针域 |
Tag=0 单元素结点 |
Data 值域 |
形式描述为:
TYPE nodeptr=↑nodetype
nodetype=RECORD
tag:0..1
CASE tag OF
0:(data:elemtp)
1:(hp,tp:nodeptr)
END;
特点:
最上层的表结点数即为广义表的长度;
层次清楚;
表结点数目多,与广义表中括号对的数目不匹配。
2. 同层结点链存储结构
有两类结点:表结点和单元素结点
Tag=1 表结点 |
Hp |
Tp |
Tag=0 单元素结点 |
Data |
Tp |
Tp为链接同层下一结点的指针域,其它域的含义同表头表尾链结构
特点:
表结点的数目与广义表的括号对数目一致;
写递归算法不方便。