常見排序演算法彙總與分析(下)(基數排序與計數排序)

2021-07-22 21:05:03 字數 4141 閱讀 8430

本篇彙總的演算法將不再是基於比較的排序演算法,因此會突破這類演算法的時間複雜度下界o(nlog2n)。如果有朋友對前面的內容感興趣,可以先去看看常見排序演算法彙總與分析(中)(選擇排序與歸併排序)

我們先來總結基數排序演算法,該演算法在排序過程中不進行比較,而是通過「分配」和「收集」兩個過程來實現的。

【基本思想】

首先設立r個佇列,對列編號分別為0~r-1,r為待排序列中元素的基數(例如10進製數,則r=10),然後按照下面的規則對元素進行分配收集

1,先按最低有效位的值,把n個元素分配到上述的r個佇列中,然後從小到大將個佇列中的元素依次收集起來

2,再按次低有效位的值把剛收集起來的關鍵字分配到r個佇列中,重複收集工作

3,重複地進行上述分配和收集,直到最高有效位。(也就是說,如果位數為d,則需要重複進行d次,d由所有元素中最長的乙個元素的位數計量)

為什麼這樣就可以完成排序呢?

以從小到大排序為例

首先當按照最低有效位完成分配和收集後,此時得到的序列,是根據元素最低有效位的值從小到大排列的。

當按照次低有效位進行第二次分配和收集後,得到的序列,是先根據元素的次低有效位的值從小到大排列,然後再根據最低有效位的值從小到大排列。

以此類推,當按照最高有效位進行最後一次分配和收集後,得到的序列,是先根據元素的最高有效位的值從小到大排列,再根據次高有效位排列,。。。,再根據次低有效位,再根據最低有效位。自然就完成了每個元素的從小到大排列。

【空間複雜度】o(n+r)

【時間複雜度】

平均情況:o(d(n+r))

因為每一趟分配的時間開銷是o(n),收集的開銷是o(r),因此執行d趟的時間開銷為o(d(n+r)),通常d,r為常數

最好情況:o(d(n+r))

最壞情況:o(d(n+r))

【穩定性】穩定

【優點】

穩定排序;時間複雜度可以突破基於關鍵字比較排序法的下界o(n)

【缺點】

需要額外的輔助空間

【演算法實現】

/**

* 基數排序

* @param arr

*/public static void radixsort(int arr)

} private static void collect(int arr, list> buf)

} }private static int getmaxbit(int arr)

} return maxbit;

} //分配

private static list> distribute(int arr, int bit)

for (int ele : arr)

return buf;

} //得到指定位數上的值

private static int getvaluebybit(int ele, int bit)

return value;

}

【本演算法解讀】演算法針對的是十進位制數,所以r=10

演算法首先獲取到待排序列元素中的最大位數。然後按照基數排序的思想,對元素的每一位進行分配和收集。

分配distribute()方法:首先構建了r=10個佇列,對應編號即是0~9。根據給定的位數,得到待排序列中每個元素在該位上的值,若元素沒有該位,則返回0。根據每位上的值,將該元素放入相應的佇列。完成一次分配。

收集collect()方法:依次從r個佇列中讀出其中的元素值,並將其存入原始序列中,即完成了一次收集。

【舉個栗子】

對於待排序列413,10,8,28

首先獲取到待排序列的最大位數,即為3。

然後根據每一位的值進行分配和收集,分配過程如下圖:

由於是10進製數,所以構造10個佇列,0~9。

首先從最低有效位開始,每個元素在該位上對應的值分別是3,0,8,8,根據該值,將對應元素放入對應的佇列。分配結束後,依次從每個佇列上讀取元素值存入原始序列。可以看到一次分配以後,元素已經按照最低有效位從小到大排列。繼續位數逐漸增大,直到最高有效位,重複上述操作即可完成排序。

【基本思想】

計數排序是基於非比較排序,主要用於對於一定範圍內的整數進行排序。採用的是空間換時間的方法。

針對待排序列中的每乙個元素x,得到序列中小於x的元素個數,有了這一資訊可以直接把x放到最終的輸出序列的正確位置上。 計數排序之所以能做到線性時間複雜度是因為使用了索引的思想。

