排序算法總結(jié)(java)

目錄:
一、排序算法說明
? ? ?1.排序的定義
? ? ?2.術(shù)語解析
? ? ?3.算法分類
? ? ?4.比較和非比較的區(qū)別
? ? ?5.排序算法中IN-PLACE和OUT-PLACE是什么意思?
? ? ?6.算法總結(jié)
二、算法實現(xiàn)

歡迎下方評論留言,文章持續(xù)更新優(yōu)化

一、排序算法說明

1.排序的定義

對一序列對象根據(jù)某個關(guān)鍵字進(jìn)行排序。

2.術(shù)語解析

穩(wěn)定 :如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不穩(wěn)定 :如果a原本在b的前面,而a=b,排序之后a可能會出現(xiàn)在b的后面;

????關(guān)于排序穩(wěn)定性的定義:
通俗地講就是能保證排序前兩個相等的數(shù)其在序列的前后位置順序和排序后它們兩個的前后位置順序相同。在簡單形式化一下,如果Ai = Aj,Ai原來在位置前,排序后Ai還是要在Aj位置前。

????問題來了,什么時候必須要求使用穩(wěn)定排序呢?
由上面的定義可知道穩(wěn)定性排序保證了排序前兩個相等的數(shù)其在序列的前后位置順序和排序后它們兩個的前后位置順序相同。那么,當(dāng)現(xiàn)實應(yīng)用中排序的需求需要區(qū)別先后順序的時候就必須用到穩(wěn)定排序。

舉個例子:
??假如發(fā)獎學(xué)金,排在前三個的分別獲一、二、三等獎,結(jié)果一排序把原來的第二位和第三位相同分?jǐn)?shù)的進(jìn)行了置換,估計拿到三等獎的不會樂意。

引用自 http://www.itdecent.cn/p/abe27f16b7b5

內(nèi)排序 :所有排序操作都在內(nèi)存中完成;
外排序:由于數(shù)據(jù)太大,因此把數(shù)據(jù)放在磁盤中,而排序通過磁盤和內(nèi)存的數(shù)據(jù)傳輸才能進(jìn)行;
時間復(fù)雜度 : 一個算法執(zhí)行所耗費的時間。
空間復(fù)雜度 :運行完一個程序所需內(nèi)存的大小。

3.算法分類

??排序算法可以分為內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。


排序算法

4.比較和非比較的區(qū)別

比較類排序:通過比較來決定元素間的相對次序,由于其時間復(fù)雜度不能突破O(nlogn),因此也稱為非線性時間比較類排序。
非比較類排序:不通過比較來決定元素間的相對次序,它可以突破基于比較排序的時間下界,以線性時間運行,因此也稱為線性時間非比較類排序

排序算法

5.排序算法中IN-PLACE和OUT-PLACE是什么意思?

IN-PLACE: 占用常數(shù)內(nèi)存,不占用額外內(nèi)存
OUT-PLACE :占用額外內(nèi)存

IN-PLACE
假如問題規(guī)模是n,在解決問題過程中,只開辟了常數(shù)量的空間,與n無關(guān),這是原址操作,就是In-place。
舉個例子:

/**
  * @param arr
  * @return 冒泡排序,輸出升序排序數(shù)組
  */
public static int[] bubbleSort(int[] arr) {
        //為了不對原數(shù)組的數(shù)據(jù)進(jìn)行修改
        arr = Arrays.copyOf(arr, arr.length);
        int length = arr.length;
        int t;
        for (int i = 1; i < arr.length; i++) {
            // 設(shè)定一個標(biāo)記,若為true,則表示此次循環(huán)沒有進(jìn)行交換,也就是待排序列已經(jīng)有序,排序已經(jīng)完成。
            boolean flag = true;
            for (int j = 0; j < length - i; j++) {
                //若是需要輸出降序的數(shù)據(jù)結(jié)果,只需要把下面的 > 換成 < 
                if (arr[j] > arr[j + 1]) {
                    t = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = t;
                    flag = false;
                }
            }
            if (flag) {
                return arr;
            }
        }
        return arr;
 }

