快速排序

  • 快速排序的切分
  • 完整的快速排序
  • 三向切分的快速排序(快排的改进版)

快速排序是目前使用最广泛的排序,同时也是目前最快的排序,它也体现了分治的思想:将数组分成两部分,并分别独立地进行排序,和归并排序不同的是,归并排序是将两个有序的数组合并为一个有序的大数组快排则是当小数组有序时, 大数组就自然有序了

快速排序是用一个数v将数组切分v左边的数全都小于v,,v右边的数全都大于v, 在将小数组继续切分, 直到不能再分为止, 这样数组就有序了。

快速排序的切分

一般取第一个元素(lo)为切分元素,
然后从左(lo+1)开始扫描,直到找到一个大于等于切分元素的元素;
从右(hi)开始扫描,直到找到一个小于等于切分元素的元素;
每次扫描完,左边找到了大的元素,右边的找到了小的元素,都要交换左右元素,变成左小右大
当 i,j 相遇时(i >= j)退出主循环。
由于 i 与 j 已经相遇,经过最后一次++i,–j,j 已经到达了左区域,所以 j 的元素小于切分元素,将 j 的元素与切分元素交换,则构成 a[lo…j-1] <= a[j] <= a[j+1…hi]

public static int partition(Comparable[] a, int lo, int hi)
{   // 将数组切分为 a[lo...i-1], a[i] 和 a[i+1...hi]int i = lo;             // 从左(lo+1)开始扫描int j = hi+1;           // 从右(hi)开始扫描Comparable v = a[lo];   // 选取第一个元素为切分元素while (true){   // 扫描左右,检查扫描是否结束并交换元素while (less(a[++i], v))     // 从左开始扫描,直到找到一个大于等于切分元素的元素if (i == hi)break;while (less(v, a[--j]))     // 从右开始扫描,直到找到一个小于等于切分元素的元素if (j == lo)break;if (i >= j)         // i,j 相遇则时退出主循环break;exchange(a, i, j);  // 每次左边找到大的元素,右边找到小的元素,交换,则变成左小右大}exchange(a, lo, j);     // 由于此时i与j已经相遇,经过最后一次++i,--j,j已经到达了左区域,所以j的元素小于切分元素return j;               // a[lo...j-1] <= a[j] <= a[j+1...hi]
}

完整的快速排序

完整的快速排序代码:

public class QuickSort {/*** 比较大小* */public static boolean less(Comparable m, Comparable n){return m.compareTo(n) < 0;}/*** 交换元素* */public static void exchange(Comparable[] a, int m, int n){Comparable temp = a[m];a[m] = a[n];a[n] = temp;}/*** 展示元素* */public static void show(Comparable[] a){for (int i = 0 ; i < a.length ; i++){System.out.print(a[i] + " ");}System.out.println();}/*** 是否排序成功* */public static boolean isSorted(Comparable[] a){for (int i = 1 ; i < a.length ; i++){if (less(a[i], a[i-1]))return false;}return true;}/*** 快速排序的切分* */public static int partition(Comparable[] a, int lo, int hi){   // 将数组切分为 a[lo...i-1], a[i] 和 a[i+1...hi]int i = lo;             // 从左(lo+1)开始扫描int j = hi+1;           // 从右(hi)开始扫描Comparable v = a[lo];   // 选取第一个元素为切分元素while (true){   // 扫描左右,检查扫描是否结束并交换元素while (less(a[++i], v))     // 从左开始扫描,直到找到一个大于等于切分元素的元素if (i == hi)break;while (less(v, a[--j]))     // 从右开始扫描,直到找到一个小于等于切分元素的元素if (j == lo)break;if (i >= j)         // i,j 相遇则时退出主循环break;exchange(a, i, j);  // 每次左边找到大的元素,右边找到小的元素,交换,则变成左小右大}exchange(a, lo, j);     // 由于此时i与j已经相遇,经过最后一次++i,--j,j已经到达了左区域,所以j的元素小于切分元素return j;               // a[lo...j-1] <= a[j] <= a[j+1...hi]}/*** 快速排序算法* */public static void sort(Comparable[] a){quickSort(a, 0, a.length-1);}public static void quickSort(Comparable[] a, int lo, int hi){if (hi <= lo)return;int j = partition(a, lo, hi);   // 切分,切分成a[lo...j-1] <= a[j] <=a[j+1...hi]quickSort(a, lo, j-1);      // 将左半部分a[lo...j-1]排序quickSort(a, j+1, hi);      // 将有半部分a[j+1...hi]排序}
}

测试案例:

public static void main(String[] args) {Comparable[] a = new Comparable[40];for (int i = 0 ; i < a.length ; i++){a[i] = new Random().nextInt(200);}System.out.println("排序前:");show(a);sort(a);System.out.println("快速排序:");show(a);System.out.println("是否排序成功:" + isSorted(a));
}

运行结果:

排序前:
117 134 26 117 192 121 124 66 51 106 165 20 87 66 51 174 121 13 153 75 110 140 50 9 28 20 144 2 4 23 122 150 21 162 32 39 67 47 152 181
快速排序:
2 4 9 13 20 20 21 23 26 28 32 39 47 50 51 51 66 66 67 75 87 106 110 117 117 121 121 122 124 134 140 144 150 152 153 162 165 174 181 192
是否排序成功:true

三向切分的快速排序(快排的改进版)

三向切分的快速排序是快速排序的改进,对于存在大量重复元素的数组,比快排效率高得多
维护一个指针 lt 使得 a[lo…lt-1] 中的元素都小于 v
指针 gt 使得 a[gt+1…hi] 中的元素都大于 v
指针 i 使得 a[lt…i-1] 中的元素都等于 v
a[i…gt] 中的元素都还未确定。
一开始 i 和 lo 相等,我们使用 Comparable 接口(而非 less())对 a[i] 进行三向比较来直接处理以下情况:

