-
问一个关于排序算法效率的问题。25
就这段代码,下面有3种简单的排序法:冒泡、选择、插入。
我的问题是,为什么冒泡排序和选择排序在对数组进行逆序排序的时候花的时间比对随机数组进行排序所花的时间少呢?
import java.util.Random; public class ArrayUtil { public static void bubbleSort(int[] array){ for(int i=0; i<array.length - 1; i++){ for(int j=0; j<array.length - i - 1; j++){ if(array[j]>array[j + 1]){ swap(array, j, j + 1); } } } } public static void selectionSort(int[] array){ for(int i=0; i<array.length - 1; i++){ int minIndex = i; for(int j=i; j<array.length - 1; j++){ if(array[j + 1] < array[minIndex]){ minIndex = j + 1; } } swap(array, i, minIndex); } } public static void insertionSort(int[] array){ for(int i=1; i<array.length; i++){ if(array[i]<array[i - 1]){ int temp = array[i]; int j = i - 1; while(j>=0 && temp<array[j]){ array[j + 1] = array[j]; j--; } array[j + 1] = temp; } } } private static void swap(int[] array, int index1, int index2) { int temp = array[index1]; array[index1] = array[index2]; array[index2] = temp; } public static void main(String[] args){ int[] array = new int[10000]; generateRandomArray(array); long b = System.currentTimeMillis(); ArrayUtil.bubbleSort(array); long e = System.currentTimeMillis(); System.out.println("冒泡+随机:" + (e - b)/1000.0); generateContradictoryArray(array); b = System.currentTimeMillis(); ArrayUtil.bubbleSort(array); e = System.currentTimeMillis(); System.out.println("冒泡+逆序:" + (e - b)/1000.0); generateRandomArray(array); b = System.currentTimeMillis(); ArrayUtil.selectionSort(array); e = System.currentTimeMillis(); System.out.println("选择+随机:" + (e - b)/1000.0); generateContradictoryArray(array); b = System.currentTimeMillis(); ArrayUtil.selectionSort(array); e = System.currentTimeMillis(); System.out.println("选择+逆序:" + (e - b)/1000.0); generateRandomArray(array); b = System.currentTimeMillis(); ArrayUtil.insertionSort(array); e = System.currentTimeMillis(); System.out.println("插入+随机:" + (e - b)/1000.0); generateContradictoryArray(array); b = System.currentTimeMillis(); ArrayUtil.insertionSort(array); e = System.currentTimeMillis(); System.out.println("插入+逆序:" + (e - b)/1000.0); } private static void generateContradictoryArray(int[] array) { for(int i=0; i<array.length; i++){ array[i] = array.length - i; } } private static void generateRandomArray(int[] array) { Random random = new Random(); for(int i=0; i<array.length; i++){ array[i] = random.nextInt(); } } }
还有,java.util.ArrayList源代码里ensureCapacity方法中这一句int newCapacity = (oldCapacity * 3)/2 + 1;
为什么用这个*3/2+1?这是什么公式?有什么好处?
问题补充:mymGrubby 写道这个是一个在增量处理的问题,有专家统计过1.5倍的增量方式是效率综合最高的。
增量的方式:
1.newCapacity = 所需要的值。
2.newCapacity = oldCapacity + 特定值。
3.newCapacity = oldCapacity * 倍数(>1)。
第一种 当前空间效率最好。但ArrayList变化时要频繁申请内存。
第二种 总体效率比第一种好,但没有第三种好。
第三种 倍数是关键,倍数太大,当前内存浪费过多,倍数太小要频繁申请内存。以前这个倍数大概是2,但数大量数据证明1.5是最好的倍数,再加1应该有更好的效率(oldCapacity 比较小的时候)。
果然,在JDK1.1中,Vector的源代码是这样:private void ensureCapacityHelper(int minCapacity) { int oldCapacity = elementData.length; Object oldData[] = elementData; int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2); if (newCapacity < minCapacity) { newCapacity = minCapacity; } elementData = new Object[newCapacity]; System.arraycopy(oldData, 0, elementData, 0, elementCount); }
谢谢mymGrubby
还有一个问题就是为什么冒泡排序和选择排序在对数组进行逆序排序的时候花的时间比对随机数组进行排序所花的时间少呢?
问题补充:xuyao 写道我来回答一地问题,因为是随即数位数太多了,比较要比较高位,所以开销很大,这样没有可比性。你的逆序最大才1000,所以逆序快,建议生成同等位数的再试试。我反正试过了。
首先谢谢你的回答。
我刚把generateRandomArray()方法给改了,改成这样:
private static void generateRandomArray(int[] array) { Random random = new Random(); for(int i=0; i<array.length; ){ int item = random.nextInt(10000); int j; if(i==0) array[i]=item; for(j=0; j<i; ){ if(array[j]==item) break; j++; } if(j==i){ array[i] = item; i++; } } }
这样保证了生成的随机数组不包含重复的数字,并且是0到10000内的数字。但结果似乎仍然是老样子。xuyao 写道你的逆序最大才1000array.length是10000,第一次循环,i=0,array.length - i应该是10000吧。
问题补充:mymGrubby 写道对冒泡排序和选择排序来说,逆序情况下比随机情况下swap操作要少很多。
这个应该是不对的,冒泡排序逆序情况下每一步都要swap操作,是49995000次,随机的时候要少得多,比如我刚运行了一下,只swap了25152355次。而选择排序的swap操作在逆序情况下和随机情况下是一样的,都是9999次。
问题补充:RednaxelaFX 写道主要还是因为冒泡排序中除了交换之外,寻找逆序对的额外消耗太大了,无法忽略。如果遍历整个数组只完成了一次交换,而这个数组的长度有很大,那么遍历的过程本身显然就有着无法忽略的开销。
可是似乎逆序的时候同样也要遍历相同次数,我并没有在哪个条件下改变i、j的增量,也没有在哪个条件下跳出某次循环。
寻找逆序对的操作同样存在于对逆序数组排序的整个过程中呀。是吧?2008年12月26日 14:35
18个答案 按时间排序 按投票排序
-
采纳的答案
稍微总结一下:
JVM一定是有优化 才造成冒泡的逆序反而快了 这一点毫无疑问 这个优化不是系统的 而是JVM的 因为在.net上结果是合理的
交换的时候的位数对交换没有影响 都是32位int类型 你看到的位数多少是没有意义的 所以是等价的2008年12月29日 16:08
-
再来提供一下JDK1.4的测试结果
平台: WinXP + JDK1.4.2_08
代码: 楼主的代码 仅改了随机数
Random random = new Random(System.currentTimeMillis());引用
C:\Programs\bea\jdk142_08\bin>javac TestSort.java
C:\Programs\bea\jdk142_08\bin>java TestSort
冒泡+随机:0.5
冒泡+逆序:0.36
选择+随机:0.172
选择+逆序:0.171
插入+随机:0.125
插入+逆序:0.235
C:\Programs\bea\jdk142_08\bin>java TestSort
冒泡+随机:0.485
冒泡+逆序:0.36
选择+随机:0.187
选择+逆序:0.172
插入+随机:0.14
插入+逆序:0.266
C:\Programs\bea\jdk142_08\bin>java TestSort
冒泡+随机:0.484
冒泡+逆序:0.375
选择+随机:0.156
选择+逆序:0.188
插入+随机:0.125
插入+逆序:0.25
C:\Programs\bea\jdk142_08\bin>java TestSort
冒泡+随机:0.485
冒泡+逆序:0.375
选择+随机:0.157
选择+逆序:0.172
插入+随机:0.11
插入+逆序:0.235
C:\Programs\bea\jdk142_08\bin>java TestSort
冒泡+随机:0.5
冒泡+逆序:0.375
选择+随机:0.156
选择+逆序:0.188
插入+随机:0.125
插入+逆序:0.234
2008年12月29日 16:07
-
C#太逗乐 居然还有复的
懂行的讲讲为什么
平台: WinXP + .net2.0 + C#2.0
代码:using System; public class TestSort { public static void bubbleSort(int[] array) { for (int i = 0; i < array.Length - 1; i++) { for (int j = 0; j < array.Length - i - 1; j++) { if (array[j] > array[j + 1]) { swap(array, j, j + 1); } } } } public static void selectionSort(int[] array) { for (int i = 0; i < array.Length - 1; i++) { int minIndex = i; for (int j = i; j < array.Length - 1; j++) { if (array[j + 1] < array[minIndex]) { minIndex = j + 1; } } swap(array, i, minIndex); } } public static void insertionSort(int[] array) { for (int i = 1; i < array.Length; i++) { if (array[i] < array[i - 1]) { int temp = array[i]; int j = i - 1; while (j >= 0 && temp < array[j]) { array[j + 1] = array[j]; j--; } array[j + 1] = temp; } } } private static void swap(int[] array, int index1, int index2) { int temp = array[index1]; array[index1] = array[index2]; array[index2] = temp; } public static void Main(string[] args) { int[] array = new int[10000]; generateRandomArray(array); long b = DateTime.Now.Millisecond; bubbleSort(array); long e = DateTime.Now.Millisecond; Console.WriteLine("冒泡+随机:" + (e - b) / 1000.0); generateContradictoryArray(array); b = DateTime.Now.Millisecond; bubbleSort(array); e = DateTime.Now.Millisecond; Console.WriteLine("冒泡+逆序:" + (e - b) / 1000.0); generateRandomArray(array); b = DateTime.Now.Millisecond; selectionSort(array); e = DateTime.Now.Millisecond; Console.WriteLine("选择+随机:" + (e - b) / 1000.0); generateContradictoryArray(array); b = DateTime.Now.Millisecond; selectionSort(array); e = DateTime.Now.Millisecond; Console.WriteLine("选择+逆序:" + (e - b) / 1000.0); generateRandomArray(array); b = DateTime.Now.Millisecond; insertionSort(array); e = DateTime.Now.Millisecond; Console.WriteLine("插入+随机:" + (e - b) / 1000.0); generateContradictoryArray(array); b = DateTime.Now.Millisecond; insertionSort(array); e = DateTime.Now.Millisecond; Console.WriteLine("插入+逆序:" + (e - b) / 1000.0); } private static void generateContradictoryArray(int[] array) { for (int i = 0; i < array.Length; i++) { array[i] = array.Length - i; } } private static void generateRandomArray(int[] array) { Random random = new Random(DateTime.Now.Millisecond); for (int i = 0; i < array.Length; i++) { array[i] = random.Next(); } } }
结果:引用
C:\>TestSort
冒泡+随机:0.516
冒泡+逆序:-0.672
选择+随机:0.218
选择+逆序:0.235
插入+随机:0.11
插入+逆序:-0.781
C:\>TestSort
冒泡+随机:-0.484
冒泡+逆序:0.328
选择+随机:-0.765
选择+逆序:0.234
插入+随机:0.109
插入+逆序:0.235
C:\>TestSort
冒泡+随机:-0.485
冒泡+逆序:0.329
选择+随机:0.234
选择+逆序:0.234
插入+随机:-0.859
插入+逆序:0.218
C:\>TestSort
冒泡+随机:0.515
冒泡+逆序:-0.672
选择+随机:0.235
选择+逆序:0.219
插入+随机:0.109
插入+逆序:0.235
C:\>TestSort
冒泡+随机:0.516
冒泡+逆序:-0.672
选择+随机:0.218
选择+逆序:0.25
插入+随机:0.125
插入+逆序:0.25
2008年12月29日 15:57
-
再来提供一下JDK6的测试结果
平台: WinXP + JDK1.6.0_07
代码: 楼主的代码 仅改了随机数
Random random = new Random(System.nanoTime());
引用
冒泡+随机:0.421
冒泡+逆序:0.266
选择+随机:0.141
选择+逆序:0.156
插入+随机:0.078
插入+逆序:0.172
冒泡+随机:0.406
冒泡+逆序:0.25
选择+随机:0.156
选择+逆序:0.141
插入+随机:0.094
插入+逆序:0.156
冒泡+随机:0.421
冒泡+逆序:0.266
选择+随机:0.141
选择+逆序:0.14
插入+随机:0.11
插入+逆序:0.156
冒泡+随机:0.406
冒泡+逆序:0.266
选择+随机:0.172
选择+逆序:0.14
插入+随机:0.094
插入+逆序:0.172
冒泡+随机:0.39
冒泡+逆序:0.266
选择+随机:0.141
选择+逆序:0.156
插入+随机:0.078
插入+逆序:0.172
2008年12月29日 15:41
-
俺来提供一些测试结果 不用JVM 用J#
平台: WinXP + .net2.0 + J#2.0
代码: 楼主的代码 基本上没改 仅 随机数那一行 改成了
Random random = new Random(System.currentTimeMillis());
引用
冒泡+随机:0.625
冒泡+逆序:0.656
选择+随机:0.141
选择+逆序:0.14
插入+随机:0.094
插入+逆序:0.219
请按任意键继续. . .
冒泡+随机:0.657
冒泡+逆序:0.656
选择+随机:0.141
选择+逆序:0.14
插入+随机:0.11
插入+逆序:0.203
请按任意键继续. . .
冒泡+随机:0.641
冒泡+逆序:0.656
选择+随机:0.141
选择+逆序:0.14
插入+随机:0.11
插入+逆序:0.203
请按任意键继续. . .
冒泡+随机:0.625
冒泡+逆序:0.688
选择+随机:0.156
选择+逆序:0.125
插入+随机:0.109
插入+逆序:0.203
请按任意键继续. . .
冒泡+随机:0.625
冒泡+逆序:0.688
选择+随机:0.125
选择+逆序:0.14
插入+随机:0.094
插入+逆序:0.203
请按任意键继续. . .2008年12月29日 15:35
-
to mymGrubby:冒泡真不知道了,难道jvm对刚变动的数据做了缓存。。。这话有道理,也就是RednaxelaFX。相邻的两个单元的操作比跳着找逆序要快,不过这是不是java特有的,我回家用c试试。
2008年12月26日 20:01
-
冒泡排序会不会应为
if(array[j]>array[j + 1])
这个总是不成立的,对比较分支jvm的预处理造成的效益2008年12月26日 19:50
-
晕哦,冒泡逆序搞错了
选择逆序时swap事实只有length/2
比如
选择逆序 4,3,2,1
第一次4和1交换:1,3,2,4
第二次是3和2交换:1,2,3,4
比较还有但交换没有了
后面的交换是自身的交换,应该比较快,或者jvm做了优化?
public static void selectionSort(int[] array) { for (int i = 0; i < array.length - 1; i++) { int minIndex = i; for (int j = i; j < array.length - 1; j++) { if (array[j + 1] < array[minIndex]) { minIndex = j + 1; } } if(i != minIndex){ swap(array, i, minIndex); } } }
冒泡真不知道了,难道jvm对刚变动的数据做了缓存。。。
2008年12月26日 19:43
-
排序的本质就是消除逆序对。
例如说按升序排列[3, 2, 1]的话,那么逆序对就有:
(3, 2)
(3, 1)
(2, 1)
这三对。
以冒泡为例来看。冒泡排序的本质是每次遇到位置相邻的逆序对时就做一次交换,并保证每一次交换只解决一对逆序对。
回到上面[3, 2, 1]的例子,冒泡的
第一轮:
(3, 2) -> [2, 3, 1] // (2, 1), (3, 1)
(3, 1) -> [2, 1, 3] // (2, 1)
第二轮:
(2, 1) -> [1, 2, 3] // done
那么,不考虑输入中有重复的情况,逆序的输入中含有的逆序对应该是最多的。假设输入长度为n,则逆序对的个数为C(n, 2)(n个元素取2个的组合)。既然如此,在对逆序输入做排序时,冒泡排序要做的swap次数也是C(n, 2)次,也是所有输入可能中最多的。想想看,如果不是正好逆序,那么从长度为n的输入中任意取2个元素,必然有至少有一组不是逆序对。
那么为什么在对随机输入排序的时候反而比较慢?主要还是因为冒泡排序中除了交换之外,寻找逆序对的额外消耗太大了,无法忽略。如果遍历整个数组只完成了一次交换,而这个数组的长度有很大,那么遍历的过程本身显然就有着无法忽略的开销。
正好我这边有些对比表图可以看看:http://rednaxelafx.iteye.com/blog/1740632008年12月26日 19:13
-
to:mymGrubby,大哥,“对冒泡排序和选择排序来说,逆序情况下比随机情况下swap操作要少很多”搞笑呢吧,逆序的swap是最多的,要把第一个放到交换的最后面,我上面已经说了,是位数的关系。lz,你随即出来的是什么你看了吗?int是几位的?能和1000一下的比吗?不慢才怪
2008年12月26日 18:08
-
第一个问题是应为整个排序算法中消耗时间资源的操作是:
swap(int[] array, int index1, int index2)
对冒泡排序和选择排序来说,逆序情况下比随机情况下swap操作要少很多。2008年12月26日 17:51
-
我来回答一地问题,因为是随即数位数太多了,比较要比较高位,所以开销很大,这样没有可比性。你的逆序最大才1000,所以逆序快,建议生成同等位数的再试试。我反正试过了。
2008年12月26日 17:23
-
这个是一个在增量处理的问题,有专家统计过1.5倍的增量方式是效率综合最高的。
增量的方式:
1.newCapacity = 所需要的值。
2.newCapacity = oldCapacity + 特定值。
3.newCapacity = oldCapacity * 倍数(>1)。
第一种 当前空间效率最好。但ArrayList变化时要频繁申请内存。
第二种 总体效率比第一种好,但没有第三种好。
第三种 倍数是关键,倍数太大,当前内存浪费过多,倍数太小要频繁申请内存。以前这个倍数大概是2,但数大量数据证明1.5是最好的倍数,再加1应该有更好的效率(oldCapacity 比较小的时候)。2008年12月26日 16:33
相关推荐
各种排序算法效率分析比较及源代码 C语言实现 各种排序包括: 直接插入排序,折半插入排序,2—路插入排序和表插入排序;希尔排序和链式基数排序;起泡排序,快速排序,归并排序;简单选择排序,树形选择排序和堆...
本篇文章将深入探讨这两种排序算法的原理、效率以及它们在C#编程语言中的实现。 首先,让我们了解冒泡排序。冒泡排序是一种简单直观的排序方法,它通过重复遍历待排序的数列,比较相邻元素并根据需要交换位置,以使...
通过几组有代表意义的随机数据的比较,算出几种这几种排序算法的关键字比较次数和关键字移动次数,以便我们分析算法效率。 1、通过修改程序,实现程序在要求的数据量下求出以下六种内部排序算法的移动次数和比较次数...
本实验通过生成大量随机数并写入文件,然后使用四种不同的排序算法进行排序,以比较它们的效率。以下是对这四种排序算法的详细解释: 1. **冒泡排序(Bubble Sort)** 冒泡排序是最基础的排序算法之一,它通过重复...
《排序算法效率分析——动态显示排序过程》 在信息技术领域,排序算法是计算机科学的基础,其性能直接影响到程序的运行效率。本软件是由资深算法研究者精心编写的,旨在通过动态展示排序算法的过程,帮助用户深入...
各种内部排序算法的时间复杂度分析结果只给出了算法执行的时间的阶,或大概执行时间。设计和实现内部排序算法效率比较平台,通过随机的数据比较各算法的关键字比较次数和关键字移动次数,以取得直观的感受。
本项目“排序算法效率比较(含报告)”旨在通过实际运行并测量程序执行时间,对多种常见的排序算法进行详细比较,以便了解它们在不同情况下的性能表现。 一、排序算法的种类及其工作原理 1. 冒泡排序:通过重复遍历...
快速排序算法是一种高效的排序算法,它的工作原理是通过选择一个元素作为pivot,然后将数组分为两个部分,以达到排序的目的。快速排序算法的时间复杂度为O(n log n),因此它适合大规模的数据排序。 5.归并排序算法 ...
快速排序是一种高效的排序算法,它的工作原理是选择一个枢轴元素,然后将数组分割成两个子数组,并递归地对每个子数组进行排序。该算法的时间复杂度为O(n log n),空间复杂度为O(log n)。在我们的实现中,我们使用了...
算法课的一个小项目,语言python。代码实习7种排序算法,TK实现简单GUI,源码可以学习7中排序算法详细实现,和GUI的搭建,基本包含了常用GUI组件。
超级经典的计数排序算法,号称效率达到了O(n) 超级经典的计数排序算法,号称效率达到了O(n) 超级经典的计数排序算法,号称效率达到了O(n) 超级经典的计数排序算法,号称效率达到了O(n) 超级经典的计数排序算法,号称...
在计算机科学领域,算法效率是衡量一个算法性能的重要指标,特别是在大数据处理和复杂问题解决中。本文将深入探讨几种常见的排序算法——冒泡排序、插入排序、希尔排序、选择排序、快速排序和堆排序,分析它们在不同...
4. **通用快速排序**:这部分介绍了一个基于模板的快速排序实现,它可以对任何数据类型进行排序。快速排序是一种高效的排序算法,平均时间复杂度为O(N log N),其核心是选取合适的“枢轴”元素来划分数组。 在编程...
稳定性是排序算法中一个重要的特性,指的是相等的元素在排序前后保持原有的相对位置不变。根据文档提供的信息,我们可以总结出以下结论: - **稳定排序**:插入排序、冒泡排序、二叉树排序、二路归并排序以及其他...
在编程领域,排序算法是计算机科学中的基础但至关重要的部分,它们用于整理数据,提高数据处理的效率。本文将深入探讨MFC(Microsoft Foundation Classes)环境下实现的各种内部排序算法,包括冒泡排序、选择排序、...
内部排序算法效率比较: 直接排序 起泡排序 快速排序 简单选择排序 堆排序 希尔排序
合并排序是一种基于分治策略的排序算法,它将大问题分解为小问题来解决。首先将数组分为两个相等或近乎相等的部分,然后对每一部分递归地进行排序,最后将结果合并。这种算法的时间复杂度为O(n log n),稳定性好,...
在编程领域,排序算法是计算机科学中的重要组成部分,特别是在数据处理和算法效率分析上。本文将详细介绍C++中实现的希尔排序、快速排序、堆排序和归并排序这四种经典排序算法。 希尔排序,由Donald Shell于1959年...
通过这样的课程设计,学生可以深入理解各种排序算法的原理和实际运行效果,同时培养了编程能力和问题解决能力。对于测试结果的简单分析,可能涉及到讨论不同算法在不同数据结构下的表现,以及波动原因,比如快速排序...
排序算法是计算机科学中最基础和...总的来说,排序算法在日常编程中扮演着至关重要的角色,选择合适的排序算法能够显著提升程序的效率和性能。理解这些基本的排序算法及其特性,对于任何IT专业人员来说都是非常必要的。