排序 時間倒序 早學會計數排序,我的面試就不會跪

2021-10-16 03:04:15 字數 3831 閱讀 2168

拜託,別再問我計數排序了!!!

到目前為止,我們已經介紹了插入排序、歸併排序、堆排序、快速排序等幾種排序演算法,他們的執行時間上界不會超過o(nlgn)。 這些演算法都有乙個有趣的性質:在排序的最終結果中,各元素的次序依賴於它們之間的比較。我們把這類排序演算法稱為比較排序。

可以證明,基於比較的排序演算法在最壞情況下的時間下界是ω(nlgn)。堆排序和歸併排序的執行時間上界為o(nlgn),因此這兩種排序演算法都是漸進最優的比較排序演算法。

從本文開始,我們**比較排序以外的排序演算法,這些演算法可以擺脫下界ω(nlgn)的限制,達到線性時間複雜度o(n)。

計數排序是一種非基於比較的排序演算法,其空間複雜度和時間複雜度均為o(n+k),其中k是整數的範圍。基於比較的排序演算法時間複雜度最小是o(nlogn)的。注意:計數排序對於實數的排序是不可行的(下面會解釋)。該演算法於2023年由 harold h. seward 提出。

借用網上舉的乙個常見的高考分數排名講解下:

我們查分數的時候,系統會顯示我們的成績以及所在省的排名。如果你所在的省有50萬考生,如何通過成績快速排序得出名次呢?

考生的滿分是 900 分,最小是 0 分,這個資料的範圍很小,所以我們可以分成 901 個桶,對應分數從 0 分到 900 分。根據考生的成績,我們將 50 萬考生劃分到 901 個桶裡。桶內的資料都是分數相同的考生,所以並不需要再進行排序。我們只需要一次掃瞄每個桶,將桶內的考生依次輸出到乙個陣列中,就實現了 50 萬考生的排序。因為只涉及掃瞄遍歷操作,所以時間複雜度是 o(n)。

另外乙個網上的**示例為:

花o(n)的時間掃瞄一下整個陣列序列a,獲取最小值 min 和最大值 max開闢一塊新的空間建立新的統計陣列count,長度為(max – min + 1)

統計陣列count中 index 的元素記錄的值是 a 中某元素出現的次數

對陣列count順序求和,即後面的元素等於前面的元素之和(count[index]=count[index]+count[index-1])

倒序遍歷陣列a,從統計陣列count中找到元素的正確排位,輸出到結果陣列中。

假設我們有乙個陣列序列a為: [16, 19, 31, 103, 19, 25, 19, 25]

按照計數排序的演算法步驟,我們的實際過程將為:

掃瞄整個陣列序列,陣列長度為8, 獲取到最小值 min=16, 最大值max=103

2. 建立乙個計數陣列count, 長度為max-min-1=88, 並且該陣列初始化全部元素為0.

3. 遍歷陣列a,元素值減去最小值min即為在陣列count中的索引index,將count[index]+1, 出現多次則加多次。 比如第一次遍歷到元素19, 19-min=19-16=3, index=3,則count[3]+1。因為遍歷a一遍,19總共有3個,所以最終count[3]=3。其它元素類似操作。

4. 我們對統計陣列count順序求和,count[index]裡面儲存的是<=(index+min)的個數,這麼做的目的是什麼呢?其實是讓統計陣列儲存的元素值,等於相應元素的最終排序位置的序號。

5. 建立乙個跟原陣列相同長度的陣列b, 我們遍歷陣列a,從後往前遍歷

6. 陣列a的倒數第乙個元素為a[7]=25, 在加和陣列也即累加後的count中發現,小於或者等於25的元素個數有:count[25-min]=count[25-16]=count[9]=6個。因為陣列下標從0開始計算,所以我們令b[5]存放25這個元素,並且count[9]自減1,表示取走乙個25元素後,小於等於25的元素又少了乙個。

