教科书的经典做法,总是选择 lo 作为基准点,lo 在 [lo, hi] 区间的左边,当跳出大的 while i < j 的时候,交换分界点 arr[x] 和 arr[lo] 的值,让基准点落在左右的分界点上,因为 arr[x] 要交换到左边,那就要求 arr[x] < arr[lo],在这种情况下,只能选择,先从右往左找,具体原因,下面分析。
先从右往左找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| def qsort(arr, lo, hi):
if lo >= hi: return
i = lo j = hi
pivot = arr[lo]
while i < j:
while i < j and arr[j] >= pivot: j -= 1 while i < j and arr[i] <= pivot: i += 1 arr[i], arr[j] = arr[j], arr[i] arr[lo], arr[j] = arr[j], arr[lo]
qsort(arr, lo, i - 1) qsort(arr, i + 1, hi)
|
代码里,L22 就是让基准点 arr[lo] 交换到分界点上。
在这里面,跳出 L12 的 while i < j 可以推出 i = j,即左右指针相遇点即为分界点。
1
| arr[lo], arr[x] = arr[x], arr[lo]
|
其中的 x 就是 i 或 j 的最终值。
主要分析 arr[x] < arr[lo] ?。
L12 要跳出,真实跳出点为 L14 或 L17,因为只有 L14 和 L17 中才有对 i 和 j 的值的更新,下面各自进行讨论。
若在 L14 跳出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| while i < j:
while i < j and arr[j] >= pivot: j -= 1 while i < j and arr[i] <= pivot: i += 1 arr[i], arr[j] = arr[j], arr[i]
|
根据上面代码块的 L4,L6 可知,arr[j] < pivot 符合 arr[x] < arr[lo] 的条件,可进行交换。
若在 L17 跳出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| while i < j:
while i < j and arr[j] >= pivot: j -= 1 while i < j and arr[i] <= pivot: i += 1 arr[i], arr[j] = arr[j], arr[i]
|
根据上面代码块的 L5,L9 可知,arr[i] < pivot 符合 arr[x] < arr[lo] 的条件,可进行交换。
先从左往右找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| def qsort(arr, lo, hi):
if lo >= hi: return
i = lo j = hi
pivot = arr[lo]
while i < j:
while i < j and arr[i] <= pivot: i += 1
while i < j and arr[j] >= pivot: j -= 1 arr[i], arr[j] = arr[j], arr[i] arr[lo], arr[j] = arr[j], arr[lo]
qsort(arr, lo, i - 1) qsort(arr, i + 1, hi)
|
首先说明,在基准点选择 lo 的情况下,先从左往右找,最终结果是不对的。
同样进行分析,主要还是跳出 L12 的 while i < j,真实跳出点为 L14 或 L17,各自进行讨论。
若在 L14 跳出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| while i < j:
while i < j and arr[i] <= pivot: i += 1
while i < j and arr[j] >= pivot: j -= 1 arr[i], arr[j] = arr[j], arr[i]
|
根据上面代码块的 L4,L6 可知,arr[i] > pivot 不符合 arr[x] < arr[lo] 的条件,不可进行交换。
若在 L17 跳出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| while i < j:
while i < j and arr[i] <= pivot: i += 1
while i < j and arr[j] >= pivot: j -= 1 arr[i], arr[j] = arr[j], arr[i]
|
根据上面代码块的 L5,L9 可知,arr[j] > pivot 不符合 arr[x] < arr[lo] 的条件,不可进行交换。
观察可发现,先从左往右,得到的跳出点的值 arr[x] > pivot,如果交换的话可以往右交换,也就是说,基准点可以选择 arr[hi],这样结果就是对的了。
关于随机基准点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| def qsort(arr, lo, hi):
if lo >= hi: return
i = lo j = hi
rand = random.randint(lo, hi) arr[lo], arr[rand] = arr[rand], arr[lo]
pivot = arr[lo]
while i < j:
while i < j and arr[i] <= pivot: i += 1
while i < j and arr[j] >= pivot: j -= 1 arr[i], arr[j] = arr[j], arr[i] arr[lo], arr[j] = arr[j], arr[lo]
qsort(arr, lo, i - 1) qsort(arr, i + 1, hi)
|
结论
| 基准点 |
交换条件 |
顺序 |
| arr[lo] |
arr[x] < arr[lo] |
先从右往左找 |
| arr[hi] |
arr[x] > arr[hi] |
先从左往右找 |