Professional Documents
Culture Documents
ch4 串、数组和广义表
ch4 串、数组和广义表
教学内容
4.1 串的定义
4.2 案例引入
4.3 串的类型定义、存储结构及其运算
4.4 数组
4.5 广义表
4.6 案例分析与实现
4.7 小结
教学目标
了解串的存储方法,理解串的两种模式匹配
算法,重点掌握 KMP 算法。
明确数组和广义表这两种数据结构的特点,
掌握数组地址计算方法,了解几种特殊矩阵
的压缩存储方法。
掌握广义表的定义、性质及其 GetHead 和
GetTail 的操作。
4.1 串的定义
串的定义
即字符串,由零个或多个字符组成的有限序列
是数据元素为单个字符的特殊线性表
串长
串中字符个数( n ≥ 0 )。 n = 0 时称为空串 ,
。
空白串
由一个或多个空格符组成的串。
4.1 串的定义
串的术语
子串
串中任意个连续的字符组成的子序列
主串
包含子串的串
字符位置
字符在串中的序号
子串的位置
子串的第一个字符在主串中的序号
串相等
串长度相等,且对应位置上字符相等。
4.1 串的定义
练 1 :串是由 字符组成的序
0 个或多个
列,一般记为
S=''a1a2……an''
。
练 2 :现有以下 4 个字符串:
a =''BEI'' b =''JING'' c = ''BEIJING'' d = ''BEI JING''
问:① 他们各自的长度?La =3 , Lb =4 , Lc = 7 , Ld=8
② a 是哪个串的子串?在主串中的位置是多少?
a 是 c 和 d 的子串,在 c 和 d 中的位置都是 1
练 3 :空串和空白串有无区别?
答:有区别。空串 (Null String) 是指长度为零的串;而空白串
(Blank String), 是指包含一个或多个空白字符‘ ’ ( 空格
键 ) 的字符串。
4.3 串的类型定义、存储结构及其运算
4.3.1 串的抽象类型定义
ADT String {
数据对象: D = { ai |ai∈CharacterSet,i=1,2,...,n,
n≥0}
数据关系: R = { < ai-1, ai >SubString
| ai-1, ai ∈D, i=2,...,n}
(&Sub, S, pos, len)
StrAssign (&T, chars)
基本操作:
StrCopy (&T, S) // 有 13 种 Index (S, T, pos)
假设 S = ‘abcaabcaaabc’, T = ‘bca’
Index(S, T, 1) = 2;
Index(S, T, 3) = 6;
Index(S, T, 8) = 0;
4.3.2 串的存储结构
串的顺序存储
串的链式存储
4.3.2 串的存储结构
串的顺序存储
串的定长顺序存储结构
#define MAXLEN 255 // 用户可用的最大串长
typedef struct {
char ch[MAXLEN+1]; // 存储串的一维数组
int length; // 串的当前长度
}SString;
typedef
串的堆式顺序存储结构
struct {
char *ch; // 若非空串 , 按串长分配空间 ; 否则 ch = NULL
int length; // 串长度
}HString;
串指派
Status StrAssign( HString &T, char *chars ) {
if (T.ch) delete []T.ch;
for (i=0, c=chars; *c; ++i, ++c); // 求串长度
if (!i) {T.ch = NULL; T.length = 0;}
else{
if (!(T.ch = new char[i]))
exit(OVERFLOW);
T.ch[0..i-1] = chars[0..i-1];
T.length =i;
}
return OK;
}
求子串
Status SubString ( HString& Sub, HString S,int pos, int len )
{ // 用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串。
// 其中 ,1<=pos<= StrLength (S) 且 0<=len<=StrLength(S)-
pos+1 。
if ( pos < 1 || pos>S.length || len<0 || len>S.length-pos+1)
return ERROR; // 参数不合法
if ( Sub.ch) delete [] Sub.ch; // 释放旧空间
if (len==0) { Sub.ch = NULL; Sub.length = 0; } // 空子串
else { // 完整子串
Sub.ch =new char[len];
Sub.ch[0..len-1] = S.ch [ pos-1.. pos+len-2] ;
Sub.length = len;
}
return OK;
}
4.3.2 串的存储结构
串的链式存储
‘ABCDEFGHI’
4.3.2 串的存储结构
串的链式存储结构
#define CHUNKSIZE 80 // 可由用户定义的块大小
typedef struct Chunk{
char ch[CHUNKSIZE];
struct Chunk *next;
}Chunk;
typedef struct{
Chunk *head,*tail; // 串的头指针和尾指针
int curlen; // 串的当前长度
}LString;
尾指针的目的是为了便于进行联结操作。
4.3.2 串的存储结构
串的链式存储
优点
操作方便
=串值所占的存储位
缺点
存储密度
实际分配的存储位
存储密度较低
head
A B C D E F G H I # # #
head
A B C ... I
4.3.3 串的模式匹配算法
模式匹配 (Pattern Matching)
即子串定位运算 ( Index(S,T,pos) 函数 )
算法目的
确定主串中所含子串在 pos 后第一次出现的位置
初始条件
串 S 和 T 存在, T 是非空串, 1≤pos≤StrLength(S)
操作结果
若主串 S 第 pos 个字符起存在和串 T 值相同的子串,
则返回它在主串 S 中第一次出现的位置,否则返回
0。
注: S 称为被匹配的串, T 称为模式串。若 S 包含串 T ,则
称
“ 匹配成功”,否则称 “匹配不成功” 。
4.3.3 串的模式匹配算法
算法种类
BF 算法
又称古典、经典、朴素、穷举
KMP 算法
Knuth Morris Pratt
特点:速度快
4.3.3 串的模式匹配算法
BF 算法设计思想
将主串 S 的第 pos 个字符和模式 T 的第 1 个字
符进行比较,
若相等,继续逐个比较后续字符;
若不等,从主串 S 的下一字符( pos+1 )起,重新与
T 的第一个字符进行比较。
直到主串 S 的一个连续子串字符序列与模式 T 相
等。返回值为 S 中与 T 匹配的子序列第一个字符
的序号,即匹配成功。否则,匹配失败,返回值
0.
BF 算法
i 回 i
溯
主串 S
si ……
模式 T
tj
j j
回溯
BF 算法
例:主串 S="ababcabcacbab" ,模式 T="abcac"
a b c a c
j j j
BF 算法
例:主串 S="ababcabcacbab" ,模式 T="abcac"
i=2 , j=1 失败
i 回溯到 3 , j 回溯
到1
i
第
2
趟 a b a b c a b c a c b a b
a b c a c
j
BF 算法
i=7 , j=5 失败
i 回溯到 4 , j 回溯
到1
i i i i i
第
3 a b a b c a b c a c b a b
趟
a b c a c
j j j j j
BF 算法
i=4 , j=1 失败
i 回溯到 5 , j 回溯
i 到1
第 a b a b c a b c a c b a b
4
趟
a b c a c
j
BF 算法
i=5 , j=1 失败
i 回溯到 6 , j 回溯
到1
i
第
5 a b a b c a b c a c b a b
趟
a b c a c
j
BF 算法
i i i i i
第 a b a b c a b c a c b a b
6
趟
a b c a c
j j j j j
4.3.3 串的模式匹配算法
BF 算法
int Index(SString S,SString T,int pos){
i=pos; j=1;
while (i<=S.length && j <=T.length){
if ( S.ch[ i ] == T.ch[ j ]) {++i; ++j; }
else { i=i-j+2; j=1; } 匹配成功后指针仍要回溯!因为要
返回的是被匹配的首个字符位置。
}
if ( j>T.length) return i-T.length;
else return 0;
}
4.3.3 串的模式匹配算法
BF 算法的时间复杂度
若 n 为主串长度, m 为子串长度,则串的 BF 匹
配算法最好、最坏情况下需比较字符的总次数是
多少?
最好情况
一配就中! 只比较了 m 次。
最坏情况
主串前面 n-m 个位置都部分匹配到子串的最后一位,即这 n-
m 位比较了 m 次。另外最后 m 位也各比较了一次。所以总
次数为: (n-m)*m+m = (n-m+1)*m
时间复杂度: O(m*n)
例如:
» S=‘aaaaaaaaaaab’ n=12
» T=‘aaab’ m=4
KMP 算法
KMP 算法设计思想
尽量利用已经部分匹配的结果信息,让 i 不要回
溯,加快模式串滑动的速度。可提速到 O(n+m)
i i
S=‘aa b a b c a b c a c b a S=‘a b a b c a b c a c b a
T=‘a
b’ a b c a c’ i i i
b’ T=‘a b c a c’
k k
S=‘a b a b c a b c a c b a
b’ T=‘a b c a c’
k
i-T.length
KMP 算法的返回值应为 6
KMP 算法
KMP 算法设计思想
改进:每趟匹配过程中出现字符比较不等时,不回溯
主指针 i ,利用已得到的“部分匹配”结果将模式滑
动一段距离,继续进行比较。
怎么计算距离?
KMP 算法的推导过程:(见教材 P94 )
k 是追求的新起点
请抓住部分匹配时的两个特征:
(1) i
设目前打算与 P 的第 k 字符开始比较
S=‘a b a b c a b c a c b a
则 P 的 1 ~ k-1 位= S 的 i-(k-1) ~
b’ P=‘a b c a c’ i-1 位
k ‘P1…Pk-1’
i
(2)
S=‘a b a b c a b c a c b a 刚才肯定是在 S 的 i 处和 P 的第 j 字符处失
P=‘a b c a c’ 则P的 配 j-(k-1) ~ j-1 位= S 的 i-(k-1) ~
b’ i-1 位
k j
但 k 有限制, 1<k<j
•当 j=1 时, Next[j]=0 ;
//Next[j]=0 表示根本不进行字符比较
•当 j>1 时, Next[j] 的值为:模式串的位置从 1 到 j-1
构成的串中所出现的首尾相同的子串的最大长度加
1。
无首尾相同的子串时 Next[j] 的值为 1 。
// Next[j]=1 表示从模式串头部开始进行字符比较
KMP 算法
KMP 算法的实现
int Index_KMP (SString S,SString T, int pos)
{
i= pos, j =1;
while (i<=S.length && j<=T.length) {
if (j==0 || S.ch[i]==T.ch[j]) {++i; ++j; }
else
j=next[j]; /*i 不变 ,j 后退 */
}
if (j>T.length) return i-T.length; /* 匹配成功 */
else return 0; /* 返回不匹配标志 */
}
KMP 算法
如何求 next 函数值
由定义得知, next[1] = 0;
设 next[j] = k ,则 next[j+1] = ?
① 若 pk=pj ,则有“ p1…pk-1pk”=“pj-k+1…pj-1pj” , 如果在
j+1 发生失配,说明 next[j+1] = k+1 。
② 若 pk≠pj ,可把求 next 值问题看成是一个模式匹配
问题,整个模式串既是主串,又是子串。
KMP 算法
j 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
模式串 a b c a a b b c a b c a a b d a
b
next[j]
0 1 1 1 2 2 3 11 2 3 4 5 6 7 1 2
KMP 算法
KMP 算法的时间复杂度
设主串 s 的长度为 n, 模式串 t 长度为 m, 在 KMP
算法中求 next 数组的时间复杂度为 O(m), 在后
面的匹配中因主串 s 的下标不减即不回溯 , 比较
次数可记为 n, 所以 KMP 算法总的时间复杂度为
O(n+m) 。
KMP 算法的用途
因为主串指针 i 不必回溯,所以从外存输入文件时可以
做到边读入边查找——“流水作业” !
注意:由于 BF 算法在一般情况下的时间复杂度也近似
于 O(n+m) ,所以至今仍被广泛采用。
讨论: next [ j ] 是否完美无缺?
前面定义的 next 函数在某些情况下还是有缺陷的,
例如模式 aaaab 与主串 aaabaaaab 匹配时的情况:
先计算 j:12345
next[j] : T:aaaab 似乎慢了一点?
next[j] : 0 1 2 3 4 能否再提速?
i: 123456789
S: aaabaaaab
T: a a aa aa aba ab
abaa ab b
此时效率不高的原因为:
当 Pj==Pnext[j] 时,则
如果 Si != Pj , == 》 Si != Pnext[j]
}}
j 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
模式串 a b c a a b b c a b c a a b d a
b
next[j]
nextval[j] 00 1 1 1 2 2 3 1 1 2 3 4 5 6 7 1 2
1 1 0 2 1 3 1 0 1 1 0 2 1 7 0 1
习题
栈 比 较 队 列 串
⑴ 基本操作的实现 ⑴ 基本操作的实现
⑵ 时间性能 ⑵ 时间性能 模式匹配
复习:串的模式匹配
1. 什么是模式匹配?
S: ababcabcacbab
T: abcab
即:串的定位运算。在主串 S 中查找和模式 T
相匹配的串。
复习:串的模式匹配
2. 有哪些模式匹配算法?
BF 算法
S: ababcabcacbab
T: abcab
(1) abc
(2) a
(3) abcab
KMP 算法
复习:串的模式匹配
2. 有哪些模式匹配算法?
BF 算法
KMP 算法
若主串为 : acabaabaabcacaabc
模式串为 : abaabcac
next() 函数值: 01122312
pos=1
请根据 next() 值写出其模式匹配过程。
next() 和 nextval()
j 1 2 3 4 5 6 7 8
模式串 a b a a b c a c
next(j) 0 1 1 2 2 3 1 2
nextval(j)
0 1 0 2 1 3 0 2
nextval(j) 0 0 2 1 0 0 2 1 0 6
}} }}
next() 和 nextval()
j 1 2 3 4 5 6 7 8
模式串 a b a b a a a b
next(j) 0 1 1 2 3 4 2 2
nextval(j)
0 1 0 1 0 4 2 1
栈——仅在表尾进行插入和删除操作的线性表。
表
特殊线性
队列——在一端进行插入操作,而另一端进行
删除操作的线性表。
串——零个或多个字符组成的有限序列 。
限制元素类型为字符
广义线性表
线性表——具有相同类型的数据元素的有限序列。
将元素的类型进行扩充
(多维)数组——线性表中的数据元素可以是
表
广义线性
线性表,但所有元素的类型相同。
广义表——线性表中的数据元素可以是线性表,
且元素的类型可以不相同。
4.4 数组
4.4.1 数组的类型定义
4.4.2 数组的顺序存储
4.4.3 特殊矩阵的压缩存储
4.4.1 数组的类型定义
数组
数组是由类型相同的数据元素构成的有序集合,
每个元素称为数组元素。
特点:元素本身也可是一个数据结构
本章所讨论的数组与高级语言中数组的区别
高级语言中的数组是顺序结构;
而本章的数组既可是顺序结构,也可是链式结
构,用户可根据需要选择。
4.4.1 数组的类型定义
一维数组
C/C++ 定义
int a[100];
a, i=0
LOC(i) =
a + i*l, i > 0
0 1 2 3 4 5 6 7 8 9
a 35 27 49 18 60 54 77 83 41 02
l l l l l l l l l l
l=4
4.4.1 数组的类型定义
二维数组
可以看成数组元素是线 a11 a12 a1n
性表的线性表。 a a a
Amn 21 22 2n
A (1 , 2 , , p ) (p m或n)
i ( ai1 , ai 2 ,, ain ) 1 i m am1 am 2 amn
数据关系:
R {R 1 ,R 2 , ,R n }
R i { aj1 ji j n ,aj1 ji 1j n |
0 jk bk 1, 1 k n,且k i,
0 ji bi 2,
aj1 ji j n ,aj1 ji 1j n D ,i 1, ,n }
4.4.1 数组的类型定义
基本操作
InitArray (&A, n, bound1, ..., boundn)
操作结果:若维数 n 和各维长度合法,则构造
相应的数组 A ,并返回 OK 。
DestroyArray(&A)
操作结果:销毁数组
A。
4.4.1 数组的类型定义
基本操作
Value(A, &e, index1, ..., indexn)
初始条件: A 是 n 维数组, e 为元素变量,随
后是 n 个下标值。
操作结果:若各下标不超界,则 e 赋值为所指
定的 A 的元素值,并返回 OK 。
Assign(&A, e, index1, ..., indexn)
初始条件: A 是 n 维数组, e 为元素变量,随
后是 n 个下标值。
操作结果:若下标不超界,则将 e 的值赋给所
指定的 A 的元素,并返回 OK 。
4.4.2 数组的顺序存储
数组一般不做插入或删除操作,一旦建立了
数组,则结构中的数据元素个数和元素之间
的关系就不再发生变动。
因此,数组采用顺序存储结构比较合适。
由于存储空间是一维的结构,而数组可能是
多维的结构,则用一组连续存储单元存放数
组元素就有次序约定问题。
4.4.2 数组的顺序存储
二维数组的顺序存储
以行序为主序
以列序为主序
4.4.2 数组的顺序存储
二维数组的行序优先表示
int a[m][n] ;
a[0][0] a[0][1] a[0][n 1]
a[1][0] a[1][1] a[1][n 1]
a a[2][0]
a[2][1] a[2][n 1]
a[m 1][0] a[m 1][1] a[m 1][n 1]
则行列索引下标为 i 和 j 对应的元素首地址
为:
LOC( i , j) = a + i * n + j
4.4.2 数组的顺序存储
三维数组
按页 / 行 / 列存放,页优先的顺序存储
u1
①
l1
l2 l3 u3
u2 ②
③
4.4.2 数组的顺序存储
三维数组
a[m1][m2] [m3]
各维元素个数为 m1, m2, m3
下标为 i1, i2, i3 的数组元素的存储位置:
LOC ( i1, i2, i3 ) = a +
i1* m2 * m3 + i2* m3 + i3
前 i1 页 第 i1 页 第 i2 行的前
总 的 i3 列元素个数
元素个数 前 i2 行
总元素个
4.4.2 数组的顺序存储
n 维数组
a[b1][b2]…[bn]
各维元素个数分别为 b1, b2, b3, …, bn
则下标为 j1, j2, j3, …, jn 的数组元素的存储位置
LOC j1 , j2 , , jn a ( j1 b2 b3 bn
j2 b3 b4 bn jn 1 bn jn ) L
n 1 n
a ji bk jn L
i 1 k i 1
n
LOC[ j1 , j2 , , jn ] LOC[0, 0, , 0] ci ji Cn=L, Ci-1=bi×Ci , 1<i≤n
i 1
4.4.2 数组的顺序存储
练习 1
设有一个二维数组 A[m][n] 按行优先顺序存储,
假设 A[0][0] 存放位置在 644 , A[2][2] 存放位置
在 676 ,每个元素占一个空间,问 A[3][3] 存放
在什么位置?
设数组元素 A[i][j] 存放在起始地址为 Loc ( i, j ) 的存
储单元中
∵ Loc ( 2, 2 ) = Loc ( 0, 0 ) + 2 * n + 2 = 644 + 2 * n + 2 = 676.
∴ n = ( 676 - 2 - 644 ) / 2 = 15
∴ Loc ( 3, 3 ) = Loc ( 0, 0 ) + 3 * 15 + 3 = 644 + 45 + 3 = 692.
练习
练习 2
设有二维数组 A[10,20] ,其每个元素占两个字
节, A[0][0] 存储地址为 100 ,若按行优先顺序
存储,则元素 A[6,6] 的存储地址为 ,
按列优先顺序存储,元素 A[6,6] 的存储地址为
。
100+(6*20+6)*2=352
100+(6*10+6)*2=232
4.4.2 数组的顺序存储
练习 3
一个二维数组 A ,行下标的范围是 1 到 6 ,
列下标的范围是 0 到 7 ,每个数组元素用相邻的
6 个字节存储,存储器按字节编址。那么,这个
数组总共占多少个字节?
48*6=288
已知二维数组 Am,m 按行存储的元素地址公式是:
Loc(aij)= Loc(a11)+[(i-1)*m+(j-1)]*K
按列存储的公式是?
Loc(aij)=Loc(a11)+[(j-1)*m+(i-1)]*K
4.4.2 数组的顺序存储
补充
链式存储方式:用带行指针向量的单链表表示
= 7 4 6 0 5
21 22 2n
8 2 9 5 7 ……………….
对称矩阵特点: aij=aji an1 an2 …….. ann
按行序为主序: a11 a21 a22 a31 a32 …... an1 …... ann
k=0 1 2 3 4 n(n-1)/2 n(n+1)/2-1
i(i 1) / 2 j 1,i j
k
j(j 1) / 2 i 1,i j
4.4.3 特殊矩阵的压缩存储
下三角矩阵的压缩存储
a11 0 0 …….. 0
a21 a22 0 …….. 0
…………………. 0
an1 an2 an3…….. ann
按行序为主序: a11 a21 a22 a31 a32 …... an1 …... ann
k=0 1 2 3 4 n(n-1)/2 n(n+1)/2-1
i(i-1)
Loc(aij)=Loc(a11)+[ +(j-1)]*L
2
4.4.3 特殊矩阵的压缩存储
对角矩阵的压缩存储
a11 a12 0 …………… . 0
a21 a22 a23 0 …………… 0
0 a32 a33 a34 0 ……… 0
……………………………
0 0 … an-1,n-2 an-1,n-1 an-1,n
0 0 … …an,n-1 ann
Loc(aij)=Loc(a11)+[3(i-1)+(j-i)]*L
4.4.3 特殊矩阵的压缩存储
稀疏矩阵的压缩存储
稀疏矩阵的定义
矩阵中只有少量的非零元,且这些非零元在矩阵中的
分布没有一定规律。
稀疏因子
在 mn 矩阵中,有 t 个非零元,则 = t/(m*n)
称为稀疏因子
通常认为 ≤ 0.05 的矩阵为稀疏矩阵
实现方法
将每个非零元用一个三元组( i , j , aij )表示,则
每个稀疏矩阵可用一个三元组线性表表示。
4.4.3 特殊矩阵的压缩存储
例:写出下图所示稀疏矩阵的压缩存储形式
矩阵 M 的三元组线性表表示为
{(1,2,12), (1,3,9), (3,1,-3), (3,6,14), (4,3,24), (5,2,18),
(6,1,15), (6,4,-7) }
以行优先顺序存储
4.4.3 特殊矩阵的压缩存储
稀疏矩阵的三元组顺序表存储表示
#define MAXSIZE=12500 // 假设非零元个数的最大值为 12500
typedef struct {
int i, j; // 该非零元的行号和列号
ElemType e; // 该非零元的值
} Triple; // 三元组
typedef struct {
Triple data[MAXSIZE + 1]; // 非零元三元组表, data[0] 未用
int mu, nu, tu; // 矩阵的行数、列数和非零元的个数
} TSMatrix; // 三元组顺序表
4.5 广义表
4.5.1 广义表的定义
4.5.2 广义表的存储结构
4.5.1 广义表的定义
定义
广义表是线性表的推广,也称为列表( lists )
记为: LS = (a1, …,ai,…, an )
ai 可以是单个数据元素,也可以是广义表
例子: A=(a, (b , c , d))
广义表与线性表的区别和联系?
广义表中元素既可以是原子类型,也可以是列表;
当每个元素都为原子且类型相同时,就是线性表。
常用约定
用小写字母表示原子类型,用大写字母表示列表。
B=(b,c,d)
A=(a,B)
4.5.1 广义表的定义
广义表常用术语
长度
广义表中元素的个数 n 。 n=0 时称为空表。
深度
定义为括号嵌套的最深层次。
(a, (b , c , d))
表头
对于任意一个非空广义表 LS=(a1,a2,…,an) ,它的第一
个数据元素 a1 被称为广义表的表头。
表尾
对于任意一个非空广义表,除表头外,由剩余数据元
素构成的广义表 (a2,a3,...,an) 被称为广义表的表尾。
4.5.1 广义表的定义
广义表示例
B = (e)
C = (a, (b , c , A B C
d))
D = (A, B, C) e a
b c d
4.5.1 广义表的定义
广义表的特点
广义表是一个多层次的结构
元素可以是子表,而子表的元素还可以是子表
广义表可以为其他子表所共享
广义表可以是一个递归的表,即广义表可以是其
本身的子表。
E = (a, E)
4.5.1 广义表的定义
广义表的基本运算
求表头 GetHead[L]
非空广义表的第一个元素,可以是单个元素,也可以
是一个子表。
求表尾 GetTail[L]
非空广义表除去表头元素以外其它元素所构成的表。
表尾一定是一个表。
4.5.1 广义表的定义
练习:给出下列广义表的基本操作结果
1. GetTail((b, k, p, h)) = (k, p, h)
;
(a,b)
2. GetHead(((a,b), (c,d) )) =
((c,d))
;
(b
3. GetTail(((a,b), (c,d) )) =
)
; ()
5. GetTail((e)) =
;
4. GetTail(GetHead(((a,b),(c,d))))
() =
;
6. GetHead ((( ))) =
()
.
4.5.1 广义表的定义
练习 已知广义表 L=((x, y, z), a, (u, t, w)) ,从 L 表中取出原子
u 的运算是:
A) head(tail(tail(L))) B) tail(head(head(tail(L))))
C) head(tail(head(tail(L)))) D) head(head(tail(tail(L))))
解:取出原子 u 的过程如下:
1 )用 tail 运算去掉表头 (x, y, z) ,即: tail(L) = (a, (u, t, w))
2 )再用 tail 运算去掉表头 a ,即: tail(tail(L)) = ((u, t, w))
3 )用 head 运算取出表头 (u, t, w) ,即:
head(tail(tail(L))) = (u, t, w)
4 )再用 head 运算取出表头 u ,即: head(head(tail(tail(L))))
4.5.2 广义表的存储结构
头尾链表的存储结构
typedef enum {ATOM, LIST} ElemTag;
// ATOM==0: 原子, LIST==1: 子表
};
} *GList; 表结点 tag=1 hp tp
C 1 1 /\
0 a 1 1 1 /\
0 b 0 c 0 d
4.5.2 广义表的存储结构
广义表的存储结构
E
1 1 ∧
0 a
E = (a, E)
F = (( ))
F
1 ∧ ∧
广义线性表
广义线性表
多维数组 广义表
⑴ 数组的定义 顺 压 ⑴ 基本概念 链
⑵ 基本操作 序 缩 · 广义表定义 接
⑶ADT 定义 存 存 · 表头、表尾 存
储 储 · 长度、深度 储
⑵ADT 定义
按 按 特殊矩阵 稀疏矩阵 头尾表示法
行 列 · 对称矩阵
优 优
· 三角矩阵
先 先
· 对角矩阵
寻址的计算方法
本章小结
串是内容受限的线性表,其限定表中的元素
为字符。要求掌握串的存储方法,理解串的
两种模式匹配算法,尤其是 KMP 算法。
明确数组和广义表这两种数据结构的特点
掌握数组地址计算方法,了解几种特殊矩阵
的压缩存储方法。
掌握广义表的定义、性质及其 GetHead 和
GetTail 的操作。
练习
假设以行序为主序存储二维数组 A=array[1..100,1..100] ,
设每个数据元素占 2 个存储单元,基地址为 10 ,则
LOC[5,5]=
B ( )。
A . 808 B . 818 C . 1010 D . 1020
设有数组 A[i,j] ,数组的每个元素长度为 3 字节, i 的值为 1
到 8 , j 的值为 1 到 10 ,数组从内存首地址 BA 开始顺序存
放,当用以列为主存放时,元素 A[5,8] 的存储首地址为( B
)。
A . BA+141 B . BA+180 C . BA+222 D . BA+225
设有一个 10 阶的对称矩阵 A ,采用压缩存储方式,以行序
为主存储, a11 为第一元素,其存储地址为
B 1 ,每个元素占
一个地址空间,则 a85 的地址为( )。
A . 13 B . 33 C . 18 D . 40
练习
若对 n 阶对称矩阵 A 以行序为主序方式将其下三角形的元素
( 包括主对角线上所有元素 ) 依次存放于一维数组 B[1..
(n(n+1))/2] 中,则在 B 中确定 aij ( i>=j )的位置 k 的关系
为(A )。
A . i*(i-1)/2+j B . j*(j-1)/2+i C . i*(i+1)/2+j D . j*(j+1)/2+i
A[N , N] 是对称矩阵,将下面三角(包括对角线)以行序
存储到一维数组 T[1..N(N+1)/2] 中,则对任一上三角元素
a[i][j] 对应 T[k] 的下标B k 是( )。
A . i(i-1)/2+j B . j(j-1)/2+i C . i(j-i)/2+1 D . j(i-1)/2+1
设二维数组 A[1.. m , 1.. n] (即 m 行 n 列)按行存储在数
组 B[1.. m*n] 中,则二维数组元素 A[i,j] 在一维数组 B 中的
下标为(
A )。
A . (i-1)*n+j B . (i-1)*n+j-1 C . i*(j-1) D . j*m+i-1
练习
数组 A[0..4,-1..-3,5..7] 中含有元素的个数(B )。
A . 55 B . 45 C . 36 D . 16
广义表 A=(a,b,(c,d),(e,(f,g))) ,则
Head[Tail[Head[Tail[Tail[A]]]]] 的值为(D )。
A . (g) B . (d) C.c D.d
广义表 ((a,b,c,d)) 的表头是(C ),表尾是(
B )。
A.a B.() C . (a,b,c,d) D . (b,c,d)
设广义表 L=((a,b,c)) ,则 L 的长度和深度分别为(
C )。
A.1和1 B.1和3 C.1和2 D.2和2
练习
设任意 n 个整数存放于数组 A(1:n) 中,试编
写算法,将所有正数排在所有负数前面(要
求算法复杂性为 O(n) )。
void Arrange(int A[],int n)
//n 个整数存于数组 A 中,本算法将数组中所有正数排在所有负数的前
面
{
int i=0,j=n-1,x; // 用类 C 编写,数组下标从 0 开始
while(i<j)
{
while(i<j && A[i]>0) i++;
while(i<j && A[j]<0) j--;
if(i<j) {x=A[i]; A[i++]=A[j]; A[j--]=x; }// 交换 A[i] 与
A[j]
}
}// 算法 Arrange 结束 .