在冒泡排序中,為了將arr排序,借用了一個t 的臨時變量,開辟了一個臨時空間,這個空間是常數(shù)量,這就是in-place。

OUT-PLACE
如果開辟的輔助空間與問題規(guī)模有關(guān),則是out-place。
拿上面的例子來說,假設(shè)你把排序時把數(shù)組中的數(shù)按順序放入了一個新的數(shù)組,我就開了一個n規(guī)模大小的數(shù)組,這個就與數(shù)據(jù)規(guī)模有關(guān)

6.算法總結(jié)

算法總結(jié)

圖片名詞解釋:
n: 數(shù)據(jù)規(guī)模
k: “桶”的個數(shù)
In-place: 占用常數(shù)內(nèi)存,不占用額外內(nèi)存
Out-place: 占用額外內(nèi)存

二、算法實現(xiàn)

算法實現(xiàn)參考以下文章 :

十大經(jīng)典排序算法-菜鳥教程
十大經(jīng)典排序算法(動圖演示)
排序算法

1.冒泡排序

冒泡排序是一種簡單的排序算法。它重復(fù)地走訪過要排序的數(shù)列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數(shù)列的工作是重復(fù)地進(jìn)行直到?jīng)]有再需要交換,也就是說該數(shù)列已經(jīng)排序完成。這個算法的名字由來是因為越小的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端。

算法步驟:

  • 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;
  • 對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對,這樣在最后的元素應(yīng)該會是最大的數(shù);
  • 針對所有的元素重復(fù)以上的步驟,除了最后一個;
  • 重復(fù)步驟1~3,直到排序完成。

代碼實現(xiàn)

/**
 * @author mumuxi
 * @date 2020/1/19
 */
public class Test {

    public static void main(String[] args) {
        int[] sourceArray = new int[]{10, 5, 34, 56, 676, 43545, 232, 232, 1};
        for (int i = 0; i < sourceArray.length; i++) {
            System.out.println("source[" + i + "] = " + sourceArray[i]);
        }
        //如果不想對原數(shù)組的數(shù)據(jù)進(jìn)行修改,可以進(jìn)行copy后再排序
//        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
//        bubbleSort(arr);
        bubbleSort(sourceArray);
        for (int i = 0; i < sourceArray.length; i++) {
            System.out.println("source[" + i + "] = " + sourceArray[i]);
        }
    }

  
    /**
     * @param arr
     * @return 冒泡排序,輸出升序排序數(shù)組
     */
    public static int[] bubbleSort(int[] arr) {
        int length = arr.length;
        int t;
        for (int i = 1; i < arr.length; i++) {
            // 設(shè)定一個標(biāo)記,若為true,則表示此次循環(huán)沒有進(jìn)行交換,也就是待排序列已經(jīng)有序,排序已經(jīng)完成。
            boolean flag = true;
            for (int j = 0; j < length - i; j++) {
                //若是需要輸出降序的數(shù)據(jù)結(jié)果,只需要把下面的 > 換成 <
                if (arr[j] > arr[j + 1]) {
                    t = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = t;
                    flag = false;
                }
            }
            if (flag) {
                return arr;
            }
        }
        return arr;
    }

}

冒泡排序還有一種優(yōu)化算法,就是立一個 flag,當(dāng)在一次序列遍歷中元素沒有發(fā)生交換,則證明該序列已經(jīng)有序。(如上面的代碼所示)

冒泡排序在什么情況下最快?
當(dāng)輸入的數(shù)據(jù)已經(jīng)是正序時。

那么在什么情況下最慢呢?
當(dāng)輸入的數(shù)據(jù)是反序時。

2.選擇排序

??選擇排序是一種簡單直觀的排序算法,無論什么數(shù)據(jù)進(jìn)去都是 O(n2) 的時間復(fù)雜度。所以用到它的時候,數(shù)據(jù)規(guī)模越小越好。唯一的好處可能就是不占用額外的內(nèi)存空間了吧。

