高速排序(Quicksort)是对冒泡排序的一种改进。

高速排序由C. A. R. Hoare在1962年提出。

一次高速排序具体过程:

选择数组第一个值作为枢轴值。

代码实现:

package QuickSort;

public class QuickSortRealize {

    public static void QuickSort(int[] arr){
QSort(arr,0,arr.length-1);
} //对顺序表子序列作高速排序 待排序序列的最小下标值low和最大下标值high
public static void QSort(int[] arr,int low,int high){
int pivot;
if(low<high){
pivot = Partition(arr,low,high);//将数组子序列一分为二 QSort(arr, low, pivot-1);//对低子表递归排序
QSort(arr, pivot+1, high);//对高子表递归排序
}
} //选择一个关键字,想尽办法将它放到一个位置。使得它左边的值都比它小,
//右边的值都比它大,我们称这个关键字叫枢轴。
public static int Partition(int[] arr,int low,int high){
if(arr == null || low<0 || high>=arr.length){
new Exception();
} int pivotkey;
pivotkey = arr[low];//选取第一个记录作枢轴记录 while(low<high)//从表的两端向中间扫描
{
while(low<high && arr[high]>=pivotkey){//假设大于枢轴值,则下标减一,否则,跳出循环。 high--;
}
Swap(arr, low, high);//交换
while (low<high && arr[low]<pivotkey){//假设小于枢轴值,则下标加一。否则,跳出循环。
low++;
}
Swap(arr, low, high);//交换
}
return low;
} public static void Swap(int[] arr,int low,int high){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
} public static void main(String[] args) {
int[] arr = {50,10,90,30,70,40,80,60,20};
QuickSort(arr);
for (int array : arr) {
System.out.print(array+" ");
}
System.out.println();
}
}

高速排序的时间性能取决于高速排序递归的深度,能够用递归数来描写叙述算法的运行情况。假设递归树是平衡的。那么此时的性能也是最好的。

也就是说。在最优的情况下,高速排序算法的时间复杂度为 O(nlogn)。

就空间复杂度来说,主要是递归造成的栈空间的使用,最好情况,递归树的深度log2n ,其空间复杂度也就为 O(logn) ,最坏情况,须要进行递归调用,其空间复杂度为 O(n),平均情况 空间复杂度也为 (logn)。

可惜的是 关键字的比較和交换是跳跃进行的,因此。高速排序是 种不稳

定的排序方法。

优化算法:

1、优化选取枢轴

三数取中,即取三个关键字先进行排序,将中间数作为枢轴。 通常是取左端、右端和中间三个数, 也能够随机选取。

对于很大的待排序的序列来说还是不足以保证能够选择出一个好的pivo tkey, 因此还有个办法是所谓的九数取中,先从数组中分三次取样,每次取三个数,三个样品各取出中数,然后从这三个中数其中再取出一个中数作为枢轴 。

package QuickSort;

public class QuickSortRealize {

    public static void QuickSort(int[] arr){
QSort(arr,0,arr.length-1);
} //对顺序表子序列作高速排序 待排序序列的最小下标值low和最大下标值high
public static void QSort(int[] arr,int low,int high){
int pivot;
if(low<high){
pivot = Partition(arr,low,high);//将数组子序列一分为二 QSort(arr, low, pivot-1);//对低子表递归排序
QSort(arr, pivot+1, high);//对高子表递归排序
}
} //选择一个关键字,想尽办法将它放到一个位置,使得它左边的值都比它小,
//右边的值都比它大。我们称这个关键字叫枢轴。
public static int Partition(int[] arr,int low,int high){ if(arr == null || low<0 || high>=arr.length){
new Exception();
} int pivotkey; ChoosePivotkey(arr,low,high);//选取枢轴值 pivotkey = arr[low]; while(low<high)//从表的两端向中间扫描
{
while(low<high && arr[high]>=pivotkey){//假设大于枢轴值,则下标减一。否则,跳出循环。 high--;
}
Swap(arr, low, high);//交换
while (low<high && arr[low]<pivotkey){//假设小于枢轴值,则下标加一。否则,跳出循环。
low++;
}
Swap(arr, low, high);//交换
}
return low;
} public static void Swap(int[] arr,int low,int high){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
} //三数取中 选择枢轴 将枢轴值调至第一个位置
public static void ChoosePivotkey(int[] arr,int low,int high){
int mid = low + (int)(high-low)/2;
if(arr[low]>arr[high]){//保证左端较小
Swap(arr, low, high);
}
if(arr[mid]>arr[high]){//保证中间较小
Swap(arr, mid, high);
}
//此时最大值在最右边
if(arr[mid]>arr[low]){//保证中间较小
Swap(arr, mid, low);
}
} public static void main(String[] args) {
int[] arr = {50,10,90,30,70,40,80,60,20};
QuickSort(arr);
for (int array : arr) {
System.out.print(array+" ");
}
System.out.println();
}
}

