380 likes | 617 Views
DATA STRUCTURE. 第 5 章 数组和广义表. 第 5 章 数组和广义表. 前述线性结构 其中的数据元素是非结构的原子类型 (值不可分解) 数组、广义表 是线性表的扩展: 数据元素本身也是一种数据结构 ( 线性表 ). §1 数组的定义和操作. 数组是所有程序设计语言都设定的固有类型 数组的基本概念和运算已在 C 程序设计中熟悉 从数据结构的观点讨论 ●逻辑结构 ●存储方式. 一、逻辑结构. 以二维数组为例
E N D
DATA STRUCTURE 第5章 数组和广义表
第5章 数组和广义表 • 前述线性结构 其中的数据元素是非结构的原子类型 (值不可分解) • 数组、广义表 是线性表的扩展: 数据元素本身也是一种数据结构(线性表)
§1 数组的定义和操作 • 数组是所有程序设计语言都设定的固有类型 • 数组的基本概念和运算已在C 程序设计中熟悉 • 从数据结构的观点讨论 ●逻辑结构 ●存储方式
一、逻辑结构 以二维数组为例 1. 形式表示:2_Array=( D,R) 其中D={ aij | i =1…m, j =1…n, aij∈D0} integer 某数据对象 R={ROW,COL} ROW={<aij,ai,j+1>|1≤i≤m, 1≤j≤n-1,aij,ai,j+1∈D} COL={<aij,ai+1,j>|1≤i≤m-1, 1≤j≤n, aij,ai+1,j∈D} 共有 m×n个数据元素
一、逻辑结构 2.每个元素受两个关系ROW(行关系)和COL(列关系)的约束。 即每个元素有两个前趋元素和两个后继元素。 数据元素 一组下标(i, j) 3.(1,m ),(1,n) 分别为下标 i,j的一对界偶(上、下界)。 4.上述定义可推广到n 维数组
二、数组的特点 1. 数据元素个数固定 2. 数据元素属同一种数据类型 3. 二维数组是多个线性表组成的线性表 C中二维数组类型说明: typedef ElemType array2[m][n]; 或: typedef ElemType array1[n]; typedef array1 array2[m];
3. 二维数组是多个线性表组成的线性表 例如: A= • 则A=(α1α2 … αn) 其中: αj= (1≤j≤n) 或A= ,其中:
三、数组的操作 • 通常只有三种操作: 存、取元素值和给元素赋值
§2 数组的存储结构 • 顺序存储结构 数组一旦建立,则元素个数和元素间关系不变动,故采用顺序存储结构。 • 用一维存储单元存储二维数组 两种方式: · 以列序为主序 (column major order) · 以行序为主序 (row major order)
§2 数组的存储结构 1、确定维数及各维界偶,便可分配存储空间 2、给定一组下标,可求相应数组元素存储位置 LOC[i,j]=LOC[1,1] + [ n (i-1) + (j-1) ] l 基地址 按行为主 C语言:LOC(i,j)=LOC(0,0) + ( n i + j ) l 随机存储结构:存取任一元素时间相等。 每个元素所占存储单元
n维数组元素存储位置的计算公式: LOC(j1,j2,…,jn)=LOC(0,0,…,0)+(b2 …bn j1+b3 … bn j2+…+bn jn-1+jn) l
§3 矩阵的压缩存储 • 矩阵是工程计算中研究的主要对象。 • 一般情况: 用二维数组来存储矩阵。 特殊矩阵: 对称、三角、对角 • 特殊情况 稀疏矩阵:非零元素个数<<零元素个数 多个值相同的元素,分配同一存储单元 • 压缩存储 零元素不存储
一、特殊矩阵 1.对称矩阵 (1)定义: 若Ann满足aij=aji(1≤i, j≤n)则称A为n阶对称矩阵。
(2) 压缩存储 共n2个元,只需存储个 元, 以行为主序存储下三角中的元素 用一维数组 s[0 .. -1]存储对称矩阵A a11 a21 a22 a31 … an1 … ann k= 0 1 2 3 s[k]与aij一一对应, k与( i , j )的对应关系?
k与( i, j )的对应关系: 当i≥j k= 0≤k≤ 当i<j
2.三角矩阵 (1)定义: 上(下)三角矩阵指矩阵的下(上)三角(不包括对角线)中的元均为0的n阶矩阵。 (2)压缩存储: 只存储上(下)三角中的元。 k与 (i, j) 的对应关系: k = ? 1≤ i ≤ j ≤n
二、稀疏矩阵(sparse matrix) ●条件:m*n 阶矩阵 ⑴非零元素个数<<零元素个数 ⑵非零元素在矩阵中分布无规律 ●压缩存储:只存储非零元素 三元组表 十字链表
(一)三元组表 1.示例: 三元组( i, j, aij ) 以行序为主序 三元组表 ⑴ a.data i j v 0 1 1 2 12 2 1 3 9 3 3 1 -3 4 3 6 14 5 4 3 24 6 5 2 18 7 6 1 15 8 6 4 -7 ⑵a.mu a.nu a.tu 6 7 8
2.三元组表类型定义 #define Maxn <非零元素个数的最大值> typedef struct { int i, j; /* 非零元素行、列号 */ ElemType v; /* 元素值 */ }Triple; typedef struct { Triple data[Maxn+1]; /*三元组表,data[0]不用 */ int mu,nu, tu; /*矩阵的行、列数和非零元个数 */ }TSMatrix;
求转置矩阵算法 TransposeSMatrix (TSMatrix a, TSMatrix *b) { b->mu=a.nu; b->nu=a.mu; b->tu=a.tu; if( a.tu) { q=1; /*指向b中三元组序号*/ for(col=1; col<=a.nu; ++col)/*按a的列序扫描a a.nu遍*/ for( p=1; p<=a.tu; ++p) /*p指向a中三元组序号*/ if(a.data[p].j==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; q++; } } } 算法思想 按b中三元组次序在a中找到相应三元组进行转置 若第p个三元组中列号j为所寻列号
转置算法时间复杂度分析 ●时间复杂度: T(n)=O(nt) 当 t 与 mn 同数量级时,为 O (mn2) ●一般矩阵的转置算法: for( col=1; col<=n; ++col) for(row=1; row<=m; ++row) B[col][row]=A[row][col]; 其 T (n)=O(m*n) 故本算法仅适合于 t<<m*n的情形。
快速转置 A中第col列第一个非零元素在b中的恰当位置(序号) 按照A中三元组的次序进行转置, 并将转置后的三元组置入b中恰当的位置。 附设两个一维数组: num[col] 和 pot[col] 显然有:pot[1] = 1 pot[col] = pot[col-1] + num[col-1] 2≤col≤n 例: 矩阵A的pot值 col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 pot[col] 1 3 5 7 8 8 9 A中第col列中非零元素个数
快速转置算法Fast_TransposeSMatrix (TSMatrix a, TSMatrix *b) { b->mu=a.nu; b->nu=a.mu; b->tu=a.tu; if( a.tu) { for(col=1; col<=a.nu; col++) num[col]=0; /*初始化*/ for( p=1; p<=a.tu;p++) /*求A中每一列含非零元素个数*/ num[a.data[p].j]++; pot[1]=1; /*统计第col 列第一个非零元素在b中的序号*/ for(col=2;col<=a.nu;col++) pot[col]=pot[col-1]+num[col-1]; for( p=1; p<=a.tu; p++) /*转置*/ { col=a.data[p].j; q=pot[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; pot[col]++; } } }
算法分析: 算法2比算法1多用两个辅助向量; 算法2采用4个并列单循环,循环次数分别为n和t; 故T(n)=O(n+t),当t与mn同级时,为O(mn)与经典算法同。
(二)十字链表 • 适用于稀疏矩阵中非零元的个数及位置变动频繁的运算(如矩阵加法)。
十字链表的类型说明: typedef struct OLnode /*非零元素结点*/ { int i,j; Elemtype v; struct lnode *right, *down; }OLnode; Typedef struct /*表头结点*/ { Olnode *rhead[MAX], *chead[MAX]; int mu,nu,tu; }
§4 广义表 • 线性表的推广,又称列表(Lists)。 • 广泛用于人工智能领域的表处理语言(LISP语言), LISP把广义表作为基本数据结构,其程序也表示为一系列广义表。
一、广义表的定义 1、定义 广义表是n(n≥0)个元素的有限序列,记作: LS=(d1,d2,…,dn) 其中: LS是广义表(d1,d2,…,dn)的名称 n是广义表的长度 di (1≤i≤n)是广义表的数据元素 单元素(原子):若di是单个元素 di 子表:若di是广义表
2、表头与表尾 ● head 当LS非空时(n>0),第一个元素d1称为表头( head ) ● tail 去除表头元素后,其余元素组成的表(d2,…,dn)称为LS的表尾 (tail);
3、例 长度 head tail A=(a) 1 a () B=(a,(x,y)) 2 a ((x,y)) C=(A,B,()) 3 A (B,()) D=(a,D)=(a, (a,…)) 2 a (D) E=() 0 F=(E)=(()) 1 E () ef. () 空表,长度为 0 (())长度为1,可分解为表头、表尾皆为空表()
4、三个结论 (1)广义表可为其它广义表所共享。 如上例中,子表A,B与C共享数据。 (2)广义表可以是一个递归的表, 即广义表可以是其自身的一个子表。如上述中D就是一个递归的表。 (3)广义表是一个多层次的表, 可用图形表示,如广义表C的图形表示如下:
广义表C的图形表示: 概念: • 层次 • 广义表的深度:最大层次数,如C的深度为3 A=(a) B=(a,(x,y)) C=(A,B,())
二、广义表的存储结构 采用链表存储结构 其中:tag为标志域, tag=0为原子结点; tag=1为表结点。 sublist为指向子表的指针域; value 域存储结点的值; link 域指向下一数据元素的结点。
例: A=(a) , B=(a,(x,y)),C=(A,B,())
D=(a,D)=(a, (a,…)) E=() F=(E)=(()) 从链表中,可以很方便地看出表的长度和深度。 在链表中,广义表各元素间的次序关系被表示得更为清晰。 例 横向箭头表示元素间的次序,竖向箭头表示元素间的层次关系。