算法步驟:

  • 首先在未排序序列中找到最?。ù螅┰?,存放到排序序列的起始位置。
  • 再從剩余未排序元素中繼續(xù)尋找最?。ù螅┰?,然后放到已排序序列的末尾。
  • 重復(fù)第二步,直到所有元素均排序完畢。

代碼實現(xiàn)

/**
  * @param arr
  * @return 選擇排序, 輸出升序結(jié)果
  */
public static int[] selectionSort(int[] arr) {
        int length = arr.length;
        int min, tmp;
        // 總共要經(jīng)過 N-1 輪比較
        for (int i = 0; i < length - 1; i++) {
            min = i;
            // 每輪需要比較的次數(shù) N-i
            for (int j = i + 1; j < length; j++) {
                //若是需要輸出降序結(jié)果,則把下面的 < 改為 > 即可
                if (arr[j] > arr[min]) {
                    // 記錄目前能找到的最小值元素的下標(biāo)
                    min = j;
                }
            }
            // 將找到的最小值和i位置所在的值進(jìn)行交換
            if (min != i) {
                tmp = arr[min];
                arr[min] = arr[i];
                arr[i] = tmp;
            }
        }
        return arr;
}

3.插入排序

??插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工作原理是通過構(gòu)建有序序列,對于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入。插入排序在實現(xiàn)上,通常采用in-place排序(即只需用到O(1)的額外空間的排序),因而在從后向前掃描過程中,需要反復(fù)把已排序元素逐步向后挪位,為最新元素提供插入空間。

算法步驟:

  • 從第一個元素開始,該元素可以認(rèn)為已經(jīng)被排序;
  • 取出下一個元素,在已經(jīng)排序的元素序列中從后向前掃描;
  • 如果該元素(已排序)大于新元素,將該元素移到下一位置;
  • 重復(fù)步驟3,直到找到已排序的元素小于或者等于新元素的位置;
  • 將新元素插入到該位置后;
  • 重復(fù)步驟2~5;

代碼實現(xiàn)

    /**
     * @param arr
     * @return 插入排序,輸出升序排序結(jié)果
     */
    public static int[] insertionSort(int[] arr) {
        int length = arr.length;
        //保存要插入的數(shù)據(jù)
        int tmp;
        int j;
        // 從下標(biāo)為1的元素開始選擇合適的位置插入,因為下標(biāo)為0的只有一個元素,默認(rèn)是有序的
        for (int i = 1; i < length; i++) {

            // 記錄要插入的數(shù)據(jù)
            tmp = arr[i];

            // 從已經(jīng)排序的序列最右邊的開始比較,找到比其小的數(shù)
            j = i;
            //輸出降序的排序結(jié)果只需要把下面的 < 換成 >
            while (j > 0 && tmp < arr[j - 1]) {
                arr[j] = arr[j - 1];
                j--;
            }

            // 存在比其小的數(shù),插入
            if (j != i) {
                arr[j] = tmp;
            }

        }
        return arr;
    }

4.希爾排序

??希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進(jìn)版本。但希爾排序是非穩(wěn)定排序算法。希爾排序的基本思想是:先將整個待排序的記錄序列分割成為若干子序列分別進(jìn)行直接插入排序,待整個序列中的記錄"基本有序"時,再對全體記錄進(jìn)行依次直接插入排序。

希爾排序是基于插入排序的以下兩點性質(zhì)而提出改進(jìn)方法的:

  • 插入排序在對幾乎已經(jīng)排好序的數(shù)據(jù)操作時,效率高,即可以達(dá)到線性排序的效率;
  • 但插入排序一般來說是低效的,因為插入排序每次只能將數(shù)據(jù)移動一位;

算法步驟:

  • 選擇一個增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  • 按增量序列個數(shù) k,對序列進(jìn)行 k 趟排序;
  • 每趟排序,根據(jù)對應(yīng)的增量 ti,將待排序列分割成若干長度為 m 的子序列,分別對各子表進(jìn)行直接插入排序。僅增量因子為 1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。

