排序(下_常數(shù)階)

2019年10月26日

桶排序

1,算法思想

  1. 根據(jù)場景設(shè)置桶子的個(gè)數(shù)捷凄。
  2. 尋訪序列瓶颠,并且把元素一個(gè)一個(gè)放到對應(yīng)的桶子去。
  3. 對每個(gè)不是空的桶子進(jìn)行排序痛垛。
  4. 從不是空的桶子里的元素再拼接到一起放回原來的序列中草慧。

2,算法圖解

1

3匙头,算法實(shí)現(xiàn)

public class Bucket {
    public static void bucketSort(int[] arr, int step) {
        int max = arr[0];
        int min = arr[0];
        for (int a : arr) {
            if (max < a) {
                max = a;
            }
            if (min > a) {
                min = a;
            }
        }
        int bucketNum = max / step - min / step + 1;
        List buckList = new ArrayList<List<Integer>>();
        for (int i = 0; i < bucketNum; i++) {
            buckList.add(new ArrayList<Integer>());
        }
        for (int value : arr) {
            int index = indexFor(value, min, step);
            ((ArrayList<Integer>) buckList.get(index)).add(value);
        }
        ArrayList<Integer> bucket = null;
        int index = 0;
        for (int i = 0; i < bucketNum; i++) {
            bucket = (ArrayList<Integer>) buckList.get(i);
            Collections.sort(bucket);
            for (int k : bucket) {
                arr[index++] = k;
            }
        }
    }

    private static int indexFor(int a, int min, int step) {
        return (a - min) / step;
    }

    public static void main(String[] args) {
        Random randomInt = new Random();
        int[] a = new int[20];
        for (int i = 0; i < a.length; i++) {
            a[i] = randomInt.nextInt(100);
        }
        System.out.println(Arrays.toString(a));
        bucketSort(a, 10);
        System.out.println(Arrays.toString(a));
    }
}

4漫谷,復(fù)雜度分析

假設(shè)待排序的數(shù)據(jù)有N個(gè),桶的個(gè)數(shù)為M個(gè)蹂析,那么每個(gè)桶平均有K=N/M個(gè)數(shù)據(jù)舔示,每個(gè)桶內(nèi)部使用對數(shù)階排序算法如快排。每個(gè)桶內(nèi)部的時(shí)間復(fù)雜度為O(KlogK)电抚,那么M個(gè)桶排序的時(shí)間復(fù)雜度就為:O(M*KlogK)斩郎,又因?yàn)?img class="math-inline" src="https://math.jianshu.com/math?formula=K%3DN%2FM" alt="K=N/M" mathimg="1">,該時(shí)間復(fù)雜度又為:O(Nlog\frac{N}{M})喻频,當(dāng)M \rightarrow N,這時(shí)桶排序的時(shí)間復(fù)雜度就接近O(N)肘迎。因此:

  • 最好時(shí)間復(fù)雜度:O(N)
  • 最壞時(shí)間復(fù)雜度:O(NlogN)
  • 空間復(fù)雜度:O(N)

其穩(wěn)定性取決于桶內(nèi)排序算法甥温。

計(jì)數(shù)排序

1,算法思想

  1. 找出待排序的數(shù)組中最大和最小的元素
  2. 統(tǒng)計(jì)數(shù)組中每個(gè)值為i的元素出現(xiàn)的次數(shù)妓布,存入數(shù)組C的第i項(xiàng)
  3. 對所有的計(jì)數(shù)累加(從C中的第一個(gè)元素開始姻蚓,每一項(xiàng)和前一項(xiàng)相加)
  4. 反向填充目標(biāo)數(shù)組:將每個(gè)元素i放在新數(shù)組的第C[i]項(xiàng),每放一個(gè)元素就將C[i]減去1

2匣沼,算法圖解

2

由圖中可以看出狰挡,當(dāng)count從下標(biāo)0開始填充時(shí),若執(zhí)行順序從前往后的話,arr[0]中的2 將會插入到temp[1]中加叁,而arr[3]中的2將會插入到temp[0]中倦沧,因此排序后,arr中的相同的元素位置被顛倒了它匕,使得算法不穩(wěn)定展融。

此外,還可以將count從下標(biāo)1開始填充豫柬,這時(shí)執(zhí)行順序從前往后就可以保證穩(wěn)定性了

3

3告希,算法實(shí)現(xiàn)

反向填充目標(biāo)數(shù)組的實(shí)現(xiàn)

