1. 程式人生 > 其它 >3.簡單排序

3.簡單排序

3 簡單排序

在我們的程式中,排序是非常常見的一種需求,提供一些資料元素,把這些資料元素按照一定的規則進行排序。比
如查詢一些訂單,按照訂單的日期進行排序;再比如查詢一些商品,按照商品的價格進行排序等等。所以,接下來
我們要學習一些常見的排序演算法。在java的開發工具包jdk中,已經給我們提供了很多資料結
構與演算法的實現,比如List,Set,Map,Math等等,都是以API的方式提供,這種方式的好處在於一次編寫,多處使
用。我們借鑑jdk的方式,也把演算法封裝到某個類中,那如果是這樣,在我們寫java程式碼之前,就需要先進行API的
設計,設計好之後,再對這些API進行實現。然後再使用java程式碼去實現它。以後我們講任何資料結構與
演算法都是以這種方式講解就比如我們先設計一套API如下:

3.1 Comparable介面介紹

由於我們這裡要講排序,所以肯定會在元素之間進行比較,而Java提供了一個介面Comparable就是用來定義排序
規則的,在這裡我們以案例的形式對Comparable介面做一個簡單的回顧。

需求:

1.定義一個學生類Student,具有年齡age和姓名username兩個屬性,並通過Comparable介面提供比較規則;
2.定義測試類Test,在測試類Test中定義測試方法Comparable getMax(Comparable c1,Comparable c2)完成測試


//學生類
public class Student implements Comparable<Student>{
    private String username;
    private int age;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
    //定義比較規則
    @Override
    public int compareTo(Student o) {
        return this.getAge()-o.getAge();
    }
}
//測試類
public class Test {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.setUsername("zhangsan");
        stu1.setAge(17);
        Student stu2 = new Student();
        stu2.setUsername("lisi");
        stu2.setAge(19);
        Comparable max = getMax(stu1, stu2);
        System.out.println(max);
    }
    //測試方法,獲取兩個元素中的較大值
    public static Comparable getMax(Comparable c1,Comparable c2){
        int cmp = c1.compareTo(c2);
        if (cmp>=0){
            return c1;
        }else{
            return c2;
        }
    }
}

3.2 氣泡排序

氣泡排序(Bubble Sort),是一種電腦科學領域的較簡單的排序演算法。


需求:
排序前:{4,5,6,3,2,1}
排序後:{1,2,3,4,5,6}


排序原理:
1. 比較相鄰的元素。如果前一個元素比後一個元素大,就交換這兩個元素的位置。
2. 對每一對相鄰元素做同樣的工作,從開始第一對元素到結尾的最後一對元素。最終最後位置的元素就是最大值。
氣泡排序API設計:
氣泡排序的程式碼實現:


//排序程式碼
public class Bubble {
    /*
    對陣列a中的元素進行排序
    */
    public static void sort(Comparable[] a){
        for(int i=a.length-1;i>0;i--){
            for (int j = 0; j <i; j++) {
                if (greater(a[j],a[j+1])){
                    exch(a,j,j+1);
                }
            }
        }
    }
    /*
    比較v元素是否大於w元素
    */
    private static boolean greater(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }
    /*
    陣列元素i和j交換位置
    */
    private static void exch(Comparable[] a,int i,int j){
        Comparable t = a[i];
        a[i]=a[j];
        a[j]=t;
    }
}
//測試程式碼
public class Test {
    public static void main(String[] args) {
        Integer[] a = {4, 5, 6, 3, 2, 1};
        Bubble.sort(a);
        System.out.println(Arrays.toString(a));
    }
}


氣泡排序的時間複雜度分析 


