排序

算法穩定性:

假定在待排序的記錄序列中,存在多個具有相同關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變。即在原序列中,ri=rj,且ri在rj之前,而在排序后,ri仍在rj之前,則稱這種排序算法是穩定的。

  • 算法穩定性的重要性
    在實際的應用中,我們交換的不一定只是一個數,而可能是一個很大的對象,交換元素存在一定的開銷。

排序分類:

排序分類

排序算法
插入排序

插入排序由N-1趟排序組成。對于i=1到N-1趟,插入排序保證從位置0到位置i上的元素已經處于排過序的狀態。

如下圖:

插入排序.png

具體實現:

public class InsertSort {

    public static void main(String[] args){
        int[] arr = {34,8,64,51,32,21};
        System.out.println("Before sort:");
        printArray(arr);
        System.out.println("After insert sort:");
        insertionSort(arr);
        printArray(arr);
    }

    public static  void printArray(int[] a){
        for (int i = 0; i < a.length; i ++){
            System.out.print(a[i] + " ");
        }
        System.out.println();
    }

    /**
     * 實現思路:從第二個位置開始遍歷,保證目標數左邊是排序好的,目標數不斷
     * 往右邊找,直到找到合適的位置放入。
     * @param a
     */
    public static  void insertionSort(int[] a){
        int j;
        //從第二個位置開始
        for (int i = 1; i < a.length; i ++){
            //設置臨時變量
            int tmp = a[i];
            //將該位置不斷往左移,直到當前的數不大于該數
            for (j = i; j > 0 && tmp < a[j-1] ; j--){
                //將數往右移,為目標數騰一個位置
                a[j] = a[j - 1];
            }
            //找到位置,將目標數放入
            a[j] = tmp;
        }
    }
}

輸出結果:
Before sort:
34 8 64 51 32 21 
After insert sort:
8 21 32 34 51 64 

評價:
插入排序的復雜度為O(N2),但算法是穩定的。

希爾排序:

先將序列按Gap劃分為元素個數相同的若干數組,使用直接插入排序法進行排序,然后不斷縮小Gap直至1,最后使用插入排序完成排序。希爾排序實際上是插入排序的增強版。

如下圖:


希爾排序.png

實現過程:

    public static  void shellSort(int[] a){
        int j;
        //設置gap,定義gap/2變化
        for (int gap = a.length / 2; gap > 0; gap /= 2){
            System.out.println("第"  + gap + "趟排序:");
            //每gap間進行插入排序
            for (int i = gap; i < a.length; i ++){
                int tmp = a[i];
                for (j = i; j >= gap && tmp < a[j-gap]; j -= gap){
                    a[j] = a[j-gap];
                }
                a[j] = tmp;
            }
            printArray(a);
        }
    }

輸出結果:以第一個位置進行比較


希爾排序.png

評價:

  • 希爾排序的時間復雜度與增量(即,步長gap)的選取有關。例如,當增量為1時,希爾排序退化成了直接插入排序,此時的時間復雜度為O(N2),而Hibbard增量的希爾排序的時間復雜度為O(N3/2)。
  • 希爾排序是不穩定的算法。
冒泡排序

它重復地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數列的工作就是重復地進行直到沒有再需要交換,也就是說數列已經排序完成。

如下圖:

冒泡排序.png

具體實現:

public static  void bubbleSort(int[] a){
    int temp;
    //遍歷N-1趟
    for (int i = 0; i < a.length - 1; i ++){
                //每一躺保證該趟的最后一個元素為最大
        for (int j = 0; j < a.length - i - 1; j ++){
            if (a[j] > a[j+1]){
                temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
            }
        }
    }
}

評價:

  • 冒泡排序與插入排序擁有相等的運行時間,但是兩種算法在需要的交換次數卻很大地不同。在最好的情況,冒泡排序需要
    O(n^{2})次交換,而插入排序只要最多O(n)。
  • 冒泡排序是穩定的。
快速排序

使用分治法策略來把一個序列分為兩個子序列。

  • 從數列中挑出一個元素,稱為“基準”。
  • 重新排序數列,所有比基準小的元素放在基準的左邊,所有比基準大的元素放在基準的右邊。
  • 對基準的左邊和右邊兩個子集,不斷重復前面兩個步驟,直到所有子集只剩下一個元素。
    如下圖:
快速排序.png

具體實現:

public class QuickSort {
    private int[] arr;