public class Count {
    public static void countSort(int[] arr) {
        int[] temp = new int[arr.length];
        int max = arr[0], min = arr[0];
        for (int i : arr) {
            if (i > max) {
                max = i;
            }
            if (i < min) {
                min = i;
            }
        }
        int k = max - min + 1;
        int[] count = new int[k];
        for (int value : arr) {
            count[value - min]++;
        }
        for (int i = 1; i < count.length; ++i) {
            count[i] = count[i] + count[i - 1];
        }

        for (int i = arr.length - 1; i >= 0; --i) {
            int index = count[arr[i] - min] - 1;
            temp[index] = arr[i];
            count[arr[i] - min]--;
        }
        System.arraycopy(temp, 0, arr, 0, arr.length);
    }

    public static void main(String[] args) {
        Random randomInt = new Random();
        int[] a = new int[20];
        for (int i = 0; i < a.length; i++) {
            a[i] = randomInt.nextInt(100);
        }
        System.out.println(Arrays.toString(a));
        countSort(a);
        System.out.println(Arrays.toString(a));
    }
}

正向填充目標(biāo)數(shù)組的實(shí)現(xiàn)

public void countSort2(int[] arr) {
        int[] temp = new int[arr.length];
        int max = arr[0], min = arr[0];
        for (int i : arr) {
            if (i > max) {
                max = i;
            }
            if (i < min) {
                min = i;
            }
        }
        int k = max - min + 1;
        int[] count = new int[k + 1];
        for (int value : arr) {
            count[value - min + 1]++;
        }
        for (int i = 0; i < count.length - 1; ++i) {
            count[i + 1] += count[i];
        }

        for (int value : arr) {
            temp[count[value - min]++] = value;
        }
        System.arraycopy(temp, 0, arr, 0, arr.length);
    }

4,復(fù)雜度分析

從代碼中可以看出烧给,其最好燕偶,最壞,平均時(shí)間復(fù)雜度都為:O(N)础嫡,空間復(fù)雜度為:O(N )指么。

而且該算法是穩(wěn)定的。

基數(shù)排序

1驰吓,算法思想

基數(shù)排序(英語:Radix sort)是一種非比較型==整數(shù)==排序算法涧尿,其原理是將整數(shù)按位數(shù)切割成不同的數(shù)字,然后按每個(gè)位數(shù)分別比較檬贰。由于整數(shù)也可以表達(dá)字符串(比如名字或日期)和特定格式的浮點(diǎn)數(shù)姑廉,所以基數(shù)排序也不是只能使用于整數(shù)。

它是這樣實(shí)現(xiàn)的:將所有待比較數(shù)值(正整數(shù))統(tǒng)一為同樣的數(shù)字長度翁涤,數(shù)字較短的數(shù)前面補(bǔ)零桥言。然后,從最低位開始葵礼,依次進(jìn)行一次排序号阿。這樣從最低位排序一直到最高位排序完成以后,數(shù)列就變成一個(gè)有序序列鸳粉。

基數(shù)排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital)扔涧,LSD的排序方式由鍵值的最右邊開始,而MSD則相反届谈,由鍵值的最左邊開始枯夜。

2,算法圖解

4

3艰山,算法實(shí)現(xiàn)

public class Radix {
    public static void radixSort(String[] arr, int stringLen) {
        final int len = 256;

        ArrayList<String>[] buckets = new ArrayList[len];

        for (int i = 0; i < len; i++) {
            buckets[i] = new ArrayList<>();
        }

        for (int pos = stringLen - 1; pos >= 0; pos--) {
            for (String s : arr) {
                buckets[s.charAt(pos)].add(s);
            }

            int idx = 0;
            for (ArrayList<String> thisBucket : buckets) {
                for (String s : thisBucket) {
                    arr[idx++] = s;
                }
                /**
                 * 每排完一次序湖雹,就將已排好的數(shù)據(jù)從buckets中清空,
                 * 否則外層再次循環(huán)時(shí)曙搬,第13行會將數(shù)據(jù)重復(fù)存入buckets中摔吏,
                 * 這樣到最后buckets中會有pos*arr.length個(gè)數(shù)據(jù)鸽嫂,即所有元素都
                 * 重復(fù)存入了pos個(gè),會造成arr的ArrayIndexOutOfBoundsException
                 */
                thisBucket.clear();
            }
        }
    }