代碼實現(xiàn)

   /**
     * @param arr
     * @return 希爾排序,返回升序排序結(jié)果
     */
    public static int[] shellSort(int[] arr) {
        //增量的選取有多種,這里取最原始的 gap = n/2;
        int gap = arr.length / 2;

        while (gap > 0) {
            //下面的for循環(huán)實際上就是普通的插入算法,普通的插入算法 gap(增量) = 1 ,這里只是把 1 換成了 gap(增量)
            for (int i = gap; i < arr.length; i++) {
                int tmp = arr[i];
                int j = i;
                //這里比普通的插入算法多加了個判斷 j-gap >=0;下面的 tmp < arr[j - gap]
                //換成 tmp < arr[j - gap]就會輸出降序排序
                while (j > 0 && j - gap >= 0 && tmp < arr[j - gap]) {
                    arr[j] = arr[j - gap];
                    j = j - gap;
                }
                if (j != i) {
                    arr[j] = tmp;
                }

            }
            gap = gap / 2;
        }

        return arr;
    }

5.歸并排序

??歸并排序(Merge sort)是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應(yīng)用。

作為一種典型的分而治之思想的算法應(yīng)用,歸并排序的實現(xiàn)由兩種方法:

  • 自上而下的遞歸(所有遞歸的方法都可以用迭代重寫,所以就有了第 2 種方法);
  • 自下而上的迭代;

算法步驟:

  • 申請空間,使其大小為兩個已經(jīng)排序序列之和,該空間用來存放合并后的序列;

  • 設(shè)定兩個指針,最初位置分別為兩個已經(jīng)排序序列的起始位置;

  • 比較兩個指針?biāo)赶虻脑?,選擇相對小的元素放入到合并空間,并移動指針到下一位置;

  • 重復(fù)步驟 3 直到某一指針達(dá)到序列尾;

  • 將另一序列剩下的所有元素直接復(fù)制到合并序列尾。

代碼實現(xiàn)

/**
     * @param arr
     * @return 歸并排序,返回升序數(shù)組,不影響原數(shù)組數(shù)據(jù)
     */
    public static int[] mergeSort(int[] arr) {
        
        if (arr.length < 2) {
            return arr;
        }
        int length = arr.length / 2;
        int middle = (int) Math.floor(length);

        int[] left = Arrays.copyOfRange(arr, 0, middle);
        int[] right = Arrays.copyOfRange(arr, middle, arr.length);

        return merge(mergeSort(left), mergeSort(right));
    }

    private static int[] merge(int[] left, int[] right) {
        int[] result = new int[left.length + right.length];
        int i = 0;
        while (left.length > 0 && right.length > 0) {
            //下面的< 換成 > 就是返回降序的數(shù)據(jù)
            if (left[0] <= right[0]) {
                result[i++] = left[0];
                left = Arrays.copyOfRange(left, 1, left.length);
            } else {
                result[i++] = right[0];
                right = Arrays.copyOfRange(right, 1, right.length);
            }
        }

        while (left.length > 0) {
            result[i++] = left[0];
            left = Arrays.copyOfRange(left, 1, left.length);
        }

        while (right.length > 0) {
            result[i++] = right[0];
            right = Arrays.copyOfRange(right, 1, right.length);
        }

        return result;
    }

圖文解析


6.快速排序

快速排序的基本思想:通過一趟排序?qū)⒋庞涗浄指舫瑟毩⒌膬刹糠?,其中一部分記錄的關(guān)鍵字均比另一部分的關(guān)鍵字小,則可分別對這兩部分記錄繼續(xù)進(jìn)行排序,以達(dá)到整個序列有序??焖倥判蛴质且环N分而治之思想在排序算法上的典型應(yīng)用。本質(zhì)上來看,快速排序應(yīng)該算是在冒泡排序基礎(chǔ)上的遞歸分治法。

算法步驟:

