Java数据结构与算法(第三章简单排序)

如何排序?java

            这一章中主要是三个比较简单的算法:冒泡排序、选择排序和插入排序。计算机程序不能像人同样通览全部的数据。它只能根据计算机的“比较”操做原理,在同一时间内对来个数据项进行比较。
算法

            这三种算法都包括以下两个步骤,这两部循环执行,直到所有数据有序为止;
数组

            一、比较两个数据项;
编码

            二、交换两个数据项,或复制其中一项。
spa

            可是,每种算法具体实现的细节有所不一样。
调试

冒泡排序code

            冒泡排序算法运行起来很是慢,但在概念上它是排序算法中最简单的,所以冒泡排序算法在刚开始研究排序技术时时一个很是好的算法。
对象

冒泡排序的Java代码:排序

public class ArrayTest {
    public static void main(String[] args) {
    //冒牌排序法
        int[] a = {34,21,5,2,3,12,56,13,37,22};
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+",");
        }
        System.out.println("");
        for (int i = a.length-1; i >1; i--) {
            for (int j = 0; j < i; j++) {
                if(a[j]>a[j+1]){
                    int temp=a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                }
            }
        }
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+",");
        }
    }
}

//输出:
34,21,5,2,3,12,56,13,37,22,
2,3,5,12,13,21,22,34,37,56,

        这个算法的思路是要将最小的数据项放在数组的最开始(数组的下标为0),并将最大的数据项放在数组的最后(数组下标为nElems-1)。外层for循环的计数器 i 从数组的最后开始,即i等于i等于nEmels-1,没通过一次循环i减一。小标大于i的数据项都已经排好序的了。变量i在每完成一次内部循环(计数器为j)后左移一位,所以算法就再也不处理那些已经排好序的数据了。
内存

        内层for循环计数器j从数组的最开始算起,即j=0,没完成一次内部循环体加一,当它等于out时结束一次循环。在内层for循环体中,数组小标为j和j+1的两个数据项进行比较,若是小标为j的数据项大于小标为j+1的数据项项,则交换两个数据项。

不变性

        在许多算法中,有些条件在算法执行时时不变的。这些条件被称为不变性。这些条件被称为不变性。认识不变性对理解算法是有用的。在必定状况先他们对调试也有用;能够反复检查不变性为否为真,若是不是的话就标记出错。

        在上述代码中不变性是指i右边的数据项为有序。在算法的整个运行过程当中这个条件始终为真。(在第一次排序开始前,还没有排序,由于i开始时在数据项的最左边,没有数据项在i的右边。)

冒泡排序法的效率

        经过分析10分数据项的数组,第一趟排序时进行了9次比较,第二趟排序进行了8次比较。如此类推,知道最后一趟进行了一次比较。对于10个数据项就是

        9+8+7+6+5+4+3+2+1=45

        通常来讲数组中有N个数据项,则第一趟排序中有N-1次比较,第二趟中有N-2次比较,如此类推公式以下

        (N-1)+(N-2)+(N-3)+…+1=N(N-1)/2

        当N为10时,N*(N-1)/2等于45  (10*9/2)。

        这样,算法做了约N²/2次比较(忽略减一,不会有很大的差异,特别是当N很大时)。


选择排序

        选择排序改进了冒泡排序,将必要的交换次数从O(N²)减小到O(N)。不幸的是比较次数仍保持为O(N²)。然而,选择排序仍然为大记录量的排序提出了一个很是重要的改进,由于这些大量的记录须要在内存中移动,这就使交换的时间和比较的时间相比起来,交换的时间更为重要。(在Java中只是改变了引用位置,而实际对象的位置并无发生改变。)

选择排序的Java代码

public static void main(String[] args) {
    //选择排序法
    int[] a = {34,21,5,2,3,12,56,13,37,22};
    for (int i = 0; i < a.length; i++) {
        System.out.print(a[i]+",");
    }
    System.out.println("");
    int out,in,min;
    for (out = 0; out < a.length-1; out++) {
        min = out;
        for (in = out+1; in < a.length; in++) {
            if(a[in]<a[min]){
                min=in;
            }
            int temp = a[out];
             a[out] =  a[min];
             a[min] = temp;
        }    
    }
    for (int i = 0; i < a.length; i++) {
        System.out.print(a[i]+",");
    }
}
//输出:
//34,21,5,2,3,12,56,13,37,22,
//2,5,12,3,13,21,22,37,34,56,

        外层循环用循环变量out,从数组开头开始(数组下标为0)向高位增加。内层循环变量in,从out指位置开始,一样是向右移位。

        在每个in的新位置,数据项a[in]和a[min]进行比较,若是a[in]更小,则min被赋值为in的值,在内层循环的最后,min指向最小的数据项,而后out和min指向的数组数据项。

不变性

        在上述代码程序中,下标小于或等于out的位置的数据项老是有序的。

