数组与广义表
一、数组
数组的顺序存储
一维数组:
A
=
a
1
,
a
2
,
.
.
.
a
i
,
.
.
.
,
a
n
A = a_1, a_2, ... a_i, ..., a_n
A=a1,a2,...ai,...,an
L
o
c
(
A
[
i
]
)
=
L
o
c
(
A
[
1
]
)
+
(
i
−
1
)
×
s
i
z
e
Loc(A[i]) = Loc(A[1]) + (i - 1)\times size
Loc(A[i])=Loc(A[1])+(i−1)×size
二维数组:
以行为主,下标从
1
开始,首元素
a
11
的地址
L
o
c
(
A
[
1
]
[
1
]
)
,
a
i
j
为第
i
行,第
j
列的元素
以行为主,下标从1开始,首元素a_{11}的地址Loc(A[1][1]),a_{ij} 为第i行,第j列的元素
以行为主,下标从1开始,首元素a11的地址Loc(A[1][1]),aij为第i行,第j列的元素
L
o
c
(
A
[
i
]
[
j
]
)
=
L
o
c
(
A
[
1
]
[
1
]
)
+
(
n
×
(
i
−
1
)
+
j
−
1
)
×
s
i
z
e
Loc(A[i][j]) = Loc(A[1][1]) + (n\times (i - 1) + j - 1)\times size
Loc(A[i][j])=Loc(A[1][1])+(n×(i−1)+j−1)×size
三维数组:
以行为主序,
r
个
m
×
n
数组
以行为主序,r个m\times n数组
以行为主序,r个m×n数组
L
o
c
(
A
[
i
]
[
j
]
[
k
]
)
=
L
o
c
(
A
[
1
]
[
1
]
[
1
]
)
+
(
(
i
−
1
)
×
m
×
n
+
(
j
−
1
)
×
n
+
(
k
−
1
)
)
×
s
i
z
e
Loc(A[i][j][k]) = Loc(A[1][1][1]) + ((i - 1)\times m\times n + (j - 1)\times n + (k - 1))\times size
Loc(A[i][j][k])=Loc(A[1][1][1])+((i−1)×m×n+(j−1)×n+(k−1))×size
n维数组:
对于
n
维数组
A
[
c
1
.
.
d
1
,
c
2
.
.
d
2
,
c
n
.
.
d
n
]
,
a
j
1
j
2
.
.
.
j
n
的地址为:
L
o
c
(
j
1
,
j
2
,
.
.
.
,
j
n
)
=
L
o
c
(
c
1
,
c
2
,
.
.
.
,
c
n
)
+
∑
i
=
1
n
α
i
×
(
j
i
−
c
i
)
其中
α
i
=
s
i
z
e
×
∏
k
=
i
+
1
n
(
d
k
−
c
k
+
1
)
,
1
≤
i
≤
n
对于n维数组A[c_1..d_1, c_2..d_2, c_n..d_n],a_{j_1j_2...j_n}的地址为:\\ Loc(j_1, j_2, ..., j_n) = Loc(c_1, c_2, ..., c_n) + \sum_{i=1}^{n}\alpha _ i\times (j_i-c_i)\\ 其中\alpha _ i = size\times\prod_{k=i+1}^{n}(d_k-c_k+1),1\le i\le n
对于n维数组A[c1..d1,c2..d2,cn..dn],aj1j2...jn的地址为:Loc(j1,j2,...,jn)=Loc(c1,c2,...,cn)+i=1∑nαi×(ji−ci)其中αi=size×k=i+1∏n(dk−ck+1),1≤i≤n
二、特殊矩阵的压缩存储
1.有特殊规律的矩阵,寻找规律公式,进行压缩存储。
2.稀疏矩阵(非0元素很少),只存储非0元素。
1.规律分布的特殊矩阵
(1)三角矩阵
( a 11 0 a 21 a 22 0 a 31 a 32 a 33 0 ⋮ ⋮ ⋮ ⋮ 0 a n 1 a n 2 a n 3 ⋮ a n n ) \begin{pmatrix} a_{11}&0\\ a_{21}&a_{22}&0\\ a_{31}&a_{32}&a_{33}&0\\ \vdots&\vdots&\vdots&\vdots&0\\ a_{n1}&a_{n2}&a_{n3}&\vdots&a_{nn}\\ \end{pmatrix} a11a21a31⋮an10a22a32⋮an20a33⋮an30⋮⋮0ann
L
o
c
[
i
,
j
]
=
L
o
c
[
1
,
1
]
+
(
i
×
(
i
−
1
)
2
+
j
−
1
)
Loc[i,j]=Loc[1,1]+(\frac{i\times(i-1)}{2}+j-1)
Loc[i,j]=Loc[1,1]+(2i×(i−1)+j−1)
将
A
压缩到
B
中
A
[
i
]
[
j
]
=
{
0
,
i
<
j
B
[
i
(
i
−
1
)
2
+
j
]
,
i
≥
j
将A压缩到B中 A[i][j]=\left\{\begin{matrix} 0,i<j\\ B[\frac{i(i-1)}{2}+j],i\ge j \end{matrix}\right.
将A压缩到B中A[i][j]={0,i<jB[2i(i−1)+j],i≥j
(2)带状矩阵
A
=
(
a
11
a
12
0
⋯
0
a
21
a
22
a
23
⋱
⋮
0
a
32
a
33
⋱
0
⋮
⋱
⋱
⋱
a
n
−
1
,
n
0
⋯
0
a
n
,
n
−
1
a
n
n
)
n
×
n
A = \begin{pmatrix} a_{11} & a_{12} & 0 & \cdots & 0 \\ a_{21} & a_{22} & a_{23} & \ddots & \vdots \\ 0 & a_{32} & a_{33} & \ddots & 0 \\ \vdots & \ddots & \ddots & \ddots & a_{n-1,n} \\ 0 & \cdots & 0 & a_{n,n-1} & a_{nn} \\ \end{pmatrix}_{n\times n}
A=
a11a210⋮0a12a22a32⋱⋯0a23a33⋱0⋯⋱⋱⋱an,n−10⋮0an−1,nann
n×n
{
当
i
=
1
时,
j
=
1
,
2
当
1
<
i
<
n
时,
j
=
i
−
1
,
i
,
i
+
1
当
i
=
n
时,
j
=
n
−
1
,
n
\left\{\begin{matrix} 当i=1时,j=1,2\\ 当1<i<n时,j=i-1,i,i+1\\ 当i=n时,j=n-1,n\\ \end{matrix}\right.
⎩
⎨
⎧当i=1时,j=1,2当1<i<n时,j=i−1,i,i+1当i=n时,j=n−1,n
i.存储原则:将带状区域上的非零元素按行序存储。
ii…确定存储空间大小:假设每个非零元素占用一个存储单元,那么所需的一维数组空间大小为 3n-2,其中 n 是矩阵的行数(因为除了第一行和最后一行只有两个非零元素外,其余各行均有三个非零元素)。
iii.确定非零元素地址:非零元素在一维数组中的地址可以通过计算得出。例如,对于三对角带状矩阵中的元素 a_ij,其在一维数组中的位置 Loc(i,j) :
L
o
c
(
A
[
i
]
[
j
]
)
=
L
o
c
(
A
[
1
]
[
1
]
)
+
(
(
3
(
i
−
1
)
−
1
)
+
(
j
−
i
+
1
)
)
×
s
i
z
e
Loc(A[i][j])=Loc(A[1][1])+((3(i-1)-1)+(j-i+1))\times size
Loc(A[i][j])=Loc(A[1][1])+((3(i−1)−1)+(j−i+1))×size
2. 稀疏矩阵
非0元素占比低于30%
(1)三元组表示法
行号row,列号col,非0元素值e
1.三元组的类型定义
#define MAXSIZE 1000
typedef struct
{
int row,col;
ElementType e;
}Triple;
typedef struct
{
Triplle data[MAXSIZE+1];
int m, n, len;
}TSMatrix;//Triple Sparse Matrix
2.矩阵转置的经典算法
void TransMatrix(ElementType source[m][n], ElementType dest[n][m])
{
int i, j;
for(i = 0; i < m; i ++)
for(j = 0; j < n; j ++)
dest[j][i] = source[i][j];//行列互换
}//O(m * n)
3.用三元组实现稀疏矩阵的转置
A转置为B,按col递增的顺序扫描A中元素,依次转置
/*列序递增转置法*/
void TransTSMatrix(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;//j为辅助计数器,记录转置后的三元组在B中的总下标值
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)
4.一次定位快速算法
空间换时间,多个单重循环记录下num,position,以避免多重循环
void FastTransposeTSMatrix(TSMatrix A, TSMatrix * B)
{
int col, t, p, q;
int num[MAXSIZE];//num[j]记录转置前A中第j列非零元素的个数
int position[MAXSIZE];//position[j]记录转置前A中第j列第一个非零元素在B.data中的下标,position[col] = position[col-1] + num[col-1]
B->m = A.n; B->n = A.m; B->len = A.len;
if(B->len)
{
for(col = 1; col <= A.n; col ++)
num[col] = 0;
for(t = 1; t <= A.len; t ++)//统计A中每列非零元素的个数
num[A.data[t].col] ++;
position[1] = 1;
for(col = 2; col <= A.n; col ++)//计算每列第一个非零元素在B.data中的下标
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->data[q].e = A.data[p].e;
position[col] ++;//下一个非零元素的位置
}
}
}//O(A.len + A.n)
(2)稀疏矩阵的链式存储结构:十字链表
为了避免大量移动元素,十字链表能够灵活地插入因运算而产生的新的非0元素,删除因运算而产生的新的非0元素,实现矩阵的各种运算。
每个结点除了row,col,e,加入right,down链域。同一行的非0元素链接成一个单链表,同一列的非0元素链接成一个单链表,各个结点好像一个个十字交叉路口。
1.十字链表的类型定义
typedef struct OLNode
{
int row, col;
ElementType e;
struct OLNode * right, * down;
}OLNode, * OLink;//Orthogonal
typedef struct
{
OLink * row_head[m + 1], * col_head[n +1];
int m, n, len;
}CrossList;
2.十字链表的创建
void CreateCrossList(CrossList * M)
{
scanf(&m, &n, &t);//输入矩阵的行数、列数和非零元素个数(伪代码)
M->m = m; M->n = n; M->len = t;
if(!(M->row_head = (OLink *)malloc((m+1)*sizeof(OLink)))) exit(OVERFLOW);
if(!(M->col_head = (OLink *)malloc((n+1)*sizeof(OLink)))) exit(OVERFLOW);
M->row_head[] = M->col_head[] = NULL;//初始化行、列头指针
for(scanf(&i, &j, &e); i != 0; scanf(&i, &j, &e))//输入非零元素的行、列和值
{
if(!(p = (OLNode *)malloc(sizeof(OLNode)))) exit(OVERFLOW);//生成新的结点
p->row = i; p->col = j; p->e =e;
if(M->row_head[i] == NULL)
M->row_head[i] == p;
else
{
q = M->row_head[i];
while(q->right != NULL&& q->right->col < j)//往右移动,找到插入位置
q = q->right;
p->right = q->right;
q->right = p;//按列递增插入
}
if(M->col_head[j] == NULL)
M->col_head[j] = p;
else
{
q = M->col_head[j];
while(q->down != NULL && q->down->row < i)//往下移动,找到插入位置
q = q->down;
p->down = q->down;
q->down = p;//按行递增插入
}
}
}//O(t*s),t为非零元素个数,s为max(m,n)
三、广义表
广义表(Generalized List)是一种非线性的数据结构,它可以包含原子(基本的数据元素)和子表(其他广义表)。广义表是线性表的推广,可以用来表示具有层次结构的数据。
1.基本概念
原子:广义表中的基本数据元素,不可再分。
子表:广义表中的元素也可以是另一个广义表。
表头:广义表的第一个元素,可以是原子或子表。
表尾:除了表头之外的其余部分,总是另一个广义表。
下面给出几个广义表的例子:
D = ( ) 空表,其长度为零
A = ( a , ( b , c ) )
B = ( A , A , D)
C = ( a , C ) 按递归定义
以A为例:
head(A) = a
tail(A) = ( ( b , c ) ) 注意表尾也是一个广义表
2.广义表的存储结构
(1)广义表的头尾链表存储结构
广义表中的每个元素用一个节点来表示,表中有两类节点:i.原子结点;ii.子表结点。用标志域tag来区分两种节点。
typedef enum {ATOM,LIST} ElemTag;
typedef struct GLNode
{
ElemTag tag;//标志域,区分原子结点和表结点
union
{
AtomType atom;//原子结点的值域
struct {struct GLNdoe * hp, * tp;} htp;//表结点的指针域htp,hp指向表头,tp指向表尾
} atom_htp;//原子结点的值域atom和表结点的指针域htp的联合体域
} GLNode, * GList;//General List
(2)广义表的同层结点链存储结构
表结点:tag,hp,tp
原子结点:tag,atom,tp
typedef struct GLNode
{
ElemTag tag;
union
{
AtomType atom;
struct GLNode * hp;//表头指针域
}atom_hp;//原子结点的值域和表结点的指针域的联合体域
struct GLNode * tp;
}GLNode, * GList;
3.广义表的操作实现(以头尾链表存储结构为例)
(1)求广义表的表头
GList Head(GList L)
{//求广义表L的表头,并返回指向表头的指针
if(L == NULL) return NULL;
if(L->tag == ATOM) exit(0); //原子不是表,没有指向它的指针
else return (L->atom_htp.htp.hp);
}
(2)求广义表的表尾
GList Tail(GList L)
{
if(L == NULL) return NULL;
if(L->tag == ATOM) exit(0);
else return (L->atom_htp.htp.tp);
}
(3)求广义表的长度
int Length(GList L)
{
int k = 0;
GLNode * s;
if(L == NULL) return 0;
if(L->tag == ATOM) exit(0);
s = L;
while(s != NULL)
{
k ++;
s = s->atom_htp.htp.tp;
}
return k;
}
(4)求广义表的深度
int Depth(GList L)
{
int d, max = 0;
GLNode * s;
if(L == NULL) return 1;//空表深度为1
if(L->tag == ATOM) return 0;//原子深度为0
s = L;
while(s != NULL)
{
d = Depth(s->aotm_htp.htp.hp);//递归求子表的深度
if(d > max) max = d;
s = s->atom_htp.htp.tp;
}
return max + 1;//表的深度为其最深子表的深度加1
}
(5)统计广义表中的原子数目
int CountAtom(GList L)
{
int n1, n2;
if(L == NULL) return 0;//空表无原子
if(L->tag == ATOM) return 1;//指向单个原子
n1 = CountAtom(L->atom_htp.htp.hp);
n2 = CountAtom(L->atom_htp.htp.tp);//神奇的递归~
return n1 + n2;
}
(6)复制广义表
int CopyGList(GList S, GList * T)
{
if(S == NULL)
{
* T = NULL;
return OK;
}
* T = (GLNode *)malloc(sizeof(GLNode));
if(* T == NULL) return ERROR;
(* T)->tag = S->tag;
if(S->tag == ATOM) (* T)->atom = S->atom;//S为原子结点,复制到T
else
{
CopyGList(S->atom_htp.htp.hp,&((* T)->atom_htp.htp.hp));
CopyGList(S->atom_htp.htp.tp,&((* T)->atom_htp.htp.tp));//还是递归,广义表天然的属性适合递归
}
}