    public static void main(String[] args) {
        String[] arr = {"4PGC938", "2IYE230", "3CIO720", "1ICK750", "1OHV845", "4JZY524", "1ICK750", "3CIO720",
                "1OHV845", "1OHV845", "2RLA629", "2RLA629", "3ATW723"};
        System.out.println(Arrays.toString(arr));
        radixSort(arr, 7);
        System.out.println(Arrays.toString(arr));
    }
}

4征讲,復(fù)雜度分析

若排序的數(shù)據(jù)長度為k据某,則需要進(jìn)行k次排序,而內(nèi)部排序的時(shí)間復(fù)雜度為O(N)稳诚,因此總的時(shí)間復(fù)雜度為O(k*N)哗脖,當(dāng)k不大時(shí),時(shí)間復(fù)雜度近似于O(N)扳还;空間復(fù)雜度為O(N)才避;是穩(wěn)定的排序算法。

5氨距,應(yīng)用

對定長字符串排序

利用基數(shù)+計(jì)數(shù)排序可以對字符串進(jìn)行排序(LSD):

反向填充:

public static void radixCountStrSort(String[] arr, int strLength) {
        final int bucket = 256;
        String[] temp = new String[arr.length];
        for (int d = strLength - 1; d >= 0; d--) {
            int[] count = new int[bucket];
            //count下標(biāo)對應(yīng)的字母中填的值為該字母的最大位次
            for (String s : arr) {
                count[s.charAt(d)]++;
            }
            for (int r = 1; r < bucket; r++) {
                count[r] += count[r - 1];
            }
            for (int i = arr.length - 1; i >= 0; i--) {
                temp[count[arr[i].charAt(d)] - 1] = arr[i];
                count[arr[i].charAt(d)]--;
            }
            System.arraycopy(temp, 0, arr, 0, arr.length);
        }
    }

也可以正向填充桑逝,同時(shí)為了避免數(shù)組間的頻繁復(fù)制,可以進(jìn)一步優(yōu)化為:

public static void radixCountStrSort2(String[] arr, int strLength) {
        final int bucket = 256;
        String[] buffer = new String[arr.length];
        String[] in = arr;
        String[] out = buffer;
        for (int d = strLength - 1; d >= 0; d--) {
            int[] count = new int[bucket + 1];
            //count下標(biāo)對應(yīng)的字母中填的值為該字母的起始位次
            for (String s : in) {
                count[s.charAt(d) + 1]++;
            }
            for (int r = 0; r < count.length - 1; r++) {
                count[r + 1] += count[r];
            }
            for (String s : in) {
                out[count[s.charAt(d)]++] = s;
            }
            String[] temp = in;
            in = out;
            out = temp;
        }
        //將in中的數(shù)據(jù)復(fù)制到out中
        if (strLength % 2 == 1) {
            System.arraycopy(in, 0, out, 0, arr.length);
        }
    }
5

由上圖可以看出俏让,每排序趟數(shù)為奇數(shù)次時(shí)楞遏,完全排好的是指向buffer內(nèi)存塊的數(shù)組,因此要將buffer內(nèi)存塊中的數(shù)據(jù)復(fù)制到arr內(nèi)存塊首昔,以保證arr內(nèi)存塊中的數(shù)據(jù)是排好序的寡喝。

變長字符串排序①

    public static void changeStringSort(String[] arr, int maxLen) {
        final int bucket = 256;

        ArrayList<String>[] wordsByLength = new ArrayList[maxLen + 1];
        ArrayList<String>[] buckets = new ArrayList[bucket];

        for (int i = 0; i < wordsByLength.length; i++) {
            wordsByLength[i] = new ArrayList<>();
        }

        for (int i = 0; i < bucket; i++) {
            buckets[i] = new ArrayList<>();
        }

        for (String s : arr) {
            wordsByLength[s.length()].add(s);
        }

        int index = 0;
        for (ArrayList<String> wordList : wordsByLength) {
            for (String s : wordList) {
                arr[index++] = s;
            }
        }

        int startingIndex = arr.length;
        for (int pos = maxLen - 1; pos >= 0; pos--) {
            startingIndex -= wordsByLength[pos + 1].size();

            for (int i = startingIndex; i < arr.length; i++) {
                buckets[arr[i].charAt(pos)].add(arr[i]);
            }

            index = startingIndex;
            for (ArrayList<String> thisBucket : buckets) {
                for (String s : thisBucket) {
                    arr[index++] = s;
                }

                thisBucket.clear();
            }
        }
    }

    public static void main(String[] args) {
        String[] arr = {"1PGCI", "3IY", "3CIO", "4O", "1I", "4JZYE", "2NL", "2ATW"};
        System.out.println(Arrays.toString(arr));
        changeStringSort(arr, 5);
        System.out.println(Arrays.toString(arr));
    }