7. 依次重複步驟6,直到count中所有值都降到0,表示全部值都已經取出,把陣列b的值copy到a中替換即完成了對原始陣列a的排序。

實現

參照上述的描述過程,我們還是可以很快的完成**編寫實現(掌握思想最重要,順著思想**實現都還是很簡單的)。

我的github專案位址

package sort

// 遍歷一次獲取乙個陣列中的最大值和最小值

func getmaxandmin(array int) (max int, min int)

if v < min

} return

}func countsort(array int)

max, min := getmaxandmin(array)

// 初始化統計陣列

countarray := make(int, max-min+1)

//遍歷原始陣列,對統計陣列進行賦值

for _, v := range array

//進行統計陣列順序累加

for i := 1; i < max-min+1; i++

// 初始化排序後陣列b

sortarray := make(int, arraylen)

// 原始陣列倒序遍歷,查詢在統計陣列中的值(代表在排序後陣列的索引位置)

for i := arraylen - 1; i >= 0; i--

//複製b陣列到原陣列a中

for i := 0; i < arraylen; i++

return

}

排序目標要能夠對映到整數域,其最大值最小值應當容易辨別。例如高中生考試的總分數,顯然用0-750就ok啦;又比如一群人的年齡,用個0-150應該就可以了,再不濟就用0-200嘍。另外,計數排序需要占用大量空間,它比較適用於資料比較集中的情況。

計數排序的侷限性主要在於:

數列的最大值和最小值差距過大的時候,不適合適用

比如我們只有100個整數,範圍在0-10億之間,這個時候使用計數排序的話,將會建立個10億長度的陣列,空間被嚴重浪費
數列元素不是整數的時候,不適合使用

(下面的n 是陣列中元素的個數,k 是陣列中最大的值)

o(n+k);也有極端情況,當陣列中最大值遠小於陣列中元素的數量,空間這時候會被運用得很有效率,o(k).

計數排序是穩定排序

計數排序是複雜度為o(n+k)的穩定的排序演算法,k是待排序列最大值,適用在對最大值不是很大的整型元素序列進行排序的情況下(整型元素可以有負數,我們可以把待排序列整體加上乙個整數,使得待排序列的最小元素為0,然後執行計數排序,完成之後再變回來。這個操作是線性的,所以計數這樣做計數排序的複雜度仍然是o(n+k))。本質上是一種空間換時間的演算法,如果k比較小,計數排序的效率優勢是很明顯的,當k變得很大的時候,這個演算法可能就不如其他優秀的排序演算法(比如我們之前講解的快速排序)

小學生**排序演算法:⑧計數排序 技術排序

排序 時間倒序 排序演算法 插入排序

你一定玩過紙牌,為了使手中的牌變的有序,我們正常人的思維是把摸到的牌插入到對應的位置,你就會得到有序的牌了。在計算機中要給給插入的元素騰空間,需要將其後的元素在插入之前後移一位,這種演算法叫插入排序 1.從第乙個元素開始,該元素認為已經排序 2.取下乙個元素,把該元素與已經排序的元素從右到左邊依次比...

排序 時間倒序 時間管理你知道多少

1專案時間管理由哪些過程組成?過程間關係是怎麼樣的?1 活動定義 指確認一些特定的工作 通過完成這些活動就完成了工程專案的各項細節工作 2 活動排序 明確各活動間的相互聯絡性 前後 並列等 3 活動時間估計 估計各活動所需的時間 4 進度計畫編制 分析活動時間排序 活動所需時間和資源以做出專案進度計...

倒序排序 詳解插入排序

插入排序 insertion sort 的過程就像我們排序撲克牌一樣 從左到右,從小到大 開始時我們左手為空,然後我們從桌子上拿起一張牌並將它插入到左手中正確的位置,為了找到這個位置,我們將這張牌與左手中從右向左的每張牌進行比較,直到找到比它小或相等的牌的後面。與排序撲克牌類似,插入排序的原理是將陣...