2018-09-24 多關(guān)鍵字排序(基數(shù)排序LSD + MSD)

多關(guān)鍵字排序

很多時(shí)候,一個(gè)對(duì)象可以用多個(gè)特征值來(lái)刻畫(huà)它,可以把每個(gè)特征值看做一個(gè)關(guān)鍵字,比如撲克牌有花色和點(diǎn)數(shù)這兩個(gè)特征,如果所要求的順序由多個(gè)關(guān)鍵字聯(lián)合決定,我們就可以利用這種特征來(lái)使用多關(guān)鍵字排序方法,多關(guān)鍵字地位不是平等的,有優(yōu)先級(jí)大小。
如撲克牌排序,我們就可以規(guī)定花色比點(diǎn)數(shù)優(yōu)先,也就是說(shuō)無(wú)論點(diǎn)數(shù)多少,只要花色大的就認(rèn)為它是大牌,比如規(guī)定黑桃大于紅心,紅心大于梅花,梅花大于方塊。
多關(guān)鍵字排序有兩種思路:
高優(yōu)先級(jí):MSD(先用高優(yōu)先級(jí)的關(guān)鍵字進(jìn)行分組)
低優(yōu)先級(jí):LSD(先用低優(yōu)先級(jí)的關(guān)鍵字進(jìn)行分組)

高優(yōu)先級(jí)排序MSD

下邊我們先看高優(yōu)先級(jí)排序方式MSD,
比如有如下?lián)淇伺疲覀円?guī)定花色優(yōu)先,花色從小到大關(guān)系如下圖:方塊,梅花,紅心,黑桃

0.png

第一步:我們先用高優(yōu)先級(jí)的關(guān)鍵字(花色)進(jìn)行分組,如下圖
我們可以想象成四個(gè)盒子,把撲克牌按照花色扔進(jìn)4個(gè)盒子中。
1.png

全部分進(jìn)四個(gè)盒子之后,在每個(gè)組內(nèi)進(jìn)行排序(每個(gè)盒子內(nèi)的排序算法隨意),每個(gè)盒子里邊元素排好序之后入下圖:
2.png

這里每個(gè)組內(nèi)(盒子)的排序也可以繼續(xù)采用分組的方式,在每個(gè)盒子按低優(yōu)先級(jí)分組收集
保證每個(gè)組內(nèi)元素排好序,再把這些排好序的各組的元素收集起來(lái)就可以了。如下圖
3.png

收集好之后,如下圖,就是已經(jīng)排好序的游戲序列。
4.png

低優(yōu)先級(jí)排序LSD

我們分析一下低優(yōu)先級(jí),還以撲克牌為例
第一步:我們先用最低優(yōu)先級(jí)的關(guān)鍵字進(jìn)行分組


5.png

第二步:分組分好之后進(jìn)行收集


image.png

第三步:收集好的數(shù)據(jù)再按花色進(jìn)行重新分組
image.png

第四步:把分組后數(shù)據(jù)再重新收集,就可以得到一個(gè)有序數(shù)組。
image.png

image.png

總結(jié):

通過(guò)上邊例子我們會(huì)發(fā)現(xiàn),整個(gè)過(guò)程我們并沒(méi)有進(jìn)行排序工作,僅僅是進(jìn)行了分組收集
這個(gè)排序的創(chuàng)新點(diǎn)是它好像并沒(méi)有進(jìn)行任何排序,而是通過(guò)不斷的分組收集,再分組再收集就完成了整個(gè)排序工作。
我們這里只有兩個(gè)關(guān)鍵字,如果有三個(gè)四個(gè)或者更多關(guān)鍵字,我們依然可以采用這種方式按照優(yōu)先級(jí)的高低來(lái)進(jìn)行分組收集,把所有的分組工作做完之后,我們的排序工作就完成了。
注意點(diǎn):關(guān)鍵字的空間不能太大,否則我們分組太多,效率會(huì)很低,這種排序適合哪種很多種關(guān)鍵字,但是每個(gè)關(guān)鍵字又不是很大的情況

Java代碼來(lái)模擬撲克牌排序
先做一個(gè)撲克牌類,和花色的枚舉類

/**
 * 撲克牌類
 * @author Administrator
 *
 */
public class Card {
    private CardType type;
    private int point;
    
    public Card(CardType type, int point) {
        this.type = type;
        this.point = point;
    }
    
    public String toString() {
        return "(" + type.getName() + " " + point + ")";
    }

    public CardType getType() {return type;}
    public int getPoint() {return point;}
}

花色枚舉類

/**
 * 黑桃(Spade) > 紅桃(Heart) > 梅花(Club) > 方塊(Diamond)
 */
public enum CardType {
    SPADE {// 黑桃
        @Override
        public int code() {
            return 3;
        }
        @Override
        public String getName() {return "黑桃";}
    },HEART { // 紅桃
        @Override
        public int code() {
            return 2;
        }
        @Override
        public String getName() {return "紅桃";}
    },CLUB { // 梅花
        @Override
        public int code() {
            return 1;
        }
        @Override
        public String getName() {return "梅花";}
    },DIAMOND { // 方塊
        @Override
        public int code() {
            return 0;
        }
        @Override
        public String getName() {return "方塊";}
    };
    