該算法圖解如下:

6

變長字符串排序②

以下使用一種高位優(yōu)先的字符串排序,即MSD方式勒奇,從左向右遍歷所有字符预鬓。

public class StringSortMsd {
    private static final int BUCKET = 256;
    private static final int CUTOFF = 15;
    private static String[] temp;

    public static void sort(String[] arr) {
        int n = arr.length;
        temp = new String[n];
        sort(arr, 0, n - 1, 0);
    }

    private static int charAt(String s, int d) {
        if (d == s.length()) {
            return -1;
        }
        return s.charAt(d);
    }

    private static void sort(String[] arr, int lo, int hi, int d) {
        if (hi <= lo + CUTOFF) {
            insertion(arr, lo, hi, d);
            return;
        }

        int[] count = new int[BUCKET + 2];
        for (int i = lo; i <= hi; i++) {
            int c = charAt(arr[i], d);
            count[c + 2]++;
        }

        for (int r = 0; r < BUCKET + 1; r++) {
            count[r + 1] += count[r];
        }

        for (int i = lo; i <= hi; i++) {
            int c = charAt(arr[i], d);
            temp[count[c + 1]++] = arr[i];
        }

        for (int i = lo; i <= hi; i++) {
            arr[i] = temp[i - lo];
        }

        for (int r = 0; r < BUCKET; r++) {
            sort(arr, lo + count[r], lo + count[r + 1] - 1, d + 1);
        }
    }

    private static void insertion(String[] arr, int lo, int hi, int d) {
        for (int i = lo; i <= hi; i++) {
            for (int j = i; j > lo && less(arr[j], arr[j - 1], d); j--) {
                exchange(arr, j, j - 1);
            }
        }
    }

    private static void exchange(String[] arr, int i, int j) {
        String temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    private static boolean less(String v, String w, int d) {
        for (int i = d; i < Math.min(v.length(), w.length()); i++) {
            if (v.charAt(i) < w.charAt(i)) {
                return true;
            }
            if (v.charAt(i) > w.charAt(i)) {
                return false;
            }
        }
        return v.length() < w.length();
    }

    public static void main(String[] args) {
        String[] strings = {"she", "sells", "seashells", "by", "the", "seashore", "the",
                "shells", "she", "sells", "are", "surely", "seashells"};
        System.out.println(Arrays.toString(strings));
        sort(strings);
        System.out.println(Arrays.toString(strings));
    }
}

上面的代碼其實(shí)使用了兩種排序算法,當(dāng)待排序數(shù)組的長度在CUTOFF以內(nèi)赊颠,則采用的是插入排序方式格二,否則,采用的是MSD方式竣蹦。這時(shí)因?yàn)?code>MSD方式要對大量的長度為256的數(shù)組進(jìn)行處理顶猜,所以當(dāng)數(shù)組長度較小時(shí),使用插入排序來提高性能痘括。

由于MSD是逐步遞歸處理首字符相同的子數(shù)組长窄,因此當(dāng)字符串中的字符超過其長度的邊界時(shí),需要設(shè)定一個(gè)標(biāo)志纲菌,讓其不在進(jìn)入下一輪的遞歸排序挠日;本算法中,將該標(biāo)志設(shè)為0驰后,將count[0]作為保留位,而字符又有256種情況矗愧,因此灶芝,count需要最多需要存入BUCKET+1個(gè)數(shù)郑原,所以count = new int[BUCKET + 2]

算法圖解如下:

7
8

關(guān)于she,shells,she的排序過程涉及到字符到達(dá)字符串的末尾夜涕,其排序圖解為:

9

變長字符串排序③

當(dāng)待排序的字符串?dāng)?shù)組存在大量的相同字符串或較長的公共前綴犯犁,MSD字符串排序會檢查其所有的字符,會創(chuàng)建大量的子數(shù)組女器,因此MSD適用于隨機(jī)字符串排序酸役;而三向字符串快速排序可以很好的解決該問題:

public class StringSortQuick {
    private static final int CUTOFF = 15;

    public static void sort(String[] arr) {
        sort(arr, 0, arr.length - 1, 0);
    }

