简单通俗易懂学算法——十大常用排序算法(快速排序)

本文介绍了快速排序的基本思想,使用分治法将序列分为两部分并递归排序。内容包括快排的直观理解、时间复杂度分析及Java代码实现,通过逐步优化展示排序过程。

快速排序:是对冒泡排序的一种改进。

先说下快排的一个基本思想

使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。

就是通过一躺排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一不部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

大喊一声,nb! 

一个图:看懂很容易,想出来是真不简单 

Sorting quicksort anim.gif

时间复杂度: 

最坏时间复杂度O(n ^{2})
最优时间复杂度O(n log n)
平均时间复杂度O(n log n)

 

一个百科!!!

代码实现:

排序的算法:

/**
 * 快速排序算法
 *
 * @param arr   待排序的数组
 * @param low   排序数组的起始位置
 * @param hight 排序的结束位置
 */

public static void quickSortV1(int[] arr, int low, int hight) {
    if (low < hight) {
        // 算出枢轴值
        int pivot = partitionV1(arr, low, hight);

        // 对小的数组进行递归排序
        quickSortV1(arr, low, pivot - 1);
        // 对大的数组进行递归排序
        quickSortV1(arr, pivot + 1, hight);
    }
}
// 简单的数组元素位置交换
private static void swap(int[] arr, int low, int hight) {
    int t = arr[low];
    arr[low] = arr[hight];
    arr[hight] = t;
}

排序的关键算法:

/**
 * 快速排序算法关键算法
 *
 * @param arr   排序的数组
 * @param low   低位的下标
 * @param hight 高位的下标
 * @return 枢轴的下标索引
 */
private static int partitionV1(int[] arr, int low, int hight) {
    // 枢轴(就是一个基数而以)
    int pivotkey = arr[low];

    while (low < hight) {
        // 找到不比枢轴元素大的元素位置
        while (low < hight && arr[hight] >= pivotkey) {
            hight--;
        }
        // 交换元素位置,使得比枢轴大的元素位置都在右边
        swap(arr, low, hight);
        
        // 找到不比枢轴元素小的元素位置
        while (low < hight && arr[low] <= pivotkey) {
            low++;
        }
        // 交换元素位置,使得比枢轴小的元素位置都在左边
        swap(arr, low, hight);
    }
    
    // 返回枢轴元素的下标位置。
    return low;
}

对上面代码进行优化: 

优化后的排序算法:

public static void quickSortV2(int[] arr, int low, int hight) {
    int pivot;

    // 在调用的函数递归的时候,这个low和hight是在动态的变化的
    // 也就是说会出现,第一次插入排序完了之后,low和hight的值会变化,排序的长度小于这个数组阀值的话会进行快速排序。
    // 所以,在数据个数并不是很多的情况下,就直接用quickSortV1 + partitionV2的实现就行了,加上还得额外判断,反而慢了
    if ((hight - low) >= MAX_ARRAY_LENGTH) {
        while (low < hight) {
            // 算出枢轴值
            pivot = partitionV2(arr, low, hight);

            /*
             * 如果待排序的序列划分的极端不平衡,递归的深度将趋近于n. 
             * 递归会耗费一定的栈空间,参数越多,耗费的空间也就越多.
             * 通过减少递归,缩减堆栈的深度的,来提升整体性能.
             */

            // 对小的数组进行递归排序
            quickSortV2(arr, low, pivot - 1);
            // 尾递归
            low = pivot + 1;
        }

    } else {
        // 当数组(元素个数)很小的时候,快速排序反而不如插入排序了。
        // 这里得加1,因为参数2是待排序序列的结束下标位置 + 1
        InsertSort.binInsertSort(arr, low, hight + 1);
    }
}

优化后的快排关键算法:

/**
 * partitionV1()的改进,优化不必要的交换
 * 
 * @param arr   待排序的数组
 * @param low   低位是下标索引
 * @param hight 高位的下标索引
 * @return 枢轴的下标位置
 */
private static int partitionV2(int[] arr, int low, int hight) {
    // -------枢轴选取的改进,使用3数取中。-----------------
    int mid = (low + hight) >>> 1;
    // 保证左边元素为小的
    if (arr[low] > arr[hight]) {
        swap(arr, low, hight);
    }
    // 保证右边元素为大
    if (arr[mid] > arr[hight]) {
        swap(arr, mid, hight);
    }
    // 保证low为中间值下标
    if (arr[mid] > arr[low]) {
        swap(arr, mid, low);
    }
    // ---------------------------------------------

    // 枢轴(就是一个基数而以)
    int pivotkey = arr[low];

    while (low < hight) {
        // 找到不比枢轴元素大的元素位置
        while (low < hight && arr[hight] >= pivotkey) {
            hight--;
        }
        // 替换元素,使得比枢轴大的元素位置都在右边
        arr[low] = arr[hight];
        
        // 找到不比枢轴元素小的元素位置
        while (low < hight && arr[low] <= pivotkey) {
            low++;
        }
        // 替换元素,使得比枢轴小的元素位置都在左边
        arr[hight] = arr[low];
    }
    
    arr[low] = pivotkey;
    // 返回枢轴元素的下标位置。
    return low;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值