氣泡排序使用了雙層for迴圈,其中內層迴圈的迴圈體是真正完成排序的程式碼,所以,
我們分析氣泡排序的時間複雜度,主要分析一下內層迴圈體的執行次數即可。
在最壞情況下,也就是假如要排序的元素為{6,5,4,3,2,1}逆序,那麼:元素比較的次數為:
 (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
元素交換的次數為:
 (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
總執行次數為:
 (N^2/2-N/2)+(N^2/2-N/2)=N^2-N;
按照大O推導法則,保留函式中的最高階項那麼最終氣泡排序的時間複雜度為O(N^2).

3.3 選擇排序

選擇排序是一種更加簡單直觀的排序方法。


需求:
排序前:{4,6,8,7,9,2,10,1}
排序後:{1,2,4,5,7,8,9,10}


排序原理:
 1.每一次遍歷的過程中,都假定第一個索引處的元素是最小值,和其他索引處的值依次進行比較,如果當前索引處
 的值大於其他某個索引處的值,則假定其他某個索引出的值為最小值,最後可以找到最小值所在的索引
 2.交換第一個索引處和最小值所在的索引處的值
選擇排序API設計:
選擇排序的程式碼實現:


//排序程式碼
public class Selection {
    /*
    對陣列a中的元素進行排序
    */
    public static void sort(Comparable[] a){
        for (int i=0;i<=a.length-2;i++){
//假定本次遍歷,最小值所在的索引是i
            int minIndex=i;
            for (int j=i+1;j<a.length;j++){
                if (greater(a[minIndex],a[j])){
//更換最小值所在的索引
                    minIndex=j;
                }
            }
//交換i索引處和minIndex索引處的值
            exch(a,i,minIndex);
        }
    }
    /*
    比較v元素是否大於w元素
    */
    private static boolean greater(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }
    /*
    陣列元素i和j交換位置
    */
    private static void exch(Comparable[] a,int i,int j){
        Comparable t = a[i];
        a[i]=a[j];
        a[j]=t;
    }
}
//測試程式碼
public class Test {
    public static void main(String[] args) {
        Integer[] a = {4,6,8,7,9,2,10,1};
        Selection.sort(a);
        System.out.println(Arrays.toString(a));
    }
}


選擇排序的時間複雜度分析:
選擇排序使用了雙層for迴圈,其中外層迴圈完成了資料交換,內層
迴圈完成了資料比較,所以我們分別統計資料交換次數和資料比較次數:
資料比較次數:
 (N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
資料交換次數:
 N-1
時間複雜度:N^2/2-N/2+(N-1)=N^2/2+N/2-1;
根據大O推導法則,保留最高階項,去除常數因子,時間複雜度為O(N^2);

3.4 插入排序

插入排序(Insertion sort)是一種簡單直觀且穩定的排序演算法。
插入排序的工作方式非常像人們排序一手撲克牌一樣。開始時,我們的左手為空並且桌子上的牌面朝下。然後,我
們每次從桌子上拿走一張牌並將它插入左手中正確的位置。為了找到一張牌的正確位置,我們從右到左將它與已在
手中的每張牌進行比較,如下圖所示:
需求:
排序前:{4,3,2,10,12,1,5,6}
排序後:{1,2,3,4,5,6,10,12}


排序原理:
1.把所有的元素分為兩組,已經排序的和未排序的;
2.找到未排序的組中的第一個元素,向已經排序的組中進行插入;
3.倒敘遍歷已經排序的元素,依次和待插入的元素進行比較,
直到找到一個元素小於等於待插入元素,那麼就把待插入元素放到這個位置,其他的元素向後移動一位;
插入排序API設計:
插入排序程式碼實現:


public class Insertion {
    /*
    對陣列a中的元素進行排序
    */
    public static void sort(Comparable[] a){
        for (int i=1;i<a.length;i++){
//當前元素為a[i],依次和i前面的元素比較,找到一個小於等於a[i]的元素
            for (int j=i;j>0;j--){
                if (greater(a[j-1],a[j])){
//交換元素
                    exch(a,j-1,j);
                }else {
//未找到該元素,結束
                    break;
                }
            }

        }
    }
    /*
    比較v元素是否大於w元素
    */
    private static boolean greater(Comparable v,Comparable w){
        return v.compareTo(w)>0;
    }
    /*
    陣列元素i和j交換位置
    */
    private static void exch(Comparable[] a,int i,int j){
        Comparable t = a[i];
        a[i]=a[j];
        a[j]=t;
    }
}


插入排序的時間複雜度分析


插入排序使用了雙層for迴圈,其中內層迴圈的迴圈體是真正完成排序的程式碼,所以,我們分析插入排序的時間復
雜度,主要分析一下內層迴圈體的執行次數即可。最壞情況,也就是待排序的陣列元素為{12,10,6,5,4,3,2,1},那麼:
比較的次數為:
(N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
交換的次數為:
(N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;
總執行次數為:
(N^2/2-N/2)+(N^2/2-N/2)=N^2-N;
按照大O推導法則,保留函式中的最高階項那麼最終插入
排序的時間複雜度為O(N^2).
艾歐尼亞,昂揚不滅,為了更美好的明天而戰(#^.^#)