快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體算法描述如下:

  • 從數(shù)列中挑出一個元素,稱為 “基準(zhǔn)”(pivot);
  • 重新排序數(shù)列,所有元素比基準(zhǔn)值小的擺放在基準(zhǔn)前面,所有元素比基準(zhǔn)值大的擺在基準(zhǔn)的后面(相同的數(shù)可以到任一邊)。在這個分區(qū)退出之后,該基準(zhǔn)就處于數(shù)列的中間位置。這個稱為分區(qū)(partition)操作;
  • 遞歸地(recursive)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序。

代碼實現(xiàn)

/**
 * @author mumuxi
 * @date 2020/1/19
 */
public class Test {

    public static void main(String[] args) {


        int[] sourceArray = new int[]{1, 0, 10, 78, 898, 874, 56, 9, 98, 100, 300};

        int[] result = Arrays.copyOf(sourceArray, sourceArray.length);
        
        result = quickSort(result, 0, result.length-1);
        
        for (int i = 0; i < sourceArray.length; i++) {
            System.out.println(sourceArray[i]);
        }

        for (int i = 0; i < result.length; i++) {
            System.out.println(result[i]);
        }
    }
    
    public static int[] quickSort(int[] arr, int left, int right) {
        if (left < right) {
            int partitionIndex = partition(arr, left, right);
            quickSort(arr, left, partitionIndex - 1);
            quickSort(arr, partitionIndex + 1, right);
        }
        return arr;
    }

    private static int partition(int[] arr, int left, int right) {
        // 設(shè)定基準(zhǔn)值(pivot)
        int pivot = left;
        int index = pivot + 1;
        for (int i = index; i <= right; i++) {
            if (arr[i] < arr[pivot]) {
                swap(arr, i, index);
                index++;
            }
        }
        swap(arr, pivot, index - 1);
        return index - 1;
    }

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

7.堆排序

堆排序(Heapsort)是指利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計的一種排序算法。堆積是一個近似完全二叉樹的結(jié)構(gòu),并同時滿足堆積的性質(zhì):即子結(jié)點的鍵值或索引總是小于(或者大于)它的父節(jié)點。堆排序可以說是一種利用堆的概念來排序的選擇排序。分為兩種方法:

  • 大頂堆:每個節(jié)點的值都大于或等于其子節(jié)點的值,在堆排序算法中用于升序排列;
  • 小頂堆:每個節(jié)點的值都小于或等于其子節(jié)點的值,在堆排序算法中用于降序排列;
    堆排序的平均時間復(fù)雜度為 Ο(nlogn)。

算法步驟:

  • 創(chuàng)建一個堆 H[0……n-1];
  • 把堆首(最大值)和堆尾互換;
  • 把堆的尺寸縮小 1,并調(diào)用 shift_down(0),目的是把新的數(shù)組頂端數(shù)據(jù)調(diào)整到相應(yīng)位置;
  • 重復(fù)步驟 2,直到堆的尺寸為 1。

代碼實現(xiàn)

public static int[] heapSort(int[] arr ) {
      
        int len = arr.length;

        buildMaxHeap(arr, len);

        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
        return arr;
    }

    private static void buildMaxHeap(int[] arr, int len) {
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
            heapify(arr, i, len);
        }
    }

    private static void heapify(int[] arr, int i, int len) {
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;

        if (left < len && arr[left] > arr[largest]) {
            largest = left;
        }

        if (right < len && arr[right] > arr[largest]) {
            largest = right;
        }

        if (largest != i) {
            swap(arr, i, largest);
            heapify(arr, largest, len);
        }
    }

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

8.計數(shù)排序

計數(shù)排序不是基于比較的排序算法,其核心在于將輸入的數(shù)據(jù)值轉(zhuǎn)化為鍵存儲在額外開辟的數(shù)組空間中。 作為一種線性時間復(fù)雜度的排序,計數(shù)排序要求輸入的數(shù)據(jù)必須是有確定范圍的整數(shù)。