选择排序的效率

        选择排序和冒泡排序执行了相同次数的比较:N*(N-1)/2。对于10个数据项,须要4次比较。然而10个数据项只须要少于10次交换。对于100个数据项,须要4950次比较,但只进行了不到100次的交换。N值很大时,比较的次数是主要的,因此结论是选择排序和冒泡排序同样运行了O(N²)时间。可是,选择排序无疑更快,由于它进行交换少得多。当N值较小时,特别是若是交换的时间比比较的时间级大得多时,选择排序其实是至关快的。

插入排序

    插入排序的Java代码

public static void main(String[] args) {
    //插入排序
    int[] a = {34,21,5,2,3,12,56,13,37,22};
    for (int i = 0; i < a.length; i++) {
        System.out.print(a[i]+",");
    }
    System.out.println("");
    for (int i = 1; i < a.length; i++) {
        int temp = a[i];
        int j = i;
        while(j>0&&a[j-1]>=temp){
            a[j] = a[j-1];
            --j;
        }
        a[j] = temp;
    }
    for (int i = 0; i < a.length; i++) {
        System.out.print(a[i]+",");
    }
}
//输出:
//34,21,5,2,3,12,56,13,37,22,
//2,5,12,3,13,21,22,37,34,56,

    在外层的for循环总,i变量从1开始,向右移动。标记了未排序部分的最左端的数据。而内层while循环中,j变量从i变量开始向左移动,直到temp变量小于j所指的数组数据项,或者它已经不能再往左移动为止。while循环的每一趟都向右移动了一个已排序的数据项。

    插入排序中的不变性:

            在每趟结束时,在将temp位置的项插入后,比i变量下标号小的数据项都是局部有序的。

     插入排序的效率

            这个算法须要多少次比较和复制呢?在第一趟排序中,他最多比较一次,第二趟最多比较两次,以此类推。最后一趟最多,比较N-1次。一次有

            1+2+3+... +N-1=N*(N-1)/2

            然而,由于在每一趟排序发现插入点以前,平均只有全体数据项的一半真的进行了比价,咱们除以2获得

            N*(N-1)/ 4

            复制的次数大体等于比较的次数。然而,一次复制与一次交换的时间耗费不一样,因此相对于随机数据,这个算法比冒泡排序快一倍,比选择排序略快。    

            对于已经有序或基本有序的数据来讲,插入排序要好得多。当数据有序的时候,while循环的条件老是false因此它变成了外层循环中一个简单语句,执行N-1次。在这种状况下,算法运行只须要O(N)的时间。若是数据基本有序,插入排序几乎只须要O(N)的时间,这对把一个基本有序进行排序是一个简单而有效的方法。

            然而对于逆序排列的数据,每次比较和移动都会执行,因此插入排序不比冒泡排序快。

对象排序

        上述都是简单的数值排序,排序例程却更多地应用于对象排序,而不是对基本数据类型的排序。

    对象排序的Java代码

public void insertSort(){
    for (int i = 1; i < a.length; i++) {
        Person temp = a[i];
        int j = i;
        while(j>0 && a[j-1].getLast().compareTo(temp.getLast())>0){
            a[j]=a[j-1];
            --j;
        }
        a[j] = temp;
    }
}

    单词排序

    compareTo()方法更具两个String的字典顺序(字母序)返回给调用者不一样的整数值,这两String一个是方法的调用者,一个是这个方法的参数。

    稳定性

    有些时候,排序要考虑数据项拥有相同关键字的状况。例如,雇员数据按雇员的姓的字典排序(排序以姓为关键字),如今又想按邮政编码排序,并但愿具备相同邮政编码的数据仍然按姓排序。这宗状况下,则值须要算法对须要排序的数据进行排序,让不须要排序的数据保持原来的顺序。某些算法知足这样的要求,它们就能够成为稳定的算法。

几种简单排序之间的比较

        除非手边没有算法书能够参考,通常状况下几乎不太实用冒泡排序算法。它太过于简单,以致于能够绝不费力地写出来。然而当数据量很小的时候它会有些应用价值。

        选择排序当数据量很小,而且交换数据相对于比较数据更加消耗的状况下,能够应用选择排序。

        当数据量比较小或基本上有序时,插入排序是这三种最好的选择。

小   结

  • 本章提到的排序算法都假定了数组做为数据存储机构。

  • 排序包括比较数组中数据项的关键字和移动相应数据项直到它们排好序为止。

  • 本章全部算法的时间复杂度都是O(n²)。不过,某些状况下某个算法能够比其余算法快不少。

  • 不变性是指在算法运行时保持不变的条件。

  • 冒泡排序算法是效率最差的算法,但他很简单。

  • 插入算法是本章上述介绍的O(n²)排序算法中应用最多的。

  • 若是具备相同关键字的数据项,通过排序它们的顺序保持不变,这样的排序就是稳定的。

  • 上述介绍的全部排序算法除了须要初始数组以外,都只须要一个临时变量

相关文章
相关标签/搜索