0

0

Java常用排序算法及性能测试集合

高洛峰

高洛峰

发布时间:2017-01-17 13:13:48

|

1163人浏览过

|

来源于php中文网

原创

现在再回过头理解,结合自己的体会, 选用最佳的方式描述这些算法,以方便理解它们的工作原理和程序设计技巧。本文适合做java面试准备的材料阅读。

先附上一个测试报告:

Array length: 20000
bubbleSort : 766 ms
bubbleSortAdvanced : 662 ms
bubbleSortAdvanced2 : 647 ms
selectSort : 252 ms
insertSort : 218 ms
insertSortAdvanced : 127 ms
insertSortAdvanced2 : 191 ms
binaryTreeSort : 3 ms
shellSort : 2 ms
shellSortAdvanced : 2 ms
shellSortAdvanced2 : 1 ms
mergeSort : 3 ms
quickSort : 1 ms
heapSort : 2 ms

通过测试,可以认为,冒泡排序完全有理由扔进垃圾桶。它存在的唯一理由可能是最好理解。希尔排序的高效性是我没有想到的;堆排序比较难理解和编写,要有宏观的思维。

元典智库
元典智库

元典智库:智能开放的法律搜索引擎

下载
package algorithm.sort;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
/**
 * Java常用排序算法及性能测试集合
 * 
 * 本程序集合涵盖常用排序算法的编写,并在注释中配合极其简单的特例讲解了各种算法的工作原理,以方便理解和吸收;
 * 程序编写过程中吸收了很多维基百科和别人blog上面的例子,并结合自己的思考,选择并改进一个最容易让人理解的写法
 *(尤其是快速排序,我觉得我写的算法最好理解)。
 * 同时包含一个集中式的性能测试和正确性测试方法,方便观测。
 * @author /link.php?url=http://blog.csdn.net/sunxing007
 * 转载请注明来自/link.php?url=http://blog.csdn.net/sunxing007
 */
public class SortUtil {
 // 被测试的方法集合
 static String[] methodNames = new String[]{
  "bubbleSort",
  "bubbleSortAdvanced",
  "bubbleSortAdvanced2",
  "selectSort",
  "insertSort",
  "insertSortAdvanced",
  "insertSortAdvanced2",
  "binaryTreeSort",
  "shellSort",
  "shellSortAdvanced",
  "shellSortAdvanced2",
  "mergeSort",
  "quickSort",
  "heapSort"
 };
    public static void main(String[] args) throws Exception{
     //correctnessTest();
     performanceTest(20000);
    }

