快速排序(QuickSort)

284 阅读2分钟

冒泡排序及优化

选择排序(SelectionSort)

堆排序(HeapSort)

插入排序及优化

归并排序(MergeSort)

学习自小码哥的《恋上数据结构与算法》,图片来自视频截图。

升序

核心思想

快速排序的本质是:将所有的元素逐渐变成轴点元素

什么是 轴点元素

选择序列中的某个元素,将序列一分为2,小于该元素的放在前面,大于该元素的放在后面,相等的随便前后。那么这个元素就是 轴点元素(pivot)

快速排序的流程:

  1. 选择一个元素作为pivot。
  2. 用pivot将序列一分为2。
  3. 重复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

    }