1. 程式人生 > >插入排序,希爾排序,選擇排序,快速排序,氣泡排序,歸併排序

插入排序,希爾排序,選擇排序,快速排序,氣泡排序,歸併排序

插入排序

1、介紹:
       簡單插入排序演算法原理:從整個待排序列中選出一個元素插入到已經有序的子序列中去,得到一個有序的、元素加一的子序列,直到整個序列的待插入元素為0,則整個序列全部有序。

  在實際的演算法中,我們經常選擇序列的第一個元素作為有序序列(因為一個元素肯定是有序的),我們逐漸將後面的元素插入到前面的有序序列中,直到整個序列有序。

2,圖解

9 為第一個元素,第一個元素都是有序的。

具體程式碼:

    /*插入排序*/
    private static int[] InserSort(int[] a){
        int i,j,num=0;
        //下包從第二個值開始,遍歷所有值
        for(i=1; i<a.length ; i++){
            //每次排序a[i]與前面的所有值比較
            for(j=0;j<=i;j++){
                if(a[i]<a[j]){
                    //如果小,則插入到數列中,所有資料往後移動
                    int temp = a[i];
                    for(int k=i;k>j;k--){
                        a[k] = a[k-1];
                    }
                    a[j] = temp;
                }
            }
            System.out.println("第"+(++num)+"次排序: "+Arrays.toString(a));
        }
        return a;
    }

 

希爾排序

1、介紹:

 希爾排序是希爾(Donald Shell)於1959年提出的一種排序演算法。希爾排序也是一種插入排序,它是簡單插入排序經過改進之後的一個更高效的版本,也稱為縮小增量排序,同時該演算法是衝破O(n2)的第一批演算法之一。本文會以圖解的方式詳細介紹希爾排序的基本思想及其程式碼實現。

2,圖解

3、程式碼

    /*希爾排序*/
    private static void shell() {
        int i;        //i為掃描次數
        int j;        //以j來定位比較的元素
        int k=1;      //k列印計數
        int tmp;      //tmp用來暫存資料
        int jmp;      //設定間隔位移量

        jmp=size/2;   //獲取增量,且分組
        while (jmp != 0)
        {
            for (i=jmp ;i<size ;i++)
            {
                tmp=data[i]; //組內最後一個值賦值給tmp
                j=i-jmp;
                System.out.println(" j ==>" + j+"\t"+" i ==>" + i +"\t"+"data["+j+"]==>"+data[j]+"\t"+"tmp ==>"+tmp );
                if(tmp < data[j])  //組內排序
                {
                    data[i] = data[j];
                    data[j]=tmp;
                }
                System.out.println(Arrays.toString(data));
            }

            System.out.println("第"+ (k++) +"次增量為【"+jmp+"】排序結果:"+Arrays.toString(data));
            System.out.println("==================================================");
            jmp=jmp/2;    //控制迴圈數
        }
    }

 

選擇排序

1、簡介

 選擇排序(Selection sort)是一種簡單直觀的排序演算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。

2、圖解

1、從左到右順序比較 
2、第一個元素依次比較第二個元素直到最後一個元素 

(這裡需要注意,當第一個元素髮生交換時,並不停止比較,而是用替換後的元素,繼續比較,這就能在首次找到最小值)
3、第二個元素依次比較第三個元素直到最後一個元素 
4、以此類推

3、程式碼

    /*選擇排序*/
    private static int[] SelectSort(int[] a){
        int temp,num = 0;
        int i ,j;
        for(i=0;i<a.length-1;i++){
            for(j=i+1;j<a.length;j++){
                if(a[i] > a[j]){
                    temp = a[j];
                    a[j] = a[i];
                    a[i]=temp;
                }
            }
            System.out.println("第"+(++num)+"趟排序: "+Arrays.toString(a));
        }
        return a;
    }

快速排序

