第五章 数组和广义表

30
第第第 第第第第第第 线线线线线线线线线 线线线线线线线线线 :。 线线线线线线线线线线线 线线线线线 线线线线线 ,, 线线线线 线 线线线线线线线线线线线线 :。 线线线 线线线线线线线线线线线 线线线线线线 :,。

Upload: callum

Post on 21-Jan-2016

68 views

Category:

Documents


0 download

DESCRIPTION

第五章 数组和广义表. 线性结构的主要特点:数据元素的有序性。 数据元素可以是原子类型,聚合类型,还可以是数据结构。 数 组:线性表的元素也是线性表。 广义表:表的元素既可以是原子,也可以是表。. 数组的定义. 一、定义 1 : 一个 N 维数组是受 N 组线 性关系约束的线性表。 二维数组的逻辑结构可形式地描述为 : 2 _ARRAY(D,R) 其中 D={a ij } | i=0,1,...,b 1 -1; j=0,1,...,b 2 -1;a ij ∈D 0 } - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第五章 数组和广义表

第五章 数组和广义表线性结构的主要特点:数据元素的有序性。 数据元素可以是原子类型,聚合类型,还可以是数据结构。 数 组:线性表的元素也是线性表。 广义表:表的元素既可以是原子,也可以是表。

Page 2: 第五章 数组和广义表

数组的定义 一、定义 1 : 一个 N 维数组是受 N 组线 性关系约束的线性表。 二维数组的逻辑结构可形式地描述为 : 2 _ARRAY(D,R) 其中 D={aij} | i=0,1,...,b1-1; j=0,1,...,b2-1;aij∈D0} R={Row,Col} Row={<aij,ai,j+1>|0<=i<=b1-1;0<=j<=b2-2;aij,ai,j+1∈D0} ai,j+1 是 aij 在行关系中的后继元素。 Col={<aij,ai+1,j>|0<=i<=b1-2;0<=j<=b2-1;aij,ai+1,j∈D0} ai+1,j 是 aij 在列关系中的后继元素。 ①每一个数组元素 a[i,j] 都受两个关系 Row 和 Col 的约束: ROW( 行关系 ): ai,j+1 是 aij 在行关系中的直接后继。 COL( 列关系 ): ai+1,j 是 aij 在列关系中的后继元素。 ②每个数组元素属于同一数据类型。 ③每个数组元素由下标 (i,j) 唯一确定其位置。 ④每个下标 i由 bi 限定其范围 ,0≤i≤bi-1

Page 3: 第五章 数组和广义表

数组的定义 n 维数组的逻辑结构可描述为: n_ARRAY(D,R) D--- 数组的元素 R--- 定义为数组元素间的关系 R=(R 1 ,R 2 , ... ,R n ) Rj={<a(i1,i2,…,ij,…in),a(i1,i2,…,ij+1,…in)> |0≤ik≤bk-1,1≤k≤n,k≠j, 0≤ij≤bj-2, a(i1,i2,…,ij,…in),a(i1,i2,…,ij+1,…in)∈D0} n ① 数组元素个数:∏ bi

i=1 ② 一个元素 a(i1,i2,…,ij,…in) 都受 n 个关系的约束 , 每个关系都是线性关系。 ③每个数组元素对应于一组下标( j1,j2,...,jn ) ④每个下标的取值范围为 0≤ji≤bi-1 ( i=1,2,...,n )。

Page 4: 第五章 数组和广义表

数组的定义 二、定义 2 : 一维数组是定长线性表; 二维数组是一个定长线性表,它的每个元素是一个一维数组; n 维数组是线性表,它的每个元素是 n-1 维数组。

((a00,a01,...,a0n-1),(a10,a11,...,a1n-1),…,(am-1,0,am-1,1,...,am-1,n-1) )

a00 a01 ... a0n-1

a10 a11 ... a1n-1

……am-1,0 am-1,1 ... am-1,n-1

数组是线性结构,基于两点: 1、一个 n 维数组被定义为一个线性表,它的元素是一个 n-1 维数组。 2 、一个 n 维数组的数据元素受 n个关系的约束,且每个关系都是线性的。

