在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

十大排序算法總結(jié)

OSC開源社區(qū) ? 來源:OSCHINA 社區(qū) ? 2023-12-20 10:39 ? 次閱讀

作者:不敗頑童1號

排序算法是最經(jīng)典的算法知識。因為其實現(xiàn)代碼短,應(yīng)該廣,在面試中經(jīng)常會問到排序算法及其相關(guān)的問題。一般在面試中最常考的是快速排序和歸并排序等基本的排序算法,并且經(jīng)常要求現(xiàn)場手寫基本的排序算法。如果這些問題回答不好,估計面試就涼涼了。所以熟練掌握排序算法思想及其特點并能夠熟練地手寫代碼至關(guān)重要。

下面介紹幾種常見的排序算法:冒泡排序、選擇排序、插入排序、歸并排序、快速排序、希爾排序、堆排序、計數(shù)排序、桶排序、基數(shù)排序的思想,其代碼均采用 Java 實現(xiàn)。

1. 冒泡排序

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

算法描述

比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;

對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對,這樣在最后的元素應(yīng)該會是最大的數(shù);

針對所有的元素重復(fù)以上的步驟,除了最后一個;

重復(fù)步驟 1~3,直到排序完成。

算法實現(xiàn)

public static void bubbleSort(int[] arr) {
    int temp = 0;
    for (int i = arr.length - 1; i > 0; i--) { 
        for (int j = 0; j < i; j++) { 
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

穩(wěn)定性

在相鄰元素相等時,它們并不會交換位置,所以,冒泡排序是穩(wěn)定排序。

適用場景

冒泡排序思路簡單,代碼也簡單,特別適合小數(shù)據(jù)的排序。但是,由于算法復(fù)雜度較高,在數(shù)據(jù)量大的時候不適合使用。

代碼優(yōu)化

在數(shù)據(jù)完全有序的時候展現(xiàn)出最優(yōu)時間復(fù)雜度,為 O (n)。其他情況下,幾乎總是 O ( n^2 )。因此,算法在數(shù)據(jù)基本有序的情況下,性能最好。要使算法在最佳情況下有 O (n) 復(fù)雜度,需要做一些改進,增加一個swap的標(biāo)志,當(dāng)前一輪沒有進行交換時,說明數(shù)組已經(jīng)有序,沒有必要再進行下一輪的循環(huán)了,直接退出。

public static void bubbleSort(int[] arr) {
    int temp = 0;
    boolean swap;
    for (int i = arr.length - 1; i > 0; i--) { 
        swap=false;
        for (int j = 0; j < i; j++) { 
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swap=true;
            }
        }
        if (swap==false){
            break;
        }
    }
}

2. 選擇排序

選擇排序是一種簡單直觀的排序算法,它也是一種交換排序算法,和冒泡排序有一定的相似度,可以認(rèn)為選擇排序是冒泡排序的一種改進。

算法描述

在未排序序列中找到最小(大)元素,存放到排序序列的起始位置

從剩余未排序元素中繼續(xù)尋找最小(大)元素,然后放到已排序序列的末尾。

重復(fù)第二步,直到所有元素均排序完畢。

算法實現(xiàn)

public static void selectionSort(int[] arr) {
    int temp, min = 0;
    for (int i = 0; i < arr.length - 1; i++) {
        min = i;
        
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[min] > arr[j]) {
                min = j;
            }
        }
        if (min != i) {
            temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
        }
    }
}

穩(wěn)定性

用數(shù)組實現(xiàn)的選擇排序是不穩(wěn)定的,用鏈表實現(xiàn)的選擇排序是穩(wěn)定的。不過,一般提到排序算法時,大家往往會默認(rèn)是數(shù)組實現(xiàn),所以選擇排序是不穩(wěn)定的。

適用場景

選擇排序?qū)崿F(xiàn)也比較簡單,并且由于在各種情況下復(fù)雜度波動小,因此一般是優(yōu)于冒泡排序的。在所有的完全交換排序中,選擇排序也是比較不錯的一種算法。但是,由于固有的 O (n^2) 復(fù)雜度,選擇排序在海量數(shù)據(jù)面前顯得力不從心。因此,它適用于簡單數(shù)據(jù)排序。

3. 插入排序

插入排序是一種簡單直觀的排序算法。它的工作原理是通過構(gòu)建有序序列,對于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入。