    public abstract int code();
    public abstract String getName();
}

低優(yōu)先級(jí)排序類

import java.util.Arrays;

public class RadixSortCard {

    //低優(yōu)先級(jí)排序
    public static void lsdSort(Card[] arr) {
        int len = arr.length;
        
        // -------------------第一步 分組-------------------------
        // 按點(diǎn)數(shù)存入桶
        // 創(chuàng)建點(diǎn)數(shù)的桶,不算大小王{A(1),2,3,4,5,6,7,8,9,10,J(11),Q(12),K(13)}同需13個(gè)桶
        Card[][] t1 = new Card[14][len+1];// 因?yàn)辄c(diǎn)數(shù)從1開(kāi)始,這里數(shù)組做成14個(gè)。
        int[] num1 = new int[14];//每個(gè)桶中元素的下標(biāo)
        
        for (Card c : arr) {//存入桶中
            int point = c.getPoint();
            t1[point][num1[point]] = c;
            num1[point]++;
        }
        // -------------------第二步 收集-------------------------
        // 按順序取出各桶中的元素
        int index1 = 0;
        for (int i = 0; i < 14; i++) {
            if (num1[i] > 0) {// 桶中有元素
                for (int j = 0; j < num1[i]; j++) {
                    if (t1[i][j] != null) {
                        arr[index1++] = t1[i][j];
                    }
                }
            }
        }
        
        // -------------------第三步 再分組-------------------------
        //再先按花色存入桶
        Card[][] t2 = new Card[4][len];// 創(chuàng)建花色的桶
        int[] num2 = new int[4];//每個(gè)桶中元素的下標(biāo)
        
        for (Card c : arr) {
            int typeCode = c.getType().code();
            t2[typeCode][num2[typeCode]] = c;
            num2[typeCode]++;
        }
        
        // -------------------第四步 再收集-------------------------
        int index2 = 0;
        for (int i = 0; i < 4; i++) {
            if (num2[i] > 0) {  // 桶中有元素
                for (int j = 0; j < num2[i]; j++) {
                    if (t2[i][j] != null) {
                        arr[index2++] = t2[i][j];
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) {
        
        Card[] arr = new Card[11];
        Card c0 = new Card(CardType.HEART, 1);//紅桃1
        Card c1 = new Card(CardType.SPADE, 9);//黑桃9
        Card c2 = new Card(CardType.SPADE, 2);//黑桃2
        Card c3 = new Card(CardType.CLUB, 5);//梅花5
        Card c4 = new Card(CardType.SPADE, 8);//黑桃8
        Card c5 = new Card(CardType.DIAMOND, 12);//方塊12
        Card c6 = new Card(CardType.CLUB, 13);//梅花13
        Card c7 = new Card(CardType.DIAMOND, 3);//方塊3
        Card c8 = new Card(CardType.HEART, 11);//紅桃11
        Card c9 = new Card(CardType.DIAMOND, 8);//方塊8
        Card c10 = new Card(CardType.HEART, 9);//紅桃9
        
        arr[0] = c0;
        arr[1] = c1;
        arr[2] = c2;
        arr[3] = c3;
        arr[4] = c4;
        arr[5] = c5;
        arr[6] = c6;
        arr[7] = c7;
        arr[8] = c8;
        arr[9] = c9;
        arr[10] = c10;
        
        lsdSort(arr);
        
        System.out.println(Arrays.toString(arr));
    }
}


關(guān)鍵字拆分

有時(shí)候如果關(guān)鍵字空間太大,我們可以把一個(gè)關(guān)鍵字拆分為多個(gè)關(guān)鍵字,比如整數(shù)如果作為一個(gè)關(guān)鍵字,他的空間就太大了。如果問(wèn)題中整數(shù)只是在一千以內(nèi),那我分成1000組顯然是不合適的,我們可以按十進(jìn)制的位作為關(guān)鍵字,可以把個(gè)位,十位,百位看做三個(gè)關(guān)鍵字(優(yōu)先級(jí) 個(gè)位 < 十位 < 百位)。
下邊分析一下數(shù)值排序(高優(yōu)先級(jí)和低優(yōu)先級(jí)排序)
原文: https://blog.csdn.net/u011948899/article/details/78027838

最低位優(yōu)先法:先從最低位開(kāi)始排序,再對(duì)次低位排序,直到對(duì)最高位排序后得到一個(gè)有序序列;具體過(guò)程如下圖所示:

初始數(shù)組序列為:15,25,105,78,34,21,32,41,按照個(gè)位數(shù)大小依次入桶;

11.png

將桶中數(shù)依次倒出,對(duì)于同一個(gè)桶中的數(shù)按先進(jìn)先出順序倒出,結(jié)果為:21,41,32,34,15,25,105,78,再按十位數(shù)大小依次入桶;

22.png

將桶中數(shù)依次倒出,結(jié)果為:105,15,21,25,32,34,41,78,再按百位上數(shù)大小依次入桶,沒(méi)有百位的數(shù)則按百位為0入桶;

33.png

將桶中數(shù)倒出,結(jié)果為:15,21,25,32,34,41,78,105

最高位優(yōu)先法:先從最高位開(kāi)始排序,再逐個(gè)對(duì)各分組按次高位進(jìn)行子排序,循環(huán)直到最低位。

仍以序列:15,25,105,78,34,21,32,41為例,從最高位百位依次入桶,只有105有百位,其他百位按0算;檢測(cè)每個(gè)桶中的數(shù)據(jù)。當(dāng)桶中的元素個(gè)數(shù)多于1個(gè)的時(shí)候,要對(duì)這個(gè)桶遞歸進(jìn)行下一位的分組。

高優(yōu)先級(jí).png

Java實(shí)現(xiàn)整數(shù)排序代碼(LSD MSD):

import java.util.Arrays;

/**
 * 基數(shù)排序,低優(yōu)先級(jí)(LSD)和高優(yōu)先級(jí)(MSD)
 * @author Administrator
 *
 */
public class RadixSort {

    // 對(duì)數(shù)組arr進(jìn)行低優(yōu)先級(jí)排序
    public static void lsdSort(int[] arr) {
        int len = arr.length;
        // 先找出數(shù)組最大的數(shù)
        int max = arr[0];
        for (int i=1; i<len; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }
        
        // 求最大數(shù)的位數(shù)
        double d = Math.pow(10, String.valueOf(max).length());
        
        int[][] t = new int[10][len];//準(zhǔn)備10個(gè)用于存放0-9的桶,每個(gè)桶最多存放數(shù)組長(zhǎng)度len個(gè)元素
        int[] num = new int[len];//記錄每個(gè)桶中存入元素個(gè)數(shù).
        int bit = 1; // 個(gè)位1,十位10,百位100
        
        while (bit < d) {
            // 從個(gè)位開(kāi)始,將元素裝入桶中
            for (int a : arr) {
                int m = (a / bit) % 10;//確定該數(shù)應(yīng)該存入幾號(hào)桶(0,1,2,3,4,5,6,7,8,9)
                t[m][num[m]] = a;
                num[m]++;
            }
            
            // 按順序從桶中(0-9)依次取出所有元素,放入arr中繼續(xù)下一位循環(huán)
            int index = 0;
            for (int i = 0; i < len; i++) {
                if (num[i] != 0) {// 桶中有元素,則取出
                    for (int j = 0; j < num[i]; j++) {
                        arr[index++] = t[i][j];// 將i桶中元素取出,存入到arr中
                    }
                }
                num[i] = 0;
            }
            bit = bit * 10;
        }
    }
    
    public static void msdSort(int[] arr) {
        int len = arr.length;
        // 取數(shù)組中最大值
        int max = arr[0];
        for (int i = 1; i < len; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }
        
        int maxL = String.valueOf(max).length();//獲取素組中最長(zhǎng)元素的長(zhǎng)度
        int bit = new Double(Math.pow(10, maxL - 1)).intValue();
        int[][] t = new int[10][len];   //準(zhǔn)備10個(gè)用于存放0-9的桶,每個(gè)桶最多存放數(shù)組長(zhǎng)度len個(gè)元素
        int[] num = new int[len];   // 記錄每個(gè)桶中存入元素的個(gè)數(shù)
        
        for (int a : arr) { //按最高位入桶
            int m = (a / bit) % 10;
            t[m][num[m]] = a;
            num[m]++;
        }
        
        int index = 0;
        for (int i = 0; i < len; i++) {
            if (num[i] == 1) {  // 如果桶中只有一個(gè)數(shù)則直接取出
                arr[index++] = t[i][0];
            } else if (num[i] > i) { //如果桶中不止一個(gè)數(shù),則另存入數(shù)組arr2,遞歸
                int[] arr2 = new int[num[i]];
                for (int j = 0; j < num[i]; j++) {
                    arr2[j] = t[i][j];
                    msdSort(arr2); // 遞歸方法
                }
            }
        }
    }
    
    public static void main(String[] args) {
        int[] arr = {12,1,20,59,123,321,652};
        lsdSort(arr);
        System.out.println(Arrays.toString(arr));
        
        int[] arr2 = {12,1,20,56,123,321,652};
        lsdSort(arr2);
        System.out.println(Arrays.toString(arr2));
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,506評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,614評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,934評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,168評(píng)論 0 287
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,404評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,662評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,398評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個(gè)記錄插入到已排序好...
    依依玖玥閱讀 1,265評(píng)論 0 2
  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    閑云清煙閱讀 761評(píng)論 0 6
  • 概述排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部的...
    Luc_閱讀 2,284評(píng)論 0 35
  • 概述:排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,737評(píng)論 0 15
  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    蟻前閱讀 5,207評(píng)論 0 52