a00 a01 ... A0n-1

a10 a11 ... A1n-1

……Am-1,1 am-1,1 ... Am-1,n-1

Page 5: 第五章 数组和广义表

数组的基本操作 1 、数组初始化:确定数组维数、长度,分配存储空间。 initarray(&A,n,bound[ ]); bound[ ]= b1,b2......bn

2 、撤消数组 destroyarray (&A);

3 、求数组元素值 value(A,&e,index[ ]); index[ ]= i1,i2,......in

4 、为数组元素赋值 assign(&A,e,index[ ]);

常用运算

Page 6: 第五章 数组和广义表

数组的顺序表示及实现 一、顺序分配: 用一遍地址连续的存储单元依次存放数据元素。二、二维数组分配方式(数组定义 2):1、行主分配:将数组元素按行的顺序依次排列。

a00 a01 … a0n-1 a10 a11 … a1n-1 … am-1,0 Am-1,1 … am-1,n-1

2 、列主分配:将数组元素按列的顺序依次排列a00 a10 … am-1,0 a01 a11 … am-1,1 … a0n-1 a1,n-1 … am-1,n-1

三、存储映像 :数组下标与存储位置之间的对应关系。 1 、行主分配: LOC(i,j)=LOC(0,0)+(i*b2+j)L 2 、列主分配: LOC(i,j)=LOC(0,0)+(i+j*b1)L

Page 7: 第五章 数组和广义表

数组的顺序表示及实现 四、 n数组的存储映象loc(j1,j2,.....jn)=loc(0,0,......0)+ (b2×b3×…×bn j1+

b3…×bn×j2+ ...... +bn×jn-1+ jn)L

=loc(0,0,......0)+ (∑ji bⅡ k+jn)Ln-1

i=1 k=i+1

n

可缩写为: loc(j1,j2,.....jn)=loc(0,0,......0)+ ∑ciji

n

i=1

其中: cn =L , ci-1= bi × ci, 1<i ≤ n ; ci 为常数上式称为 n 维数组的存储映象函数

Page 8: 第五章 数组和广义表

数组基本操作的实现 1 、数据类型描述 #define MAX_ARRAY_DIM 8 typedef struct { ElemType *base; // 数组元素空间 int dim; // 数组维数 int *bounds; // 数组维长 int *constant; // 常数因子 }ARRAY;数组初始化:确定结构中各字段的值。

Page 9: 第五章 数组和广义表

数组初始化操作的实现 Status initarray(Array &A,int n,int bound[ ]); { if(n<1‖n> Max_Array_dim) return ERROR A.dim=n; if(!A.bounds=(int *)malloc(n*sizeof(int))) return ERROR; elemtotal=1; for (i=0;i<n;++i) // 计算数组元素个数 { A.bounds[i]=bound[i]; if (bound[i]<0) return ERROR; elemtotal*=bound[i]; } A.base =(Elemtype *)malloc(elemtotal *sizeof(Elemtype)); if (!A.base ) return ERROR; // 分配元素空间 A.constants=(int *)malloc(n*sizeof(int)); if (!A.constants) return ERROR; A.constants[n-1]=1; for (i=n-2;i>=0;--i) // 计算常数因子 A.constants[i]=A.bounds[i+1]*A.constants[i+1]; return OK;}

Page 10: 第五章 数组和广义表

数组撤消操作的实现 释放数组占用的所有存储空间Status destroyarray(Array &A)

{ if (!A.base) return ERROR;

free(A.base); A.base=NULL;

if(!A.bounds) return ERROR;

free(A.bounds);A.bounds=NULL;

if(!A.constants) return ERROR;

free(A.constants);A. constants =NULL;

return OK;

}

Page 11: 第五章 数组和广义表

取数组元素值操作的实现 核心:判断元素下标合法性,计算数组元素地址Status value(Array A,elemtype &e,int index[ ]);

//index 中存放数组元素下标 { off=0;

for(i=0;i<A.dim;++i)

{ if(index[i]<0||index[i]>=A.bounds[i]) return ERROR;

off+=A.constants[i]*index[i];

}

e=*(A.base+off);

return OK;

}

