一. 普通快速排序

找一个基准值base,然后一趟排序后让base左边的数都小于base,base右边的数都大于等于base。再分为两个子数组的排序。如此递归下去。

public class QuickSort {public static <T extends Comparable<? super T>> void sort(T[] arr) {sort(arr, 0, arr.length - 1);}public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right) {if (left >= right) return;int p = partition(arr, left, right);sort(arr, left, p - 1);sort(arr, p + 1, right);}private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) {T base = arr[left];int j = left;for (int i = left + 1; i <= right; i++) {if (base.compareTo(arr[i]) > 0) {j++;swap(arr, j, i);}}swap(arr, left, j);return j;//返回一躺排序后基准值的下角标}public static void swap(Object[] arr, int i, int j) {if (i != j) {Object temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}private static void printArr(Object[] arr) {for (Object o : arr) {System.out.print(o);System.out.print("\t");}System.out.println();}public static void main(String args[]) {Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6};printArr(arr);//3   5   1   7   2   9   8   0   4   6sort(arr);printArr(arr);//0   1   2   3   4   5   6   7   8   9}
}

  

二. 快速排序优化:随机选取基准值base

在数组几乎有序时,快排性能不好(因为每趟排序后,左右两个子递归规模相差悬殊,大的那部分最后很可能会达到O(n^2))。

解决:基准值随机地选取,而不是每次都取第一个数。这样就不会受“几乎有序的数组”的干扰了。但是对“几乎乱序的数组”的排序性能可能会稍微下降,至少多了排序前交换的那部分,乱序时这个交换没有意义...有很多“运气”成分..

public class QuickSort {public static <T extends Comparable<? super T>> void sort(T[] arr) {sort(arr, 0, arr.length - 1);}public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right) {if (left >= right) return;int p = partition(arr, left, right);sort(arr, left, p - 1);sort(arr, p + 1, right);}private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) {//排序前,先让基准值和随机的一个数进行交换。这样,基准值就有随机性。//就不至于在数组相对有序时,导致左右两边的递归规模不一致,产生最坏时间复杂度swap(arr,left,(int)(Math.random()*(right - left + 1)+left));T base = arr[left];int j = left;for (int i = left + 1; i <= right; i++) {if (base.compareTo(arr[i]) > 0) {j++;swap(arr, j, i);}}swap(arr, left, j);return j;//返回一躺排序后,基准值的下角标}public static void swap(Object[] arr, int i, int j) {if (i != j) {Object temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}private static void printArr(Object[] arr) {for (Object o : arr) {System.out.print(o);System.out.print("\t");}System.out.println();}public static void main(String args[]) {Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6};printArr(arr);//3   5   1   7   2   9   8   0   4   6sort(arr);printArr(arr);//0   1   2   3   4   5   6   7   8   9}
}

  

三. 快速排序继续优化:配合着使用插入排序

快排是不断减小问题规模来解决子问题的,需要不断递归。但是递归到规模足够小时,如果继续采用这种 不稳定+递归 的方式执行下去,效率不见得会很好。

所以当问题规模较小时,近乎有序时,插入排序表现的很好。Java自带的Arrays.sort()里经常能看到这样的注释:“Use insertion sort on tiny arrays”,“Insertion sort on smallest arrays”

public class QuickSort {public static <T extends Comparable<? super T>> void sort(T[] arr) {sort(arr, 0, arr.length - 1, 16);}/*** @param arr   待排序的数组* @param left  左闭* @param right 右闭* @param k     当快排递归到子问题的规模 <= k 时,采用插入排序优化* @param <T>   泛型,待排序可比较类型*/public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right, int k) {// 规模小时采用插入排序if (right - left <= k) {insertionSort(arr, left, right);return;}int p = partition(arr, left, right);sort(arr, left, p - 1, k);sort(arr, p + 1, right, k);}public static <T extends Comparable<? super T>> void insertionSort(T[] arr, int l, int r) {for (int i = l + 1; i <= r; i++) {T cur = arr[i];int j = i - 1;for (; j >= 0 && cur.compareTo(arr[j]) < 0; j--) {arr[j + 1] = arr[j];}arr[j + 1] = cur;}}private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) {//排序前,先让基准值和随机的一个数进行交换。这样,基准值就有随机性。//就不至于在数组相对有序时,导致左右两边的递归规模不一致,产生最坏时间复杂度swap(arr, left, (int) (Math.random() * (right - left + 1) + left));T base = arr[left];int j = left;for (int i = left + 1; i <= right; i++) {if (base.compareTo(arr[i]) > 0) {j++;swap(arr, j, i);}}swap(arr, left, j);return j;//返回一躺排序后,基准值的下角标}public static void swap(Object[] arr, int i, int j) {if (i != j) {Object temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}private static void printArr(Object[] arr) {for (Object o : arr) {System.out.print(o);System.out.print("\t");}System.out.println();}public static void main(String args[]) {Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6};printArr(arr);//3   5   1   7   2   9   8   0   4   6sort(arr);printArr(arr);//0   1   2   3   4   5   6   7   8   9}
}

  