  • a[i] 小于 v,将 a[lt] 和 a[i] 交换,lt 和 i 加一;
  • a[i] 大于 v,将 a[gt] 和 a[i] 交换,将 gt 减一;
  • a[i] 等于 v,将 i 加一。
    这些操作都会保证数组元素不变且缩小 gt-i 的值(这样循环才会结束)。另外,除非和切分元素相等,其他元素都会被交换。

package com.yusael.sortTest;import java.util.Random;public class QuickSort2 {/*** 比较大小* */public static boolean less(Comparable m, Comparable n){return m.compareTo(n) < 0;}/*** 交换元素* */public static void exchange(Comparable[] a, int m, int n){Comparable temp = a[m];a[m] = a[n];a[n] = temp;}/*** 展示元素* */public static void show(Comparable[] a){for (int i = 0 ; i < a.length ; i++){System.out.print(a[i] + " ");}System.out.println();}/*** 是否排序成功* */public static boolean isSorted(Comparable[] a){for (int i = 1 ; i < a.length ; i++){if (less(a[i], a[i-1]))return false;}return true;}/*** 三向切分的快速排序* */public static void sort(Comparable[] a){quick3way(a, 0, a.length-1);}public static void quick3way(Comparable[] a, int lo, int hi){if (hi <= lo)   //单个元素或者没有元素的情况return;int lt = lo;    // lt使得a[lo...lt-1]中的元素都小于vint i = lo+1;   // i使得a[lt...i-1]中的元素都等于v// a[i...gt] 中的元素都还未确定int gt = hi;    // gt使得a[gt+1...hi]中的元素都大于vComparable v = a[lo]; // 选取第一个元素为切分元素while (i <= gt) // a[i...gt] 中的元素都还未确定,确定后就跳出循环了{if (a[i].compareTo(v) < 0)  //小于切分元素的放在lt左边,因此指针lt和指针i整体右移exchange(a, lt++, i++);else if (a[i].compareTo(v) > 0) //大于切分元素的放在gt右边,因此指针gt需要左移exchange(a, i, gt--);else i++;}// lt-gt的元素已经排定,只需对it左边和gt右边的元素进行递归求解quick3way(a, lo, lt-1);quick3way(a, gt+1, hi);}      if (cmp < 0)exchange(a, lt++, i++);else if (cmp > 0)exchange(a, i, gt--);else i++;}quick3way(a, lo, lt-1);quick3way(a, gt+1, hi);}}

测试案例:

public static void main(String[] args) {Comparable[] a = new Comparable[40];for (int i = 0 ; i < a.length ; i++){a[i] = new Random().nextInt(200);}System.out.println("排序前:");show(a);sort(a); // 三向切分的快速排序System.out.println("排序后:");show(a);System.out.println(isSorted(a));
}

运行结果:

排序前:
12 75 52 50 111 12 92 177 38 27 192 193 66 155 73 167 127 54 127 55 199 136 181 78 109 66 119 82 0 186 137 79 55 131 5 162 79 182 195 84
排序后:
0 5 12 12 27 38 50 52 54 55 55 66 66 73 75 78 79 79 82 84 92 109 111 119 127 127 131 136 137 155 162 167 177 181 182 186 192 193 195 199
true

下面是《算法(第四版)》上对算法切分轨迹的一个示例说明:

《Algorithms》Comparable 实现快速排序三向切分的快速排序相关推荐

  1. Quick Sort(三向切分的快速排序)(Java)

    1 //三向切分的快速排序 2 //这种切分方法对于数组中有大量重复元素的情况有比较大的性能提升 3 4 public static void main(String[] args) 5 { 6 Sc ...

  2. javascript 实现快排 ,三向切分快排

    比如说对数组快排的思路就是: 选取一个基准(可以选数组的开头最为基准,令 i = 0 ;j = array.length -1) 从arr[j]向前遍历(j--),当该值大于基准,则交换,退出循环(b ...

  3. 快速排序三种实现方式及其优化

    快速排序三种实现方式及其优化 1.关于快速排序 快速排序是分治法的一个应用. 根据分治法的思想,快速排序算法可描述为: 分解∶数组A[p-r]被划分为两个子数组A[p-q-1]和A[q+1,r],使得 ...

  4. c语言对随机数进行快速排序,C语言自带快速排序对比插入排序

    #include #include #include void getRandomArr (int arr[], int n); void printArr (int arr[], int n); v ...

  5. PHP快速排序(原地切分)

    2019独角兽企业重金招聘Python工程师标准>>> 快速排序是一种分治的排序算法,采用递归的思想,将数组元素分为两部分,选择切分元素,左右扫描数组,将大于切分元素的数据放在右边, ...

  6. 快速排序——三种划分方式

    思路: 1.分治思想:先划分成两个问题,然后对两个子问题递归排序,最后再合并. 2.核心算法:快排的核心在于划分问题(找到分界点). 代码: //快速排序 public static void qui ...

  7. 排序(三)冒泡排序与快速排序(C语言实现)

    冒泡排序与快速排序都属于交换排序,其中冒泡排序也是十分的出名,实现起来也比较简便,下面一一介绍这两种排序. 1.冒泡排序 冒泡排序的意思就是将最大的数沉底,或者最小的数提到最前面来,之后再抛开这个数找 ...

  8. 排序1:快速排序(三种)、归并排序、计数排序

    快排 1. hoare法 思路: 每次确定一个key[首元素]位置,从此分了两个区间:[0,key-1],[key+1,end]. 每个内部排序是双循环:取第一个为key,R先走,R找小,L找大,注意 ...

  9. 快速排序 java cutoff_排序之 快速排序

    采用算法导论上的实现方式,用java实现. 快排算法核心的部分便是partition过程,这里的partition采取最后一个元素作为pivot,i和j两个指针都从头向后扫描,如下图所示,数组被分为4 ...

最新文章

  1. XamarinEssentials教程设置首选项Preferences的值
  2. Libc堆管理机制及漏洞利用技术 (一)
  3. java 操作oracle 源码_java对oracle的常用操作示例源码
  4. 洛谷P1091 合唱队形
  5. Part1_1 pycharm的一些基础设置
  6. LightOJ 1078 Integer Divisibility (同余定理)
  7. ActionScript3学习笔记2-包
  8. Hands-On Modeler (建模人员参与程序开发)
  9. 拳王公社:网赚高手的零成本引流秘籍,这4个才是核心思维!
  10. 执行计划有时不准确_一张表格,帮助学生制定良好每日学习计划,提升学习积极性主动性...
  11. as400 c语言程序,AS400上的C編程
  12. CAD/CASS插件断面图批量提取横纵断面数据(左负右正、左零开始)可附加提取桩号XY坐标(北坐标东坐标)
  13. 【漏洞分析】流行开源电子邮件程序Roundcube v1.2.2命令执行漏洞分析
  14. 话剧《燃烧的梵高》:梵高的世界并非理所当然
  15. 小孩孩子应用题计算机错误,为什么一二年级的孩子数学应用题总出错?家长该怎么办?...
  16. 实例讲解统计学基础知识(5):假设检验
  17. linux手动焦距,摄影师必学!如何精准的手动对焦
  18. PTA7-22 龟兔赛跑
  19. Python爬虫实例--新浪热搜榜[xpath语法]
  20. Uni-app开发App和插件以后如何开通广告盈利:uni-AD

热门文章

  1. 如何在几天时间内快速理解一个陌生行业?
  2. 小企业如何才能做强做大?
  3. 为什么有的安卓手机越用越慢,刷机也不行?
  4. 有没有用过苹果手机的人,使用后放弃苹果
  5. As Foxit Software disclosed in its prospectus
  6. 类的构造函数和析构函数、默认构造函数
  7. 解决“/dev/mapper/centos-root 100%”满了
  8. 软件工程--瀑布模型
  9. azure blob_在Azure中使用表,blob,队列和文件存储
  10. react sql格式化_为SQL Server数据库损坏做准备; 初步React与分析