2、优化不必要的交换

package QuickSort;

public class QuickSortRealize3 {

    public static void QuickSort(int[] arr){
QSort(arr,0,arr.length-1);
} //对顺序表子序列作高速排序 待排序序列的最小下标值low和最大下标值high
public static void QSort(int[] arr,int low,int high){
int pivot;
if(low<high){
pivot = Partition(arr,low,high);//将数组子序列一分为二 QSort(arr, low, pivot-1);//对低子表递归排序
QSort(arr, pivot+1, high);//对高子表递归排序
}
} //选择一个关键字,想尽办法将它放到一个位置。使得它左边的值都比它小。
//右边的值都比它大,我们称这个关键字叫枢轴。
public static int Partition(int[] arr,int low,int high){ if(arr == null || low<0 || high>=arr.length){
new Exception();
} int pivotkey;
pivotkey = arr[low];//选取第一个记录作枢轴记录 int tempCopy = pivotkey;//将枢轴值备份到tempCopy中 while(low<high)//从表的两端向中间扫描
{
while(low<high && arr[high]>=pivotkey){//假设大于枢轴值,则下标减一,否则。跳出循环。
high--;
}
//Swap(arr, low, high);//交换
arr[low] = arr[high];//採用替换而不是交换的方式进行操作
while (low<high && arr[low]<pivotkey){//假设小于枢轴值,则下标加一,否则,跳出循环。
low++;
}
//Swap(arr, low, high);//交换
arr[high] = arr[low];//採用替换而不是交换的方式进行操作
}
arr[low] = tempCopy;//将枢轴值替换回arr[low]
return low;//返回枢轴值所在位置
} public static void Swap(int[] arr,int low,int high){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
} public static void main(String[] args) {
int[] arr = {50,10,90,30,70,40,80,60,20};
QuickSort(arr);
for (int array : arr) {
System.out.print(array+" ");
}
System.out.println();
}
}

3、优化小数组时的排序方案

高速排序适用于很大的数组的解决的方法。 那么相反的情况,假设数组很小,事实上高速排序反而不如直接插入排序来得更好(直接插入是简单排序中性能最好的)。

其原因在于高速排序用到了递归操作。在大量数据排序时。这点性能影响相对于它的总体算法优势是能够忽略的,但假设数组唯独几个记录须要排序时,这就成了大材小用,因此我们须要改进一下 QSort函数。

package QuickSort;

public class QuickSortRealize4 {
final static int MAX_LENGTH_INSERT_SORT = 7; public static void QuickSort(int[] arr){
QSort(arr,0,arr.length-1);
} //对顺序表子序列作高速排序 待排序序列的最小下标值low和最大下标值high
public static void QSort(int[] arr,int low,int high){
int pivot;
if((high-low)>MAX_LENGTH_INSERT_SORT){
pivot = Partition(arr,low,high);//将数组子序列一分为二 QSort(arr, low, pivot-1);//对低子表递归排序
QSort(arr, pivot+1, high);//对高子表递归排序
}
else{
insertSort(arr);
}
} //选择一个关键字。想尽办法将它放到一个位置。使得它左边的值都比它小,
//右边的值都比它大,我们称这个关键字叫枢轴。
public static int Partition(int[] arr,int low,int high){ if(arr == null || low<0 || high>=arr.length){
new Exception();
} int pivotkey;
pivotkey = arr[low];//选取第一个记录作枢轴记录 int tempCopy = pivotkey;//将枢轴值备份到tempCopy中 while(low<high)//从表的两端向中间扫描
{
while(low<high && arr[high]>=pivotkey){//假设大于枢轴值,则下标减一,否则,跳出循环。
high--;
}
//Swap(arr, low, high);//交换
arr[low] = arr[high];//採用替换而不是交换的方式进行操作
while (low<high && arr[low]<pivotkey){//假设小于枢轴值。则下标加一,否则,跳出循环。 low++;
}
//Swap(arr, low, high);//交换
arr[high] = arr[low];//採用替换而不是交换的方式进行操作
}
arr[low] = tempCopy;//将枢轴值替换回arr[low]
return low;//返回枢轴值所在位置
} public static void Swap(int[] arr,int low,int high){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
} public static void insertSort(int[] arr){
int i,j;
//4,2,1,7,8
for(i=1;i<arr.length;i++){
if(arr[i-1]>arr[i]){
//temp=2
int temp = arr[i];//设置哨兵
//必须要保证数组下标>=0,才for循环
for(j= i-1; j>=0&&arr[j]>temp ;j--){
arr[j+1]=arr[j];//arr[1]=4
}
//j=-1
arr[j+1]=temp;//arr[0]=2
//2 4 1 7 8
}
}
} public static void main(String[] args) {
int[] arr = {50,10,90,30,70,40,80,60,20};
QuickSort(arr);
//insertSort(arr);
for (int array : arr) {
System.out.print(array+" ");
}
System.out.println();
}
}