算法描述

把待排序的數(shù)組分成已排序和未排序兩部分,初始的時候把第一個元素認(rèn)為是已排好序的。

從第二個元素開始,在已排好序的子數(shù)組中尋找到該元素合適的位置并插入該位置。

重復(fù)上述過程直到最后一個元素被插入有序子數(shù)組中。

算法實現(xiàn)

public static void insertionSort(int[] arr){
    for (int i=1; i0 && arr[position-1]>value){
            arr[position] = arr[position-1];
            position--;
        }
        arr[position] = value;
    }
}

穩(wěn)定性

由于只需要找到不大于當(dāng)前數(shù)的位置而并不需要交換,因此,直接插入排序是穩(wěn)定的排序方法。

適用場景

插入排序由于 O (n^2) 的復(fù)雜度,在數(shù)組較大的時候不適用。但是,在數(shù)據(jù)比較少的時候,是一個不錯的選擇,一般做為快速排序的擴充。例如,在 STL 的 sort 算法和 stdlib 的 qsort 算法中,都將插入排序作為快速排序的補充,用于少量元素的排序。又如,在 JDK 7 java.util.Arrays 所用的 sort 方法的實現(xiàn)中,當(dāng)待排數(shù)組長度小于 47 時,會使用插入排序。

4. 歸并排序

歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法的一個非常典型的應(yīng)用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為 2 - 路歸并。

算法描述

兩種方法

遞歸法(Top-down)

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

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

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

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

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

迭代法(Bottom-up)

原理如下(假設(shè)序列共有 n 個元素):

將序列每相鄰兩個數(shù)字進行歸并操作,形成 ceil (n/2) 個序列,排序后每個序列包含兩 / 一個元素

若此時序列數(shù)不是 1 個則將上述序列再次歸并,形成 ceil (n/4) 個序列,每個序列包含四 / 三個元素

重復(fù)步驟 2,直到所有元素排序完畢,即序列數(shù)為 1

算法實現(xiàn)

public static void mergeSort(int[] arr){
    int[] temp =new int[arr.length];
    internalMergeSort(arr, temp, 0, arr.length-1);
}
private static void internalMergeSort(int[] arr, int[] temp, int left, int right){
    
    if (left

穩(wěn)定性

因為我們在遇到相等的數(shù)據(jù)的時候必然是按順序 “抄寫” 到輔助數(shù)組上的,所以,歸并排序同樣是穩(wěn)定算法。

適用場景

歸并排序在數(shù)據(jù)量比較大的時候也有較為出色的表現(xiàn)(效率上),但是,其空間復(fù)雜度 O (n) 使得在數(shù)據(jù)量特別大的時候(例如,1 千萬數(shù)據(jù))幾乎不可接受。而且,考慮到有的機器內(nèi)存本身就比較小,因此,采用歸并排序一定要注意。

5. 快速排序

快速排序是一個知名度極高的排序算法,其對于大數(shù)據(jù)的優(yōu)秀排序性能和相同復(fù)雜度算法中相對簡單的實現(xiàn)使它注定得到比其他算法更多的寵愛。

算法描述

從數(shù)列中挑出一個元素,稱為 "基準(zhǔn)"(pivot),

重新排序數(shù)列,所有比基準(zhǔn)值小的元素擺放在基準(zhǔn)前面,所有比基準(zhǔn)值大的元素擺在基準(zhǔn)后面(相同的數(shù)可以到任何一邊)。在這個分區(qū)結(jié)束之后,該基準(zhǔn)就處于數(shù)列的中間位置。這個稱為分區(qū)(partition)操作。

遞歸地(recursively)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序。

動圖演示

7b226aa0-9e64-11ee-8b88-92fbcf53809c.gif

快速排序

算法實現(xiàn)

public static void quickSort(int[] arr){
    qsort(arr, 0, arr.length-1);
}
private static void qsort(int[] arr, int low, int high){
    if (low >= high)
        return;
    int pivot = partition(arr, low, high);        
    qsort(arr, low, pivot-1);                   
    qsort(arr, pivot+1, high);                  
}
private static int partition(int[] arr, int low, int high){
    int pivot = arr[low];     
    while (low < high){
        while (low < high && arr[high] >= pivot) --high;
        arr[low]=arr[high];             
        while (low < high && arr[low] <= pivot) ++low;
        arr[high] = arr[low];           
    }
    
    arr[low] = pivot;
    
    return low;
}

穩(wěn)定性

快速排序并不是穩(wěn)定的。這是因為我們無法保證相等的數(shù)據(jù)按順序被掃描到和按順序存放。

適用場景

快速排序在大多數(shù)情況下都是適用的,尤其在數(shù)據(jù)量大的時候性能優(yōu)越性更加明顯。但是在必要的時候,需要考慮下優(yōu)化以提高其在最壞情況下的性能。

6. 堆排序

堆排序 (Heapsort) 是指利用堆積樹(堆)這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計的一種排序算法,它是選擇排序的一種。可以利用數(shù)組的特點快速定位指定索引的元素。堆排序就是把最大堆堆頂?shù)淖畲髷?shù)取出,將剩余的堆繼續(xù)調(diào)整為最大堆,再次將堆頂?shù)淖畲髷?shù)取出,這個過程持續(xù)到剩余數(shù)只有一個時結(jié)束。

