Transcript 第5章
数据结构---数组和广义表
齐 恒
[email protected]
大黑楼 B0912
什么是大数据?
大数据的理解
大数据是指无法在一定时间内用常规软件工具对
其内容进行抓取、管理和处理的数据集合(维基
百科定义)
Big data usually includes data sets with
sizes beyond the ability of commonly-used
software tools to capture, curate, manage,
and process the data within a tolerable
elapsed time. --- Wiki
大数据的理解
当数据的规模和性能要求成为数据管理分析系统
的重要设计和决定因素时,这样的数据就被称为
大数据
–不是简单地以数据规模来界定大数据,要考
虑数据查询与分析的复杂程度
• 以目前计算机硬件的发展水平看
–针对简单查询(如关键字搜索),数据量为
TB至PB级时可称为大数据
–针对复杂查询(如数据挖掘),数据量为GB
至TB级时即可称为大数据
大数据的理解
特性
问题
范围广
稠密与稀疏共存
模态多
大数据
增长快
关联繁
冗余与缺失并在
动态与静态互现
显式与隐藏均有
时序长
数据规模较小
数据结构相对比
较简单
模态属性受限
常规数据
增长速度较慢
关联相对简单
持续时间较短
处理方法通常为
模型化、参数化
应用目标
相对比较明确
挑战
描述与存储的挑战
分析与理解的挑战
挖掘与预测的挑战
大数据价值
• 科研价值
–
–
1998 年图灵奖得主、数据库技术奠基人Jim Gray认为数据驱动的研究将是第
四种科学研究范式”The Fourth Paradigm: Data-Intensive Scientific Discovery”
大数据已为多个不同学科的研究工作提供了宝贵机遇
• 经济价值
–
–
麦肯锡全球研究院:大数据可为世界经济创造巨大价值,提高企业和公共部门
的生产率和竞争力,并为消费者创造巨大的经济利益
著名Gartner公司:到2015年,采用大数据和海量信息管理的公司将在各项财
务指标上,超过未做准备的竞争对手20%
• 工业价值
–
–
分析使用:揭示隐藏其中的信息,例如零售业中对门店销售、地理和社会信息
的分析能提升对客户的理解
二次开发:创造出新产品和服务。例如Facebook通过结合大量用户信息,定
制出高度个性化的用户体验,并创造出一种新的广告模式
• 社会价值
–
例如:2009年淘宝网推出淘宝CPI来反映网络购物的消费趋势和价格动态
• 其他价值…
大数据的现实需求:感知现在
感知现在:历史数据与当前数据的融合, 潜在线索与模式的挖掘,
事件、群体与社会发展状态的感知
中国发展指数(物价、环境、健康)
需求:掌握现状,如淘宝CPI、环境指数
难点:PB级社会媒体数据,百亿级日志数据,
结构与非结构数据关联,历史与流式数据并存
犯罪线索挖掘
需求:发现线索,如罪犯行为轨迹
难点:PB级日志数据、EB级监控数据中
发现嫌疑人及其行为模式犹如大海捞针
问题与挑战:数据规模巨大、模态多样、关联复杂、真伪难辨
现有数据处理方法感知度量难、特征融合难、模式挖掘难
大数据的现实需求:预测未来
预测未来:全量数据、流式数据、离线数据的关联分析,态势与效应的判定与调控
,揭示事物发展的演变规律,进而对事物发展趋势进行预测
基于Twitter 数据的选举结果预测:
联合国“全球脉动”(Global Pulse):
通过对Twitter等网上公开数据的实时感知、动
态获取与综合分析,结合仿真调控,预测大选
结果。
利用网络大数据预测失业率与疾病爆发等现
象,利用数字化的早期预警信号来提前指导
援助项目。
问题与挑战:数据交互性强、实时性强、动态演变,导致传统数据计算方法:
数据生命周期的割裂、时效性与准确性难以兼顾、演变趋势难以预测
大数据技术发展路线图(Gartner解读)
大数据行业发展预测
"Big Five" IT Trends of the Next Half Decade
Gartner预测
ZDNet预测
Next-Gen Mobile - Smart Devices and Tablets
Social Media - Social Business and Enterprise 2.0
Cloud Computing
Big Data
Consumerization of IT
调研的目标、方法和过程
• 70位委员
– 学术界46位、产业界14位、海外10位
• 思考热点问题
• 给出2013年大数据发展趋势预测
热点问题候选项
一、科学问题
1 数据的科学问题(55)
2 大数据的基本内涵(43)
3 计算模式(61)
7 如何将大数据变小(42)
8 数据的价值提炼(49)
三、应用实践问题
9 大数据应用领域(49)
10 大数据对于系统的要求(49)
二、技术问题
4 数据的多样性与数据态(53) 11 数据质量问题 (41)
12 大数据安全和隐私(59)
5 大数据的空间维问题(44)
(分布、感知与传输)
四、大数据生态问题
6 大数据的时间维问题(63)
13 数据资源化和共享管理(33)
(流式化、时效性、在线处理)14 大数据的生态环境(32)
大数据 大价值
八大热点问题
1、数据科学与大数据的学科边界
2、数据计算的基本模式与范式
3、大数据的作用力和变换反应
4、大数据特性与数据态
5、大数据安全和隐私问题
6、大数据对IT技术架构的挑战
7、大数据的生态环境问题
8、大数据的应用及产业链
第四章回顾
4.1 串的相关术语
4.2 串的抽象数据类型的定义
4.3 串的表示和实现
4.4 串的模式匹配算法
4.1 串的定义及相关术语
由零个或多个字符组成的有限序列.
一般记为:s= a1a2…an ,n>=0
串名:s => 变量名 => 字符串
串值: a1a2…an
=〉空格串: a1,a2,…,an 均为空格
串长:n => n=0 =〉空串
空格串不同于空串
子串:串中任意个字符组成的子序列
=〉原串:主串
位置:字符的位置,子串的位置
串相等:当且仅当两个串的串值相等
4.2 串的抽象数据类型的定义如下:
ADT String {
数据对象:
D={ ai | ai∈CharacterSet,
i=1,2,...,n,
n≥0 }
数据关系:
R1={ < ai-1, ai > | ai-1, ai ∈D,
i=2,...,n }
4.3 串的表示和实现
在程序设计语言中,串只是作为输
入或输出的常量出现,则只需存储此串
的串值(即字符序列)即可。但在多数
非数值处理的程序中,串也以变量的形
式出现。
一、串的定长顺序存储表示
二、串的堆分配存储表示
三、串的块链存储表示
4.4 串的模式匹配算法
这是串的一种重要操作,很多
软件,若有“编辑”菜单项的话,
则其中必有“查找”子菜单项。
首先,回忆一下串匹配(查找)的定义:
INDEX (S, T, pos)
初始条件:串S和T存在,T是非空串,
1≤pos≤StrLength(S)。
操作结果:若主串S中第pos个字符之后
存在和串T值相同的子串
返回它在主串S中第pos个
字符之后第一次出现的位置;
否则函数值为0。
下面讨论以定长顺序结构表示串时
的几种INDEX (S, T, pos)算法
一、简单算法
二、首尾匹配算法
三、KMP算法
(D.E.Knuth,J.H.Morris ,V.R.Pratt)
第五章
数组和广义表
5.1 数组的类型定义
5.2 数组的顺序表示和实现
5.3 矩阵的压缩存储
5.4 广义表的类型定义
5.5 广义表的表示方法
5.6 广义表操作的递归函数
5.1 数组的类型定义
二维数组的定义:
数据对象:
D = {aij | 0≤i≤b1-1, 0 ≤j≤b2-1}
数据关系:
R = { ROW, COL }
ROW = {<ai,j,ai+1,j>| 0≤i≤b1-2, 0≤j≤b2-1}
COL = {<ai,j,ai,j+1>| 0≤i≤b1-1, 0≤ j≤b2-2}
n维数组的定义:
ADT Array {
数据对象:
D={aj , j , ...,,j , j | ji =0,...,bi -1, i=1,2,..,n }
数据关系:
R={R1, R2, ..., Rn}
Ri={<aj ,... j ,... j , aj , ...j +1, ...j > | 0 jk bk -1,
1 k n 且k i, 0 ji bi -2, i=2,...,n }
1
2
i
1
基本操作:
} ADT Array
i
n
n
1
i
n
基本操作:
InitArray(&A, n, bound1, ..., boundn)
DestroyArray(&A)
Value(A, &e, index1, ..., indexn)
Assign(&A, e, index1, ..., indexn)
InitArray(&A, n, bound1, ..., boundn)
操作结果:若维数 n 和各维长度合法,
则构造相应的数组A,并
返回OK。
DestroyArray(&A)
操作结果:销毁数组A。
取值函数:Value(A, &e, index1, ..., indexn)
初始条件:A是n维数组,e为元素变量,
随后是n 个下标值。
操作结果:若各下标不超界,则e赋值为
A 的对应元素值,并返回OK。
赋值函数:Assign(&A, e, index1, ..., indexn)
初始条件:A是n维数组,e为元素变量,
随后是n 个下标值。
操作结果:若下标不超界,则将e的值赋
给A中由下标指定的元素,
并返回OK。
数组特性:
1. 可以将二维数组看成是一个定长的线
性表,其中每一个数据元素也是一个
定长的线性表。
2. N维数组也可以看成是一个定长的线
性表
3. 当数组维数n=1,则数组退化为一个
定长的线性表。
5.2 数组的顺序表示和实现
特点:
数组是多维的结构,而存储空间是
一个一维的结构,需要进行映象。
有两种顺序映象方式:
1)以行序为主序(低下标优先)
扩展Basic, PL/1, Cobol, Pascal, C
2) 以列序为主序(高下标优先)
Fortran语言
以“行序为主序”的存储映象
例如:
a0,0 a0,1 a0,2
a1,0 a1,1 a1,2
a0,0 a0,1 a0,2 a1,0 a1,1 a1,2
L
二维数组A中任一元素ai,j 的存储位置
LOC(i,j) = LOC(0,0) + (b2×i+j)× L
称为基地址或基址。
推广到一般情况,可得到 n 维数组数据元
素存储位置的映象关系
n
LOC(j1, j2, ..., jn ) = LOC(0,0,...,0) + ∑ ci ji
i =1
其中 cn = L,ci-1 = bi ×ci , 1 < i n。
称为 n 维数组的映象函数。
=〉数组元素的存储位置是其下标的
线性函数=〉随机存储结构
//------数组的顺序存储表示-----# include <stdarg.h> //标准头文件,提供宏
# define MAX_ARRAY_DIM 8 //最大8维
typedef struct {
ElemType *base //数组元素基址
int dim
//数组维数n
int *bounds
//数组维界bi基址
int *constant // 数组映象函数常量ci基址
} Array
5.3 矩阵的压缩存储
1) 特殊矩阵
矩阵中数据分布有一定规则。例如:
对称矩阵:ai, j= aj, i
(上/下)三角矩阵:只在对角线的上部/下
部有非零元,且非零元为常数c
对角矩阵 =〉只在对角线上有非零元
n对角矩阵 =〉只在对角线附近的n条对角线
上有非零元(n为奇数)
2) 稀疏矩阵
非零元素少,且在矩阵中随机出现
何谓稀疏?
假设 m 行 n 列的矩阵含 t 个非零元素,
则称
t
mn 为稀疏因子。
通常认为 0.05 的矩阵是稀疏的。
以常规方法,即以二维数组表示
高阶稀疏矩阵时产生的问题:
1) 零值元素占了绝大部分空间;
2) 计算中进行了很多与零值的运算;
遇到除法还需判别除数是否为零。
解决问题的原则:
1) 尽可能少存或不存零值元素;
2) 多个值相同的非零元素只分配一个空间
3) 操作方便,即:
• 能尽可能快地找到与下标值(i,j)对应的元
素
• 能尽可能快地找到同一行或同一列的非零
值元
=〉压缩存储
特殊矩阵的压缩存储方法:
一、对称矩阵
为每对对称元素分配一个存储空间
二、对角矩阵
只存储其对角线上的元素。
稀疏矩阵的压缩存储方法:
一、三元组顺序表
二、行逻辑联接的顺序表
三、十字链表
一、三元组顺序表
三元组顺序表又称有序的双下
标法。它的特点是:非零元在表中
按行序有序存储,每个非零元存储
的是行号、列号和元素值三项数据。
#define MAXSIZE 12500 //最大非零元数
typedef struct {
int i, j;
// 非零元的行下标和列下标
ElemType e; // 非零元的值
} Triple; // 三元组元素类型
typedef struct {
Triple data[MAXSIZE + 1];
int mu, nu, tu; //行数, 列数, 非零元数
} TSMatrix; // 稀疏矩阵类型
如何求转置矩阵?
M: mn=> M’: n m
M(i,j)
=> M’(j, i)
0 14 0 0 5
0 7 0 0 0
36 0 0 28 0
0 0 36
14 7 0
0 0 0
0
0
28
5 0 0
实现方法:
1. 对矩阵M的转置矩阵M’,逐行找该行
元素;
用常规的二维数组表示时的算法
for (col=1; col<=nu; ++col)
for (row=1; row<=mu; ++row)
T[col][row] = M[row][col];
其时间复杂度为: O(mu×nu)
实现方法:
2. 按照a.data中三元组的次序进行转置,
并将转置后的三元组置放到b中的适
当位置。
(a和b分别为矩阵M和M’的三元组表示)
用“三元组”表示时如何实现?
1
1
2
3
3
2
5
2
1
4
15
-5
-7
36
28
1
2
2
4
5
3
1
2
3
1
36
15
-7
28
-5
需要完成三个任务:
1. 矩阵的行和列互换;
2. 矩阵中的每个三元组中元素的i和j值
互换;
3. 重新排列三元组元素的次序。
关键是第3项任务
* 需要确定M的每一列中的非零元个数,
以及每列第一个非零元在三元组中的位置。
1
1
2
3
3
2
5
2
1
4
15
-5
-7
36
28
col
Num[pos]
Cpot[col]
1
1
1
2
2
2
3
0
4
4
1
4
5
1
5
cpot[1] = 1;
for (col=2; col<=M.nu; ++col)
cpot[col] = cpot[col-1] + num[col-1];
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T){
T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
if (T.tu) { //T.tu=0无需转置
for (col=1; col<=M.nu; ++col) num[col] = 0;
for (t=1; t<=M.tu; ++t) ++num[M.data[t].j];
cpot[1] = 1;
for (col=2; col<=M.nu; ++col)
cpot[col] = cpot[col-1] + num[col-1];
for ( t=1; t<=M.tu; ++t) { 转置矩阵元素 }
} // if
return OK;
} // FastTransposeSMatrix
转置矩阵元素:
Col = M.data[t].j;
q = cpot[col];
T.data[q].i = M.data[t].j;
T.data[q].j = M.data[t].i;
T.data[q].e = M.data[t].e;
++cpot[col]
分析算法FastTransposeSMatrix的时间
复杂度:
for (col=1; col<=M.nu; ++col) … …
for (t=1; t<=M.tu; ++t) … …
for (col=2; col<=M.nu; ++col) … …
for (t=1; t<=M.tu; ++t) … …
时间复杂度为: O(M.nu+M.tu)
二、行逻辑链接的顺序表
三元组顺序表便于进行按行序处理的
矩阵运算。然而,若需快速存取某一行中
的非零元,则需从头开始进行查找。
行逻辑链接的顺序表:
修改前述的稀疏矩阵的结构定义,
增加一个数据项rpos---各行第一个非零
元的位置
#define MAXMN 500
typedef struct {
Triple data[MAXSIZE + 1];
int mu, nu, tu;
int rpos[MAXMN + 1];
} RLSMatrix;
// 行逻辑链接顺序表形式的稀疏矩阵类型
例如:给定一组下标,求矩阵的元素值
ElemType value(RLSMatrix M, int r, int c)
{ p = M.rpos[r];
while (M.data[p].i==r &&M.data[p].j < c)
p++;
if (M.data[p].i==r && M.data[p].j==c)
return M.data[p].e;
else return 0;
} // value
矩阵乘法的一般算法:
for (i=1; i<=m1; ++i)
for (j=1; j<=n2; ++j) {
Q[i][j] = 0;
for (k=1; k<=n1; ++k)
Q[i][j] += M[i][k] * N[k][j];
}
其时间复杂度为: O(m1×n2×n1)
如何求稀疏矩阵的乘积?
=〉不能采用传统方法
利用行链接的顺序表便于求Q=M×N:
1) 对M.data[p],只需与N.data[q]相乘
M.data[p].j= N.data[q].i
2) 对于Q[i, j], 需设一个累加器ctemp[],
并需最后判断其元素值是否为零
3) 先求中间结果,最后再压缩存储
两个稀疏矩阵相乘(QMN)
的过程可大致描述如下:
Q初始化;
if Q是非零矩阵 { // 逐行求积
for (arow=1; arow<=M.mu; ++arow) {
// 处理M的每一行
ctemp[] = 0;
// 累加器清零
计算Q中第arow行的积并存入ctemp[] 中;
将ctemp[] 中非零元压缩存储到Q.data;
} // for arow
} // if
Status MultSMatrix
(RLSMatrix M, RLSMatrix N, RLSMatrix &Q) {
if (M.nu != N.mu) return ERROR;
Q.mu = M.mu; Q.nu = N.nu; Q.tu = 0;
if (M.tu*N.tu != 0) { // Q是非零矩阵
for (arow=1; arow<=M.mu; ++arow) {
// 处理M的每一行
} // for arow
} // if
return OK;
} // MultSMatrix
ctemp[] = 0;
// 当前行各元素累加器清零
Q.rpos[arow] = Q.tu+1;
if (arow < M.mu ) tp = M.rpos[arow+1];
else { tp = M.tu+1 }
for (p=M.rpos[arow]; p<tp; ++p) {
//对当前行中每一个非零元
处 brow=M.data[p].j;
理 if (brow < N.nu ) t = N.rpos[brow+1];
M else { t = N.tu+1 }
的 for (q=N.rpos[brow]; q< t; ++q) {
ccol = N.data[q].j; // 乘积元素在Q中列号
每
ctemp[ccol] += M.data[p].e * N.data[q].e;
一
行 } // for q
} // for p,求得Q中第crow( =arow)行的非零元
处
理
M
的
每
一
行
for (ccol=1; ccol<=Q.nu; ++ccol)
if (ctemp[ccol]) {
if (++Q.tu > MAXSIZE)
return ERROR;
Q.data[Q.tu] = {arow, ccol, ctemp[ccol]};
} // if
分析上述算法的时间复杂度
累加器ctemp初始化的时间复杂度为:
(M.muN.nu),
求Q的所有非零元的时间复杂度为:
(M.tuN.tu/N.mu),
进行压缩存储的时间复杂度为:
(M.muN.nu),
总的时间复杂度就是:
(M.muN.nu+M.tuN.tu/N.mu)
若M是m行n列的稀疏矩阵,N是n行p列的稀疏
矩阵,
则M中非零元的个数 M.tu = Mmn,
N中非零元的个数 N.tu = Nnp,
相乘算法的时间复杂度就是
(mp(1+nMN)) ,
当M<0.05 和N<0.05及 n <1000时,
相乘算法的时间复杂度就相当于 (mp)。
结果相当理想!
三、 稀疏矩阵的十字链表表示
利用行逻辑链接的顺序表表示稀疏矩
阵,便于查找同一行中的非零元,但是不
便于查找同一列中的非零元。
十字链表:
每一个非零元均有指向分别指向同一
行和同一列中下一个非零元的指针,形
成行、列两个链表 --- 十字链表。
十字链表实例
M.chead
^
M.rhead
1 13
1 45
^^
2 2 -1
^^
31 2
^^
3 0 0 5
0 -1 0 0
2 0 0 0
typedef struct OLNode {
int i, j ; //非零元素的行和列下标
Elemtype e; //非零元素的值
struct OLNode *right, *down;
//分别指向行和列的下一元素
} OLNode; *Olink;
typedef struct {
Olink *rhead, *chead; //头指针向量
int mu, nu, tu;
} CrossList;
5.4
广义表
广义表是线性表的扩展(表中有表的
线性表),也称为列表(lists)。
例如:X=(a, (b, c, (d)))
现实世界中的许多数据的结构可以用
广义表来定义,例如数组。
广义表的类型定义
ADT Glist {
数据对象:D={ei | i=1,2,..,n; n≥0;
ei∈AtomSet 或 ei∈GList,
AtomSet为某个数据对象 }
数据关系:
LR={<ei-1, ei >| ei-1 ,ei∈D, 2≤i≤n}
基本操作:
} ADT Glist
基 结构的创建和销毁
CreateGList(&L, S); DestroyGList(&L);
本
InitGList(&L);
CopyGList(&T, L);
操
作 状态函数
GListLength(L); GListDepth(L);
GListEmpty(L); GetHead(L); GetTail(L);
插入和删除操作
InsertFirst_GL(&L, e);
DeleteFirst_GL(&L, &e);
遍历
Traverse_GL(L, Visit());
广义表是递归定义的线性结构,
LS = ( 1, 2, , n )
其中:i 或为原子 或为广义表
例如: A = ( ) --- 空表
F = (d, (e))
D = ((a,(b,c)), F)
C = (A, D, F)
B = (a, B) = (a, (a, (a, , ) ) )
广义表是一个多层次的线性结构
例如:
D=(E, F)
其中:
a
E=(a, (b, c))
F=(d, (e))
D
E
F
( ) d
b
c
( )
e
广义表 LS = ( 1, 2, …, n )的结构特点:
1) 数据元素有相对次序;
2) 其长度定义为最外层所包含的元素个数;
3) 其深度定义为所含括弧的最大重数;
注意:“原子”的深度为 0
“空表”的深度为 1 ,长度为0
4) 可以共享定义(引用子表名称);
5) 可以是一个递归的表。例:B={a, B}
其长度是有限值, 深度是无穷值()。
6) 任何一个非空广义表 LS = ( 1, 2, …, n)
均可分解为:
表头 Head(LS) = 1 和
表尾 Tail(LS) = ( 2, …, n) 两部分。
例如: D = ( E, F ) = ((a, (b, c)),F )
Head( D ) = E
Head( E ) = a
Tail( D ) = ( F )
Tail( E ) = ( ( b, c) )
Head( (( b, c)) ) = ( b, c) Tail( (( b, c)) ) = ( )
Head( ( b, c) ) = b Tail( ( b, c) ) = ( c )
Head( ( c ) ) = c
Tail( ( c ) ) = ( )
5.5 广义表的表示方法
难以用顺序结构表示;
通常采用头、尾指针的链表结构
表结点:
tag=1 hp tp
原子结点: tag=0 data
构造存储结构的两种分析方法:
1) 表头、表尾分析法:
空表
ls=NIL
非空表 ls
tag=1
指向表尾的指针
指向表头的指针
若表头为原子,则为 tag=0 data
否则,依次类推。
广义表的头尾链表存储表示:
typedef enum {ATOM, LIST} ElemTag;
// ATOM==0:原子, LIST==1:子表
typedef struct GLNode {
ElemTag tag; // 标志域
原子结点 tag=0 data
union{
AtomType data; // 原子结点的数据域
struct {struct GLNode *hp, *tp;} ptr;
};
表结点 tag=1 hp tp
} *GList
ptr
例如:
L=(a, (x, y), ((x)) )
a
((x, y), ((x)) )
(x, y)
x
(y)
y
( ((x)) )
((x)) ( )
( ) (x) ( )
x ( )
L
L == (( aa, ( x, y ),
) ((X
x ) )) )
L
1
1
0 a
1
1
0 x
1
1
1
0 y
0 X
2) 子表分析法:
空表
ls=NIL
非空表
ls
1
1
指向子表1 指向子表2
的指针
的指针
…
1
指向子表n
的指针
若子表为原子,则为 tag=0 data
否则,依次类推。
例如:
LS=( a, (x,y), ((x)) )
ls
a
(x, y)
((x))
广义表的子表链表存储表示:
typedef enum {ATOM, LIST} ElemTag;
typedef struct GLNode {
ElemTag tag; // 标志域
union{
AtomType data; // 原子结点的数据域
struct GLNode *hp;
};
struct GLNode *tp;
} *GList
m元多项式的表示
例:
P(x,y,z)=x10y3z2+2x6y3z2+3x5y2z2
+x4y4z+6x3y4z+2yz+15
可以改写成:
P(x,y,z)=((x10+2x6)y3+3x5y2)z2
+((x4+6x3)y4+2y)z+15
=Az2+Bz+15
同理,A、B可以改写成:
A=(x10+2x6)y3+3x5y2=Cy3+Dy2
B=(x4+6x3)y4+2y=Ey4+Fy
……
任何一个m元多项式都可以如此分解:
一个m元多项式首先是它的主变
元的多项式,其系数又是其第二变元
的多项式,层层分解。
因此,可以采用广义表来表示m
元多项式。
P=Az2+Bz+15 => z((A,2),(B,1),(15,0))
A= Cy3+Dy2 => y((C,3),(D,2))
C=> x((1,10),(2,6))
D=> x((3,5))
B=Ey4+Fy => y((E,4),(F,1))
E=> x((1,4),(6,3))
F=> x(2,0)
采用广义表结构表示m元多项式:
表结点:
tag=1 exp hp tp
同层下一元素结点
系数子表结点
原子结点:
tag=0 exp coef tp
同层下一元素结点
5.6 广义表操作的递归函数
递归函数:
一个含有直接或间接调用本函数语句
的函数被称之为递归函数。
它必须满足以下两个条件:
1)在每一次调用自己时,必须是(在某
种意义上)更接近于解;
2)必须有一个终止处理或计算的准则。
例如: 汉诺塔的递归函数
void hanoi (int n, char x, char y, char z)
{
if (n==1)
move(x, 1, z);
else {
hanoi(n-1, x, z, y);
move(x, n, z);
hanoi(n-1, y, x, z);
}
}
一、 求广义表的深度
二 、 复制广义表
一、 求广义表的深度
将广义表分解成 n 个子表,分别(递归)
求得每个子表的深度,
广义表的深度=Max {子表的深度} +1
可以直接求解的两种简单情况为:
空表的深度 = 1
原子的深度 = 0
int GlistDepth(Glist L) {
// 返回指针L所指的广义表的深度
if (!L) return 1;
if (L->tag == ATOM) return 0;
for (max=0, p=L; p; p=p->ptr.tp)
{
dep = GlistDepth(p->ptr.hp);
if (dep > max) max = dep;
}
return max + 1;
} // GlistDepth
二、 复制广义表
思路:将广义表分解成表头和表尾两部分,
分别(递归)复制其表头和表尾,然后合成
可以直接求解的两种简单情况为:
空表复制求得的新表自然也是空表;
原子结点可以直接复制求得。
复制求广义表的算法描述如下:
若 ls= NIL 则 newls = NIL
否则
构造结点 newls,
由 表头ls->ptr.hp 复制得 newhp
由 表尾 ls->ptr.tp 复制得 newtp
并使 newls->ptr.hp = newhp,
newls->ptr.tp = newtp
Status CopyGList(Glist &T, Glist L) {
if (!L) T = NULL; // 复制空表
else {
if ( !(T = (Glist)malloc(sizeof(GLNode))) )
exit(OVERFLOW); // 建新表结点
T->tag = L->tag;
if (L->tag == ATOM)
T->data = L->data; // 复制单原子结点
else { 分别复制表头和表尾 }
} // else
return OK;
} // CopyGList
CopyGList(T->ptr.hp, L->ptr.hp);
// 复制求得表头T->ptr.hp的一个副本L->ptr.hp
CopyGList(T->ptr.tp, L->ptr.tp);
// 复制求得表尾T->ptr.tp 的一个副本L->ptr.tp
语句 CopyGList(T->ptr.hp, L->ptr.hp);
等价于
CopyGList(newhp, L->ptr.tp);
T->ptr.hp = newhp;
本章学习要点
1. 了解数组的两种存储表示方法,
并掌握数组在以行为主的存储结构中
的地址计算方法。
2. 掌握对特殊矩阵进行压缩存储时
的下标变换公式。
3. 了解稀疏矩阵的两类压缩存储方
法的特点和适用范围,领会以三元
组表示稀疏矩阵时进行矩阵运算采用
的处理方法。
4. 掌握广义表的结构特点及其存储
表示方法,学会对非空广义表进行
分解的两种分析方法:即可将一个
非空广义表分解为表头和表尾两部
分或者分解为n个子表。
调课信息:
11月2日 课程 调到 11月16日 下午 5-8 节
11月9日 课程 调到 11月30日 下午 5-8 节