我们添加了一个推断, high-low不大于某个常数时(有资料觉得7较合适,觉得5更合理理,实际应用可适当调整) 。就用直接插入排序,这样就能保证最大化地利用两种排序的优势来完毕排序。

4、优化递归操作

我们知道,递归对性能是有一定影响的, QSort 函数在其尾部有两次递归操作。

假设待排序的序列划分极端不平衡,递归深度将趋近与N ,而不是平衡时的 logN,就不不过速度快慢的问题了,栈的大小是很有限的,每次递归调用都会耗费一定的空间 。函数的參数越多,每次递归耗费的空间也越多。假设能降低递归,将会提高性能。我们对 QSort 实施尾递归优化

package QuickSort;

public class QuickSortRealize5 {
final static int MAX_LENGTH_INSERT_SORT = 7; public static void QuickSort(int[] arr){
QSort(arr,0,arr.length-1);
} //对顺序表子序列作高速排序 待排序序列的最小下标值low和最大下标值high
public static void QSort(int[] arr,int low,int high){
int pivot;
if((high-low)>MAX_LENGTH_INSERT_SORT){
while(low<high){
pivot = Partition(arr,low,high);//将数组子序列一分为二
QSort(arr, low, pivot-1);//对低子表递归排序
/////////////////////////////////////////////////////
//QSort(arr, pivot+1, high);//对高子表递归排序
low = pivot + 1;
}
}
else{
insertSort(arr);
}
} //选择一个关键字。想尽办法将它放到一个位置。使得它左边的值都比它小。
//右边的值都比它大。我们称这个关键字叫枢轴。
public static int Partition(int[] arr,int low,int high){ if(arr == null || low<0 || high>=arr.length){
new Exception();
} int pivotkey;
pivotkey = arr[low];//选取第一个记录作枢轴记录 int tempCopy = pivotkey;//将枢轴值备份到tempCopy中 while(low<high)//从表的两端向中间扫描
{
while(low<high && arr[high]>=pivotkey){//假设大于枢轴值,则下标减一,否则。跳出循环。
high--;
}
//Swap(arr, low, high);//交换
arr[low] = arr[high];//採用替换而不是交换的方式进行操作
while (low<high && arr[low]<pivotkey){//假设小于枢轴值,则下标加一,否则。跳出循环。 low++;
}
//Swap(arr, low, high);//交换
arr[high] = arr[low];//採用替换而不是交换的方式进行操作
}
arr[low] = tempCopy;//将枢轴值替换回arr[low]
return low;//返回枢轴值所在位置
} public static void Swap(int[] arr,int low,int high){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
} public static void insertSort(int[] arr){
int i,j;
//4,2,1,7,8
for(i=1;i<arr.length;i++){
if(arr[i-1]>arr[i]){
//temp=2
int temp = arr[i];//设置哨兵
//必须要保证数组下标>=0,才for循环
for(j= i-1; j>=0&&arr[j]>temp ;j--){
arr[j+1]=arr[j];//arr[1]=4
}
//j=-1
arr[j+1]=temp;//arr[0]=2
//2 4 1 7 8
}
}
} public static void main(String[] args) {
int[] arr = {50,10,90,30,70,40,80,60,20};
QuickSort(arr);
//insertSort(arr);
for (int array : arr) {
System.out.print(array+" ");
}
System.out.println();
}
}

当我们将 if 改成 while 后,由于第一次递归以后。变量low就没实用处了,所以能够将 pivot+1 赋值给low。再循环后,来一次 Partition

(arr,low,high)时,其效果等同于 “QSort(arr, pivot+1, high);”。结果同样,但因採用迭代而不是递归的方法能够缩减堆栈深度,从而提高了总体性能。

