高速排序及优化(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 ...
随机推荐
- 标准C程序设计七---53
Linux应用 编程深入 语言编程 标准C程序设计七---经典C11程序设计 以下内容为阅读: <标准C程序设计>(第7版) 作者 ...
- configure: error: C++ preprocessor "/lib/cpp" fails sanity check
configure: error: C++ preprocessor "/lib/cpp" fails sanity check 参考链接: error: C++ preproce ...
- UVA - 10050 Hartals
#include <cstdio> #include <cstring> ]; ]; int main() { int t; scanf("%d", &am ...
- LeetCode OJ--ZigZag Conversion
https://oj.leetcode.com/problems/zigzag-conversion/ 将字符串Z形字排列后,再重新一行一行输出. 可以找到每一行字符位置的规律,然后填充进去. 敲代码 ...
- 腾讯IM的那些坑
项目中接入腾讯IM,在这里记录下,以便大家解决问题时少走弯路 1.首先讲一下IM返回对象的问题: /** * 消息工厂方法 */ public static Message getMessage(TI ...
- HDU 1018.Big Number-Stirling(斯特林)公式 取N阶乘近似值
最近一堆题目要补,一直咸鱼,补了一堆水题都没必要写题解.备忘一下这个公式. Stirling公式的意义在于:当n足够大时,n!计算起来十分困难,虽然有很多关于n!的等式,但并不能很好地对阶乘结果进行估 ...
- Swoole RPC 的实现
目录 概述 实现效果 代码 小结 概述 这是关于 Swoole 学习的第七篇文章:Swoole RPC 的实现. 第六篇:Swoole 整合成一个小框架 第五篇:Swoole 多协议 多端口 的应用 ...
- 快速掌握分布式搜索引擎ElasticSearch(一)
前言 由于最近在项目中接触使用到了ElasticSearch,从本篇博客开始将给大家分享这款风靡全球的产品.将涉及到ElasticSearch的安装.基础概念.基本用法.高级查询.中文分词器.与Spr ...
- [功能集锦] 001 - java下载文件
@RequestMapping("/downloadxls.action") public void downloadxls(HttpServletRequest request, ...
- Chrom查看Flash缓存文件及Flash下载方法
比如在优酷看视频时,或者熊猫直播,如果使用Flash进行播放的基本都会先缓存在本地,只不过这个缓存的名字后缀不叫flv,而是类似tmp这样:通常只要找到这个缓存文件,然后改为flv即可播放:如果出现文 ...