在《啊哈算法》一书上的例子是从右开始的,还强调了几次必须。其他书籍教程应该也一样
为什么一定要从右边开始呢?
让我们来试试从左边开始会怎样~
还是用了书上的从小到大排序的例子:
不过我们是从 i 向右走开始,前面几步的 i , j 换位没出现什么问题,图片就不放出来了,下面放了张会出现问题的步骤的图。
i[4] 开始向右走,i 希望寻找一个比基准数 6 大的数,i 走到 3 时,不满足,继续走到 9 ,这时找到了要找的数停了下来。
然后 j[9] 准备,因为 i 和 j 的位置重合了, 所以 j 没有动, 所以数字 9 和基准数 6 的位置交换。
原来的 | 6 | 1 | 2 | 5 | 4 | 3 | 9 | 7 | 10 | 8 |
交换后 | 9 | 1 | 2 | 5 | 4 | 3 | 6 | 7 | 10 | 8 |
👆 | 👆 |
按快排规则,基准数归位后
基准数左边的数都小于等于基准数(6)
基准数右边的数都大于等于基准数(6)
然而,9在最左边,明显左边的数没有都小于等于基准数6,这时就出现了排序错误。
总结
如果从小到大排序的快排从 i 开始向右走,也就是寻找比基准大的数:
while (a[i] <= key && i < j) ++i;
while (a[j] >= key && i < j) --j;
当 i 停下来时,a[i] 的值大于基准数或走到了尽头。
当 i 没有走到尽头,也就是说 i 找到了一个大于基准的数,
接着 j 开始向左走,企图寻找一个小于基准的数,但是存在 i < j 这个条件的限制,j 有可能没找到小于基准的数时就被迫停下来了,此时 i == j 的位置与基准数交换位置,结果就会出现错误。
核心逻辑
从小到大排序的话
基准在左边时:
- 如果从 i 左侧开始向右寻找大于基准的值,当停下来时,这个位置左边可能存在比基准小的值,但是 j 已经被循环条件卡住了,过不来,导致排序异常。
- 如果从 i 右侧开始向左寻找小于基准的值,当停下来时,这个位置右侧可能存在比基准大的值,而这刚好符合“基准数右边的数都大于等于基准数”的逻辑,所以是正确的!
基准在右边时反过来就行。
所以不管基准值在左还是右,只要从它的对面的方向开始寻找,那么算法就是正确的。
只有了解了这个逻辑,疑问就迎刃而解啦!