高速排序及优化(Java版)的更多相关文章

  1. 内部排序比较(Java版)

    内部排序比较(Java版) 2017-06-21 目录 1 三种基本排序算法1.1 插入排序1.2 交换排序(冒泡)1.3 选择排序(简单)2 比较3 补充3.1 快速排序3.2 什么是桶排序3.3 ...

  2. 排序算法(java版)

    一直想理解一下基本的排序算法,最近正好在搞java所以就一并了(为了便于理解,这儿都是以从小到大排序为目的) 冒泡排序 也就是比较连续的两个值,如果前面一个值大于后面一个值,则交换. 时间复杂度为O( ...

  3. 面试常用算法总结——排序算法(java版)

    排序算法 重要性不言而喻,很多算法问题往往选择一个好的排序算法往往问题可以迎刃而解 1.冒泡算法 冒泡排序(Bubble Sort)也是一种简单直观的排序算法.它重复地走访过要排序的数列,一次比较两个 ...

  4. 经典排序算法(Java版)

    1.冒泡排序 Bubble Sort 最简单的排序方法是冒泡排序方法.这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮.在冒泡排序算法中我们要对这个“气泡” ...

  5. 《剑指offer》面试题17 合并两个排序的链表 Java版

    我的方法:新初始化一个链表头,比较两个链表当前节点的大小,然后连接到该链表中.遍历两个链表直到null为止. public ListNode merge(ListNode first, ListNod ...

  6. 排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

    常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结 ...

  7. 高速排序算法C++实现

    //quick sort //STL中也有现成的高速排序算法.内部实现採用了下面技巧 //1)枢轴的选择採取三数取中的方式 //2)后半段採取循环的方式实现 //3)高速排序与插入排序结合 #incl ...

  8. 高速排序(Java版)

    package com.love.test; import java.util.Scanner; /** * @author huowolf *高速排序实现 *快排是十分优秀的排序算法. *核心:分治 ...

  9. java:高速排序算法与冒泡排序算法

     Java:高速排序算法与冒泡算法 首先看下,冒泡排序算法与高速排序算法的效率: 例如以下的是main方法: /**   *  * @Description:  * @author:cuiyaon ...

随机推荐

  1. pycharm提示your evalluation license has expired解决方法

    安装pycharm,一段时间后提示your evalluation license has expired:打开pycharm--点击help--register--选中license server, ...

  2. UE3客户端服务器GamePlay框架

    客户端(当前玩家)与服务器对应关系图: 整体上看,UE3的GamePlay框架使用的是MVC架构 ① 橙色的Actor对象及橙色箭头相连的成员变量只会被同步给Owner客户端 Controller:控 ...

  3. spring+jpa+HiKariCP+P6spy SSH HiKariCP P6spy

    =============p6spy准备https://www.cnblogs.com/qgc88===================== 1.简单介绍p6spy,p6spy是一个开源项目,通常使用 ...

  4. 空间划分的数据结构(网格/四叉树/八叉树/BSP树/k-d树/BVH/自定义划分)

    目录 网格 (Grid) 网格的应用 四叉树/八叉树 (Quadtree/Octree) 四叉树/八叉树的应用 BSP树 (Binary Space Partitioning Tree) 判断点在平面 ...

  5. Java泛型中的类型擦除机制简单理解

    Java的泛型是JDK1.5时引入的.下面只是简单的介绍,不做深入的分析. Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...

  6. weblogic92 启动慢解决办法

      使用SUN JDK 启动时要很长时间,但启动启来weblogic正常. 特征见下面标红部分,时间很长. test01@linux-suse:~/bea/weblogic92/samples/dom ...

  7. Java自定义注解和运行时靠反射获取注解

    转载:http://blog.csdn.net/bao19901210/article/details/17201173/ java自定义注解 Java注解是附加在代码中的一些元信息,用于一些工具在编 ...

  8. UVA - 1416 Warfare And Logistics (最短路)

    Description The army of United Nations launched a new wave of air strikes on terroristforces. The ob ...

  9. 如何使用电骡eMule上传资源

    1 在电脑中创建一个文件夹专门为上传资源使用 在里面放入任意文件并右击显示ED2K链接   链接效果如下 2 登陆verycd网站,并点击上传资源 复制ED2K地址并选择分类  

  10. Linux/ visual studio 编译使用Poco

    1. 下载源码包.在POCO的官方网站下载最新的POCO源码包.http://pocoproject.org/download/index.html2.解压源码包.下载的文件名是“poco-1.6.0 ...