常見的排序算法 (下)

5. 歸併排序

​ 兩個有序數組合並並不難, 可是歸併的思想確實是這個, 可是如何分, 分到什麼時候呢 ?java

這個名字含義就是分爲歸 和 並 兩個階段執行node

先說並吧, 並要求是兩個已經排序好了的數組(兩個連續數組是位置上也連續) , 好比1,2,3,4 , 連續數組1,23,4 , 不能是 1,2,4進行排序 ,api

對於兩個已經排序好了的數據, 好處是一次遍歷即可以兩個數組合併成一個有序的數組數組

而後再說 歸吧, 歸的思想就是 , 4,3,2,1 , 先分 4,32,1 , 將4,3繼續分 4,3,此時這就是倆位置有序的數組, 將他倆數組進行並 , 就成了 3,4 , 而後2,1分爲2,1 , 此時再將這倆數組排序,此時是1,2 , 而後將3,41,2進行並排序, 此時就是1,2,3,4 , OK了ui

/**
 * 歸併排序
 *
 * 
 * @author: <a href='mailto:fanhaodong516@qq.com'>Anthony</a>
 */
public class MergeSort {

    public static void main(String[] args) {
        int[] arr_1000 = Common.generate_Arr_1000();
        splitAndMergerSort(arr_1000, 0, arr_1000.length - 1);
        Common.showArr(arr_1000);
    }


    /**
     * 將[1,2,3]數組分割成直到 [1][2][3]這種 ,遞歸實現
     *
     * @param arr   數組
     * @param start 開始位置 ,索引從0開始
     * @param end   結束位置 , 索引0開始
     */
    private static void splitAndMergerSort(int[] arr, int start, int end) {
        if (start == end) return;

        int middle = (end + start) >> 1;

        splitAndMergerSort(arr, start, middle);

        splitAndMergerSort(arr, middle + 1, end);

        merge(arr, start, end, middle + 1);
    }


    /**
     * @param arr       數據
     * @param start     索引從0開始 , 數組開始位置
     * @param end       索引從0開始 , 數組結束位置
     * @param delimiter 切割位置 : [1,2,4,5]   參數(arr,0 , 3 , 2) , 分隔符是 (star+end)/2+1
     */
    private static void merge(int[] arr, int start, int end, int delimiter) {
        if (start == end) return;

        // 1. 初始化數組
        int llen = delimiter - start;
        int rlen = (end - delimiter) + 1;

        int[] left = new int[llen];
        int[] right = new int[rlen];

        //2. 將分割數組 , 填充數據
        System.arraycopy(arr, start, left, 0, llen);
        System.arraycopy(arr, delimiter, right, 0, rlen);

        // 3.歸併步驟
        int l = 0;
        int r = 0;
        int len = end + 1;
        for (int i = start; i < len; ++i) {
            if (l < llen && r < rlen) {
                if (left[l] < right[r]) {
                    arr[i] = left[l];
                    l++;
                } else {
                    arr[i] = right[r];
                    r++;
                }
            } else {
                if (l < llen) {
                    arr[i] = left[l];
                    l++;
                }
                if (r < rlen) {
                    arr[i] = right[r];
                    r++;
                }
            }
        }
    }
    
    // 第二種合併方式
    private static void merge(int[] left_arr, int[] right_arr) {
        int lL = left_arr.length;
        int rL = right_arr.length;
        int resultL = lL + rL;
        int[] result = new int[resultL];

        int l = 0;
        int r = 0;
        int w = 0;
        while (w < resultL) {
            if (l < lL && r < rL) {
                if (left_arr[l] < right_arr[r]) {
                    result[w] = left_arr[l];
                    l++;
                    w++;
                } else {
                    result[w] = right_arr[r];
                    r++;
                    w++;
                }
            } else {
                if (l < lL) {
                    System.arraycopy(left_arr, l, result, w, lL - l);
                    break;
                }
                if (r < rL) {
                    System.arraycopy(right_arr, r, result, w, rL - r);
                    break;
                }
            }
        }

        Common.showArr(result);
    }
}

6. 堆排序

數組他能夠利用索引關係構建出一個徹底二叉樹 , 因此利用這個特性很好的組成堆,code

堆排序 分爲兩個階段排序

一階段 : 堆化 - > 將數據轉換成 大頂堆或者小頂堆 , 就是根節點數據大於子葉數據 , 就是大頂堆.遞歸

二階段 : 利用大頂堆或者小頂堆進行排序 , 就是將堆頂和最後一個數據進行交換, 由於堆頂是最大值或者最小值, 而後堆化, 重複操做 ,索引

/**
 * 堆排序
 * 
 * @author: <a href='mailto:fanhaodong516@qq.com'>Anthony</a>
 */
public class HeapSort {

    public static void main(String[] args) {
        int[] arr_1000 = Common.generate_Arr_1000();
        heap_sort(arr_1000, arr_1000.length);

        Common.showArr(arr_1000);
    }

    /**
     * 堆排序
     *
     * @param tree
     * @param n
     */
    static void heap_sort(int[] tree, int n) {

        // 1. 構建一個堆
        build_heap(tree, n);

        // 2.
        // 堆頂和最後一個節點作交換 , 可是咱們須要在數組上截取 , 因此就是每次
        for (int i = n - 1; i >= 0; i--) {
            // 交換節點
            swap(tree, i, 0);

            // 第0個位置 開始堆從新排序
            heapify(tree, i, 0);
        }
    }