四. 快速排序继续优化:两路快排

在最开始的普通快速排序说过,让基准值base左边的都比base小,而base右边的都大于等于base。等于base的这些会聚集到右侧(或者稍微改改大小关系就会聚集到左侧)。总之就会聚集到一边。这样在数组中重复数字很多的时候,就又会导致两边子递归规模差距悬殊的情况。这时想把等于base的那些数分派到base两边,而不是让他们聚集到一起。

(注:测试代码的时候,最好把插入排序那部分注视掉,向我下面代码中那样...不然数据量小于k=16的时候执行的是插入排序.....)

public class QuickSort {public static <T extends Comparable<? super T>> void sort(T[] arr) {sort(arr, 0, arr.length - 1, 16);}/*** @param arr   待排序的数组* @param left  左闭* @param right 右闭* @param k     当快排递归到子问题的规模 <= k 时,采用插入排序优化* @param <T>   泛型,待排序可比较类型*/public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right, int k) {// 规模小时采用插入排序
//        if (right - left <= k) {
//            insertionSort(arr, left, right);
//            return;
//        }if (left >= right) return;int p = partition(arr, left, right);sort(arr, left, p - 1, k);sort(arr, p + 1, right, k);}public static <T extends Comparable<? super T>> void insertionSort(T[] arr, int l, int r) {for (int i = l + 1; i <= r; i++) {T cur = arr[i];int j = i - 1;for (; j >= 0 && cur.compareTo(arr[j]) < 0; j--) {arr[j + 1] = arr[j];}arr[j + 1] = cur;}}private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) {//排序前,先让基准值和随机的一个数进行交换。这样,基准值就有随机性。//就不至于在数组相对有序时,导致左右两边的递归规模不一致,产生最坏时间复杂度swap(arr, left, (int) (Math.random() * (right - left + 1) + left));T base = arr[left];//基准值,每次都把这个基准值抛出去,看成[left+1.....right]左闭右闭区间的排序int i = left + 1; //对于上一行提到的[left+1.....right]区间,i表示 [left+1......i)左闭右开区间的值都小于等于base。int j = right;//对于上二行提到的[left+1.....right]区间,j表示 (j......right]左开右闭区间的值都大于等于base。while (true) {//从左到右扫描,扫描出第一个比base大的元素,然后i停在那里。while (i <= right && arr[i].compareTo(base) < 0) i++;//从右到左扫描,扫描出第一个比base小的元素,然后j停在那里。while (j >= left && arr[j].compareTo(base) > 0) j--;if (i > j) {//虽说是i>j,但其实都是以j=i-1为条件结束的break;}swap(arr, i++, j--);}swap(arr, left, j);return j;//返回一躺排序后,基准值的下角标}public static void swap(Object[] arr, int i, int j) {if (i != j) {Object temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}private static void printArr(Object[] arr) {for (Object o : arr) {System.out.print(o);System.out.print("\t");}System.out.println();}public static void main(String args[]) {Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6};printArr(arr);//3   5   1   7   2   9   8   0   4   6sort(arr);printArr(arr);//0   1   2   3   4   5   6   7   8   9}
}

  

五. 快速排序继续优化:两路快排 不用swap, 用直接赋值

上面的两路在找到大于base的值和小于base的值时,用的是swap()方法来进行交换。两数交换涉及到第三个变量temp的操作,多了读写操作。接下来用直接赋值的方法,把小于的放到右边,大于的放到左边,当i和j相遇时,那个位置就是base该放的地方。至此一趟完成。递归即可。

