(Hoare划分的正确性)本章中的PARTITION算法并不是其最初的版本。下面给出的是最早由C. R. Hoare所设计的划分算法:
a. 试说明HOARE-PARTITION在数组A = {13, 19, 9, 5, 12, 8, 7, 4, 11, 2, 6, 21}上的操作过程,并说明在每一次执行第4~14行while循环时数组元素的值和辅助变量的值。
后续的三个问题要求读者仔细论证HOARE-PARTITION的正确性。在这里假设子数组
至少包含2个元素,试证明下列问题:
b. 下标
和
可以使我们不会访问在子数组
以外的数组
的元素。
c. 当HOARE-PARTITION结束时,它返回的值
满足
。
d. 当HOARE-PARTITION结束时,
中的每一个元素都小于或等于
中的元素。
在7.1节的PARTITION过程中,主元(原来存储在
中)是与它所划分的两个分区分离的。与之对应,在HOARE-PARTITION中,主元(原来存储在
中)是存在于分区
或
中的。因为有
,所以这一划分总是非平凡的。
e. 利用HOARE-PARTITION,重写QUICKSORT算法。
解
a.
b.
虽然下标
和
分别被初始化为
和
,但是在第一轮while迭代中,
会被先加1,而
会被先减1,这样下标
和
分别从
和
开始。所以在迭代开始,下标
和
没有超出数组范围。
由于以首个元素作为划分主元,所以在第一轮while迭代中,
会停留在
,而
会一直向左移动,直到遇到一个小于或等于
的元素。下面分两种情况说明:
1) 如果
在向左移动的过程中一直没有遇到小于或等于
的元素,那么
会一直向左移动直到
为止,因为
满足
的停止条件。此时,由于
一直停留在
的位置,所以
,故while迭代退出。在这个过程中,
和
都没有超出数组
的范围。
2) 如果
遇到了一个小于或等于
的元素 (该元素不为
),那么
与
(
即
) 交换。这说明在下标
的左侧至少有一个小于或等于
的元素,并且在下标
的右侧至少有一个大于或等于
的元素,这可保证
和
在移动过程中不会超出数组范围。
c.
在第一轮while迭代中,下标
一定会停留在
,而下标
分2种情况:
1) 如果
在
位置停留下来了,此时有
(因为已经假设数组元素不少于2个),那么交换
和
,继续进行第二轮while迭代。在第二轮while迭代中,
一定至少向左移动一个位置,故一定有
。
2) 如果
没有在
位置停留,而是继续向左移动,此时也有
。
再根据b的结论,
不会超出数组范围,故
。所以
一定满足
。
d.
先给出循环不变式:在每轮while迭代开始之前,
中的元素都小于或等于
,并且
中的元素都大于或等于
。这个循环不变式应该不难证明,这里省略证明过程。
下面分析while迭代的终结时的情况。在while迭代中,代码第5 ~ 7行的repeat迭代和第8~10行的repeat迭代结束时,必然有
中的元素都小于或等于
中的元素。在最后一次while迭代中,repeat迭代结束后,必然有
或
。如果
,那么必然有
,由于
中的元素都小于或等于
中的元素,将
加入
,于是有
(即
) 中的元素都小于或等于
中的元素。如果
,那么
,于是有
中的元素都小于或等于
中的元素。
e.
代码链接:https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter07/Problem_7-1