數(shù)據(jù)結(jié)構(gòu)與算法-算法篇:排序—計數(shù)排序(八)

數(shù)據(jù)結(jié)構(gòu)與算法系列文章:數(shù)據(jù)結(jié)構(gòu)與算法目錄

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

計數(shù)排序的特征:
數(shù)組的元素都要是大于0的整數(shù)苞冯,計數(shù)排序不是比較排序袖牙,排序的速度快于任何比較排序算法。
由于用來計數(shù)的數(shù)組C的長度取決于待排序數(shù)組中數(shù)據(jù)的范圍(等于待排序數(shù)組的最大值與最小值的差加上1)舅锄,這使得計數(shù)排序?qū)τ跀?shù)據(jù)范圍很大的數(shù)組鞭达,需要大量時間和內(nèi)存。
所以計數(shù)排序是用來排序1到100之間的數(shù)字的最好的算法皇忿。

#######圖解計數(shù)排序:
以[ 3畴蹭,5,8鳍烁,2叨襟,5,4 ]這組數(shù)字示例:
1幔荒、首先糊闽,我們找到這組數(shù)字中最大的數(shù): 8,創(chuàng)建一個最大下標為 8 (數(shù)組中最大值+1)的空數(shù)組 arr 爹梁。


計數(shù)排序1.png

2右犹、遍歷數(shù)據(jù),將數(shù)據(jù)的出現(xiàn)次數(shù)填入arr中對應的下標位置中姚垃。數(shù)組中的值為新創(chuàng)建數(shù)組的索引念链,對應內(nèi)容為該值出現(xiàn)的次數(shù)。


計數(shù)排序2.png

3积糯、遍歷 arr 钓账,將數(shù)據(jù)依次取出即可。


計數(shù)排序3.png
實現(xiàn):
/// <summary>
/// 計數(shù)排序
/// </summary>
/// <param name="arr"></param>
public void CountingSort(int[] arr)
{
    // 找出數(shù)組中最大的
    int max = arr[0];
    for (int i = 0; i < arr.Length; i++)
    {
        if (arr[i] > max)
        {
            max = arr[i];
        }
    }

    // 創(chuàng)建計數(shù)數(shù)組
    int[] countArr = new int[max + 1];

    // 開始計數(shù)
    for (int i = 0; i < arr.Length; i++)
    {
        countArr[arr[i]]++;
    }

    // 排序
    int sortedIndex = 0;
    for (int i = 0; i < countArr.Length; i++)
    {
        while (countArr[i] > 0)
        {
            arr[sortedIndex++] = i;
            countArr[i]--;
        }
    }
}
穩(wěn)定排序:

有一個需求就是當對成績進行排名次的時候絮宁,如何在原來排前面的人,排序后還是處于相同成績的人的前面服协。

解題的思路是對 countArr 計數(shù)數(shù)組進行一個變形绍昂,來和名次掛鉤,我們知道 countArr 存放的是分數(shù)的出現(xiàn)次數(shù)偿荷,那么其實我們可以算出每個分數(shù)的最大名次窘游,就是將 countArr 中的每個元素順序求和。

如下圖:


穩(wěn)定排序.png

變形意思:

我們把原數(shù)組[ 2跳纳,5忍饰,8,2寺庄,5艾蓝,4 ]中的數(shù)據(jù)依次拿來去 countArr 去找力崇,你會發(fā)現(xiàn) 3 這個數(shù)在 countArr[3] 中的值是 2 ,代表著排名第二名赢织,(因為第一名是最小的 2亮靴,對吧?)于置,5 這個數(shù)在 countArr[5] 中的值是 5 茧吊,為什么是 5 呢?我們來數(shù)數(shù)八毯,排序后的數(shù)組應該是[ 2搓侄,3,4话速,5讶踪,5,8 ]尿孔,5 的排名是第五名俊柔,那 4 的排名是第幾名呢?對應 countArr[4] 的值是 3 活合,第三名雏婶,5 的排名是第五名是因為 5 這個數(shù)有兩個,自然占據(jù)了第 4 名和第 5 名白指。

