学习自小码哥的《恋上数据结构与算法》,图片来自视频截图。
升序
核心思想
快速排序的本质是:将所有的元素逐渐变成轴点元素
什么是 轴点元素?
选择序列中的某个元素,将序列一分为2,小于该元素的放在前面,大于该元素的放在后面,相等的随便前后。那么这个元素就是 轴点元素(pivot)
快速排序的流程:
- 选择一个元素作为pivot。
- 用pivot将序列一分为2。
- 重复1~2直到不能再分割,即子序列元素个数为1(也可以认为此时的子序列中的元素也是自己的pivot)。
代码实现
根据分析核心代码就是上面三步,实现如下:
/**
* 对 [begin,end)范围元素进行快速排序
*
* @param begin
* @param end
*/
private void sort(int begin, int end) {
//获取当前序列的轴点
int mid = pivotIndex(begin, end);
//对左边子序列 快速排序
sort(begin, mid);
//对右边子序列 快速排序
sort(mid + 1, end);
}
比较麻烦的是如何获取轴点。
/**
* 确定 [begin,end)范围内的轴点元素
*
* @return pivot index
*/
private int pivotIndex(int begin, int end) {
//备份begin位置的元素
E pivot = array[begin];
//由于区间是左闭右开的,想要获取最后一个元素需要 end-1
end--;
while (begin < end) {
while (begin < end) {
//先从右往左
if (cmp(pivot, array[end]) < 0) {
//右边元素大于 轴点元素
end--;
} else {
//右边元素小于等于 轴点元素
//将end元素放到begin位置
array[begin++] = array[end];
//切换方向
break;
}
}
while (begin < end) {
//从左往右
if (cmp(pivot, array[begin]) > 0) {
//左边元素 < 轴点元素
begin++;
} else {
//将begin元素放到end位置
array[end--] = array[begin];
//切换方向
break;
}
}
}
//将轴点元素放入最终的轴点位置
array[begin] = pivot;
//返回轴点
return begin;//end==begin
}
复杂度
如果轴点两边数据比较均匀,那么时间复杂度是O(NlogN)。
如果轴点两边数据极度不均匀,那么最坏情况是O(N²)。
优化
为了减少上面所说最坏情况,我们在选择轴点元素时,可以采用随机的方式。代码优化如下:
保持原有的代码不变的情况下只需要将begin换一下:
/**
* 确定 [begin,end)范围内的轴点元素
*
* @return pivot index
*/
private int pivotIndex(int begin, int end) {
//随机选择一个元素与begin交互
swap(begin, begin + (int) Math.random() * (end - begin));
...
保持原有不变
...
return begin;//end==begin
}