殷人昆数据结构(第二版)C++语言描述p428介绍了静态链表排序结果的重排算法,书中用具体的重排实例完整地演绎了重排算法,但是书中没有给出算法的正确性证明,本文完整地描述了重排算法并给出正确性证明。
所谓静态链表排序结果的重排,就是重新安排已完成排序的静态链表中各元素的物理存放顺序,使静态链表中各元素的物理位置关系和逻辑顺序(从小到大)保持一致,整个重排过程不能借助辅助存储空间,即直接在排好序的静态链表上进行操作,实现元素的重排
要证明重排算法的正确性,需要寻找循环不变式并证明每轮循环结束时循环不变式保持不变
算法的完整描述(该伪代码的正确性我没有验证过,如有错误请指出)
输入数据:已完成排序的静态链表L,L[0]为附加头节点,它的link域存放指向链表中已排好序的元素序列中最小元素所在数据单元的指针
初始化:head = L[0].link
for (i from 1 to n-1) //n为被重排的元素序列长度,为静态链表数组长度减一
if (head<=i-1) then
j=head
while(j<=i-1)
j=L[j].link
end while
else
j=head
end if
if (j
=
=
==
==i) then
head=L[i].link
L[i].link=i
else
swap(L[i].data, L[j].data)
head=L[j].link
L[j].link=L[i].link
L[i].link=j
end if
end for
循环不变式是,重排过程中第i轮循环开始时,原排好序的静态链表中最小,第2小,—–第i-1小的元素已顺序存放于当前静态链表数组L[1], L[2],—,L[i-1]存储单元中,head为原排好序的静态链表中第i小的元素在原排好序的静态链表中的下标,L[i], L[i+1],—,L[n]的link域分别为L[i].data, L[i+1].data,—,L[n].data在原排好序的静态链表中的有序元素序列中的后继元素在原排好序的静态链表中的下标
此外,还需要注意算法的执行特点:在第i轮循环中,算法只执行一次交换,这次交换要么是数据单元L[i]和其自身的交换要么是L[i]和其后继数据单元Lj的交换,这次交换是在L[i]上进行的最后一次交换,一旦交换完成L[i]此后就不会发生任何交换,L[i].data值直到算法结束都保持不变,该保持不变的值所在位置下标i就是算法结束后生成的重排结果中该值所在位置下标,即i就是该值重排后在静态链表数组中的位置。了解这一点后就可以着手证明算法的正确性了,证明如下:
显然根据初始化条件,第一轮循环开始时上述循环不变式成立,现设第i轮循环开始时循环不变式成立,此时若
(
h
e
a
d
=
=
j
)
<
=
i
−
1
(head==j)<=i-1
(head==j)<=i−1,那么根据算法的执行特点,L[j]的最后一次交换已经结束,可以断言在L[j]的最后一次交换前L[j]没有发生任何交换,若不然取L[j]最后一次交换前发生的诸交换中最先发生的交换,根据算法的执行特点该交换只能是L[j]和Lbefore的交换,且很显然交换前L[j].data就是原排好序的静态链表中第i小的值,这是因为交换前L[j].data等于原排好序的静态链表中L[j]的data域,而根据循环不变式,j为第i小的元素在原排好序的静态链表中的下标,所以交换前L[j].data就是原排好序的静态链表中第i小的值。而L[j]和L[before]的交换就是L[before]发生的最后一次交换,因此根据算法的执行特点,L[j]和L[before]的交换发生后,第i小的元素值会被放置在L[before].data中,此后L[before].data始终为第i小的元素值,保持不变。这样进入第i轮循环时L[before].data 仍为第i小元素值,但根据循环不变式此时L[before].data应为第before(1<=before<i)小元素值,矛盾,因此在L[j]的最后一次交换前L[j]没有发生任何交换,L[j]的最后一次交换就是L[j]发生的唯一一次交换。另外L[j]的最后一次交换不可能是和其自身的交换,否则L[j].data在最后一次交换交换前的值(根据前文分析它就是第i小的元素值)交换后仍保持不变,根据算法执行特点,该将一直保持不变,直到第i轮循环开始时L[j].data仍为L[j]和自身交换前的原值,于是原值即第i小的元素值在第i轮循环开始时位于静态链表数组的j(1<=j<=i-1)位置,根据循环不变式此时j位置存放的是第j小的元素值,矛盾。因此L[j]的最后一次交换是和它后继项Lm1的交换,若m1<=i-1,那么在L[m1]的最后一次交换前L[j]的最后一次交换后L[m1]没有发生任何交换,否则取L[m1]的最后一次交换前L[j]的最后一次交换后L[m1]最先发生的交换,根据算法的执行特点这一交换是L[m1]和Ltemp的交换,同时也是L[temp]的最后一次交换(该交换发生后L[temp].data为第i小元素值),按照和以上相关分析类似的方法进行分析可知第i轮循环开始时第i小元素值存放于temp位置和循环不变式矛盾,因此在L[m1]的最后一次交换前L[j]的最后一次交换后L[m1]没有发生任何交换,这样L[m1]最后一次交换发生时L[m1].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[m1].link为原排好序的静态链表中L[j].link的值,类似分析知L[m1]的最后一次交换不可能是和其自身的交换,必为和其后继项Lm2的交换,这样L[m1]的最后一次交换完成后L[m2].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[m2].link为原排好序的静态链表中L[j].link的值,若m2<=i-1,那么同理知L[m1]的最后一次交换后L[m2]的最后一次交换前L[m2]没有发生任何交换,这样L[m2]最后一次交换发生时L[m2].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[m2].link为原排好序的静态链表中L[j].link的值,类似分析知L[m2]的最后一次交换不可能是和其自身的交换,必为和其后继项Lm3的交换,这样L[m2]的最后一次交换完成后L[m3].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[m3].link为原排好序的静态链表中L[j].link的值,若m3<=i-1———-
按照这一步骤反复进行下去,我们得到第i轮循环开始前,原排好序的静态链表中L[j]的data,link域通过交换从L[j]移至L[m1],然后从L[m1]移至L[m2],接着从L[m2]移至L[m3]——-
j<m1<m2<m3<—— 且j<=i-1 m1<=i-1 m2<=i-1 m3<=i-1 ———
由于i-1为有限值所以这一过程不可能无限进行下去,所以必存在k使得Lmk最后一次交换发生时L[mk].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[mk].link为原排好序的静态链表中L[j].link的值,类似分析知L[mk]的最后一次交换不可能是和其自身的交换,必为和其后继项Lmk+1的交换,这样L[mk]的最后一次交换完成后L[mk+1].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[mk+1].link为原排好序的静态链表中L[j].link的值,而mk+1>i-1(这里要做一点说明,L[q]和L[s]交换时(q<=s),前者data,link送至后者对应域,后者data送至前者data,后者下标送至前者link,这是上述算法中交换操作的定义,根据以上分析,按照该定义L[j]和L[m1]交换后L[j].link
=
=
==
==m1,L[ms]和Lms+1交换后L[ms].link
=
=
==
==ms+1,按照算法的执行特点,交换后L[j].link和L[ms].link会一直保持不变,因此第i轮循环开始时L[j].link和L[ms].link仍为交换后的值。这样,第i轮循环开始时,我们可以从L[j]开始,循link域向后搜索最终找到L[mk+1]),另外在L[mk]和L[mk+1]交换后,第i轮循环开始之前,L[mk+1]必定没有发生过任何交换(否则取最先发生的交换,它只能是L[mk+1]和Lq的交换,这样L[mk+1].data也就是第i小元素值会被送入L[q].data,通过和上述类似的分析可以导出矛盾),这样第i轮循环开始时L[mk+1].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[mk+1].link为原排好序的静态链表中L[j].link的值,于是此时若mk+1
=
=
==
==i, 令head =L[i].link,这样head为第i+1小元素值在原排好序的静态链表中的存放下标,然后L[i].data不变,将i送入L[i].link,这样做的目的是保证L[i]的操作符合算法中交换操作的定义,这样在i+1轮循环和以后的循环中基于该定义的本文证明才能保持有效,另外不难验证此时i+1轮循环的循环不变式得到满足(循环不变式中也可添加L[1]—L[i-1]的移动操作符合定义,但没有必要,因为在算法中已经体现,是隐含的)。若此时,mk+1>i,则令head=L[mk+1].link,这样head为第i+1小元素值在原排好序的静态链表中的存放下标,然后交换L[i]和L[mk+1]的data,L[i].link送入L[mk+1].link,mk+1送入L[i].link,这样做的理由同上,此时不难验证循环不变式同样得到满足
若
(
h
e
a
d
=
=
j
)
>
i
−
1
(head==j)>i-1
(head==j)>i−1,可以证明第i轮循环开始之前L[j]没有发生任何交换,若不然取最先发生的交换,该交换必然为L[j]和Lq的交换,这样L[j].data即第i大元素值被送入L[q].data,通过和上述类似的分析可以导出矛盾。这样第i轮循环开始时L[j==head].data为第i小元素值(原排好序的静态链表中L[j].data的值),L[mk+1].link为原排好序的静态链表中L[j].link的值,接来进行的是L[j]和L[mk+1]的交换操作细节和理由和上文所述相同,操作结束后循环不变式成立
综上若第i轮循环开始时循环不变式成立,则算法在第i轮循环中执行完毕后,循环不变式仍成立。于是由归纳法,第n-1轮循环结束后循环不变式成立。此时经过一系列交换操作后,最小,第二小,—-,第n-1小的元素值被有序存放在数组单元L[1],L[2],L[n-1]中,数组L[n]中存放的当然为最大元素值,静态链表重排完毕,算法执行结果正确,证毕
链表排序结果的重排算法正确性证明
于 2024-05-28 12:13:56 首次发布