    /**
     * 構建一個 大頂堆
     *
     * @param tree
     * @param n
     */
    static void build_heap(int[] tree, int n) {

        // 最後一個節點
        int last_node = n - 1;

        // 開始遍歷的位置是 : 最後一個堆的堆頂 , 意思就是 , 整個樹中最小的一個堆 , 其實就是最後一個節點的父節點
        int parent = (last_node - 1) / 2;

        // 遞減向上遍歷
        for (int i = parent; i >= 0; i--) {
            heapify(tree, n, i);
        }
    }




    /**
     * @param tree 表明一棵樹
     * @param n    表明多少個節點
     * @param i    對哪一個節點進行 heapify
     */
    static void heapify(int[] tree, int n, int i) {

        // 若是當前值 大於 n 直接返回了 ,通常不會出現這種問題 .....
        if (i >= n) {
            return;
        }

        // 子節點
        int c1 = 2 * i + 1;
        int c2 = 2 * i + 2;

        // 假設最大的節點 爲 i (父節點)
        int max = i;

        // 若是大於  賦值給 max
        if (c1 < n && tree[c1] > tree[max]) {
            max = c1;
        }

        // 若是大於  賦值給 max
        if (c2 < n && tree[c2] > tree[max]) {
            max = c2;
        }

        // 若是i所在的就是最大值咱們不必去作交換
        if (max != i) {

            // 交換最大值 和 父節點 的位置
            swap(tree, max, i);

            // 交換完之後 , 此時的max其實就是 i原來的數 ,就是最小的數字 ,因此須要遞歸遍歷
            heapify(tree, n, max);
        }

    }


    static void swap(int[] tree, int max, int i) {
        int temp = tree[max];
        tree[max] = tree[i];
        tree[i] = temp;
    }

}

7. 桶排序

其實我認爲他是 區間排序, 舉個例子 [1,2,3,4,5,6] , 將他放入6個區間內 , (0,1] ,(1,2] , 依次到最後 , 那麼這個放置過程徹底是能夠經過計算獲得的. 因此一次遍歷即可以完成排序 , 若是區間內部排序, 能夠選擇其餘排序方式.it

package com.sort;

import java.util.Arrays;

/**
 * 桶排序  ,其實就是區間排序  1,2,3,4,5,6  ,咱們分紅 0-3, 3-6的區間(首先區間是有順序的)
 * , 1,2,3 進去區間一, 4,5,6進去區間二 , 而後區間內排序, 此時就構建了新的數組
 *
 *
 * @author: <a href='mailto:fanhaodong516@qq.com'>Anthony</a>
 */
public class BucketSort {

    public static void main(String[] args) {

        int[] arr_1000 = Common.generate_Arr_1000();

        bucketSort(arr_1000, 10);

        Common.showArr(arr_1000);
    }

    /**
     * @param arr         數組
     * @param bucketCount 桶的個數
     */
    public static void bucketSort(int[] arr, int bucketCount) {
        int len = arr.length;
        if (len <= 1 || bucketCount <= 0) {
            return;
        }

        // 遍歷一次找到最大值 最小值
        int max = arr[0], min = arr[0];
        for (int i : arr) {
            if (i > max) {
                max = i;
            }
            if (i < min) {
                min = i;
            }
        }

        /**
         * 劃分區間 , 好比  5 - 11 ,此時咱們須要 / 桶數量 (假如 是 2), 若是咱們不+1 , 6 / 2 = 3 ,那麼 (11-5)/3=2 , 此時座標2這個桶
         *
         * 因此區間須要+1 操做 , 因此上面就是 7/2=3.5=4 , (11-5)/4=1
         */
        int range = ((max - min + 1) % bucketCount) == 0 ? (max - min + 1) / bucketCount : (max - min + 1) / bucketCount + 1;


        // 建立桶 ,是一個二維數組
        int[][] bucket = new int[bucketCount][];

        for (int i : arr) {
            bucket[(i - min) / range] = arrAppend(bucket[(i - min) / range], i);
        }


        for (int[] ints : bucket) {
            sort(ints);
        }

        int count = 0;
        for (int[] ints : bucket) {
            if (ints != null) {
                for (int anInt : ints) {
                    arr[count++] = anInt;
                }
            }
        }


    }

    /**
     * 數組拷貝
     *
     * @param arr
     * @param value
     * @return
     */
    private static int[] arrAppend(int[] arr, int value) {
        //數組若是爲空, 新建一個數組,
        if (arr == null) {
            arr = new int[0];
        }
        // 數組拷貝 , 其實就是長度+1
        arr = Arrays.copyOf(arr, arr.length + 1);
        // 將值複製
        arr[arr.length - 1] = value;

        //返回
        return arr;
    }


    private static void sort(int[] arr) {

        /**
         * 空 或者  0 / 1 都直接返回
         */
        if (null == arr || arr.length <= 1) {
            return;
        }

        // 2 3 1
        for (int index = 1; index < arr.length; index++) {

            // 當前位置 , 開始必須從第二個開始
            int temp = arr[index];

            // 左邊位置
            int left = index - 1;

            // 移動座標其實就是 ...
            while (left >= 0 && arr[left] > temp) {

                // 互換位置
                arr[left + 1] = arr[left];

                // 向前移動
                left--;
            }

            // 最後保存數據
            arr[left + 1] = temp;
        }
    }

}

8. 基數排序

我沒有寫, 他和桶排序相似 , 一次比較個位數 , 十位數 , 百位數數據, 分紅10個桶 , 對號入座, 第一遍比較個位數, 第二遍比較十位數, 第三遍比較百位數 .

相關文章
相關標籤/搜索