public class QuickSort {public static <T extends Comparable<? super T>> void sort(T[] arr) {sort(arr, 0, arr.length - 1, 16);}/*** @param arr   待排序的数组* @param left  左闭* @param right 右闭* @param k     当快排递归到子问题的规模 <= k 时,采用插入排序优化* @param <T>   泛型,待排序可比较类型*/public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right, int k) {// 规模小时采用插入排序
//        if (right - left <= k) {
//            insertionSort(arr, left, right);
//            return;
//        }if (left >= right) return;int p = partition(arr, left, right);sort(arr, left, p - 1, k);sort(arr, p + 1, right, k);}public static <T extends Comparable<? super T>> void insertionSort(T[] arr, int l, int r) {for (int i = l + 1; i <= r; i++) {T cur = arr[i];int j = i - 1;for (; j >= 0 && cur.compareTo(arr[j]) < 0; j--) {arr[j + 1] = arr[j];}arr[j + 1] = cur;}}private static <T extends Comparable<? super T>> int partition(T[] arr, int left, int right) {//排序前,先让基准值和随机的一个数进行交换。这样,基准值就有随机性。//就不至于在数组相对有序时,导致左右两边的递归规模不一致,产生最坏时间复杂度swap(arr, left, (int) (Math.random() * (right - left + 1) + left));T base = arr[left];//基准值,每次都把这个基准值抛出去,看成[left+1.....right]左闭右闭区间的排序int i = left; //对于上一行提到的[left+1.....right]区间,i表示 [left+1......i)左闭右开区间的值都小于等于base。int j = right;//对于上二行提到的[left+1.....right]区间,j表示 (j......right]左开右闭区间的值都大于等于base。while (i < j) {//从右到左扫描,扫描出第一个比base小的元素,然后j停在那里。while (j > i && arr[j].compareTo(base) > 0) j--;arr[i] = arr[j];//从左到右扫描,扫描出第一个比base大的元素,然后i停在那里。while (i < j && arr[i].compareTo(base) < 0) i++;arr[j] = arr[i];}arr[j] = base;return j;//返回一躺排序后,基准值的下角标}public static void swap(Object[] arr, int i, int j) {if (i != j) {Object temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}private static void printArr(Object[] arr) {for (Object o : arr) {System.out.print(o);System.out.print("\t");}System.out.println();}public static void main(String args[]) {Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6};printArr(arr);//3   5   1   7   2   9   8   0   4   6sort(arr);printArr(arr);//0   1   2   3   4   5   6   7   8   9}
}

  

六. 快速排序继续优化:当大量数据,且重复数多时,用三路快排

把数组分为三路,第一路都比base小,第二路都等于base,第三路都大于base。

用指针从前到后扫描,如果:

1.cur指向的数小于base,那么:交换arr[cur]和arr[i]的值,然后i++,cur++。

2.cur指向的数等于base,  那么:cur++

3.cur指向的数大于base,那么:交换arr[cur]和arr[j]的值,然后j--。

当cur > j的时候说明三路都已经完成。

public class QuickSort {public static <T extends Comparable<? super T>> void sort(T[] arr) {sort(arr, 0, arr.length - 1, 16);}/*** @param arr   待排序的数组* @param left  左闭* @param right 右闭* @param k     当快排递归到子问题的规模 <= k 时,采用插入排序优化* @param <T>   泛型,待排序可比较类型*/public static <T extends Comparable<? super T>> void sort(T[] arr, int left, int right, int k) {// 规模小时采用插入排序
//        if (right - left <= k) {
//            insertionSort(arr, left, right);
//            return;
//        }if (left >= right) return;int[] ret = partition(arr, left, right);sort(arr, left, ret[0], k);sort(arr, ret[1], right, k);}public static <T extends Comparable<? super T>> void insertionSort(T[] arr, int l, int r) {for (int i = l + 1; i <= r; i++) {T cur = arr[i];int j = i - 1;for (; j >= 0 && cur.compareTo(arr[j]) < 0; j--) {arr[j + 1] = arr[j];}arr[j + 1] = cur;}}/*** @param arr   待排序的数组* @param left  待排序数组的左边界* @param right 待排序数组的右边界* @param <T>   泛型* @return*/private static <T extends Comparable<? super T>> int[] partition(T[] arr, int left, int right) {//排序前,先让基准值和随机的一个数进行交换。这样,基准值就有随机性。//就不至于在数组相对有序时,导致左右两边的递归规模不一致,产生最坏时间复杂度swap(arr, left, (int) (Math.random() * (right - left + 1) + left));T base = arr[left];//基准值,每次都把这个基准值抛出去,看成[left+1.....right]左闭右闭区间的排序//三路快排分为下面这三个路(区间)int i = left; // left表示,[lleft...left) 左闭右开区间里的数都比base小int j = right;// left表示,(rright...right] 左开右闭区间里的数都比base大int cur = i;//用cur来遍历数组。[left...cur)左闭右开区间里的数都等于basewhile (cur <= j) {if (arr[cur].compareTo(base) == 0) {cur++;} else if (arr[cur].compareTo(base) < 0) {swap(arr, cur++, i++);} else {swap(arr, cur, j--);}}return new int[]{i - 1, j + 1};//[i...j]都等于base,子问题就只需要解决i左边和j右边就行了}public static void swap(Object[] arr, int i, int j) {if (i != j) {Object temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}private static void printArr(Object[] arr) {for (Object o : arr) {System.out.print(o);System.out.print("\t");}System.out.println();}public static void main(String args[]) {Integer[] arr = {3, 5, 1, 7, 2, 9, 8, 0, 4, 6};printArr(arr);//3   5   1   7   2   9   8   0   4   6sort(arr);printArr(arr);//0   1   2   3   4   5   6   7   8   9}
}

  

  

转载于:https://www.cnblogs.com/noKing/p/7922397.html

快速排序及优化(Java实现)相关推荐