Page 12: 第五章 数组和广义表

为数组元素赋值操作的实现 核心:判断元素下标合法性,计算数组元素地址Status Assign(Array A,elemtype e,int index[ ]);

{ off=0;

for(i=0;i<A.dim;++i)

{ if(index[i]<0||index[i]>=A.bounds[i]) return Error;

off+=A.constants[i]*index[i];

}

*(A.base+off)=e;

return OK;

}

Page 13: 第五章 数组和广义表

矩阵的压缩存储一、矩阵压缩存储的概念 ㈠特殊矩阵:值相同的元素或 0 元素在矩阵中的分布有一定规律。 ⒈对称矩阵:矩阵中的元素满足 aij=aji 1≤i,j≤n

⒉三角矩阵:上(下)三角矩阵指矩阵的下(上)三角(不包括对角线)中的元素均为常数 c或 0的 n阶矩阵。 ⒊对角矩阵(带状矩阵):矩阵中所有非 0元素集中在以主对角线为中心的区域中。 ㈡稀疏矩阵:非 0元素很少( ≤ 5%)且分布无规律。二、矩阵的压缩存储 为多个相同值的元分配一个存储单元;对零元不分配空间。

Page 14: 第五章 数组和广义表

对称矩阵的压缩存储 存储分配策略: 每一对对称元只分配一个存储单元 ,即只存储下三角(包括对角线)的元 , 所需空间数为 :

n× ( n+1 ) /2 。 存储分配方法: 用一维数组 sa[n(n+1)/2] 作为存储结构。 sa[k] 与 aij 之间的对应关系为 :

K=i(i-1)/2+j-1 i>=j

j(j-1)/2+i-1 i< j

k=0,1,…,n(n+1)/2-1

a11 a21 a22 a31 … an1 … ann 0 1 2 3 n(n-1)/2 n(n+1)/2-1

Page 15: 第五章 数组和广义表

稀疏矩阵一、存储分配策略 只存储稀疏矩阵的非 0 元素。 一个三元组( i , j , aij) 唯一确定了一个非 0 元素。 逻辑上,用三元组表来表示稀疏矩阵的非 0 元

三元组表 ---- 以三元组为元素的线性表:((1,2,12),(1,3,9),(3,1,-3),(3,6,14),(4,3,24),(5,2,18),(6,1,15),(6,4,-7))

0 12 9 0 0 0 0 0 0 0 0 0 0 0 -3 0 0 0 0 14 0 0 0 24 0 0 0 0 0 18 0 0 0 0 0 15 0 0 -7 0 0 0

Page 16: 第五章 数组和广义表

稀疏矩阵二、三元组表的顺序实现 用顺序存储结构来表示三元组表 ---- 三元组顺序表。

typedef struct