算法步驟:

  • 找出待排序的數(shù)組中最大和最小的元素
  • 統(tǒng)計數(shù)組中每個值為i的元素出現(xiàn)的次數(shù),存入數(shù)組C的第i項
  • 對所有的計數(shù)累加(從C中的第一個元素開始,每一項和前一項相加)
  • 反向填充目標(biāo)數(shù)組:將每個元素i放在新數(shù)組的第C(i)項,每放一個元素就將C(i)減去1

代碼實現(xiàn)

 public static int[] countingSort(int[] arr ) {
        
        int maxValue = getMaxValue(arr);

        return countingSort(arr, maxValue);
    }

    private static int[] countingSort(int[] arr, int maxValue) {
        int bucketLen = maxValue + 1;
        int[] bucket = new int[bucketLen];

        for (int value : arr) {
            bucket[value]++;
        }

        int sortedIndex = 0;
        for (int j = 0; j < bucketLen; j++) {
            while (bucket[j] > 0) {
                arr[sortedIndex++] = j;
                bucket[j]--;
            }
        }
        return arr;
    }

    private static int getMaxValue(int[] arr) {
        int maxValue = arr[0];
        for (int value : arr) {
            if (maxValue < value) {
                maxValue = value;
            }
        }
        return maxValue;
    }

9.桶排序

桶排序是計數(shù)排序的升級版。它利用了函數(shù)的映射關(guān)系,高效與否的關(guān)鍵就在于這個映射函數(shù)的確定。為了使桶排序更加高效,我們需要做到這兩點:

  • 在額外空間充足的情況下,盡量增大桶的數(shù)量
  • 使用的映射函數(shù)能夠?qū)⑤斎氲?N 個數(shù)據(jù)均勻的分配到 K 個桶中

同時,對于桶中元素的排序,選擇何種比較排序算法對于性能的影響至關(guān)重要。

什么時候最快
當(dāng)輸入的數(shù)據(jù)可以均勻的分配到每一個桶中。

什么時候最慢
當(dāng)輸入的數(shù)據(jù)被分配到了同一個桶中。

算法步驟:

  • 設(shè)置一個定量的數(shù)組當(dāng)作空桶;
  • 遍歷輸入數(shù)據(jù),并且把數(shù)據(jù)一個一個放到對應(yīng)的桶里去;
  • 對每個不是空的桶進(jìn)行排序;
  • 從不是空的桶里把排好序的數(shù)據(jù)拼接起來。

代碼實現(xiàn)

public class Test {

    public static void main(String[] args) {


        int[] sourceArray = new int[]{1, 0, 10, 78, 898, 874, 56, 9, 98, 100, 300,234,8348,823484,1000000};

        int[] result = Arrays.copyOf(sourceArray, sourceArray.length);

        result = bucketSort(result,5);

        for (int i = 0; i < sourceArray.length; i++) {
            System.out.println(sourceArray[i]);
        }

        for (int i = 0; i < result.length; i++) {
            System.out.println(result[i]);
        }
    }

    public static int[] bucketSort(int[] arr, int bucketSize) {
        if (arr.length == 0) {
            return arr;
        }

        int minValue = arr[0];
        int maxValue = arr[0];
        for (int value : arr) {
            if (value < minValue) {
                minValue = value;
            } else if (value > maxValue) {
                maxValue = value;
            }
        }

        int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
        int[][] buckets = new int[bucketCount][0];

        // 利用映射函數(shù)將數(shù)據(jù)分配到各個桶中
        for (int i = 0; i < arr.length; i++) {
            int index = (int) Math.floor((arr[i] - minValue) / bucketSize);
            buckets[index] = arrAppend(buckets[index], arr[i]);
        }

        for (int[] bucket : buckets) {
            int arrIndex = 0;
            if (bucket.length <= 0) {
                continue;
            }
            // 對每個桶進(jìn)行排序,這里使用了插入排序
            bucket = insertionSort(arr);
            for (int value : bucket) {
                arr[arrIndex] = value;
                arrIndex++;
            }
        }

        return arr;
    }

    /**
     * 自動擴容,并保存數(shù)據(jù)
     *
     * @param arr
     * @param value
     */
    private static int[] arrAppend(int[] arr, int value) {
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }

