(搜索已排序的紧凑链表)练习9-2问题2讨论了如何将 n 个元素的链表紧凑地维持在数组的前 n 个位置。假设所有的关键字均不相同,且紧凑链表是已排序的,即对所有的 i=1,2,⋯,n 且 next[i]=NIL ,有 key[i]<key[next[i]] 。又假设有一个变量 L 存放链表的首元素的下标。在这些假设下,试说明可以利用下列随机算法在 O(n) 的期望时间内搜索链表。
如果忽略过程中第 3 ~ 7 行,就得到一个普通的搜索已排序链表的算法,其中下标 i 依次指向链表的各个位置。当下标 i 越出表的末端或 key[i]≥k 时,搜索终止。在后一种情况中,如果 key[i]=k ,显然,我们已找到值为 k 的关键字。但如果 key[i]>k ,则我们永远也找不到值为 k 的关键字,因而终止查找是正确的。
第 3 ~ 7 行意图向前跳至某个随机选择的位置 j 。当 key[j] 大于 key[i] 而不大于 k 时,这种跳跃是有益的。因为这种情况下, j 在链表中标识了一个正常搜索中 i 将要到达的位置。由于该链表是紧凑的,所以在 1 到 n 中任意选择一个 j 都会指向链表中的某个对象,而不会是自由表中的某个位置。
我们不直接分析 COMPACT-LIST-SEARCH 的性能,而是要分析一个相关的算法 COMPACT-LIST-SEARCH’ ,该算法执行两个独立的循环。该算法增加了一个参数 t ,用来决定第一个循环迭代次数的上限。
假设 COMPACT-LIST-SEARCH(L,n,k) 中第 2 ~ 8 行的 while 循环经过了 t 次迭代。论证 COMPACT-LIST-SEARCH’(L,n,k,t) 会返回同样的结果,且 COMPACT-LIST-SEARCH’ 中的 for 循环和 while 循环的迭代次数之和至少为 t 。
在 COMPACT-LIST-SEARCH’(L,n,k,t) 的调用中,设随机变量 Xt 描述了第 2 ~ 7 行的 for 循环经 t 次迭代后链表中从位置 i 到目标关键字 k 所在位置之间的距离(即通过 next 指针链的次数)。
如果 COMPACT-LIST-SEARCH 是通过第 7 行的 return 语句跳出循环的,也就是通过第 t 次“随机步”找到目标的,那么 COMPACT-LIST-SEARCH’ 也会在第 t 次“随机步”找到目标,从而也会通过第 7 行的 return 语句终止,从而 for 循环迭代了 t 次, while 循环迭代了 0 次,一共迭代了 t 次, t≥t 。
如果 COMPACT-LIST-SEARCH 是通过第 2 行的 while 循环条件跳出循环的,也就是通过第 t 次“正常步”找到目标的,那么说明目标处于第 t 次“随机步”对应位置的下一个位置,因此 COMPACT-LIST-SEARCH’ 在 for 循环处运行了 t 次“随机步”之后,在第 8 ~ 9 行的 while 循环处只会运行 1 次“正常步”,从而一共迭代了 t+1 次, t+1≥t 。
综上,COMPACT-LIST-SEARCH’ 中的 for 循环和 while 循环的迭代次数之和至少为 t 。
从第 1 问的分析中我们已经知道,算法 COMPACT-LIST-SEARCH’(L,n,k,t) 先执行 t 轮“随机步”,然后再不断执行“正常步”从 t 轮“随机步”给出的离目标最近的位置 i 的位置到目标关键字 k ,也就是 Xt 步。因此该算法 for 循环和 while 循环 一共执行了 t+Xt 轮迭代,每轮迭代都是常数项时间,所以运行时间为 O(t+Xt) ,从而期望运行时间为
O(E[t+Xt])=O(t+E[Xt])
Xt 是 for 循环结束后 i 到目标 k 所在位置的距离,
所以其取值范围为 [0,n−1] ,根据提示的公式不难有
E[Xt]=r=1∑nPr{Xt≥r}
因此,只需要说明 Pr{Xt≥r}≤(1−r/n)t 即可。
Xt≥r 说明每次随机选择的位置都不在 k 之前的 r 个位置上,一共 n 个位置,随机选择不在其中 r 个固定位置的概率为 1−r/n ,每次随机选择都是独立的,因此根据独立时间概率的乘法公式, t 次随机选择都不在的概率为 (1−r/n)t ,即