Java排序算法总结(七):快速排序

开发 后端 算法
排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。下面让我们一起来看快速排序。

快速排序(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。快速排序不稳定,O(log(n))的额外空间,时间复杂度为O(nlog(n)),不是自适应的。

快速排序(Quicksort)有几个值得一提的变种算法,这里进行一些简要介绍:

随机化快排:快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。一位前辈做出了一个精辟的总结:“随机化快速排序可以满足一个人一辈子的人品需求。”

随机化快速排序的唯一缺点在于,一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。对于极限情况,即对于n个相同的数排序,随机化快速排序的时间复杂度将毫无疑问的降低到O(n^2)。解决方法是用一种方法进行扫描,使没有交换的情况下主元保留在原位置。

平衡快排(Balanced Quicksort):每次尽可能地选择一个能够代表中值的元素作为关键数据,然后遵循普通快排的原则进行比较、替换和递归。通常来说,选择这个数据的方法是取开头、结尾、中间3个数据,通过比较选出其中的中值。取这3个值的好处是在实际问题(例如信息学竞赛……)中,出现近似顺序数据或逆序数据的概率较大,此时中间数据必然成为中值,而也是事实上的近似中值。万一遇到正好中间大两边小(或反之)的数据,取的值都接近最值,那么由于至少能将两部分分开,实际效率也会有2倍左右的增加,而且利于将数据略微打乱,破坏退化的结构。

外部快排(External Quicksort):与普通快排不同的是,关键数据是一段buffer,首先将之前和之后的M/2个元素读入buffer并对该buffer中的这些元素进行排序,然后从被排序数组的开头(或者结尾)读入下一个元素,假如这个元素小于buffer中最小的元素,把它写到最开头的空位上;假如这个元素大于buffer中最大的元素,则写到最后的空位上;否则把buffer中最大或者最小的元素写入数组,并把这个元素放在buffer里。保持最大值低于这些关键数据,最小值高于这些关键数据,从而避免对已经有序的中间的数据进行重排。完成后,数组的中间空位必然空出,把这个buffer写入数组中间空位。然后递归地对外部更小的部分,循环地对其他部分进行排序。

三路基数快排(Three-way Radix Quicksort,也称作Multikey Quicksort、Multi-key Quicksort):结合了基数排序(radix sort,如一般的字符串比较排序就是基数排序)和快排的特点,是字符串排序中比较高效的算法。该算法被排序数组的元素具有一个特点,即multikey,如一个字符串,每个字母可以看作是一个key。算法每次在被排序数组中任意选择一个元素作为关键数据,首先仅考虑这个元素的第一个key(字母),然后把其他元素通过key的比较分成小于、等于、大于关键数据的三个部分。然后递归地基于这一个key位置对“小于”和“大于”部分进行排序,基于下一个key对“等于”部分进行排序。

代码实现:

  1. public class QuickSort {   
  2. public static void sort(Comparable[] data, int low, int high) {   
  3. // 枢纽元,一般以第一个元素为基准进行划分   
  4. int i = low;   
  5. int j = high;   
  6. if (low < high) {   
  7. // 从数组两端交替地向中间扫描   
  8. Comparable pivotKey = data[low];   
  9. // 进行扫描的指针i,j;i从左边开始,j从右边开始   
  10. while (i < j) {   
  11. while (i < j && data[j].compareTo(pivotKey) > 0) {   
  12. j--;   
  13. }// end while   
  14. if (i < j) {   
  15. // 比枢纽元素小的移动到左边   
  16. data[i] = data[j];   
  17. i++;   
  18. }// end if   
  19. while (i < j && data[i].compareTo(pivotKey) < 0) {   
  20. i++;   
  21. }// end while   
  22. if (i < j) {   
  23. // 比枢纽元素大的移动到右边   
  24. data[j] = data[i];   
  25. j--;   
  26. }// end if   
  27. }// end while   
  28. // 枢纽元素移动到正确位置   
  29. data[i] = pivotKey;   
  30. // 前半个子表递归排序   
  31. sort(data, low, i - 1);   
  32. // 后半个子表递归排序   
  33. sort(data, i + 1, high);   
  34. }// end if   
  35. }// end sort   
  36. public static void main(String[] args) {   
  37. // 在JDK1.5版本以上,基本数据类型可以自动装箱   
  38. // int,double等基本类型的包装类已实现了Comparable接口   
  39. Comparable[] c = { 49231452752 };   
  40. sort(c, 0, c.length - 1);   
  41. for (Comparable data : c) {   
  42. System.out.println(data);   
  43. }   
  44. }   

快速排序是目前使用的最广泛的排序算法了,快速排序的核心在于分割算法,也可以说是最有技巧的部分。希望通过本文的介绍,能给你带来帮助。

【编辑推荐】

  1. 5.11 三行代码的快速排序
  2. 12.3.2 基于任务窃取的快速排序
  3. 19.4.2 用Parallel_For进行并行快速排序
  4. C#快速排序的趣味实现:从Haskell说起

 

责任编辑:于铁 来源: 百度
相关推荐

2011-04-20 13:56:08

选择排序

2011-04-20 14:19:00

希尔排序

2011-04-20 14:07:37

冒泡排序

2011-04-20 15:06:44

堆排序

2011-04-20 14:29:07

归并排序

2011-04-20 12:49:44

插入排序

2011-04-20 16:05:15

基数排序

2015-08-26 10:13:55

排序算法总结

2019-09-17 16:30:18

java排序算法

2014-10-30 15:14:54

快速排序编程算法

2015-09-01 10:21:53

排序算法总结

2021-03-04 07:24:28

排序算法优化

2014-10-30 15:08:21

快速排序编程算法

2021-01-26 05:33:07

排序算法快速

2023-03-07 08:02:07

数据结构算法数列

2023-05-08 07:55:05

快速排序Go 语言

2014-03-03 16:44:57

算法

2023-10-05 09:01:05

插入排序对象序列log2i

2022-01-06 16:20:04

Java排序算法排序

2022-03-07 09:42:21

Go快速排序
点赞
收藏

51CTO技术栈公众号