快速排序(Quick Sort)介绍

原理介绍

快速排序是分治法(Divide and Conquer)的典型应用,基本思想是在数组中适当的轴心(partition),然后将数组一分为二,再分别对左边和右边数组进行排序,而影响快速排序法效率的正是轴心的选择。

快速排序根据分割点的不同,有三种版本:

选择最左端的数为比较对象,低指针从左边开始向右移动,高指针从右边开始向左移动;

选择中间的数为比较对象,低指针从左边开始向右移动,高指针从右边开始向左移动;

选择最右端的数为比较对象,低指针和高指针都从左端开始移动。

 

快排的三种版本

下面分别介绍三种快速排序的版本。

第一种版本:

(1)将最左边的数设定为轴,其值为val

循环:

         (2)令索引i 从数组左方往右方找,直到找到大于 val 的

         (3)令索引 j 从数组右方往左方找,直到找到小于 val 的数

         (4)如果 i >= j,则离开循环,否则,交换索引 i 与 j 两处的值

         (5)将左侧的轴与 j 进行交换

(6)对轴左边(j-1)的数组进行递归处理

(7)对轴右边(j+1)的数组进行递归处理






   public static void QuickSort(int[] a, int lo, int hi)
   {
      if (lo >= hi) return;
      int i = lo;
      int j = hi + 1;
     
      while (true)
      {
         while ((a[++i] < a[lo]))
            if (i == hi) break;
         while ((a[--j] > a[lo]))
            if (j == lo) break;
         if (i >= j) break;
         exch(a, i, j);
      }    
      exch(a, lo, j);
     
      QuickSort(a, lo, j-1);
      QuickSort(a, j+1, hi);
   }


第二种版本:

(1)将中间的数val设为轴

循环:

         (2)令索引i 从数组左方往右方找,直到找到大于 val 的

         (3)令索引 j 从数组右方往左方找,直到找到小于 val 的数

         (4)如果 i >= j ,则离开循环,否则,交换索引 i 与 j 两处的值

(5)对轴左边(i-1)的数组进行递归处理

(6)对轴右边(j+1)的数组进行递归处理

边界正确性证明:

(1)若 i == j ,则 i-1 和 j+1 正好是某个数两边的界限

(2)若 i > j ,则不难知道 i-1 == j (即 i == j+1),则 i-1 与 j+1 正好是两个子数组的边界

 

   public static void QuickSort (int[] a, int lo, int hi)
   {
      if (lo >= hi) return;
      int i = lo-1, j = hi+1;
      int mid = (lo + hi) / 2;
      int val = a[mid];
     
      while (true)
      {
         while (a[++i] < val)
            if (i == hi) break;
         while (a[--j] > val)
            if (j == lo) break;
         if (i >= j) break;
         exch(a, i, j);
      }
     
      QuickSort(a, lo, j);
      QuickSort(a, j+1, hi);
   }


 

第三种版本:

(1)将最右边的数 val 设为轴

循环:

        

         (2)令索引 j 从数组左方开始往右方找,直到找到小于 val的数

         (3)若 j 小于高位hi ,则交换 i 和 j 位置的元素,然后 i++

         (4)若j 大于或等于高位hi ,则退出循环

            

(5)交换 i位置的数和最右边的数


(6)对 i-1 左边的数组进行递归排序

(7)对i+1 右边的数组进行递归排序


 

  public static void QuickSort (int[] a, int lo, int hi)
   {
      if (lo >= hi) return;
      int i = lo, j = lo-1;
      int val = a[hi];
     
      while (true)
      {
         while (a[++j] >= val)
            if (j == hi) break;
         if (j >= hi) break;
         exch(a, i++, j);
      }
      exch(a, i, hi);
     
      QuickSort(a, lo, i-1);
      QuickSort(a, i+1, hi);
   }


含有重复键(duplicate key)的快排程序

若序列中含有大量重复键,若使用前面三种方法进行排序,会使得重复键参与到下一次排序中,极大地降低了排序算法的效率。Robert Sedgewick 提出了一种方法,使得重复键不参与下一次的排序中,提升了算法的效率。不过键交换的次数会增多。

算法的步骤如下:

(1)设定三个指针分别为lt,i,gt。其中,在指针lt左边的数组值小于val,在lt到i之间的数组值等于val,在gt右边的数组值大于val,在i到gt之间的值是未处理的。

  




循环(i <= gt时):

         (2)当索引i处的值小于val时,交换i 和lt的值

         (3)当索引i处的值大于val时,交换i 和gt的

         (4)否则(当索引i处的值等于val时,i自增

(5)对lt左边的数组进行排序

(6)对gt右边的数组进行排序

 

   public static void QuickSort_DuplicateKeys(int[] a, int lo, int hi)
   {
      if (lo >= hi) return;
      int lt = lo, gt = hi;
      int i = lt;
      int val = a[lo];
     
      while (i <= gt)
      {
         if (a[i] < val)
            exch(a, i++, lt++);
         else if (a[i] > val)
            exch(a, i, gt--);
         else i++;
      }
     
      QuickSort_DuplicateKeys(a, lo, lt-1);
      QuickSort_DuplicateKeys(a, gt+1, hi);
   }


快排的特性

1.快排是一种内排序算法

2.快排时间复杂度的最好情况是~NlgN,最坏情况是~(1/2)*N^2,平均情况是~1.39NlgN

3.快排的空间复杂度:对于Partitioning:需要常数级的额外空间;对于递归的深度:需要对数级的额外空间

 

快排的优化

1.在JDK API的数组排序算法中,会先对数组的长度进行判断。若是长度较短的数组,则直接使用插入排序。对于较长的数组才使用快速排序。

2.Robert Sedgewick认为在快速排序之前,应先对数组进行Shuffle操作,以提高快速排序的性能。因为在最坏的情况下,快排的时间复杂度是~(1/2)*N^2。而平均情况下,快排的时间复杂度是~1.39NlgN


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值