高速排序及优化(Java版)
高速排序(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版)的更多相关文章
- 内部排序比较(Java版)
内部排序比较(Java版) 2017-06-21 目录 1 三种基本排序算法1.1 插入排序1.2 交换排序(冒泡)1.3 选择排序(简单)2 比较3 补充3.1 快速排序3.2 什么是桶排序3.3 ...
- 排序算法(java版)
一直想理解一下基本的排序算法,最近正好在搞java所以就一并了(为了便于理解,这儿都是以从小到大排序为目的) 冒泡排序 也就是比较连续的两个值,如果前面一个值大于后面一个值,则交换. 时间复杂度为O( ...
- 面试常用算法总结——排序算法(java版)
排序算法 重要性不言而喻,很多算法问题往往选择一个好的排序算法往往问题可以迎刃而解 1.冒泡算法 冒泡排序(Bubble Sort)也是一种简单直观的排序算法.它重复地走访过要排序的数列,一次比较两个 ...
- 经典排序算法(Java版)
1.冒泡排序 Bubble Sort 最简单的排序方法是冒泡排序方法.这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮.在冒泡排序算法中我们要对这个“气泡” ...
- 《剑指offer》面试题17 合并两个排序的链表 Java版
我的方法:新初始化一个链表头,比较两个链表当前节点的大小,然后连接到该链表中.遍历两个链表直到null为止. public ListNode merge(ListNode first, ListNod ...
- 排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题
常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结 ...
- 高速排序算法C++实现
//quick sort //STL中也有现成的高速排序算法.内部实现採用了下面技巧 //1)枢轴的选择採取三数取中的方式 //2)后半段採取循环的方式实现 //3)高速排序与插入排序结合 #incl ...
- 高速排序(Java版)
package com.love.test; import java.util.Scanner; /** * @author huowolf *高速排序实现 *快排是十分优秀的排序算法. *核心:分治 ...
- java:高速排序算法与冒泡排序算法
Java:高速排序算法与冒泡算法 首先看下,冒泡排序算法与高速排序算法的效率: 例如以下的是main方法: /** * * @Description: * @author:cuiyaon ...
随机推荐
- pycharm提示your evalluation license has expired解决方法
安装pycharm,一段时间后提示your evalluation license has expired:打开pycharm--点击help--register--选中license server, ...
- UE3客户端服务器GamePlay框架
客户端(当前玩家)与服务器对应关系图: 整体上看,UE3的GamePlay框架使用的是MVC架构 ① 橙色的Actor对象及橙色箭头相连的成员变量只会被同步给Owner客户端 Controller:控 ...
- spring+jpa+HiKariCP+P6spy SSH HiKariCP P6spy
=============p6spy准备https://www.cnblogs.com/qgc88===================== 1.简单介绍p6spy,p6spy是一个开源项目,通常使用 ...
- 空间划分的数据结构(网格/四叉树/八叉树/BSP树/k-d树/BVH/自定义划分)
目录 网格 (Grid) 网格的应用 四叉树/八叉树 (Quadtree/Octree) 四叉树/八叉树的应用 BSP树 (Binary Space Partitioning Tree) 判断点在平面 ...
- Java泛型中的类型擦除机制简单理解
Java的泛型是JDK1.5时引入的.下面只是简单的介绍,不做深入的分析. Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...
- weblogic92 启动慢解决办法
使用SUN JDK 启动时要很长时间,但启动启来weblogic正常. 特征见下面标红部分,时间很长. test01@linux-suse:~/bea/weblogic92/samples/dom ...
- Java自定义注解和运行时靠反射获取注解
转载:http://blog.csdn.net/bao19901210/article/details/17201173/ java自定义注解 Java注解是附加在代码中的一些元信息,用于一些工具在编 ...
- UVA - 1416 Warfare And Logistics (最短路)
Description The army of United Nations launched a new wave of air strikes on terroristforces. The ob ...
- 如何使用电骡eMule上传资源
1 在电脑中创建一个文件夹专门为上传资源使用 在里面放入任意文件并右击显示ED2K链接 链接效果如下 2 登陆verycd网站,并点击上传资源 复制ED2K地址并选择分类
- Linux/ visual studio 编译使用Poco
1. 下载源码包.在POCO的官方网站下载最新的POCO源码包.http://pocoproject.org/download/index.html2.解压源码包.下载的文件名是“poco-1.6.0 ...