堆的概念

堆是一種特殊的完全二叉樹(complete binary tree)。完全二叉樹的一個 “優(yōu)秀” 的性質(zhì)是,除了最底層之外,每一層都是滿的,這使得堆可以利用數(shù)組來表示(普通的一般的二叉樹通常用鏈表作為基本容器表示),每一個結(jié)點對應(yīng)數(shù)組中的一個元素。如下圖,是一個堆和數(shù)組的相互關(guān)系:

7b44294c-9e64-11ee-8b88-92fbcf53809c.jpg

image 對于給定的某個結(jié)點的下標(biāo) i,可以很容易的計算出這個結(jié)點的父結(jié)點、孩子結(jié)點的下標(biāo):

Parent (i) = floor (i/2),i 的父節(jié)點下標(biāo)

Left (i) = 2i,i 的左子節(jié)點下標(biāo)

Right (i) = 2i + 1,i 的右子節(jié)點下標(biāo)

二叉堆一般分為兩種:最大堆和最小堆。最大堆:最大堆中的最大元素值出現(xiàn)在根結(jié)點(堆頂) 堆中每個父節(jié)點的元素值都大于等于其孩子結(jié)點(如果存在)

7b4e46d4-9e64-11ee-8b88-92fbcf53809c.jpg

image 最小堆:最小堆中的最小元素值出現(xiàn)在根結(jié)點(堆頂) 堆中每個父節(jié)點的元素值都小于等于其孩子結(jié)點(如果存在)

7b5650e0-9e64-11ee-8b88-92fbcf53809c.jpg

image

堆排序原理

堆排序就是把最大堆堆頂?shù)淖畲髷?shù)取出,將剩余的堆繼續(xù)調(diào)整為最大堆,再次將堆頂?shù)淖畲髷?shù)取出,這個過程持續(xù)到剩余數(shù)只有一個時結(jié)束。在堆中定義以下幾種操作:

最大堆調(diào)整(Max-Heapify):將堆的末端子節(jié)點作調(diào)整,使得子節(jié)點永遠小于父節(jié)點

創(chuàng)建最大堆(Build-Max-Heap):將堆所有數(shù)據(jù)重新排序,使其成為最大堆

堆排序(Heap-Sort):移除位在第一個數(shù)據(jù)的根節(jié)點,并做最大堆調(diào)整的遞歸運算 繼續(xù)進行下面的討論前,需要注意的一個問題是:數(shù)組都是 Zero-Based,這就意味著我們的堆數(shù)據(jù)結(jié)構(gòu)模型要發(fā)生改變

7b5d5ebc-9e64-11ee-8b88-92fbcf53809c.jpg

image 相應(yīng)的,幾個計算公式也要作出相應(yīng)調(diào)整:

Parent (i) = floor ((i-1)/2),i 的父節(jié)點下標(biāo)

Left (i) = 2i + 1,i 的左子節(jié)點下標(biāo)

Right (i) = 2 (i + 1),i 的右子節(jié)點下標(biāo)

堆的建立和維護

堆可以支持多種操作,但現(xiàn)在我們關(guān)心的只有兩個問題:

給定一個無序數(shù)組,如何建立為堆?

刪除堆頂元素后,如何調(diào)整數(shù)組成為新堆?

