再谈线性表——顺序分配
Yea,from the table of my memory
I'll wipe will all trivial fond records.
是的,我要从我的记忆中
抹去我喜爱的一切微不足道的印象。
UNDERFLOW和OVERFLOW
在出现UNDERFLOW的时候,说明我们正试图删去一个不存在的项——这通常是一个有意义的操作——不是一个出错的情况——我们可以用它来支配一个程序的流动。
例如:我们可能要重复地删除线性表中的项,直到出现UNDERFLOW为止,
然后出现OVERFLOW的情况,通常意味着这是一个错误——它意味着线性表已经满了,但仍然有更多等待被放入线性表的信息。
在OVERFLOW的情况下,通常的对策是直接报错,由于线性表的存储能力已经到达极限,程序不能再往里面放入更多的信息,因此程序不能继续执行,于是程序终止。
多个线性表
程序不可能只有一个线性表——就像计算机内存不可能只有栈一个内存区,当只有一个线性表变得太大,而其他线性表还剩下有大量的空间可以使用时,我们当然不愿意在一遇到OVERFLOW的情况下就终止程序。在上面的讨论中,我们主要是考虑仅有一个线性表的程序。然而,我们经常会遇到涉及好些个栈的程序(这里的栈指的是数据结构,而不是计算机内存的栈区),每个栈的大小都是动态变化的,在这种情况下,我们不要对每个栈都强加一个极大的容量——因为这个容量通常是不可预测的,而且即使赋予每个栈一个极大的容量,我们将会发现,很少会有所有栈都同时装满它们的极大容量。
该怎么办
当我们的程序恰好有两个大小可变的线性表时,我们可以令这两个线性表相向增长,则它们可以很好地共存:
在这里,表1向右扩展,而表2向左扩展(以相反方向存储)。除非两个表的总容量穷尽所有存储空间,否则OVERFLOW将不再出现。这些表可以独立地进行扩展或收缩,使得每一个表的最大有限容量都要大大超过可用空间的一半,存储空间这种布局使用得比较频繁。
我们在这里可以很轻易地得到一个结论:没有办法在内存中存储三个或者三个以上大小可变的线性表,使得(a)仅当所有表的总容量超出总的存储空间时,才出现OVERFLOW,以及(b)每个表的底部元素都存储在一个固定的内存单元中。比如说,当有十个大小可变的线性表时,内存的分配问题将会变得十分重要。如果我们希望满足(a),就必须放弃(b),即允许这些线性表的"底部"元素改变它们的内存位置。
这意味着,
L
O
C
(
X
[
j
]
)
=
L
0
+
c
j
LOC(X[j]) = L_0 + cj
LOC(X[j])=L0+cj这个等式的
L
0
L_0
L0将不再是常数。
由于所有的访问都是相对于基地址
L
0
L_0
L0进行的,因此对于线性表的访问也就不能做成是对于一个绝对的存储地址进行的。为了应对这种变化,通常情况下,会使用相对寻址,哪怕相对寻址将明显地比固定基地址寻址花费更长的时间。
线性表是栈
当每个可变大小的线性表都是栈时,出现一个重要的特殊情况。这个时候,由于栈永远是后进先出的,所以在任何时刻,只有栈的顶部元素是跟我们的操作有关系的,因此我们可以像以前一样高效地进行操作。
假设我们有
n
n
n个栈,如果对于第
i
i
i个栈,
B
A
S
E
[
i
]
BASE[i]
BASE[i]和
T
O
P
[
i
]
TOP[i]
TOP[i]是链接变量,而且每个节点都是一个字长,则插入和删除算法就会变成:
插入:
T
O
P
[
i
]
⇐
T
O
P
[
i
+
1
]
,如果
T
O
P
[
i
]
大于
B
A
S
E
[
i
+
1
]
,则
O
V
E
R
F
L
O
W
;
插入:TOP[i] \Leftarrow TOP[i+1],如果TOP[i]大于BASE[i+1],则OVERFLOW;
插入:TOP[i]⇐TOP[i+1],如果TOP[i]大于BASE[i+1],则OVERFLOW;
否则置
C
O
N
T
E
N
T
S
(
T
O
P
[
i
]
)
⇐
Y
否则置CONTENTS(TOP[i])\Leftarrow Y
否则置CONTENTS(TOP[i])⇐Y 。
(
9
)
_(9_)
(9)
删除:如果
T
O
P
[
i
]
=
B
A
S
E
[
i
]
,则
U
N
D
E
R
F
L
O
W
,否则,置
删除:如果TOP[i]=BASE[i],则UNDERFLOW,否则,置
删除:如果TOP[i]=BASE[i],则UNDERFLOW,否则,置
Y
⇐
C
O
N
T
E
N
T
S
(
T
O
P
[
i
]
)
,
T
O
P
[
i
]
⇐
T
O
P
[
i
]
−
1
Y\Leftarrow CONTENTS(TOP[i]),TOP[i]\Leftarrow TOP[i] - 1
Y⇐CONTENTS(TOP[i]),TOP[i]⇐TOP[i]−1。
(
1
0
)
_(10_)
(10)
在这里, B A S E [ i + 1 ] BASE[i+1] BASE[i+1]是第 i + 1 i+1 i+1个栈的基地址,条件 B A S E [ i ] = T O P [ i ] BASE[i] = TOP[i] BASE[i]=TOP[i]意味着栈为空。 C O N T E N T S ( T O P [ i ] ) CONTENTS(TOP[i]) CONTENTS(TOP[i])表示在内存位置 T O P [ i ] TOP[i] TOP[i]中存放的值。
在 ( 9 ) _(9_) (9)中,OVERFLOW不再是一个危机了,因为我们可以 重新组装存储器 重新组装存储器 重新组装存储器,从还未填满的线性表中腾出空间给已经溢出的线性表,进行存储重装有许多方法,它们也都各有特点,我们现在将详细地考虑其中的某一部分,因为当对线性表进行顺序分配时,它们可能是十分重要的。我们将从最简单的方法开始,然后再考虑更复杂的方法。