    public QuickSort(int[] arr){
        this.arr = arr;
    }
    //打印數組
    public  void printArray(){
        for (int i = 0; i < arr.length; i ++){
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public  void quick_sort(){
        quick_sort_recursive(0, arr.length - 1);
    }

    private void quick_sort_recursive(int start, int end){
        //只有一個元素的情況,直接返回
        if (start >= end) return;
        //取最后一個元素作為基準
        int mid = arr[end];
        //設置左右邊
        int left = start;
        int right = end - 1;
        //進行比較交換
        while (left < right){
            //左邊的數往右找,直到找到大于基準的數
            while (arr[left] <= mid && left < right)
                left ++;
            //右邊的數往左找,直到找到小于基準的數
            while (arr[right] >= mid && left < right)
                right --;
            //交換兩個數
            Swap(left,right);
        }
        //若中間元素大于基準,則進行交換
        if (arr[left] > arr[end])
            Swap(left,end);
        //否則left++,目的是排除掉中間元素
        else
            left ++;
        //遞歸左右兩邊
        quick_sort_recursive(start,left-1);
        quick_sort_recursive(left+1, end);
    }

    //交換元素
    private void Swap(int x, int y){
        int temp = arr[x];
        arr[x] = arr[y];
        arr[y] = temp;
    }

}

評價:

  • 快速排序的時間復雜度為O(nlgn),最壞情況為O(n2),其空間復雜度為O(lgn)。

  • 快速排序的算法是不穩定的。

選擇排序

首先在未排序序列中找到最大(小)元素,存放到排序序列中的起始位置,然后,再從剩余未排序元素中繼續尋找最大(小)元素,然后放到已排序序列的末尾。以此類推,直到所有元素排序完畢。

實現如下:

private static void selection_sort(int[] arr){
    int i,j,min,temp,len = arr.length;
    for (i = 0; i < len - 1; i ++){
            //記錄當前元素
        min = i;
            //獲取到未排序的序列中找到最小元素的位置
        for (j = i + 1; j < len; j ++)
            if (arr[min] > arr[j])
                min = j;
        //將最小的元素放在起始位置
        temp = arr[i];
        arr[i] = arr[min];
        arr[min] = temp;
    }
}

評價:

  • 選擇排序的時間復雜度為O(n2)
  • 選擇排序是穩定的。
堆排序

利用堆這種數據結構設計的一種排序算法。堆積是一個近似的完全二叉樹的結構,并同時滿足堆積的性質:即子結點的鍵值或索引總是小于(或大于)它的父節點。

  • 堆節點的訪問:
    a. 父親節點i 的左子節點的位置(2i+1)
    b. 父節點i的右子節點的位置(2
    i+2)
    c. 子節點的父節點位置floor((i-1)/2)

  • 堆的操作:
    a. 創建最大堆:將堆所有數據重新排序
    b. 最大堆調整:將堆的末端子節點作調整,使得子節點永遠小于父節點
    c. 堆排序:移除位于第一個數據的根節點,并做出最大堆調整的遞歸運算

  • 具體實現過程:
    http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/

  • 具體實現方法:
public class HeapSort {
    private int[] arr;

    public HeapSort(int[] arr){
        this.arr = arr;
    }

    //進行排序
    public void sort(){
        //建立最大堆
        buildMaxHeap();

        //移除頂點,并調整堆順序
        for (int i = arr.length - 1; i > 0; i --){
            //將頂點與最后的元素(最小值)交換
            swap(i,0);
            //然后調整堆順序
            maxHeapify(0,i);
        }
    }

    private void swap(int i , int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    //建立最大堆,遞歸實現
    private void maxHeapify(int index, int len){
        //默認父節點為最大值
        int max = index;
        //獲取左右孩子節點位置
        int left = (index * 2) + 1;
        int right = left + 1;
        //若左孩子大于父親,則賦值左孩子
        if (left < len && arr[max] < arr[left])
            max = left;
        //若右孩子大于父親,則賦值右孩子
        if (right < len && arr[max] < arr[right])
            max = right;
        //若需要更新父節點,則交換,并調整
        if (max != index){
            swap(index, max);
            maxHeapify(max,len);
        }
    }

    //建立最大堆
    private void buildMaxHeap(){
        //設置根節點位置
        int parent = arr.length/2 - 1;
        //從根開始創建堆
        for (int i = parent; i >= 0; i --){
            maxHeapify(i,arr.length);
        }
    }

    private  void printArray(int[] a){
        for (int i = 0; i < a.length; i ++){
            System.out.print(a[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args){
        int[] arr = {81,94,11,96,12,35,17,95,28,58,41,75,15};
        System.out.println("Before sort:");
        HeapSort hs = new HeapSort(arr);
        hs.printArray(arr);
        hs.sort();
        System.out.println("After heapSort sort:");
        hs.printArray(arr);
    }

}

輸出結果:
Before sort:
81 94 11 96 12 35 17 95 28 58 41 75 15 
After heapSort sort:
11 12 15 17 28 35 41 58 75 81 94 95 96 
歸并排序

將數組劃分為若干個部分進行排序,再將這些排序好的部分合并

實現過程:

歸并排序

實現方法:

/**
 * 實現歸并排序
 * @param arr 原始數組
 * @param rs 結果數組
 * @param start 開始位置
 * @param end 結束位置
 */
private static void merge_sort_recursive(int[] arr, int[] rs, int start, int end){
    if (start >= end)   return;
        //劃分為兩段,并分別設置這兩段的始末位置
    int len = end - start;
    int mid = len / 2 + start;
    int s1 = start, e1 = mid;
    int s2 = mid + 1, e2 = end;
        //對這兩段分別進行遞歸操作,劃分數組
    merge_sort_recursive(arr,rs,s1,e1);
    merge_sort_recursive(arr,rs,s2,e2);
        //進行合并數組
    int k = start;
        //從兩個劃分的數組中選取較小的數放入結果數組中
    while (s1 <= e1 && s2 <= e2)
        rs[k++] = arr[s1] < arr[s2] ? arr[s1++] : arr[s2++];
        //對于未排完的,直接放入結果數組中
    while (s1 <= e1)
        rs[k++] = arr[s1++];
    while (s2 <= e2)
        rs[k++] = arr[s2++];
        //賦值給原始數組
    for (k= start; k <=end; k++)
        arr[k] = rs[k];
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內容