    /**
     * 正确性测试
* 简单地测试一下各个算法的正确性
* 只是为了方便观测新添加的算法是否基本正确;
* @throws Exception 主要是反射相关的Exception;
*/ public static void correctnessTest() throws Exception{ int len = 10; int[] a = new int[len]; for(int i=0; i * 数组长度用参数len传入,每个方法跑20遍取耗时平均值;
* @param len 数组长度 建议取10000以上,否则有些算法会显示耗时为0;
* @throws Exception 主要是反射相关的Exception;
*/ public static void performanceTest(int len) throws Exception{ int[] a = new int[len]; int times = 20; System.out.println("Array length: " + a.length); for(int i=0; i * 两层遍历,外层控制扫描的次数,内层控制比较的次数;
* 外层每扫描一次,就有一个最大的元素沉底;所以内层的比较次数将逐渐减小;
*/ public static void bubbleSort(int[] a){ for(int i=0; ia[j+1]){ int tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp; } } } } /** * 改进的冒泡法
* 改进之处在于:设一个标志位,如果某趟跑下来,没有发生交换,说明已经排好了;
*/ public static void bubbleSortAdvanced(int[] a){ int k = a.length-1; boolean flag = true; while(flag){ flag = false; for(int i=0;ia[i+1]){ int tmp = a[i]; a[i] = a[i+1]; a[i+1] = tmp; //有交换则继续保持标志位; flag = true; } } k--; } } /** * 改进的冒泡法2
* 改进之处在于吸收上面的思想(没有交换意味着已经有序),如果局部的已经是有序的,则后续的比较就不需要再比较他们了。
* 比如:3142 5678,假如刚刚做完了2和4交换之后,发现这趟比较后续再也没有发生交换,则后续的比较只需要比到4即可;
* 该算法就是用一个标志位记录某趟最后发生比较的地点;
*/ public static void bubbleSortAdvanced2(int[] a){ int flag = a.length - 1; int k; while(flag>0){ k = flag; flag = 0; for(int i=0; i a[i+1]){ int tmp = a[i]; a[i] = a[i+1]; a[i+1] = tmp; //有交换则记录该趟最后发生比较的地点; flag = i+1; } } } } /** * 插入排序 * * 关于插入排序,这里有几个约定,从而可以快速理解算法:
* i: 无序表遍历下标;i * j: 有序表遍历下表;0<=j * a[i]:表示当前被拿出来做插入排序的无序表头元素;
* a[j]:有序表中的任意元素;
*
* 算法关键点:把数组分割为a[0~i-1]有序表,a[i~n-1]无序表;每次从无序表头部取一个,
* 把它插入到有序表适当的位置,直到无序表为空;
* 初始时,a[0]为有序表,a[1~n-1]为无序表;
*/ public static void insertSort(int[] a){ //从无序表头开始遍历; for(int i=1; i=0; j--){ if(a[j] < a[i]){ break; } } //如果找到恰当的位置,则从该位置开始,把元素朝后移动一格,为插入的元素腾出空间; if(j!=(i-1)){ int tmp = a[i]; int k; for(k = i-1; k>j;k--){ a[k+1] = a[k]; } a[k+1] = tmp; } } } /** * 改进的插入排序1 * 改进的关键在于:首先拿无序表头元素a[i]和有序表尾a[i-1]比较, * 如果a[i]=0&&a[j]>tmp;j--){ a[j+1] = a[j]; } a[j+1] = tmp; } } } /** * 改进的插入排序2 * 总体思想和上面相似,拿无序表头元素从有序表尾元素开始朝前比较, * 如果a[i]比a[i-1]小,则把a[i]从有序表尾用冒泡交换的方式朝前移动,直到到达恰当的位置; */ public static void insertSortAdvanced2(int[] a){ //遍历无序表 for(int i=1; i=0 && a[j] > a[j+1]; j--){//a[j+1]就是a[i] int tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp; } } } /** * 快速排序
* 算法的思想在于分而治之:先找一个元素(一般来说都是数组头元素),把比它大的都放到右边,把比它小的都放到左边;
* 然后再按照这样的思想去处理两个子数组; 下面说的子数组头元素通指用来划分数组的元素;
*
* 下面程序关键点就在于!forward, low0++, high0--这些运算; 这三个运算使得a[low0],a[high0]里面总有一个指向子数组头元素;
* 可以用极端的情况来方便理解这三个值的运作:
* 假如我的数列为0123456789, 初始时forward=false,0作为子数组划分依据,很显然第一轮的时候不会发生任何交换,low0一直指向0,
* high0逐渐下降直到它指向0为止; 同理可思考9876543210这个例子;
*
* @param a 待排序数组
* @param low 子数组开始的下标;
* @param high 子数组结束的下标;
*/ public static void quickSort(int[] a, int low, int high){ if(low>=high){ return; } int low0 = low; int high0 = high; boolean forward = false; while(low0!=high0){ if(a[low0]>a[high0]){ int tmp = a[low0]; a[low0] = a[high0]; a[high0] = tmp; forward = !forward; } if(forward){ low0++; } else{ high0--; } } low0--; high0++; quickSort(a, low, low0); quickSort(a, high0, high); } /** * 快速排序的简单调用形式
* 方便测试和调用
* @param a */ public static void quickSort(int[] a){ quickSort(a, 0, a.length-1); } /** * 归并排序
* 所谓归并,就是合并两个有序数组;归并排序也用了分而治之的思想,把一个数组分为若干个子数组;
* 当子数组的长度为1的时候,则子数组是有序的,于是就可以两两归并了;
*
* 由于归并排序需要分配空间来转储归并的结果,为了算法上的方便,归并算法的结果以返回值的形式出现;
*/ /** * 合并两个有序数组 * @param a 有序数组1 * @param b 有序数组2 * @return 合并之后的有序数组; */ public static int[] merge(int[] a, int[] b){ int result[] = new int[a.length+b.length]; int i=0,j=0,k=0; while(i * 把数组从中间一分为二,并对左右两部分递归调用,直到数组长度为1的时候,开始两两归并;
* @param 待排序数组; * @return 有序数组; */ public static int[] mergeSort(int[] a){ if(a.length==1){ return a; } int mid = a.length/2; int[] leftPart = new int[mid]; int[] rightPart = new int[a.length-mid]; System.arraycopy(a, 0, leftPart, 0, leftPart.length); System.arraycopy(a, mid, rightPart, 0, rightPart.length); leftPart = mergeSort(leftPart); rightPart = mergeSort(rightPart); return merge(leftPart, rightPart); } /** * 选择排序
* 和插入排序类似,它也把数组分割为有序区和无序区,所不同的是:
* 插入排序是拿无序区的首元素插入到有序区适当的位置,而
* 选择排序是从无序区中挑选最小的放到有序区最后;
*
* 两层循环,外层控制有序区的队尾,内层用来查找无序区最小元素;
* @param a */ public static void selectSort(int[] a){ for(int i=0; i * 其思想是把数组按等步长(/间距)划分为多个子序列,对各个子序列做普通的插入排序,
逐次降低步长,直到为1的时候最后再做一次普通的插入排序; * 用一个极端的例子作比方,我有数列如下:
* [1,2,3,4,5,6,7,8,9,10];
* 初始的时候,步长gap=5;则划分的子数组为[1,6], [2,7], [3,8], [4,9], [5,10];
对他们分别排序(当然由于本数组特殊,所以结果是不变的);
* 然后gap=2=5/2; 子数组为[1,3,5,7,9], [2,4,6,8,10];
* 最后gap=1=2/2; 做一次全局排序;
*
* 希尔排序克服了插入/冒泡排序的弱点(一次只能把元素移动一个相邻的位置),
依靠大步长,可以把元素尽快移动到目标位置(或附近);
* 希尔排序实际上是插入排序的变种。它适用于:当数组总体有序,个别需要调整的情况;这时候利用插入排序的优势,可以达到O(n)的效率;
* 影响希尔算法的一个重要的因素是步长选择,一个好步长的优点是:后面的短步长排序不会破坏前面的长步长排序;
* 怎么理解这种破坏呢?前面的长步长把一个较小的数移到了左面,但是在缩小步长之后有可能又被交换到了右面 (因为它被分到了一个有很多比它更小的组);
* 关于步长,可以查看http://zh.wikipedia.org上面关于希尔排序的页面;
* 下面的程序是希尔排序最基础的写法,适合用来理解希尔排序思想;
*/ public static void shellSort(int[] a){ // 控制间距;间距逐渐减小,直到为1; for(int gap = a.length/2; gap>0; gap/=2){ // 扫描每个子数组 for(int i=0; i=0&&a[k]>tmp){ a[k+gap] = a[k]; k-=gap; } a[k+gap] = tmp; } } } } } /** * 改进的希尔排序
* 改进之处在于:上面的写法用一个for循环来区别对待每个字数组;而实际上是不必要的;
* a[0,1,...gap-1]作为所有子数组的有序区,a[gap,...n-1]作为所有字数组的无序区;
*
* 该改进在时间效率上没有改进;只是让程序看起来更简洁;
* @param a */ public static void shellSortAdvanced(int[] a){ // 控制步长 for(int gap = a.length/2; gap>0; gap/=2){ // 从无序区开始处理,把多个子数组放在一起处理; for(int j=gap; j=0&&a[k]>tmp){ a[k+gap] = a[k]; k-=gap; } a[k+gap] = tmp; } } } } /** * 改进的希尔排序2
* 在吸收shellSortAdvanced思想的基础上,采用insertAdvanced2的做法;
即无序区首元素通过朝前冒泡的形式移动的适当的位置;
* @param a */ public static void shellSortAdvanced2(int[] a){ for(int gap = a.length/2; gap>0; gap/=2){ for(int i=gap; i=0&&a[j+gap]>a[j]; j-=gap){ int tmp = a[j]; a[j] = a[j+gap]; a[j+gap] = tmp; } } } } } /** * 堆排序
* 堆的定义:堆是一个完全,或近似完全的二叉树,堆顶元素的值大于左右孩子的值,左右孩子也需要满足这个条件;
* 按照堆的定义,堆可以是大顶堆(maxHeap),或小顶堆(minHeap);
* 一般用数组即可模拟二叉树,对于任意元素i,左孩子为2*i+1,右孩子为2*i+2;父节点为(i-1)/2; * @param a */ public static void heapSort(int[] a){ // 先从最后一个非叶子节点往上调整,使满足堆结构; for(int i=(a.length-2)/2; i>=0; i--){ maxHeapAdjust(a, i, a.length); } // 每次拿最后一个节点和第一个交换,然后调整堆;直到堆顶; for(int i=a.length-1; i>0; i--){ int tmp = a[i]; a[i] = a[0]; a[0] = tmp; maxHeapAdjust(a, 0, i); } } /** * 调整堆
* 把以i为跟节点的二叉树调整为堆;
* 可以这么来思考这个过程:这个完全二叉树就像一个金字塔,塔顶的小元素沿着树结构,往下沉降;
* 调整的结果是最大的元素在金字塔顶,然后把它从堆中删除(把它交换到堆尾,然后堆收缩一格);
* 堆排序快的原因就是根据二叉树的特点,一个节点要沉降到合适的位置,只需要logn步;同时前期调整的结果(大小顺序)会被记录下来,从而加快后续的调整;
* @param a 待排数组 * @param i 堆顶 * @param len 堆长度 */ public static void maxHeapAdjust(int[] a, int i, int len){ int tmp = a[i]; // j是左孩子节点 int j = i*2+1; // while(ja[j]){ j++; } // 找到恰当的位置就不再找 if(a[j] * 二叉树的定义是嵌套的:
节点的值大于左叶子节点的值,小于右叶子节点的值;叶子节点同样满足这个要求;
* 二叉树的构造过程就是排序的过程:
* 先构造跟节点,然后调用add方法添加后续节点为跟节点的子孙节点;这个过程也是嵌套的;
*
* 中序遍历二叉树即得到有序结果;
* 二叉树排序用法特殊,使用情形要视情况而定;
* @param a */ public static void binaryTreeSort(int[] a){ // 构造一个二叉树节点内部类来实现二叉树排序算法; class BinaryNode{ int value; BinaryNode left; BinaryNode right; public BinaryNode(int value){ this.value = value; this.left = null; this.right = null; } public void add(int value){ if(value>this.value){ if(this.right!=null){ this.right.add(value); } else{ this.right = new BinaryNode(value); } } else{ if(this.left!=null){ this.left.add(value); } else{ this.left = new BinaryNode(value); } } } /** * 按中序遍历二叉树,就是有序的。 */ public void iterate(){ if(this.left!=null){ this.left.iterate(); } // 在测试的时候要把输出关掉,以免影响性能; // System.out.print(value + ", "); if(this.right!=null){ this.right.iterate(); } } } BinaryNode root = new BinaryNode(a[0]); for(int i=1; i

更多Java常用排序算法及性能测试集合相关文章请关注PHP中文网!

立即学习Java免费学习笔记(深入)”;

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

54

2026.01.31

高干文在线阅读网站大全
高干文在线阅读网站大全

汇集热门1v1高干文免费阅读资源,涵盖都市言情、京味大院、军旅高干等经典题材,情节紧凑、人物鲜明。阅读专题下面的文章了解更多详细内容。

40

2026.01.31

无需付费的漫画app大全
无需付费的漫画app大全

想找真正免费又无套路的漫画App?本合集精选多款永久免费、资源丰富、无广告干扰的优质漫画应用,涵盖国漫、日漫、韩漫及经典老番,满足各类阅读需求。阅读专题下面的文章了解更多详细内容。

50

2026.01.31

漫画免费在线观看地址大全
漫画免费在线观看地址大全

想找免费又资源丰富的漫画网站?本合集精选2025-2026年热门平台,涵盖国漫、日漫、韩漫等多类型作品,支持高清流畅阅读与离线缓存。阅读专题下面的文章了解更多详细内容。

12

2026.01.31

漫画防走失登陆入口大全
漫画防走失登陆入口大全

2026最新漫画防走失登录入口合集,汇总多个稳定可用网址,助你畅享高清无广告漫画阅读体验。阅读专题下面的文章了解更多详细内容。

13

2026.01.31

php多线程怎么实现
php多线程怎么实现

PHP本身不支持原生多线程,但可通过扩展如pthreads、Swoole或结合多进程、协程等方式实现并发处理。阅读专题下面的文章了解更多详细内容。

1

2026.01.31

php如何运行环境
php如何运行环境

本合集详细介绍PHP运行环境的搭建与配置方法,涵盖Windows、Linux及Mac系统下的安装步骤、常见问题及解决方案。阅读专题下面的文章了解更多详细内容。

0

2026.01.31

php环境变量如何设置
php环境变量如何设置

本合集详细讲解PHP环境变量的设置方法,涵盖Windows、Linux及常见服务器环境配置技巧,助你快速掌握环境变量的正确配置。阅读专题下面的文章了解更多详细内容。

0

2026.01.31

php图片如何上传
php图片如何上传

本合集涵盖PHP图片上传的核心方法、安全处理及常见问题解决方案,适合初学者与进阶开发者。阅读专题下面的文章了解更多详细内容。

2

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go语言教程-全程干货无废话
Go语言教程-全程干货无废话

共100课时 | 10万人学习

JavaScript OOP调试技巧视频教程
JavaScript OOP调试技巧视频教程

共5课时 | 1.0万人学习

FastJson教程手册
FastJson教程手册

共25课时 | 18.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号