計數排序對輸入的資料有附加的限制條件:

1、輸入的線性表的元素屬於有限偏序集s;

2、設輸入的線性表的長度為n,|s|=k(表示集合s中元素的總數目為k),則k=o(n)。

在這兩個條件下,計數排序的複雜性為o(n)。

對於下面我即將給出的演算法,它的限制條件是待排序列中的元素是有限個正整數(包括0,由於將元素值作為陣列下標),最大值用k=o(n)表示,元素個數用n表示。它利用元素的實際值來確定它們在輸出序列中的位置

【空間複雜度】o(n+k)

【時間複雜度】

平均情況:o(n+k)

最好情況:o(n+k)

最壞情況:o(n+k)

【穩定性】穩定

【優點】

穩定,在k值較小時突破了基於比較的排序演算法下界

【缺點】

存在前提條件,需要大量額外空間,k值較大時效率很低

【演算法實現】

/**

* 計數排序

* @param arr

*/public static void countsort(int arr)

int k = 0;

for (int i = 0; i <= max; i++)

} }//獲得待排元素中的最大值

private static int getmax(int arr)

} return max;

}

【本演算法解讀】可以看到演算法首先獲得待排序列元素中的最大值,然後構建最大值+1的長度的計數陣列。遍歷待排序列的每個元素,並在以元素值為下標的陣列值中加1。然後遍歷計數陣列,若對應下標的位置上值大於0(等於幾就表示有幾個元素),則表示存在有元素且其值為下標的大小。將該元素新增到原始序列中。由於下標是從小到大的,所以對應得到的序列也是從小到大排列的。

【舉個栗子】

對於待排序列4,0,2,8,2

首先獲取最大值即為0,然後構建8+1長度的計數陣列。初始化時,計數陣列中的值均為0.如下圖所示:

將對應的元素值放在對應的下標裡,例如元素4,放在了下標為4的位置裡,計數陣列的值加1,表示其中有乙個元素值為下標的元素。待排序列中有兩個2,所以下標為2的計數陣列值是2,表示其中有兩個元素值為下標的元素。

最後過遍歷計數陣列,如,遍歷到0下標位置,陣列值為1,表示有乙個元素,元素值是下標0,新增到原始序列中。遍歷到2下標位置,陣列值為2,表示有兩個元素,元素值均為下標2,將兩個元素依次新增到原始序列中。以此類推,最終得到有序序列0,2,2,4,8。

那麼,到這裡我們這個常用排序演算法的彙總就順利完成了。本彙總分析了常用排序演算法中的,交換排序(氣泡排序,快速排序),插入排序(直接插入排序,希爾排序),選擇排序(直接選擇排序,堆排序),歸併排序,基數排序,計數排序。應該說是這個總結是暫時告一段落了,後面遇到一些好的演算法,還會繼續與大家分享。

如有紕漏,敬請海涵。

常見排序演算法 基數排序 Radix sort

檔案中任一記錄r i 的關鍵字均由d個分量 構成。若這d個分量中每個分量都是乙個獨立的關鍵字,則檔案是多關鍵字的 如撲克牌有兩個關鍵字 點數和花色 否則檔案是單關鍵字的,0 j 2 基數 設單關鍵字的每個分量的取值範圍均是 c0 kj crd 1 0 j0 0,c9 9,d為最長整數的位數 2 若關...

八排序演算法彙總

1.直接插入排序 原理 將陣列分為無序區和有序區兩個區,然後不斷將無序區的第乙個元素按大小順序插入到有序區中去,終於將全部無序區元素都移動到有序區完畢排序。要點 設立哨兵,作為暫時儲存和推斷陣列邊界之用。實現 void insertsort node l,int length l i 1 l 0 將...

常見排序演算法分析

歸併排序,插入排序 希爾排序,快速排序,氣泡排序,桶排序。不同的樣本空間各種排序花費時間如圖一所示 二 演算法效能分析 從圖中可以看出在樣本空間很大時快排和希爾排序有很高的效率,因為他們的複雜度為 nlogn 在樣本空間很大時就體現出了優勢,但是不穩定。就從氣泡排序的結果看,可以知道當樣本空間很小的...