所以我們?nèi)∨琶臅r候應該特別注意留晚,原數(shù)組中的數(shù)據(jù)要從右往左取,從 countArr 取出排名后要把 countArr 中的排名減 1 告嘲,以便于再次取重復數(shù)據(jù)的時候排名往前一位错维。

實現(xiàn):
/// <summary>
/// 計數(shù)排序
/// </summary>
/// <param name="arr"></param>
public void CountingSort(int[] arr)
{
    // 找出數(shù)組中最大的
    int max = arr[0];
    for (int i = 0; i < arr.Length; i++)
    {
        if (arr[i] > max)
        {
            max = arr[i];
        }
    }

    // 創(chuàng)建計數(shù)數(shù)組
    int[] countArr = new int[max + 1];

    // 開始計數(shù)
    for (int i = 0; i < arr.Length; i++)
    {
        countArr[arr[i]]++;
    }

    //順序累加
    for (int i = 1; i < max + 1; ++i)
    {
        countArr[i] = countArr[i - 1] + countArr[i];
    }

    //排序后的數(shù)組
    int[] sortedArr = new int[arr.Length];

    //排序
    for (int i = arr.Length - 1; i >= 0; --i)
    {
        int value = arr[i];
        sortedArr[countArr[value] - 1] = value;
        countArr[value]--;
    }

    //將排序后的數(shù)據(jù)拷貝到原數(shù)組
    for (int i = 0; i < arr.Length; ++i)
    {
        arr[i] = sortedArr[i];
    }
}
計數(shù)局限性:

計數(shù)排序?qū)?shù)據(jù)范圍偏差比較大的,比如數(shù)組 1橄唬,9999 ]赋焕,則會比較耗時;
如果是[ 9998仰楚,9999 ]這種雖然值大但是相差范圍不大的數(shù)據(jù)我們可以使用偏移量解決隆判,比如這兩個數(shù)據(jù),減掉 9997 后只需要申請一個 int[3] 的數(shù)組就可以進行計數(shù)僧界。

所以侨嘀,計數(shù)排序只適用于正整數(shù)并且取值范圍相差不大的數(shù)組排序使用,如1到100之間捂襟,它的排序的速度是非骋螅可觀的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葬荷,一起剝皮案震驚了整個濱河市涨共,隨后出現(xiàn)的幾起案子纽帖,更是在濱河造成了極大的恐慌,老刑警劉巖煞赢,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抛计,死亡現(xiàn)場離奇詭異,居然都是意外死亡照筑,警方通過查閱死者的電腦和手機吹截,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凝危,“玉大人波俄,你說我怎么就攤上這事《昴” “怎么了懦铺?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長支鸡。 經(jīng)常有香客問我冬念,道長,這世上最難降的妖魔是什么牧挣? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任急前,我火速辦了婚禮,結(jié)果婚禮上瀑构,老公的妹妹穿的比我還像新娘裆针。我一直安慰自己,他們只是感情好寺晌,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布世吨。 她就那樣靜靜地躺著,像睡著了一般呻征。 火紅的嫁衣襯著肌膚如雪耘婚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天陆赋,我揣著相機與錄音边篮,去河邊找鬼。 笑死奏甫,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的凌受。 我是一名探鬼主播阵子,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼胜蛉!你這毒婦竟也來了挠进?” 一聲冷哼從身側(cè)響起色乾,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎领突,沒想到半個月后暖璧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡君旦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年澎办,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片金砍。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡局蚀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恕稠,到底是詐尸還是另有隱情琅绅,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布鹅巍,位于F島的核電站千扶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏骆捧。R本人自食惡果不足惜澎羞,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凑懂。 院中可真熱鬧煤痕,春花似錦、人聲如沸接谨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脓豪。三九已至巷帝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扫夜,已是汗流浹背楞泼。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笤闯,地道東北人堕阔。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像颗味,于是被迫代替她去往敵國和親超陆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345