{ int i , j; // 行号,列号 EelemType e; // 非 0 元值 } triple

三元组

typedef struct

{ triple *data // 三元组表 int mu , nu , tu; // 行、列、非 0 元个数 } TSMatrix

三元组顺序表

Page 17: 第五章 数组和广义表

稀疏矩阵二、三元表顺序表的转置运算 矩阵的转置:矩阵行列互换。 M: m×n 矩阵 转置 =>T:n×m 矩阵 T[i][j] = M[j][i] 1≤i≤n , 1≤j≤m

i j v i j v[1 2 12][1 3 9][3 1 –3][3 6 14][4 3 24][5 2 18][6 1 15][6 4 –7] 6 7 8

[1 3 –3][1 6 15][2 1 12][2 5 18][3 1 9][3 4 24][4 6 –7][6 3 14]7 6 8

a.data b.data

Page 18: 第五章 数组和广义表

稀疏矩阵转置三、三元表顺序表求矩阵转置的方法 1 、将矩阵的行、列值互换: b.mu=a.nu;b.nu=a.mu; 2 、将每个三元组的行、列值互换 3、重排三元组的次序方法 1 :按照 b 矩阵中的行次序依次在 a.data 中找到相应的三元组进行转置。

[ 1 2 12][ 1 3 9][ 3 1 –3][ 3 6 14][ 4 3 24][ 5 2 18][ 6 1 15][6 4 –7]

ije

mu nu tu 6 7 8

[ 1 3 –3][ 1 6 15][ 2 1 12][ 2 5 18][ 3 1 9][ 3 4 24][ 4 6 –7][ 6 3 14]

ije

mu nu tu 7 6 8

④⑤

Page 19: 第五章 数组和广义表

稀疏矩阵 Status TransposeSmatrix (TSmatrix M, TSmatrix &T) { T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if(tu){ pt=0; for (col=1;col<=M.nu;++col) for(pm=1;pm<=M.tu;++pm) if(M.data[pm].j==col) { T.data[pt].i=M.data[pm].j; T.data[pt].j=M.data[pm].i; T.data[pt].e=M.data[pm].e; pt++; } } return OK;

} 算法时间复杂度为 O(nu*tu) 。 当 tu 与 m*n 同数量级时,算法时间复杂度为 O(m*n2) 。

Page 20: 第五章 数组和广义表

稀疏矩阵 方法 2:按照 a.data 中三元组的次序进行转置,并将转置后的三元组放到 b.data 中的恰当位置。 恰当位置的确定:首先计算 M矩阵的每一列(即 T 的每一行)中非 0元的个数,然后求得 M 矩阵每一列第一个非 0元在 b.data 中的位置。 算法基本思想: 设置两个向量: num[col]:第 col 列的非零元素个数。 cpot[col]:第 col 列第一个非零元在 b.data 中的恰当位置。 在转置过程中,指示该列下一个非零元在 b.data 中的位置。 1 、 num[col] 的计算: 顺序扫描 a.data 三元组,累计各列非 0元个数。 2 、 cpot[col] 计算:

cpot[1]=0; col=1cpot[col]=cpot[col-1]+num[col-1] 2≤col≤a.nu

Col 1 2 3 4 5 6 7

Num[col] 2 2 2 1 0 1 0

cpot[col] 0 2 4 6 7 7 8

Page 21: 第五章 数组和广义表

稀疏矩阵void fastransposeSMatrix ( TSMatrix M , TSMatrix &T ) { T.mu=M.nu; T.nu=M.mu; T.tu=M.tu; if ( T.tu<>0 ) { for ( col=1; col<=M.nu; col++ ) num[col]=0; /* 初始化 */ for ( t=0;t<M.tu;t++ ) num[M.data[t].j]= num[M.data[t].j]+1; /* 求 M中每一列非零元个数*/ cpot[1]=0; /* 求第 col 列中第一个非零元在 b.data 中的序号 */ for ( col=2;col<=M.nu;col++ ) cpot[col]=cpot[col-1]+num[col-1]; for ( p=0;p<M.tu;p++ ) { col=M.data[p].j; q=cpot[col]; T.data[q].i=M.data[p].j; T.data[q].j=M.data[p].i; T.data[q].e=M.data[p].e; ++cpot[col]; } } }

算法时间复杂度为 O(nu+tu) 。 当 tu 与 mu*nu 同数量级时,算法时间复杂度为 O(mu*nu) 。

Page 22: 第五章 数组和广义表

稀疏矩阵相加 条件:两相加矩阵行、列相同。 方法:与两多项式相加相同,比较当前两处理元素: 若行列号相同:元素相加,结果不为 0时写入结果稀疏矩阵。 列号不同:把列号小的写入结果稀疏矩阵。 行号不同:把行号小的写入结果稀疏矩阵。

(152)(223)(344)(475)

(ije)

mu nu tu 4 7 4

(127)(154)(245)(344)(439)(472)

(ije)

mu nu tu 4 7 6

(127)(156)(223)(245)(348)(439)(477)

(ije)

mu nu tu 4 7 7

+ =

Page 23: 第五章 数组和广义表

稀疏矩阵相加 void AddSmatrix (TSmatrix M1, TSmatrix M2, TSmatrix &M){ M.mu=M1.mu; M.nu=M1.nu; p1=0;p2=0;p=0; while (p1<M1.tu &&p2<M2.tu) { if (M1. data[p1].i== M2. data[p2].i&& M1. data[p1].j== M2. data[p2].j) { M.data[p] =M1.data[p1++]; M.data[p].e+=M2.data[p2++].e; if(M.data[p].e!=0) p++;} else if (M1. data[p1].i< M2. data[p2].i|| M1. data[p1].i== M2. data[p2].i && M1. data[p1].j< M2. data[p2].j) { M.data[p++]=M1.data[p1++]; } else { M.data[p++]=M1.data[p2++]; } } while (p1<M1.tu ){ M.data[p++]=M1.data[p1++];} while (p2<M2.tu ){ M.data[p++]=M2.data[p2++];} M.tu=p; }算法时间复杂度: O(M1.tu+M2.tu)

Page 24: 第五章 数组和广义表

稀疏矩阵的十字链表表示方法:每个非零元用一个结点表示;每行和每列的非零元连成链表。特点:一个结点既是行链表的结点,也是列链表的结点。 在图形表示中,一个结点处在链表连线的十字中心,故称为十字链表 结点结构:

i j edown right 向右域

向下域

i , j分别表示该数组某非零元素的行、列值。 e 表示该非零元素的值。 down 指向该列的下一个非零元素结点。 right 指向该行的下一个非零元素结点。 行指针数组:存放行链表头指针。 列指针数组:存放列链表头指针。

Page 25: 第五章 数组和广义表

稀疏矩阵的十字链表表示

0002

0010

5003

M1 1 3 1 4 5

^ ^

2 2 -1^^

M.chead

M.rhead

3 1 2^^

typedef struct OLNode{ int i, j; ElemType e; struct OLNode *right, *down; } OLNode, *Olink;

typedef struct{

Olink *rhead, *chead;

int mu,nu,tu;

}CrossList;

Page 26: 第五章 数组和广义表

广义表的定义 广义表又称为列表( lists ),是 n≥0 个元素 a1,a2,...,an 的有限序列,记为: A=( a1,a2,...,an)其中: A 是广义表的表名, n是广义表的长度 ai 是单个元素或广义表, 若 ai 是单个元素,则称为广义表的单元素 (或原子 )。 若是广义表,则称 ai 是广义表的子表。所以广义表又称为列表。 即 ai ∈D0 或 ai ∈lists 广义表的表头( Head ):非空表 A 的第一个元素 a1 。 广义表的头与 a1具有相同的表示形式。 广义表的表尾 (Tail) :除其头之外的其余元素 ( a2,...,an) 组成的表。 广义表的尾一定是一个广义表。 特点:广义表的定义是一个递归的定义。

Page 27: 第五章 数组和广义表

广义表的定义 二、广义表示例 :

A=( ) ; 空表 B=(e) ; n=1, 表头 =e, 表尾 =( )

C=(a,(b,c,d)) ; n=2, 表头 =a, 表尾 =((b,c,d) )

D=(A,B,C) ; n=3, 表头 =A, 表尾 =(B,C )

E=(a,E) ; n=2, 表头 =a, 表尾 =(E ) , E 称为递归表三、结论 1 、广义表描述的是一个层次结构。 2 、广义表可以为其它广义表所共享。 3、广义表可以是一个递归表,表名本身是表的一个元素。

Page 28: 第五章 数组和广义表

广义表的存储结构 广义表链式存储表示: ai 有两种类型 : 原子(单元素)、表。 表可以分成表头和表尾。

tag=0 atom原子结点

tag=1 hp tp 表结点

表头指针 表尾指针

typedef enun{ATOM , LIST}Elemtag ;typedef struct GLnode

{ Elemtag tag ; union

{ Atomtype atom ; struct {GLnode *hp , *tp ; }ptr ; }

}*Glists

Page 29: 第五章 数组和广义表

广义表的存储结构

A=( )

B=(e)

C=(a,(b,c,d))

D=(A,B,C)

E=(a,E)

A=Null

C 1

1 ^

1 1 1 ^

0 a

0 b 0 c 0 d

D 1 ^

E 1

0 e

1 ^

1 1 ^

0 e

1 ^

B

Page 30: 第五章 数组和广义表

作业

5.15.25.35.55.65.75.105.11*5.185.19