pivot的选取方式
- 取第一个元素或最后一个元素(最坏情况:数组已经排好序)
- 取首个元素、中间元素、最后一个元素的中位数(三数取中,不是平均数!!)
- 随机取一个元素
pivot选取禁忌
- 如果对连续的两个子区间排序,不能选择大于或小于所有数组中的数。因为同时满足这两个条件可能会导致无限循环。假设
parition()
方法返回一个分割点p
,那么array[left, p]
是小于等于pivot
的,array[p + 1, right]
是大于等于pivot
的,但是array[p]
与它左右两边元素的大小关系是不能确定的,所以我们只能递归地去排序这两个相连的子区间,而当p == left
时,就会无限循环。例如,考虑这样一个数组{1, 1, 1}
,选择pivot = 三个数的平均数 = 1
,返回的划分为[0, 2]
和[3, 2]
= Ø,那么继续对{1, 1, 1}
进行递归排序……这样无限循环下去了。
- 如果
pivot
不是数组中的元素,partition
的算法就会很麻烦、容易出bug,所以有“三数取中”而没有“三数取平均”
partition的方式
Lomuto Scheme
- 从头到尾扫,把小于pivot的都交换到divideIndex的前面
- partition返回的位置是pivot的最终位置
- 最后对两个距离为1的子区间排序
- 最坏情况(可能StackOverflow)
- 所有元素相等
- 选择第一个或最后一个元素做pivot + 数组已经排好序
- 巧合:每次都做最坏的划分
private int partition(int[] array, int left, int right, int pivotIndex) {
int pivotValue = array[p