    /**
     * @param arr
     * @return 插入排序,輸出升序排序結(jié)果
     */
    public static int[] insertionSort(int[] arr) {
        // 對 arr 進(jìn)行拷貝,不改變參數(shù)內(nèi)容
//        arr = Arrays.copyOf(arr, arr.length);
        int length = arr.length;
        //保存要插入的數(shù)據(jù)
        int tmp;
        int j;
        // 從下標(biāo)為1的元素開始選擇合適的位置插入,因為下標(biāo)為0的只有一個元素,默認(rèn)是有序的
        for (int i = 1; i < length; i++) {

            // 記錄要插入的數(shù)據(jù)
            tmp = arr[i];

            // 從已經(jīng)排序的序列最右邊的開始比較,找到比其小的數(shù)
            j = i;
            //輸出降序的排序結(jié)果只需要把下面的 < 換成 >
            while (j > 0 && tmp < arr[j - 1]) {
                arr[j] = arr[j - 1];
                j--;
            }

            // 存在比其小的數(shù),插入
            if (j != i) {
                arr[j] = tmp;
            }

        }
        return arr;
    }

10.基數(shù)排序

基數(shù)排序是一種非比較型整數(shù)排序算法,其原理是將整數(shù)按位數(shù)切割成不同的數(shù)字,然后按每個位數(shù)分別比較。由于整數(shù)也可以表達(dá)字符串(比如名字或日期)和特定格式的浮點數(shù),所以基數(shù)排序也不是只能使用于整數(shù)。

算法步驟:

  • 取得數(shù)組中的最大數(shù),并取得位數(shù);
  • arr為原始數(shù)組,從最低位開始取每個位組成radix數(shù)組;
  • 對radix進(jìn)行計數(shù)排序(利用計數(shù)排序適用于小范圍數(shù)的特點);

代碼實現(xiàn)

public class Test {

    public static void main(String[] args) {


        int[] sourceArray = new int[]{1, 0, 10, 78, 898, 874, 56, 9, 98, 100, 300};

        int[] result = Arrays.copyOf(sourceArray, sourceArray.length);
        
        int maxDigit = getMaxDigit(result);
        
        result =  radixSort(result, maxDigit);
        
        for (int i = 0; i < sourceArray.length; i++) {
            System.out.println(sourceArray[i]);
        }

        for (int i = 0; i < result.length; i++) {
            System.out.println(result[i]);
        }
    }

    /**
     * 獲取最高位數(shù)
     */
    private static int getMaxDigit(int[] arr) {
        int maxValue = getMaxValue(arr);
        return getNumLenght(maxValue);
    }

    private static int getMaxValue(int[] arr) {
        int maxValue = arr[0];
        for (int value : arr) {
            if (maxValue < value) {
                maxValue = value;
            }
        }
        return maxValue;
    }

    protected static int getNumLenght(long num) {
        if (num == 0) {
            return 1;
        }
        int lenght = 0;
        for (long temp = num; temp != 0; temp /= 10) {
            lenght++;
        }
        return lenght;
    }

    private static int[] radixSort(int[] arr, int maxDigit) {
        int mod = 10;
        int dev = 1;

        for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
            // 考慮負(fù)數(shù)的情況,這里擴展一倍隊列數(shù),其中 [0-9]對應(yīng)負(fù)數(shù),[10-19]對應(yīng)正數(shù) (bucket + 10)
            int[][] counter = new int[mod * 2][0];

            for (int j = 0; j < arr.length; j++) {
                int bucket = ((arr[j] % mod) / dev) + mod;
                counter[bucket] = arrayAppend(counter[bucket], arr[j]);
            }

            int pos = 0;
            for (int[] bucket : counter) {
                for (int value : bucket) {
                    arr[pos++] = value;
                }
            }
        }

        return arr;
    }

    /**
     * 自動擴容,并保存數(shù)據(jù)
     *
     * @param arr
     * @param value
     */
    private static int[] arrayAppend(int[] arr, int value) {
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容