`

java.util.Arrays中的快速排序

阅读更多
http://hxraid.iteye.com/blog/665095
【java.uti.Arrays】 包含用来操作数组(比如排序和搜索)的各种方法。这篇文章我们就来研究一些大师们写的排序算法。


(1) 基本数据类型数组的排序,如Arrays.sort(int[])等。采用了一种经 过调优的快速排序 。 该算法改编自 Jon L. Bentley 和 M. Douglas McIlroy 合著的 Engineering a Sort Function", Software-Practice and Experience Vol. 23(11) P. 1249-1265 (November 1993)。此算法在许多数据集上提供 n*log(n) 性能,这导致其他快速排序会降低二次型性能。


下面是JDK中调优快速排序算法的源代码:
  /** 
     * 将指定范围的整形数组升序排序。 
     * x[] 待排数组 
     * off 从数组的第off个元素开始排序 
     * len 数组长度 
     */  
    private static void sort1(int x[], int off, int len) {  
 //优化1:在小规模(size<7)数组中,直接插入排序的效率要比快速排序高。  
 if (len < 7) {  
     for (int i=off; i<len+off; i++)  
         for (int j=i; j>off && x[j-1]>x[j]; j--)  
             swap(x, j, j-1);  
     return;  
 }  
   
 //优化2:精心选择划分元素,即枢轴  
 //如果是小规模数组(size<=7),直接取中间元素作为枢轴  
 //如果是中等规模数组(7=<size<=40),则在数组首、中、尾三个位置上的数中取中间大小的数作为枢轴  
 //如果是大规模数组(size>40),则在9个指定的数中取一个伪中数(中间大小的数s)  
 int m = off + (len >> 1);  
 if (len > 7) {   
     int l = off;  
     int n = off + len - 1;  
     if (len > 40) {          
         int s = len/8;  
         l = med3(x, l, l+s, l+2*s);  
         m = med3(x, m-s,   m,   m+s);  
         n = med3(x, n-2*s, n-s, n);  
     }  
     m = med3(x, l, m, n);  
 }  
 int v = x[m];  
   
         //优化3:每一次枢轴v的划分,都会形成形成一个形如  (<v)* v* (>v)*  
        //阶段一,形成 v* (<v)* (>v)* v* 的数组  
        int a = off, b = a, c = off + len - 1, d = c;  
        while(true) {  
     while (b <= c && x[b] <= v) {  
         if (x[b] == v)  
             swap(x, a++, b);  
         b++;  
     }  
     while (c >= b && x[c] >= v) {  
         if (x[c] == v)  
             swap(x, c, d--);  
         c--;  
     }  
     if (b > c)  
         break;  
     swap(x, b++, c--);  
 }  
   
 //阶段二,将枢轴和与枢轴相等的元素交换到数组中间  
 int s, n = off + len;  
 s = Math.min(a-off, b-a  );  vecswap(x, off, b-s, s);  
 s = Math.min(d-c,   n-d-1);  vecswap(x, b,   n-s, s);  
   
 //阶段三,递归排序与枢轴不相等都元素区间  
 if ((s = b-a) > 1)  
     sort1(x, off, s);  
 if ((s = d-c) > 1)  
     sort1(x, n-s, s);  
    }   

★ 优化1:在小规模(size<7)数组中,直接插入排序的效率要比快速排序高。

      没有一种排序在任何情况下都是最优的《基于比较的内部排序总结 》。 O(N^2)级别的排序看起来似乎比所有先进排序要差的多。但实际上也并非如此,Arrays中的sort()算法就给了我们一个很好的例子。当待排数组规模非常小的时候(JDK中规模的阈值为INSERTIONSORT_THRESHOLD=7),直接插入排序反而要比快排,归并排序要好。

           这个道理很简单。数组规模小,简单算法的比较次数不会比先进算法多多少。相反,诸如快排,归并排序等先进算法使用递归操作,所付出的运行代价更高。



★ 优化2:精心选择划分元素,即枢轴。

      快排有一种最差的情况,即蜕化成效率最差的起跑排序(见《 交换排序 》)。 导致这种情况产生的主要原因就是枢轴的选择并不能把整个数组划分成两个大致相等的部分。比如对于基本有序的数组,选择第一个元素作为枢轴就会产生这种蜕化。

      既然如此,我们可以看看Arryas.sort()是如何为我们选择枢轴的。

      ● 如果是小规模数组(size<=7),直接取中间元素作为枢轴。     

      ● 如果是中等规模数组(7=<size<=40),则在数组首、中、尾三个位置上的数中取中间大小的数作为枢轴
      ● 如果是大规模数组(size>40),则在9个指定的数中取一个伪中数(中间大小的数s)

      中小规模时,这种取法尽量可以避免数组的较小数或者较大数成为枢轴。值得一提的是大规模的时候,首先在数组中寻找9个数据(可以通过源代码发现这9个数据的位置较为平均的分布在整个数组上);然后每3个数据找中位数;最后在3个中位数上再找出一个中位数作为枢轴。

      仔细想想,这种精心选择的枢轴,使得快排的最差情况成为了极小概率事件了。



★ 优化3:根据枢轴v划分,形成一个形如  (<v)*   v* (>v)* 的数组

      普通快排算法,都是使得枢轴元素移动到数组的较中间位置。枢轴之前的元素全部小于或等于枢轴,之后的元素全部大于枢轴。但与枢轴相等的元素并不能移动到枢轴附近位置。这一点在Arrays.sort()算法中有很大的优化。

      我们举个例子来说明Arrays的优化细节       15、93、15、41、6、15、22、7、15、20

      第一次枢轴:v=15

      阶段一,形成 v* (<v)* (>v)* v* 的数组:

                                          15、15、 7、6、 41、20、22、93、 15、15

                  我们发现,与枢轴相等的元素都移动到了数组的两边。而比枢轴小的元素和比枢轴大的元素也都区分开来了。

      阶段二,将枢轴和与枢轴相等的元素交换到数组中间的位置上

                                          7、6、 15、15、 15、15、 41、20、22、93

      阶段三,递归排序与枢轴不相等都元素区间{7、6}和{41、20、22、93}

      仔细想想,对于重复元素较多的数组,这种优化无疑能到达更好的效率。





(1) 对象数组的排序,如Arrays.sort(Object[])等。采用了一种经 过修改的归并排序 。 其也有几个优化的闪光点。

     下面是JDK中改进归并排序算法的源代码:
  /** 
     * 将指定范围的对象数组按自然顺序升序排序。 
     * src[] 原待排数组 
     * dest[] 目的待排数组 
     * low 待排数组的下界位置 
     * high 待排数组的上界位置 
     * off 从数组的第off个元素开始排序 
     */      
    private static void mergeSort(Object[] src,  
               Object[] dest,  
               int low,  
               int high,  
               int off) {  
 int length = high - low;  
   
 //优化1:规模很小的数组的排序,直接插入排序的效率反而比归并要高。  
 //规模定在INSERTIONSORT_THRESHOLD=7之内  
        if (length < INSERTIONSORT_THRESHOLD) {  
            for (int i=low; i<high; i++)  
                for (int j=i; j>low &&  
          ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)  
                    swap(dest, j, j-1);  
            return;  
        }  
   
        // 递归排序dest的一半元素并赋值给src  
        int destLow  = low;  
        int destHigh = high;  
        low  += off;  
        high += off;  
        int mid = (low + high) >> 1;  
        mergeSort(dest, src, low, mid, -off);  
        mergeSort(dest, src, mid, high, -off);  
   
        //优化2:如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并          
        //如果需要归并的两端low~(middle-1),middle~high已经有序,即src[mid-1]==src[mid]。  
        //那么只需要将src的low~high赋值对应的dest即可,无需再归并。  
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {  
            System.arraycopy(src, low, dest, destLow, length);  
            return;  
        }  
   
        //将src的两个部分合并,并赋值给dest  
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {  
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)  
                dest[i] = src[p++];  
            else  
                dest[i] = src[q++];  
        }  
    }  

★ 优化1: 同上面的快速排序


★ 优化2: 如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并。 这个优化措施无疑对基本有序序列是极大的效率改进。
分享到:
评论

相关推荐

    java.util包

    Java.util包是Java标准库中的核心包之一,它包含了大量用于通用编程的类和接口,是Java开发中不可或缺的一部分。这个包提供了数据结构、集合框架、事件处理、日期时间、随机数生成、位集以及与I/O流操作相关的辅助...

    java.util包总结

    Java.util包是Java标准库中的核心包之一,它包含了大量用于日常编程的工具类和接口。这个包在Java 2版本中得到了显著增强,引入了许多重要的数据结构和算法,为Java程序员提供了更丰富的功能。 首先,Java.util包中...

    java.util源码-java-util:javautil源代码

    7. **实用工具类**:`java.util.Arrays`和`java.util.Collections`提供静态方法,用于操作数组和集合,如排序、复制和填充。 8. **并发编程**:`java.util.concurrent`包虽然不在`java.util`下,但与之紧密相关,...

    30个常用java工具类

    3. **`java.util.Collections`**:与`Arrays`类似,但针对集合框架中的接口和类,如`List`、`Set`和`Map`,提供排序、搜索和转换功能。 4. **`java.util.Date`** 和 **`java.time`** 包:处理日期和时间,`java....

    最最常用的 100 个 Java类分享

    9. `java.util.Arrays`:Arrays类提供了静态方法来操作数组,如排序、比较和填充。 10. `java.util.Iterator`:Iterator接口用于遍历集合中的元素,提供`hasNext()`和`next()`方法。 11. `java.util.Collections`...

    JAVA开发常用工具类

    2. **`java.util.Arrays`**: 提供了各种操作数组的方法,如排序、复制、填充以及搜索特定元素等。它还包含一个`deepToString()`方法,用于打印多维数组的内容。 3. **`java.util.Collections`**: 类似于`Arrays...

    java.util源码-java-source-code:java.util源码阅读

    Java.util 源码分析 Java.util 包是 Java 核心库的重要组成部分,它包含了许多用于日常编程的工具类和接口,如集合框架、日期时间处理、随机数生成、事件处理等。深入理解这个包的源码对于提升Java开发者的技能至关...

    Java实训教程 Java软件开发实战 Java类库 第4章 集合操作 共31页.pptx

    - **类介绍**:`java.util.Arrays` 类提供了大量的静态方法来操作数组。这些方法包括排序、搜索、填充等。 - **排序**: - `sort()` 方法可以对指定的数组内容进行排序,排序后的数组元素按照自然顺序或自定义比较...

    java开发过程中常用的工具类

    7. **`java.util.HashMap` 和 `java.util.TreeMap`**: 这两个类都是Map接口的实现,`HashMap`提供快速的插入和查找,而`TreeMap`则按照键的自然顺序或自定义比较器进行排序。 8. **`java.util.ArrayList` 和 `java....

    java常用工具类

    1. **`java.util.Arrays`**:这个类提供了处理数组的各种实用方法,如排序、复制、填充、查找和比较。例如,`Arrays.sort()`可以对整型、浮点型、字符型以及自定义对象类型的数组进行排序。 2. **`java.util....

    比较全的java工具类

    - `java.util.Arrays`:处理数组的操作,如排序、搜索、复制等。 - `java.util.Collections`:操作集合的工具类,如排序、反转、填充等。 以上只是Java工具类的一部分,实际中还有很多实用的工具类,例如用于XML...

    java的1000个常用类

    8. `java.util.Arrays`(2068次):Arrays类提供了静态方法来操作数组,如排序、复制和填充。 9. `java.util.Collections`(2028次):Collections是集合类的工具类,提供了对集合进行操作的各种方法,如排序、翻转...

    java.util源码-JavaUtility-SourceCode:JavaUtility-SourceCode

    在Java编程语言中,`java.util`包是核心库的一部分,包含了大量用于处理日常编程任务的类和接口。这个源码分析将深入探讨`java.util`包中的关键组件,了解它们的工作原理,这对于任何Java开发者来说都是至关重要的。...

    28个java常用的工具类源码

    1. **`java.util.Arrays`**:这个工具类提供了一系列静态方法来操作数组,如排序、复制、填充和查找。例如,`Arrays.sort()`用于对数组进行升序或降序排序,`Arrays.asList()`则能将数组转换为列表。 2. **`java....

    java-util包资料

    Java Util包,全称为`java.util`,是Java标准库中的核心包之一,包含了大量用于通用编程任务的类和接口。这个包自Java 1.0版本以来就存在,随着时间的发展,不断添加了新的功能和类,使得Java程序员在处理各种常见...

    快速排序JAVA实现 - QuickSort.java

    import java.util.Arrays; public class QuickSort { public static void quickSort(int[] arr) { if (arr == null || arr.length ) { return; } quickSort(arr, 0, arr.length - 1); } public ...

    java工具类 java开发助手 java util

    Java工具类(Java Util)是Java开发中不可或缺的一部分,它为开发者提供了大量便捷的功能,极大地提高了开发效率。在Java标准库中,`java.util`包是核心工具类库,包含了各种容器类、集合框架、日期时间处理、随机数...

    java performance12

    `Arrays.sort()` 方法实现了快速排序算法。对于对象数组,它会先创建一个与原数组相同的副本,然后对该副本进行排序。 ```java public static void sort(Object[] a) { Object aux[] = (Object[]) a.clone(); ...

    java util工具类1

    Java的`util`工具类是Java标准库中的核心部分,为开发者提供了丰富的功能,极大地简化了编程工作。这个包包含了各种集合框架、日期时间处理、随机数生成、字符串操作、IO流处理等实用工具类。在Java编程中,熟练掌握...

    常用的工具类大全

    2. **`java.util.Arrays`**: - 这个工具类提供了一系列静态方法来操作数组,如排序、复制、填充等。`Arrays.sort()` 方法可以对基本类型数组和对象数组进行排序。 3. **`java.util.Collections`**: - 提供了...

Global site tag (gtag.js) - Google Analytics