先看第二個問題。假定我們已經(jīng)有一個現(xiàn)成的大根堆。現(xiàn)在我們刪除了根元素,但并沒有移動別的元素。想想發(fā)生了什么:根元素空了,但其它元素還保持著堆的性質(zhì)。我們可以把最后一個元素(代號 A)移動到根元素的位置。如果不是特殊情況,則堆的性質(zhì)被破壞。但這僅僅是由于 A 小于其某個子元素。于是,我們可以把 A 和這個子元素調(diào)換位置。如果 A 大于其所有子元素,則堆調(diào)整好了;否則,重復(fù)上述過程,A 元素在樹形結(jié)構(gòu)中不斷 “下沉”,直到合適的位置,數(shù)組重新恢復(fù)堆的性質(zhì)。上述過程一般稱為 “篩選”,方向顯然是自上而下。

刪除后的調(diào)整,是把最后一個元素放到堆頂,自上而下比較

刪除一個元素是如此,插入一個新元素也是如此。不同的是,我們把新元素放在末尾,然后和其父節(jié)點做比較,即自下而上篩選。

插入是把新元素放在末尾,自下而上比較

那么,第一個問題怎么解決呢? 常規(guī)方法是從第一個非葉子結(jié)點向下篩選,直到根元素篩選完畢。這個方法叫 “篩選法”,需要循環(huán)篩選 n/2 個元素。 但我們還可以借鑒 “插入排序” 的思路。我們可以視第一個元素為一個堆,然后不斷向其中添加新元素。這個方法叫做 “插入法”,需要循環(huán)插入 (n-1) 個元素。 由于篩選法和插入法的方式不同,所以,相同的數(shù)據(jù),它們建立的堆一般不同。大致了解堆之后,堆排序就是水到渠成的事情了。

動圖演示

7b67d7c0-9e64-11ee-8b88-92fbcf53809c.gif

image

算法描述

我們需要一個升序的序列,怎么辦呢?我們可以建立一個最小堆,然后每次輸出根元素。但是,這個方法需要額外的空間(否則將造成大量的元素移動,其復(fù)雜度會飆升到 O (n^2) )。如果我們需要就地排序(即不允許有 O (n) 空間復(fù)雜度),怎么辦? 有辦法。我們可以建立最大堆,然后我們倒著輸出,在最后一個位置輸出最大值,次末位置輸出次大值…… 由于每次輸出的最大元素會騰出第一個空間,因此,我們恰好可以放置這樣的元素而不需要額外空間。很漂亮的想法,是不是?

算法實現(xiàn)

