zoukankan      html  css  js  c++  java
  • 排序算法之快速排序

    一、算法思想

      快速排序(Quick Sort)使用分治法策略。它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

    二、算法流程

    (1) 从数列中挑出一个基准值。

    (2) 将所有比基准值小的摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边);在这个分区退出之后,该基准就处于数列的中间位置。

    (3) 递归地把"基准值前面的子数列"和"基准值后面的子数列"进行排序。

     

     

     三、算法实现

     1 //快速排序
     2     static class QuickSort implements Sort {
     3         @Override
     4         public String sortName() {
     5             return "快速排序";
     6         }
     7         @Override
     8         public Comparable[] sort(Comparable[] data) {
     9             int lo = 0, hi = data.length - 1;
    10             sort(data, lo, hi);
    11             return data;
    12         }
    13         //递归体
    14         private static void sort(Comparable[] data, int lo, int hi) {
    15             if(hi <= lo) return;
    16             int partition = partition(data, lo, hi);
    17             sort(data, lo, partition - 1);
    18             sort(data, partition + 1, hi);
    19         }
    20         //分组
    21         private static int partition(Comparable[] data, int lo, int hi) {
    22             //确定分界值
    23             Comparable key = data[lo];
    24             //定义两个指针,分别指向待切分元素的最小索引处和最大索引处的下一个位置
    25             int left = lo, right = hi + 1;
    26             //切分
    27             while(true) {
    28                 //先从左往右扫描,移动right指针,找到一个比分界值小的元素,停止
    29                 while(!Sort.greater(key, data[--right])) {
    30                     if(right == lo) break;
    31                 }
    32                 //再从左往右扫描,移动left指针,找到一个比分界值大的元素,停止
    33                 while(!Sort.greater(data[++left], key)) {
    34                     if(left == hi) break;
    35                 }
    36                 //判断left>=right,如果是,则证明元素扫描完毕,结束循环,如果不是,则交换元素即可
    37                 if(left >= right) {
    38                     break;
    39                 } else {
    40                     Sort.swap(data, left, right);
    41                 }
    42             }
    43             //交换分界值
    44             Sort.swap(data, lo, right);
    45             return right;
    46         }
    47     }

    四、算法分析

    复杂度

    因为快速排序有两个主要操作,所以分析时间复杂度的时候咱们也是从这两个操作着手。

    首先是切割操作,对于长度为 [公式] 的队列,切割操作有两种可能:

    1. [公式] :不执行切割操作,所以时间复杂度为常数 1
    2. [公式] :执行切割操作,因为 [公式] 次比较,所以时间复杂度为 [公式]

    接着是递归操作,设对于长度为 [公式] 的队列递归操作的时间复杂度为 [公式] ,执行切割操作后其中一个子队列的长度为 [公式] 。

    那么根据递归操作的逻辑,咱们可以得出递归操作的时间复杂度计算公式:

    [公式]

    分析完两个主要操作的时间复杂度之后,咱们就可以开始从整体上分析快排的时间复杂度了。快排的时间复杂度分析分三种情况:最坏、最好和平均。

      1. 最坏:每次都只切割出一个子队列,即 [公式] 。这时候每次递归操作的时间复杂度 [公式] ,所以对于长度为 [公式] 的队列来说,快排最坏情况下的时间复杂度 [公式] ,即 [公式]
      2. 最好:每次都对半切割,即 [公式] ,总共会执行 [公式] 次递归。这时候每次递归操作的时间复杂度 [公式] ,而 [公式] ,代入 [公式] 可得
        [公式][公式][公式]
        ……
        [公式][公式]
        所以最好情况下快排的时间复杂度为 [公式]
      3. 平均:每次都随机切割,即 [公式] 。这时候每次递归操作的时间复杂度
        [公式][公式] ,最后得出时间复杂度约为 [公式] 。

    稳定性

    快速排序出于性能上的考虑,牺牲了算法的稳定性。虽然可以改变交换规则,使得算法保持稳定性,但却牺牲了性能,对于一个排序算法而言,这是得不偿失的,除非是上下文有稳定性方面的需求,否则,不建议改变交换规则。

  • 相关阅读:
    数据库存储语句
    数据库练习总结
    数据库练习
    数据库增添
    数据库创建
    cookie 和 session的区别 & 三种传值方式
    内置对象——cookie
    webform跨页面传值
    复合控件
    repeater(控件)数据展示
  • 原文地址:https://www.cnblogs.com/guanghe/p/13625876.html
Copyright © 2011-2022 走看看