大話數據結構系列之排序的初體驗

排序的定義

假設含有 n 個記錄的序列爲 {r1, r2, r3 … rn },其相應的關鍵字分別爲{k1, k2, k3 … kn},需確定 1, 2,… 的一種排列 p1, p2, …, pn, 使其相應的關鍵字滿足 kp1 <= kp2 <= … <= kpn(非遞減或非遞增)關係,即使得序列成爲一個按關鍵字有序的序列{ rp1, rp2, … , rpn },這樣的操作叫作排序。

排序的穩定性:

假設 Ki = Kj (1 <= i <= n, 1 <= j <= n, i 不等於 j),且在排序前的序列中 ri (即 i < j)。如果排序後 ri 仍領先於 rj,則稱所用的排序方法是穩定的;反之,若可能使得排序後的序列中 rj 領先於 ri,則稱所用的排序方法是不穩定的。

穩定性的意義

1、如果只是簡單的進行數字的排序,那麼穩定性將毫無意義。
2、如果排序的內容僅僅是一個複雜對象的某一個數字屬性,那麼穩定性依舊將毫無意義。
3、如果要排序的內容是一個複雜對象的多個數字屬性,但是其原本的初始順序毫無意義,那麼穩定性依舊將毫無意義。
4、除非要排序的內容是一個複雜對象的多個數字屬性,且其原本的初始順序存在意義,那麼我們需要在二次排序的基礎上保持原有排序的意義,才需要使用到穩定性的算法,例如要排序的內容是一組原本按照價格高低排序的對象,如今需要按照銷量高低排序,使用穩定性算法,可以使得想同銷量的對象依舊保持着價格高低的排序展現,只有銷量不同的纔會重新排序。(當然,如果需求不需要保持初始的排序意義,那麼使用穩定性算法依舊將毫無意義)。

內排序

內排序是在排序整個過程中,待排序的所有記錄全部被放置在內存中。

外排序

外排序是由於排序的記錄個數太多,不能同時放置在內存,整個排序過程需要在內外存之間多次交換數據才能進行。

內排序的性能影響因素:
1、時間性能:時間複雜度
2、輔助空間:輔助空間是除了存放排序所佔用的存儲空間之外,執行算法所需要的其他存儲空間
3、算法的複雜性:指算法本身的複雜程度,所需要的步驟是否過多?隨之而來的指令的增多

冒泡排序(Bubble Sort)

一種交換排序,它的基本思想是:兩兩比較相鄰記錄的關鍵字,如果反序則交換,直到沒有反序的記錄爲止。

易混淆的基本交換排序:

//冒牌冒泡排序(交換排序)
    public static void bubbleSort0(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = i+1; j < list.length; j++){
                if(list[i] > list[j])
                    swap(list,i,j);
            }
        }
    }

正宗冒泡排序:
冒泡名稱的由來:較小的數字如同氣泡般慢慢浮到上面

//正宗冒泡排序
    public static void bubbleSort1(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = list.length - 2; j >= i; j--){
                if(list[j] > list[j+1])
                    swap(list,j,j+1);
            }
        }
    }

交換排序 VS 冒泡排序
後者算法在排序過程中會未後續的排序步驟減少壓力(即減少交換次數)

優化後的冒泡排序:
針對已經排序好的序列,停止判斷操作

//優化版冒泡排序
    public static void bubbleSort2(int[] list){
        //flag 用來標記
        boolean flag = true;
        //flag 爲 true 時退出循環
        for(int i = 0; i < list.length && flag; i++){
            //初始化爲false,如果內循環完成,flag的值不變,則說明後面的序列已經完成排序
            flag = false;
            for(int j = list.length - 2; j > i; j--){
                if(list[j] > list[j+1]){
                    swap(list,j,j+1);
                    flag = true;
                }
            }
        }
    }

整體代碼:

public static void main(String[] args){
        //基礎數據
        int[] sortList = {1,4,3,10,9,6,7,8,5,2,20,18,16,11,13,15,14,17,19,12};
        System.out.println(Arrays.toString(sortList));
        //bubbleSort0(sortList);
        //bubbleSort1(sortList);
        //bubbleSort2(sortList);
        //selectSort(sortList);
        System.out.println(Arrays.toString(sortList));
        int[] sortList2 = {0,1,4,3,10,9,6,7,8,5,2,20,18,16,11,13,15,14,17,19,12};
        System.out.println(Arrays.toString(sortList2));
        //insertSort(sortList2);
        System.out.println(Arrays.toString(sortList2));
    }
    
    //冒牌冒泡排序(交換排序)
    public static void bubbleSort0(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = i+1; j < list.length; j++){
                if(list[i] > list[j])
                    swap(list,i,j);
            }
        }
    }
    
    //正宗冒泡排序
    public static void bubbleSort1(int[] list){
        for(int i = 0; i < list.length; i++){
            for(int j = list.length - 2; j >= i; j--){
                if(list[j] > list[j+1])
                    swap(list,j,j+1);
            }
        }
    }
    
    //優化版冒泡排序
    public static void bubbleSort2(int[] list){
        //flag 用來標記
        boolean flag = true;
        //flag 爲 true 時退出循環
        for(int i = 0; i < list.length && flag; i++){
            //初始化爲false,如果內循環完成,flag的值不變,則說明後面的序列已經完成排序
            flag = false;
            for(int j = list.length - 2; j > i; j--){
                if(list[j] > list[j+1]){
                    swap(list,j,j+1);
                    flag = true;
                }
            }
        }
    }

時間複雜度比較:
最好: O[n ]
最壞: O[n^2)

簡單選擇排序

思想:
在排序時找到適合的關鍵字再做交換,並且只移動一次就完成相應關鍵字的排序定位工作
生活類比:類似於買股票一樣,抓住最關鍵點去買

//選擇排序法
    public static void selectSort(int[] list){
        int min;
        for(int i = 0; i < list.length; i++){
            //將當前下標定義爲最小值
            min = i;
            for(int j = i+1; j < list.length; j++){
                //把後續的值與當前最小值比較
                if(list[min] > list[j])
                    min = j;
            }
            //若 i 不等於 min,說明找到了最小值,進行交換
            if( i != min)
                swap(list,i,min);
        }
    }

時間複雜度比較:
最好: O[n ]
最壞: O[n2)

直接插入排序

思想:將一個記錄插入到已經排好序的有序表中,從而得到一個新的,記錄數增 1 的有序表
生活類比:類似於玩撲克牌或者打麻將,選出一張牌兒,直接插入
特點:需要佔用一個輔助空間,就是頭部的空間,並且不作爲最後的排序結果

哨兵的思想 待補充在各種算法的體驗,以及哨兵思想的由來

//插入排序法
    public static void insertSort(int[] list){
        int i,j;
        for( i = 2; i < list.length; i++){
            //需要將 i 序號的值插入有序子表
            if(list[i] < list[i-1]){
                list[0] = list[i];
                for( j = i-1; list[j] > list[0]; j--)
                     //大於哨兵的記錄後移
                     list[j+1] = list[j];
                //插入到有序位置
                list[j+1] = list[0];
            }
        }
    }

時間複雜度比較:
最好: O[n ]
最壞: O[n2)

發佈了240 篇原創文章 · 獲贊 91 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章