public class ArrayHeap {
    private int[] arr;
    public ArrayHeap(int[] arr) {
        this.arr = arr;
    }
    private int getParentIndex(int child) {
        return (child - 1) / 2;
    }
    private int getLeftChildIndex(int parent) {
        return 2 * parent + 1;
    }
    private void swap(int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
    private void adjustHeap(int i, int len) {
        int left, right, j;
        left = getLeftChildIndex(i);
        while (left <= len) {
            right = left + 1;
            j = left;
            if (j < len && arr[left] < arr[right]) {
                j++;
            }
            if (arr[i] < arr[j]) {
                swap(array, i, j);
                i = j;
                left = getLeftChildIndex(i);
            } else {
                break; 
            }
        }
    }
    
    public void sort() {
        int last = arr.length - 1;
        
        for (int i = getParentIndex(last); i >= 0; --i) {
            adjustHeap(i, last);
        }
        
        while (last >= 0) {
            swap(0, last--);
            adjustHeap(0, last);
        }
    }

}

穩(wěn)定性

堆排序存在大量的篩選和移動過程,屬于不穩(wěn)定的排序算法。

適用場景

堆排序在建立堆和調(diào)整堆的過程中會產(chǎn)生比較大的開銷,在元素少的時候并不適用。但是,在元素比較多的情況下,還是不錯的一個選擇。尤其是在解決諸如 “前 n 大的數(shù)” 一類問題時,幾乎是首選算法。

7. 希爾排序(插入排序的改良版)

在希爾排序出現(xiàn)之前,計算機界普遍存在 “排序算法不可能突破 O (n2)” 的觀點。希爾排序是第一個突破 O (n2) 的排序算法,它是簡單插入排序的改進版。希爾排序的提出,主要基于以下兩點:

插入排序算法在數(shù)組基本有序的情況下,可以近似達到 O (n) 復(fù)雜度,效率極高。

但插入排序每次只能將數(shù)據(jù)移動一位,在數(shù)組較大且基本無序的情況下性能會迅速惡化。

算法描述

先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,具體算法描述:

選擇一個增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;

按增量序列個數(shù) k,對序列進行 k 趟排序;

每趟排序,根據(jù)對應(yīng)的增量 ti,將待排序列分割成若干長度為 m 的子序列,分別對各子表進行直接插入排序。僅增量因子為 1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。

動圖演示

7b83f7fc-9e64-11ee-8b88-92fbcf53809c.gif

image

算法實現(xiàn)

Donald Shell 增量

public static void shellSort(int[] arr){
    int temp;
    for (int delta = arr.length/2; delta>=1; delta/=2){                              
        for (int i=delta; i=delta && arr[j]

O(n^(3/2)) by Knuth

public static void shellSort2(int[] arr){
    int delta = 1;
    while (delta < arr.length/3){
        delta=delta*3+1;    
    }         
    int temp;
    for (; delta>=1; delta/=3){
        for (int i=delta; i=delta && arr[j]

希爾排序的增量

希爾排序的增量數(shù)列可以任取,需要的唯一條件是最后一個一定為 1(因為要保證按 1 有序)。但是,不同的數(shù)列選取會對算法的性能造成極大的影響。上面的代碼演示了兩種增量。切記:增量序列中每兩個元素最好不要出現(xiàn) 1 以外的公因子!(很顯然,按 4 有序的數(shù)列再去按 2 排序意義并不大)。下面是一些常見的增量序列。

第一種增量是最初 Donald Shell 提出的增量,即折半降低直到 1。據(jù)研究,使用希爾增量,其時間復(fù)雜度還是 O (n2)。

第二種增量 Hibbard:{1, 3, ..., 2k-1}。該增量序列的時間復(fù)雜度大約是 O (n1.5)。 第三種增量 Sedgewick 增量:(1, 5, 19, 41, 109,...),其生成序列或者是 94^i - 92^i + 1 或者是 4^i - 3*2^i + 1。

穩(wěn)定性

我們都知道插入排序是穩(wěn)定算法。但是,Shell 排序是一個多次插入的過程。在一次插入中我們能確保不移動相同元素的順序,但在多次的插入中,相同元素完全有可能在不同的插入輪次被移動,最后穩(wěn)定性被破壞,因此,Shell 排序不是一個穩(wěn)定的算法。

適用場景

Shell 排序雖然快,但是畢竟是插入排序,其數(shù)量級并沒有后起之秀 -- 快速排序 O (n㏒n) 快。在大量數(shù)據(jù)面前,Shell 排序不是一個好的算法。但是,中小型規(guī)模的數(shù)據(jù)完全可以使用它。

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。

動圖演示

7ba1da9c-9e64-11ee-8b88-92fbcf53809c.gif

image

算法實現(xiàn)

public static void countSort(int[] a, int max, int min) {
     int[] b = new int[a.length];
     int[] count = new int[max - min + 1];

     for (int num = min; num <= max; num++) {
        
        count[num - min] = 0;
     }

     for (int i = 0; i < a.length; i++) {
        int num = a[i];
        count[num - min]++;
     }

     for (int num = min + 1; num <= max; num++) {
        
        count[num - min] += sum[num - min - 1]
     }

     for (int i = 0; i < a.length; i++) {
          int num = a[i];
          int index = count[num - min] - 1;
          b[index] = num;
          count[num - min]--;
     }

     
     for(int i=0;i

穩(wěn)定性

最后給 b 數(shù)組賦值是倒著遍歷的,而且放進去一個就將 C 數(shù)組對應(yīng)的值(表示前面有多少元素小于或等于 A [i])減去一。如果有相同的數(shù) x1,x2,那么相對位置后面那個元素 x2 放在(比如下標(biāo)為 4 的位置),相對位置前面那個元素 x1 下次進循環(huán)就會被放在 x2 前面的位置 3。從而保證了穩(wěn)定性。

適用場景

排序目標(biāo)要能夠映射到整數(shù)域,其最大值最小值應(yīng)當(dāng)容易辨別。例如高中生考試的總分?jǐn)?shù),顯然用 0-750 就 OK 啦;又比如一群人的年齡,用個 0-150 應(yīng)該就可以了,再不濟就用 0-200 嘍。另外,計數(shù)排序需要占用大量空間,它比較適用于數(shù)據(jù)比較集中的情況。

9、桶排序

桶排序又叫箱排序,是計數(shù)排序的升級版,它的工作原理是將數(shù)組分到有限數(shù)量的桶子里,然后對每個桶子再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續(xù)使用桶排序進行排序),最后將各個桶中的數(shù)據(jù)有序的合并起來。

計數(shù)排序是桶排序的一種特殊情況,可以把計數(shù)排序當(dāng)成每個桶里只有一個元素的情況。網(wǎng)絡(luò)中很多博文寫的桶排序?qū)嶋H上都是計數(shù)排序,并非標(biāo)準(zhǔn)的桶排序,要注意辨別。

算法描述

找出待排序數(shù)組中的最大值 max、最小值 min

我們使用 動態(tài)數(shù)組 ArrayList 作為桶,桶里放的元素也用 ArrayList 存儲。桶的數(shù)量為 (max-min)/arr.length+1

遍歷數(shù)組 arr,計算每個元素 arr [i] 放的桶

每個桶各自排序

遍歷桶數(shù)組,把排序好的元素放進輸出數(shù)組

圖片演示

7bb6c858-9e64-11ee-8b88-92fbcf53809c.jpg

image

算法實現(xiàn)

public static void bucketSort(int[] arr){
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for(int i = 0; i < arr.length; i++){
        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);
    }
    
    int bucketNum = (max - min) / arr.length + 1;
    ArrayList> bucketArr = new ArrayList<>(bucketNum);
    for(int i = 0; i < bucketNum; i++){
        bucketArr.add(new ArrayList());
    }
    
    for(int i = 0; i < arr.length; i++){
        int num = (arr[i] - min) / (arr.length);
        bucketArr.get(num).add(arr[i]);
    }
    
    for(int i = 0; i < bucketArr.size(); i++){
        Collections.sort(bucketArr.get(i));
    }
    System.out.println(bucketArr.toString());
}

穩(wěn)定性

可以看出,在分桶和從桶依次輸出的過程是穩(wěn)定的。但是,由于我們在對每個桶進行排序時使用了其他算法,所以,桶排序的穩(wěn)定性依賴于這一步。如果我們使用了快排,顯然,算法是不穩(wěn)定的。

適用場景

桶排序可用于最大最小值相差較大的數(shù)據(jù)情況,但桶排序要求數(shù)據(jù)的分布必須均勻,否則可能導(dǎo)致數(shù)據(jù)都集中到一個桶中。比如 [104,150,123,132,20000], 這種數(shù)據(jù)會導(dǎo)致前 4 個數(shù)都集中到同一個桶中。導(dǎo)致桶排序失效。

10、基數(shù)排序

基數(shù)排序 (Radix Sort) 是桶排序的擴展,它的基本思想是:將整數(shù)按位數(shù)切割成不同的數(shù)字,然后按每個位數(shù)分別比較。排序過程:將所有待比較數(shù)值(正整數(shù))統(tǒng)一為同樣的數(shù)位長度,數(shù)位較短的數(shù)前面補零。然后,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以后,數(shù)列就變成一個有序序列。

算法描述

取得數(shù)組中的最大數(shù),并取得位數(shù);

arr 為原始數(shù)組,從最低位開始取每個位組成 radix 數(shù)組;

對 radix 進行計數(shù)排序(利用計數(shù)排序適用于小范圍數(shù)的特點);

動圖

7bc3ae9c-9e64-11ee-8b88-92fbcf53809c.gif

image

算法實現(xiàn)

public abstract class Sorter {
     public abstract void sort(int[] array);
}
 
public class RadixSorter extends Sorter {
     
     private int radix;
     
     public RadixSorter() {
          radix = 10;
     }
     
     @Override
     public void sort(int[] array) {
          
          
          int[][] bucket = new int[radix][array.length];
          int distance = getDistance(array); 
          int temp = 1;
          int round = 1; 
          while (round <= distance) {
               
               int[] counter = new int[radix];
               
               for (int i = 0; i < array.length; i++) {
                    int which = (array[i] / temp) % radix;
                    bucket[which][counter[which]] = array[i];
                    counter[which]++;
               }
               int index = 0;
               
               for (int i = 0; i < radix; i++) {
                    if (counter[i] != 0)
                         for (int j = 0; j < counter[i]; j++) {
                              array[index] = bucket[i][j];
                              index++;
                         }
                    counter[i] = 0;
               }
               temp *= radix;
               round++;
          }
     }
     
     private int getDistance(int[] array) {
          int max = computeMax(array);
          int digits = 0;
          int temp = max / radix;
          while(temp != 0) {
               digits++;
               temp = temp / radix;
          }
          return digits + 1;
     }
     
     private int computeMax(int[] array) {
          int max = array[0];
          for(int i=1; imax) {
                    max = array[i];
               }
          }
          return max;
     }
}

穩(wěn)定性

通過上面的排序過程,我們可以看到,每一輪映射和收集操作,都保持從左到右的順序進行,如果出現(xiàn)相同的元素,則保持他們在原始數(shù)組中的順序。可見,基數(shù)排序是一種穩(wěn)定的排序。

適用場景

基數(shù)排序要求較高,元素必須是整數(shù),整數(shù)時長度 10W 以上,最大值 100W 以下效率較好,但是基數(shù)排序比其他排序好在可以適用字符串,或者其他需要根據(jù)多個條件進行排序的場景,例如日期,先排序日,再排序月,最后排序年 ,其它排序算法可是做不了的。

總結(jié)

7bd2a9c4-9e64-11ee-8b88-92fbcf53809c.jpg

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關(guān)注

    關(guān)注

    20

    文章

    2983

    瀏覽量

    106554
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4882

    瀏覽量

    70039
  • 排序算法
    +關(guān)注

    關(guān)注

    0

    文章

    53

    瀏覽量

    10190

原文標(biāo)題:【算法總結(jié)]】十大排序算法

文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦
    熱點推薦

    C語言經(jīng)典排序算法總結(jié)

    本文將通過動態(tài)演示+代碼的形式系統(tǒng)地總結(jié)十大經(jīng)典排序算法
    發(fā)表于 06-05 10:56 ?681次閱讀
    C語言經(jīng)典<b class='flag-5'>排序</b><b class='flag-5'>算法</b><b class='flag-5'>總結(jié)</b>

    Matlab數(shù)學(xué)建模常用的十大算法

    Matlab數(shù)學(xué)建模常用的十大算法
    發(fā)表于 05-20 17:33

    C語言十大濾波算法

    C語言十大濾波算法
    發(fā)表于 08-15 18:41

    Java常用排序算法&程序員必須掌握的8大排序算法+二分法查找

    Java常用排序算法&程序員必須掌握的8大排序算法+二分法查找
    發(fā)表于 10-19 19:33

    《Visual C# 2008程序設(shè)計經(jīng)典案例設(shè)計與實現(xiàn)》---利用冒泡算法實現(xiàn)從小到大排序

    《Visual C# 2008程序設(shè)計經(jīng)典案例設(shè)計與實現(xiàn)》---利用冒泡算法實現(xiàn)從小到大排序.zip
    發(fā)表于 05-22 21:01

    2008年十大智能手機大排

    2008年十大智能手機大排行 超熱智能機之——萬千寵愛于一身   前言:諾基亞全線
    發(fā)表于 11-05 07:40 ?2.5w次閱讀

    汽車led大燈十大排

    說起汽車led大燈,可能很多朋友都有所了解。它是中高檔汽車上的一項配置。那么,你知道汽車led大燈十大排名么?下面小編就和大家一塊看一下。
    發(fā)表于 12-13 15:21 ?21.5w次閱讀

    數(shù)據(jù)結(jié)構(gòu)常見的八大排序算法

    本文總結(jié)了數(shù)據(jù)結(jié)構(gòu)常見的八大排序算法。詳細分析請看下文
    發(fā)表于 02-05 15:26 ?1877次閱讀
    數(shù)據(jù)結(jié)構(gòu)常見的八<b class='flag-5'>大排序</b><b class='flag-5'>算法</b>

    行車記錄儀十大排

    本視頻主要詳細介紹了行車記錄儀十大排名,分別是papago行車記錄儀、PRTDT行車記錄儀、捷渡行車記錄儀、任E行行車記錄儀、途美行車記錄儀、凌度行車記錄儀。
    的頭像 發(fā)表于 02-15 08:55 ?5.6w次閱讀

    程序員的內(nèi)功:C語言八大排序算法

    一 前言 如果說各種編程語言是程序員的招式,那么數(shù)據(jù)結(jié)構(gòu)和算法就相當(dāng)于程序員的內(nèi)功。 想寫出精煉、優(yōu)秀的代碼,不通過不斷的錘煉,是很難做到的。 二 八大排序算法 排序
    的頭像 發(fā)表于 10-26 10:33 ?2285次閱讀
    程序員的內(nèi)功:C語言八<b class='flag-5'>大排序</b><b class='flag-5'>算法</b>

    C++中十大排序算法前五個詳解

    本期是C++基礎(chǔ)語法分享的第十五節(jié),今天給大家來梳理一下十大排序算法前五個! 冒泡排序 冒泡排序思路: 1. 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。 2. 對每一對相鄰
    的頭像 發(fā)表于 09-29 17:47 ?1693次閱讀

    C++基礎(chǔ)語法十大排序算法后五個分享

    本期是C++基礎(chǔ)語法分享的第十六節(jié),今天給大家來梳理一下十大排序算法后五個! 歸并排序 歸并排序:把數(shù)據(jù)分為兩段,從兩段中逐個選最小的元素移入新數(shù)據(jù)段的末尾。可從上到下或從下到上
    的頭像 發(fā)表于 10-08 15:06 ?1404次閱讀
    C++基礎(chǔ)語法<b class='flag-5'>十大排序</b><b class='flag-5'>算法</b>后五個分享

    空氣凈化器十大排名,口碑最好的空氣凈化器排名推薦

    冰尊空氣凈化器是世界空氣凈化器十大排名領(lǐng)導(dǎo)品牌。 空氣質(zhì)量的好壞直接影響著我們身體的健康情況,為了呼吸到干凈的空氣,很多人都會在家里放置一臺空氣凈化器 ,由于空氣凈化器是近年來新興起來的產(chǎn)品,很多人
    的頭像 發(fā)表于 03-08 16:24 ?1.5w次閱讀
    空氣凈化器<b class='flag-5'>十大排</b>名,口碑最好的空氣凈化器排名推薦

    C語言動圖演示十大經(jīng)典排序算法(含代碼)

    本文將通過動態(tài)演示+代碼的形式系統(tǒng)地總結(jié)十大經(jīng)典排序算法
    的頭像 發(fā)表于 01-29 11:34 ?1473次閱讀

    常見排序算法分類

    本文將通過動態(tài)演示+代碼的形式系統(tǒng)地總結(jié)十大經(jīng)典排序算法排序算法
    的頭像 發(fā)表于 06-22 14:49 ?1196次閱讀
    常見<b class='flag-5'>排序</b><b class='flag-5'>算法</b>分類
    主站蜘蛛池模板: 色综合天天五月色 | 五月天婷婷网亚洲综合在线 | 亚洲成a人一区二区三区 | 亚洲天堂最新地址 | 亚洲成在人线中文字幕 | 欧美黄色免费网站 | 亚洲精品美女久久久 | 午夜毛片免费看 | 在线天堂中文官网 | 午夜毛片不卡高清免费 | 天天爽夜夜爽 | 日本aaaaa毛片在线视频 | 国产综合精品久久久久成人影 | 色狠狠xx | www.av免费| 国产20岁美女一级毛片 | 亚洲欧美一区二区三区麻豆 | 四虎影院永久地址 | 在线色站| xx在线| 欧洲精品不卡1卡2卡三卡 | 狠狠操天天操夜夜操 | 国产黄在线观看免费观看不卡 | 三级黄色一级视频 | 一级视频片 | 久久久久久人精品免费费看 | 一级一级女人18毛片 | 日本不卡视频一区二区三区 | 亚欧洲乱码专区视频 | 在线 你懂 | 久久精品国产精品亚洲婷婷 | 一区二区免费看 | 人操人操| 五月婷婷激情视频 | 拍拍拍美女黄色1000视频 | 免费视频现线观看 | 人人爱天天做夜夜爽毛片 | 欧美一级欧美三级在线 | 唯美久草 | 老司机51精品视频在线观看 | 国产精品欧美一区二区 |