方法其實很簡單:分別從初始序列“6 1 2 7 9 3 4 5 10 8”兩端開始“探測”。這裡將 6 設定為基準數,先從右往左找一個小於6的數,再從左往右找一個大於6的數,然後交換他們。這裡可以用兩個變數i和j,分別指向序列最左邊和最右邊。我們為這兩個變數起個好聽的名字“哨兵i”和“哨兵j”。剛開始的時候讓哨兵i指向序列的最左邊(即i=1),指向數字6。讓哨兵j指向序列的最右邊(即=10),指向數字。

è¿éåå¾çæè¿°

       首先哨兵j開始出動。因為此處設定的基準數是最左邊的數,所以需要讓哨兵j先出動,這一點非常重要(請自己想一想為什麼)。哨兵j一步一步地向左挪動(即j–),直到找到一個小於6的數停下來。接下來哨兵i再一步一步向右挪動(即i++),直到找到一個數大於6的數停下來。最後哨兵j停在了數字5面前,哨兵i停在了數字7面前。 

è¿éåå¾çæè¿°

è¿éåå¾çæè¿°
現在交換哨兵i和哨兵j所指向的元素的值。交換之後的序列如下: 
6 1 2 5 9 3 4 7 10 8

       到此,第一次交換結束。接下來開始哨兵j繼續向左挪動(再友情提醒,每次必須是哨兵j先出發)。他發現了4(比基準數6要小,滿足要求)之後停了下來。哨兵i也繼續向右挪動的,他發現了9(比基準數6要大,滿足要求)之後停了下來。此時再次進行交換,交換之後的序列如下:

6 1 2 5 4 3 9 7 10 8

è¿éåå¾çæè¿°

è¿éåå¾çæè¿°

       第二次交換結束,“探測”繼續。哨兵j繼續向左挪動,他發現了3(比基準數6要小,滿足要求)之後又停了下來。哨兵i繼續向右移動,糟啦!此時哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。說明此時“探測”結束。我們將基準數6和3進行交換。交換之後的序列如下:

3 1 2 5 4 6 9 7 10 8

è¿éåå¾çæè¿°

è¿éåå¾çæè¿°

è¿éåå¾çæè¿°

       到此第一輪“探測”真正結束。此時以基準數6為分界點,6左邊的數都小於等於6,6右邊的數都大於等於6。回顧一下剛才的過程,其實哨兵j的使命就是要找小於基準數的數,而哨兵i的使命就是要找大於基準數的數,直到i和j碰頭為止。

       OK,解釋完畢。現在基準數6已經歸位,它正好處在序列的第6位。此時我們已經將原來的序列,以6為分界點拆分成了兩個序列,左邊的序列是“3 1 2 5 4”,右邊的序列是“9 7 10 8”。接下來還需要分別處理這兩個序列。因為6左邊和右邊的序列目前都還是很混亂的。不過不要緊,我們已經掌握了方法,接下來只要模擬剛才的方法分別處理6左邊和右邊的序列即可。現在先來處理6左邊的序列現吧。

       左邊的序列是“3 1 2 5 4”。請將這個序列以3為基準數進行調整,使得3左邊的數都小於等於3,3右邊的數都大於等於3。好了開始動筆吧

       如果你模擬的沒有錯,調整完畢之後的序列的順序應該是:

       2 1 3 5 4

       OK,現在3已經歸位。接下來需要處理3左邊的序列“2 1”和右邊的序列“5 4”。對序列“2 1”以2為基準數進行調整,處理完畢之後的序列為“1 2”,到此2已經歸位。序列“1”只有一個數,也不需要進行任何處理。至此我們對序列“2 1”已全部處理完畢,得到序列是“1 2”。序列“5 4”的處理也仿照此方法,最後得到的序列如下:

       1 2 3 4 5 6 9 7 10 8

       對於序列“9 7 10 8”也模擬剛才的過程,直到不可拆分出新的子序列為止。最終將會得到這樣的序列,如下

       1 2 3 4 5 6 7 8 9 10

       到此,排序完全結束。細心的同學可能已經發現,快速排序的每一輪處理其實就是將這一輪的基準數歸位,直到所有的數都歸位為止,排序就結束了。下面上個霸氣的圖來描述下整個演算法的處理過程。

