数据结构:排序之快速排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40042143/article/details/87896023

基本思想

快速排序:在每轮排序中,选取一个基准元素,通过一趟排序将待排记录分割成独立的两个部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序的目的。

核心代码对于数组a中从下标为low至下标为high的元素,选取一个基准元素(pivotKey),根据与基准比较的大小,将这些元素排到基准元素的两端。

如何选择基准元素

最简单的是选择数组的第一个元素,but如果对于原本是逆序的数组,期望排成正序,就会有问题,如下图

                                                                图片来自程序员小灰

数组并没有分成一半一半的,在这种极端情况下,快排需要进行n轮,时间复杂度O(n*n)

如何避免:随机选择一个元素作为基准元素

接下来进行比较和移动操作

元素移动

挖坑法

package sort_algorithms;

/**
 * 快速排序
 */
public class QuickSort {
    public static void quickSort(int[] arr, int startIndex, int endIndex) {
        if (startIndex >= endIndex)
            return;
        //基准元素位置
        int pivotIndex = partition(arr, startIndex, endIndex);
        //使用分治方法递归数组的两部分
        quickSort(arr, startIndex, pivotIndex-1);
        quickSort(arr, pivotIndex + 1, endIndex);

    }

    public static int partition(int[] arr, int startIndex, int endIndex) {

        //获取第一个位置的元素作为基准元素
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;

        //坑的位置,初始等于pivot的位置
        int index = startIndex;
        while (right >= left)//大循环在左右指针重合或者交错的时候结束
        {
            while (right >= left)//right指针从右向左进行比较
            {
                if (arr[right] < pivot) {
                    arr[left] = arr[right];
                    index = right;
                    left++;
                    break;
                }
                right--;

            }

            //left指针从左向右进行比较
            while (right >= left) {
                if (arr[left] > pivot) {
                    arr[right] = arr[left];
                    index = left;
                    right--;
                    break;
                }
                left++;
            }
        }
        arr[index] = pivot;
        return index;
    }
}

指针移动法

和挖坑法相比,区别在于partition算法上面

    public static int partition_2(int[] arr, int startIndex, int endIndex) {
        //获取第一个位置的元素作为基准元素
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;

        while (left < right) {//两侧交替向中间扫描
            //控制right指针比较并向左移动
            while (left <= right && arr[right] >= pivot) {
                right--;
            }
            change(arr, left, right);//比基准小的元素放在left
            //控制left指针比较并向右移动
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            change(arr, left, right);//比基准大的元素放在right

        }

        return left;//返回基准元素所在位置
    }

复杂度分析

时间效率

                快排的运行时间依赖于划分是否平衡,根据指定的基准元素将数组划分为两个子序列的元素个数。

                 最坏的情况:每次进行划分时,在所得的子序列中有一个为空..即待排的序列本身已经有序或逆序,快速排序的时间复杂度为O(n*n)

                 最好情况:每次划分时都将序列一分为二,正好在序列中间将序列分成长度相等的两个子序列。

空间效率

          快排需要一个堆栈来实现递归。若每次划分都将序列均匀分割为长度相近的两个子序列,则堆栈的最大深度为

,but,在最坏的情况下,堆栈的最大深度为n.

快排优化

1. 基准元素的选择优化

        随机选取:随机获得left和right之间的一个随机数,让此关键字与letf的关键字进行交换-------可以解决基本有序的序列快排时的瓶颈

        三数取中(median-of-three):取三个关键字先进行排序,将中间数作为基准元素,一半取left,right和中间三个数,也可以随机选取。

                                       图片来源于:https://www.cnblogs.com/chengxiao/p/6262208.html

public static int partition_3(int[] arr, int startIndex, int endIndex) {

        int left = startIndex;
        int right = endIndex;
        int middle = left + (right - left) / 2; //计算数组中间元素的下标

        //交换左右两端数据,保证左端较小
        if (arr[left] > arr[right]) {
            change(arr, left, right);
        }
        //交换中间和右端的数据,保证中间的较小
        if (arr[middle] > arr[right]) {
            change(arr, middle, right);
        }
        //交换中间和左端的数据,保证左端较小
        if (arr[left] > arr[middle]) {
            change(arr, left, middle);
        }

        int pivot = arr[left];
        arr[0] = pivot;

        while (left < right) {
            //控制right指针比较并向左移动
            while (left <= right && arr[right] >= pivot) {
                right--;
            }
            arr[left] = arr[right];//替换
            //控制left指针比较并向右移动
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            arr[right] = arr[left];

        }
        arr[left] = arr[0];//将基准替换回arr[left]
        return left;

    }

猜你喜欢

转载自blog.csdn.net/weixin_40042143/article/details/87896023