    private static int charAt(String s, int d) {
        if (d == s.length()) {
            return -1;
        }
        return s.charAt(d);
    }

    private static void sort(String[] arr, int lo, int hi, int d) {
        if (hi <= lo + CUTOFF) {
            insertion(arr, lo, hi, d);
            return;
        }

        int lt = lo, gt = hi;
        int v = charAt(arr[lo], d);
        int i = lo + 1;
        while (i <= gt) {
            int t = charAt(arr[i], d);
            if (t < v) {
                exchange(arr, lt++, i++);
            } else if (t > v) {
                exchange(arr, i, gt--);
            } else {
                i++;
            }
        }

        sort(arr, lo, lt - 1, d);
        if (v >= 0) {
            sort(arr, lt, gt, d + 1);
        }
        sort(arr, gt + 1, hi, d);
    }

    private static void insertion(String[] a, int lo, int hi, int d) {
        for (int i = lo; i <= hi; i++) {
            for (int j = i; j > lo && less(a[j], a[j - 1], d); j--) {
                exchange(a, j, j - 1);
            }
        }
    }

    private static void exchange(String[] a, int i, int j) {
        String temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    private static boolean less(String v, String w, int d) {
        for (int i = d; i < Math.min(v.length(), w.length()); i++) {
            if (v.charAt(i) < w.charAt(i)) {
                return true;
            }
            if (v.charAt(i) > w.charAt(i)) {
                return false;
            }
        }
        return v.length() < w.length();
    }

    public static void main(String[] args) {
        String[] strings = {"she", "sells", "seashells", "by", "the", "seashore", "the",
                "shells", "she", "sells", "are", "surely", "seashells"};
        System.out.println(Arrays.toString(strings));
        sort(strings);
        System.out.println(Arrays.toString(strings));
    }
}

10
11

字符串排序總結(jié)

算法 穩(wěn)定性 時(shí)間復(fù)雜度 空間復(fù)雜度 適用領(lǐng)域
字符串插入排序 穩(wěn)定 O(N)~O(N^2) O(1) 小數(shù)組或者已經(jīng)有序的數(shù)組
低位優(yōu)先的字符串排序(LSD) 穩(wěn)定 O(KN) O(N) 較短的定長字符串
高位優(yōu)先的字符串排序(MSD) 穩(wěn)定 O(N) ~ O(KN) O(N+K*BUDGET) 隨機(jī)字符串
三向字符從快速排序 不穩(wěn)定 O(N) ~ O(KN) O(K+logN) 通用字符串排序算法,特別適用于含有較長公共前綴的字符串

總結(jié)

算法 穩(wěn)定性 時(shí)間復(fù)雜度 空間復(fù)雜度
桶排序 取決于桶內(nèi)排序算法 O(N)~O(NlogN) O(N)
計(jì)數(shù)排序 穩(wěn)定 O(N) O(N)
基數(shù)排序 穩(wěn)定 O(N)~O(KN) O(N)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驾胆,一起剝皮案震驚了整個(gè)濱河市涣澡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丧诺,老刑警劉巖入桂,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異驳阎,居然都是意外死亡抗愁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門呵晚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜘腌,“玉大人,你說我怎么就攤上這事饵隙〈橹椋” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵癞季,是天一觀的道長劫瞳。 經(jīng)常有香客問我,道長绷柒,這世上最難降的妖魔是什么志于? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮废睦,結(jié)果婚禮上伺绽,老公的妹妹穿的比我還像新娘。我一直安慰自己嗜湃,他們只是感情好奈应,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著购披,像睡著了一般杖挣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刚陡,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天惩妇,我揣著相機(jī)與錄音株汉,去河邊找鬼。 笑死歌殃,一個(gè)胖子當(dāng)著我的面吹牛乔妈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氓皱,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼路召,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了波材?” 一聲冷哼從身側(cè)響起股淡,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎各聘,沒想到半個(gè)月后揣非,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躲因,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年早敬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片大脉。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搞监,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出镰矿,到底是詐尸還是另有隱情琐驴,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布秤标,位于F島的核電站绝淡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏苍姜。R本人自食惡果不足惜牢酵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衙猪。 院中可真熱鬧馍乙,春花似錦、人聲如沸垫释。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棵譬。三九已至显蝌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間订咸,已是汗流浹背曼尊。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工扭屁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涩禀。 一個(gè)月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像然眼,于是被迫代替她去往敵國和親艾船。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

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