è¿éåå¾çæè¿°

2、程式碼

 private static void quick(int d[], int left, int right) {
        int i, j, tmp;
        int lf_idx;
        int rg_idx;
        //1:第一個鍵值為d[lf]
        if (left < right) {
            lf_idx = left + 1;
            rg_idx = right;

            //單次迴圈內排序
            while (true) {
                System.out.print("[處理過程" + (++process) + "]=> " + Arrays.toString(data) + "\n");

                for (j = right; j >= left + 1; j--)   //3:由右向左找出一個鍵值小於d[lf]者
                {
                    if (d[j] <= d[left]) {
                        rg_idx = j;
                        for (i = left + 1; i <= rg_idx; i++)  //2:由左向右找出一個鍵值大於d[lf]者
                        {
                            if ((d[i] >= d[left]) || (i == rg_idx)) {
                                lf_idx = i;
                                break;
                            }
                        }
                        break;
                    }
                }

                if (lf_idx < rg_idx)        //4-1:若lf_idx<rg_idx
                {
                    tmp = d[lf_idx];
                    d[lf_idx] = d[rg_idx]; //則d[lf_idx]和d[rg_idx]互換
                    d[rg_idx] = tmp;       //然後繼續排序
                } else {
                    break;             //否則跳出排序過程
                }
            }

            //重新設定哨兵,通過遞迴先將左邊排好,再將右邊排好
            if (lf_idx == rg_idx)              //5-1:若lf_idx大於等於rg_idx
            {                               //則將d[left]和d[rg_idx]互換
                tmp = d[left];
                d[left] = d[rg_idx];
                d[rg_idx] = tmp;
                //5-2:並以rg_idx為基準點分成左右兩半
                quick(d, left, rg_idx - 1); //以遞迴方式分別為左右兩半進行排序
                quick(d, rg_idx + 1, right); //直至完成排序
            }
        }
    }

 

氣泡排序

1、簡介:氣泡排序(Bubble Sort,臺灣譯為:泡沫排序或氣泡排序)是一種簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。

2、圖解

è¿éåå¾çæè¿°

3、程式碼

   private static int[] BubbleSort(int[] a){

        int temp,num = 0;
        int i , j ;

        for(i = a.length - 1; i > 0; i--){
            for(j = 0 ; j < i; j++) {
                if(a[j] > a[j+1]){
                    temp = a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                }
            }
            System.out.println("第"+(++num)+"趟排序: "+Arrays.toString(a));
        }
        return a;
    }

 

歸併排序

1、簡介: 歸併排序(MERGE-SORT)是利用歸併的思想實現的排序方法,該演算法採用經典的分治(divide-and-conquer)策略(分治法將問題(divide)成一些小的問題然後遞迴求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。

2、圖解

 

3、程式碼:

private static int[] sort(int[] nums, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左邊
            sort(nums, low, mid);
            // 右邊
            sort(nums, mid + 1, high);
            // 左右歸併
            merge(nums, low, mid, high);
        }
        return nums;
    }

    private static void merge(int[] nums, int low, int mid, int high) {
        System.out.println("合併的次數"+(++k));
        int[] temp = new int[high - low + 1];
        int i = low;// 左指標
        int j = mid + 1;// 右指標
        int k = 0;

        // 把較小的數先移到新陣列中
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                temp[k++] = nums[i++];
            } else {
                temp[k++] = nums[j++];
            }
        }

        // 把左邊剩餘的數移入陣列
        while (i <= mid) {
            temp[k++] = nums[i++];
        }

        // 把右邊邊剩餘的數移入陣列
        while (j <= high) {
            temp[k++] = nums[j++];
        }

        // 把新陣列中的數覆蓋nums陣列
        for (int k2 = 0; k2 < temp.length; k2++) {
            nums[k2 + low] = temp[k2];
        }
    }