  1. java版排序算法简介及冒泡排序以及优化,选择排序,直接插入排序,希尔排序,堆排序,快速排序及其优化前言 2 分类 2 稳定性 3 时间复杂度 4 Java实现版本 5 1、冒泡排序 6 2、选择排序

    好吧 ~~csdn太难用了....尼玛...写了半天的也无法弄进去...lz正在找更好地博客,or放在github上打算.. 下边是lz自己的有道云分享,大概内容是 http://note.youda ...

  2. asp java 变量_ASP_用stack变量优化Java代码,  java程序包含了大量的对象 - phpStudy...

    用stack变量优化Java代码 java程序包含了大量的对象,我们需要了解它们是从哪里被访问的,变量存储于何处对程序的性能有显著的影响--尤其是某些需要被频繁访问的变量. 我们写一个Java类,在其 ...

  3. jvm 系列(九):如何优化 Java GC 「译」

    本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的"Become a Java GC Expert ...

  4. jvm系列(十):如何优化Java GC「译」

    本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的"Become a Java GC Expert ...

  5. java 堆大小的最大值_优化Java堆大小的5个技巧

    优化Java堆大小的5个技巧 摘要:Java堆容量不足可以对性能造成很大影响,这样无疑就给程序带来不可必要的麻烦,本文总结了影响Java堆容量不足的五大原因以及巧妙地去优化? 本文作者Pierre是一 ...

  6. jvm系列(九):如何优化Java GC

    转载自 jvm系列(九):如何优化Java GC「译」 本文翻译自Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三篇&l ...

  7. kryo java_优化Java序列化– Java,XML,JSON,Kryo,POF

    kryo java 也许我很天真,但是我一直认为Java序列化肯定是将Java对象序列化为二进制形式的最快,最有效的方法. 毕竟Java是第7个主要发行版,所以这不是新技术,而且由于每个JDK似乎都比 ...

  8. 优化Java序列化– Java,XML,JSON,Kryo,POF

    也许我很天真,但是我一直认为Java序列化肯定是将Java对象序列化为二进制形式的最快,最有效的方法. 毕竟Java是第7个主要发行版,所以这不是新技术,并且由于每个JDK似乎都比上一个快,因此我错误 ...

  9. java+set+split_阿里资深工程师教你如何优化 Java 代码!

    原标题:阿里资深工程师教你如何优化 Java 代码! 作者 | 王超 责编 | 伍杏玲 明代王阳明先生在<传习录>谈为学之道时说: 私欲日生,如地上尘,一日不扫,便又有一层.着实用功,便见 ...

最新文章

  1. 【推荐系统】协同过滤 零基础到入门
  2. spring代码异常捕获到logback logging.config=logback-spring.xml文件中不能输出异常e.printStackTrace...
  3. 原生js实现table表格的各行变色功能
  4. 教你移除IE 7.0浏览器的默认搜索框
  5. ios 使用UINavagationController时,push,pop方法执行的一些方法
  6. 基于新版 node 的 vue 脚手架搭建
  7. h264 ES流文件通过计算first_mb_in_slice区分帧边界
  8. 我的Android进阶之旅------经典的大牛博客推荐(排名不分先后)!!
  9. 电力系统微型计算机继电保护试题及答案,全国7月高中等教育自学考试电力系统微型计算机继电保护试题及答案...
  10. Freeswitch 常用功能命令及配置详解2021-06-06
  11. Python数据分析-绘图-2-Seaborn进阶绘图-4-分类图
  12. Python模拟登陆强智教务以及使用(持续更新中)
  13. 揭开虚拟主机供应商的面纱
  14. 从红海里面寻找蓝海,看一个人的思维模式
  15. The retrospective material for final English exam unit_3 disease
  16. idea自定义过滤器
  17. 1、初识OSPF协议
  18. 芝村乡明确个人小额投资理财方式
  19. 客户成功经理(CSM)
  20. ESP8266连接阿里云(STM32)

热门文章

  1. 5款实用的硬盘、SSD固态硬盘、U盘、储存卡磁盘性能测试工具绿色版
  2. 1.网页学习-开始学习第一步:
  3. 引领潮流云电视机遇与挑战并现
  4. 把Array说透(续一)
  5. 经常用到取整的函数,今天小小的总结一下!其实很简单,就是几个函数而已~~主要是:ceil,floor,round,intval...
  6. Python 错误和异常小结
  7. freemarker入门教程
  8. javascript跨域实践
  9. DWR取session,request,response
  10. Win64 驱动内核编程-30.枚举与删除线程回调