數(shù)據(jù)結(jié)構(gòu)與算法--查找之順序查找和二分查找

數(shù)據(jù)結(jié)構(gòu)與算法--查找之順序查找和二分查找

符號(hào)表的目的是將一個(gè)鍵和一個(gè)值關(guān)聯(lián)起來介汹,可以將一對鍵值對插入到符號(hào)表中仰担,也可以根據(jù)給定的鍵從符號(hào)表所有的鍵值對中快速或者直接找到相對應(yīng)的值咱娶。對于鍵和值躯肌,我們作如下約定,以后的實(shí)現(xiàn)都遵循這些約定炉擅。

  • 每個(gè)鍵只對應(yīng)一個(gè)值辉懒,也就是說不允許存在重復(fù)的鍵
  • 新插入的鍵值對在表中鍵已經(jīng)存在,此時(shí)稱為沖突谍失,讓新的值取代舊的值
  • 鍵不能為空null眶俩,因?yàn)樵诒容^中會(huì)調(diào)用鍵的equals或者compareTo方法;若為空會(huì)造成空指針異常
  • 值不能為空null快鱼,因?yàn)樵谖覀兊膶?shí)現(xiàn)中颠印,當(dāng)查找一個(gè)不存在的鍵時(shí),默認(rèn)返回null抹竹。因此我們可以用get(Key key)判斷某個(gè)鍵在表中是否存在线罕;也可以用put(Key key, null)將某鍵值對刪除(延時(shí)實(shí)現(xiàn))。

無序鏈表的順序查找

順序查找也就是遍歷表中元素窃判,鏈表實(shí)現(xiàn)即每個(gè)結(jié)點(diǎn)都表示一個(gè)鍵值對钞楼。如get(Key key)方法,查找表中所有鍵值對袄琳,如果匹配成功就返回相應(yīng)的值询件,否則返回null;put(Key key, Value value)方法也是查找表中所有鍵值對唆樊,如果鍵已經(jīng)存在就更新值宛琅,否則在鏈表頭插入這個(gè)新的鍵值對。實(shí)現(xiàn)起來很簡單窗轩,我們來看代碼。

package Chap8;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

public class SequentialST<Key, Value> {
    private Node first;
    private int N;

    private class Node {
        Key key;
        Value value;
        Node next;

        public Node(Key key, Value value, Node next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    public Value get(Key key) {
        for (Node cur = first; cur != null; cur = cur.next) {
            if (key.equals(cur.key)) {
                return cur.value;
            }
        }
        return null;
    }
    // 若是鍵不存在座咆,則返回一個(gè)指定的默認(rèn)值
    public Value getDefault(Key key, Value value) {
        for (Node cur = first; cur != null; cur = cur.next) {
            if (key.equals(cur.key)) {
                return cur.value;
            }
        }
        return value;
    }

    public void put(Key key, Value value) {
        // 如果傳入null痢艺,就是刪除鍵值對
        if (value == null) {
            delete(key);
            return;
        }

        for (Node cur = first; cur != null; cur = cur.next) {
            if (key.equals(cur.key)) {
                cur.value = value;
                return;
            }
        }
        /*  新鍵值對。new Node的next指向first介陶,然后取代它成為新的first, 是以下代碼的簡化版

            Node oldfirst = first;
            first = new Node();
            first.next = oldfirst;
        */
        first = new Node(key, value, first);
        N++;
    }
// 未采用的延時(shí)刪除堤舒,使用下面的delete
//    public void delete(Key key) {
//        put(key, null);
//    }

    public Value delete(Key key) {
        if (isEmpty()) {
            return null;
        }

        Node cur = first;
        Value value = null;
        // 刪除的鍵值對如果在鏈表頭,處理方式不一樣
        if (key.equals(cur.key)) {
            value = cur.value;
            Node next = cur.next;
            // 下面三行是幫助垃圾回收
            cur.key = null;
            cur.value = null;
            cur.next = null;
            first = next;
            N--;
        } else {
            // 現(xiàn)在pre是first哺呜,而cur是first的下一個(gè)結(jié)點(diǎn)
            Node pre = cur;
            cur = cur.next;

            while (cur != null) {
                if (key.equals(cur.key)) {
                    value = cur.value;
                    Node next = cur.next;
                    // 下面三行是幫助垃圾回收
                    cur.key = null;
                    cur.value = null;
                    cur.next = null;
                    pre.next = next;
                    N--;
                    return value;
                }
                // 下輪比較指向下一個(gè)結(jié)點(diǎn)舌缤,所以更新pre和cur
                pre = cur;
                cur = cur.next;
            }
        }
        return value;
    }

    public Set<Key> keys() {
        // 保證和values是一樣的順序
        Set<Key> keys = new LinkedHashSet<>();
        for (Node cur = first;cur != null;cur = cur.next) {
            keys.add(cur.key);
        }
        return keys;
    }

    public Collection<Value> values() {
        Collection<Value> values = new ArrayList<>();
        for (Node cur = first;cur != null;cur = cur.next) {
            values.add(cur.value);
        }
        return values;
    }

    @Override
    public String toString() {
        if (isEmpty()) {
            return "{}";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("{");
        Node cur = first;

        while (true) {
            sb.append(cur.key).append("=").append(get(cur.key));
            if (cur.next == null) {
                return sb.append("}").toString();
            } else {
                sb.append(", ");
            }
            cur = cur.next;
        }
    }
    public boolean contains(Key key) {
        return get(key) != null;
    }

    public int size() {
        return N;
    }

    public boolean isEmpty() {
        return N == 0;
    }

    public static void main(String[] args) {
        SequentialST<String, Integer> st = new SequentialST<>();
        st.put("admin", 8888);
        st.put("password", 123456);
        st.put("pcNumber", 5);
        st.put("money", 6666);
        Integer password = st.delete("password");
        st.delete("money");
        System.out.println(password);

        System.out.println(st.get("pcNumber"));
        System.out.println(st.get("admin"));
        System.out.println(st.keys());
        System.out.println(st.values());
        System.out.println(st);
    }
}

get方法沒什么好說的,有一個(gè)getDefault方法,當(dāng)給出的鍵不存在時(shí)国撵,將返回一個(gè)由調(diào)用者自定義的默認(rèn)值陵吸。put方法中,如果值傳入null介牙,相當(dāng)于是刪除該鍵值對壮虫,所以調(diào)用了delete。否則遍歷鏈表环础,如果查找到鍵已經(jīng)存在囚似,則用新的value取代舊的value;如果沒找到线得,說明這是新的鍵值對饶唤,直接插入到鏈表頭(相當(dāng)于push操作)。

重點(diǎn)看delete方法贯钩,我們并不打算用延時(shí)實(shí)現(xiàn)——雖然它相當(dāng)簡單募狂。如下

public void delete(Key key) {
    put(key, null);
}

注意:如果使用上述實(shí)現(xiàn),put方法頭幾行判斷就得去掉魏保,否則兩個(gè)方法會(huì)互相調(diào)用導(dǎo)致StackOverflow熬尺。我們的實(shí)現(xiàn)是即時(shí)刪除的,其實(shí)就是鏈表刪除結(jié)點(diǎn)的操作谓罗。即時(shí)delete中粱哼,需要一個(gè)Node pre指針,用于指向當(dāng)前結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)檩咱,如果你對鏈表結(jié)點(diǎn)的刪除操作熟悉的話,應(yīng)該清楚為什么需要這個(gè)pre指針。鏈表頭的刪除和其他地方結(jié)點(diǎn)的刪除操作還有些不一樣:刪除鏈表頭只需first.next成為新的first霸褒;其他位置的刪除抖誉,刪除的是結(jié)點(diǎn)cur耿币,需要將cur的前一個(gè)結(jié)點(diǎn)pre的next指向cur的next塑悼,即pre.next = cur.next

keysvalues方法可以返回符號(hào)表中所有的鍵值對巷屿,鍵是唯一的所以用了Set存放旬昭。

在含有N對鍵值對的無序鏈表中绪杏,未命中和插入新鍵值對的比較次數(shù)均為N腔彰,命中的最壞情況為比較N次(最后一個(gè)結(jié)點(diǎn)才匹配成功)辖佣。

有序數(shù)組中的二分查找

上面鏈表實(shí)現(xiàn)的符號(hào)表中霹抛,表是無序的。如果能在插入過程中保證表一直有序卷谈,在查找的時(shí)候就不必順序查找杯拐,使用二分查找可大大提高查找的效率。我們需要兩個(gè)數(shù)組世蔗,Key[]Value[]分別存儲(chǔ)鍵和值端逼。為了保證插入過程中表一直有序,對標(biāo)準(zhǔn)的二分查找稍作修改污淋,如下

public int rank(Key key) {
    int low = 0;
    int high = N - 1;
    while (low <= high) {
        int mid = low + (high - low) / 2;
        int cmp = key.compareTo(keys[mid]);
        if (cmp < 0) {
            high = mid - 1;
        } else if (cmp > 0) {
            low = mid + 1;
        } else {
            return mid;
        }
    }
    return low;
}

倒數(shù)第二行顶滩,未命中時(shí)原來返回-1,現(xiàn)在返回low——恰好是小于參數(shù)Key的鍵的數(shù)量寸爆,說得直白些就是在key之前的鍵的數(shù)量礁鲁。

插一句,二分查找的思想:首先查找的數(shù)組必須是有序的赁豆。先將被查找的鍵與數(shù)組中的中間鍵比較(如果表長度為偶數(shù)則會(huì)向下取整)仅醇,若小于中間的鍵就在中間鍵的左邊子數(shù)組繼續(xù)查找然遏;若大于中間的鍵就在中間鍵的右邊子數(shù)組中繼續(xù)查找杖们;否則就是查找成功了默怨。如此迭代捺萌,直到high > low終止农猬。

查找成功時(shí)返回的是mid闸迷,說明該鍵在數(shù)組中的位置就是keys[mid]教沾,小于該key的鍵的數(shù)量就是mid沾瓦,這個(gè)很好理解心铃。但為什么查找失敗最后high > low迭代終止時(shí)准谚,返回low就是小于key的鍵的數(shù)量呢?

因?yàn)椴檎沂∏埃?strong>最后查找范圍一定是只有一個(gè)元素了去扣,此時(shí)low = high = mid柱衔, 最后一次比較后發(fā)現(xiàn)也不相等,它要么比mid大愉棱,要么比mid小唆铐。如果它比mid小說明它應(yīng)該在mid的前一個(gè)位置,比它小的鍵的數(shù)量和比mid小的數(shù)量是一樣的奔滑,再看代碼中:if分支low沒有改變艾岂,返回的low和mid值一樣;如果它比mid大說明它應(yīng)該在mid的后一個(gè)位置朋其,比它小的鍵的數(shù)量應(yīng)該比小于mid的鍵的數(shù)量多1(比mid大王浴,所以把mid也算進(jìn)去)脆炎,再看代碼中:else分支low變成了mid + 1,最后返回的low實(shí)際也是mid + 1氓辣。由此驗(yàn)證了秒裕,這樣實(shí)現(xiàn)的二分查找可以返回?cái)?shù)組中小于給定key的鍵的數(shù)量!

看圖加深理解:先看查找成功時(shí)钞啸,對P查找几蜻。最后一步查找,mid=6体斩,此時(shí)命中并退出梭稚。表示P在keys[]中的位置是6,同時(shí)也表示小于P的鍵的數(shù)量為6絮吵;再看查找Q時(shí)的查找失敗哨毁。最后一步查找時(shí),和查找P一樣low = high = mid = 6源武,只不過這次并不是返回mid扼褪,查找的Q大于mid位置的P,會(huì)執(zhí)行else if分支粱栖,Q的位置應(yīng)該在圖中第二個(gè)紅色箭頭處话浇,返回的low實(shí)際上等于mid + 1 = 7,結(jié)合圖中闹究,Q的位置之前確實(shí)有7個(gè)鍵幔崖。

上述rank方法是全部實(shí)現(xiàn)的核心,現(xiàn)在給出全部代碼渣淤,再解釋其中一些方法赏寇。

package Chap8;

import java.util.*;

public class BinarySearchST<Key extends Comparable<Key>, Value> {
    private Key[] keys = (Key[]) new Comparable[1];
    private Value[] values = (Value[]) new Object[1];
    private int N;

    public int rank(Key key) {
        int low = 0;
        int high = N - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            int cmp = key.compareTo(keys[mid]);
            if (cmp < 0) {
                high = mid - 1;
            } else if (cmp > 0) {
                low = mid + 1;
            } else {
                return mid;
            }
        }
        return low;
    }

    public void put(Key key, Value value) {
        // 如果傳入null,就是刪除鍵值對
        if (value == null) {
            delete(key);
            return;
        }

        // 如果容量滿了价认,增容
        if (N == keys.length) {
            resize(2 * keys.length);
        }

        int i = rank(key);
        // 鍵已經(jīng)存在嗅定,新值取代舊值
        if (i < N && keys[i].compareTo(key) == 0) {
            values[i] = value;
            return;
        }
        // 否則插入新的鍵值對,i及之后的元素都需要后移一個(gè)位置用踩,騰出位置i給新的鍵值對使用
        for (int j = N; j > i; j--) {
            keys[j] = keys[j - 1];
            values[j] = values[j - 1];
        }
        keys[i] = key;
        values[i] = value;
        N++;
    }

    public Value get(Key key) {
        if (isEmpty()) {
            return null;
        }
        int i = rank(key);
        if (i < N && keys[i].compareTo(key) == 0) {
            return values[i];
        } else {
            return null;
        }
    }

    public Value getDefault(Key key, Value defaultValue) {
        if (get(key) == null) {
            return defaultValue;
        } else {
            return get(key);
        }
    }

    private void resize(int max) {
        Key[] tempKeys = (Key[]) new Comparable[max];
        Value[] tempValues = (Value[]) new Object[max];
        for (int i = 0; i < N; i++) {
            tempKeys[i] = keys[i];
            tempValues[i] = values[i];
        }
        keys = tempKeys;
        values = tempValues;
    }


    public boolean isEmpty() {
        return N == 0;
    }

    public int size() {
        return N;
    }

    public int size(Key low, Key high) {
        if (high.compareTo(low) < 0) {
            return 0;
        } else if (contains(high)) {
            return rank(high) - rank(low) + 1;
        } else {
            return rank(high) - rank(low);
        }
    }

    public boolean contains(Key key) {
        return get(key) != null;
    }

    public Key min() {
        return keys[0];
    }

    public Key max() {
        return keys[N - 1];
    }

    public Value deleteMin() {
        return delete(min());

    }

    public Value delete(Key key) {
        int i = rank(key);
        Value value = null;

        if (keys[i].compareTo(key) == 0) {
            value = values[i];
            for (int j = i; j < N - 1; j++) {
                keys[j] = keys[j + 1];
                values[j] = values[j + 1];
            }
            // 防止對象游離
            keys[N - 1] = null;
            values[N - 1] = null;
            N--;
            // 如果只用了總?cè)萘康乃姆种磺耍s減容量一半
            if (N > 0 && N == keys.length / 4) {
                resize(keys.length / 2);
            }
        }

        return value;
    }

    public Value deleteMax() {
        return delete(max());
    }

    // k = rank(select(k))
    // key = select(rank(key)
    public Key select(int k) {
        return keys[k];
    }

    public Set<Key> keys() {
        return keys(min(), max());
    }

    public Collection<Value> values() {
        return values(min(), max());
    }

    public Collection<Value> values(Key low, Key high) {
        Collection<Value> q = new ArrayList<>();
        for (int j = rank(low); j < rank(high); j++) {
            q.add(values[j]);
        }
        if (contains(high)) {
            q.add(values[rank(high)]);
        }
        return q;
    }

    public Set<Key> keys(Key low, Key high) {
        // 保持原來的順序,使用LinkedHashSet
        Set<Key> q = new LinkedHashSet<>();
        for (int j = rank(low); j < rank(high); j++) {
            q.add(keys[j]);
        }
        if (contains(high)) {
            q.add(keys[rank(high)]);
        }
        return q;
    }

    // 大于等于key的最小鍵脐彩,如果key在表中就是等于key碎乃;否則是大于key的最小鍵,即i的下一個(gè)位置的鍵
    public Key ceiling(Key key) {
        int i = rank(key);
        // i可能等于N惠奸,此時(shí)返回null梅誓,也符合
        return keys[i];
    }

    // 小于等于key的最大鍵
    public Key floor(Key key) {
        int i = rank(key);
        if (contains(key)) {
            return keys[i];
            // 考慮負(fù)數(shù)腳標(biāo)的情況,i == 0會(huì)造成keys[-1]
        } else if (!contains(key) && i != 0) {
            return keys[i - 1];
        } else {
            // 表中不沒有鍵key且i == 0,說明key是表中最小的梗掰,不存在比它還小的所以返回null
            return null;
        }
    }

    @Override
    public String toString() {
        if (isEmpty()) {
            return "{}";
        }
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        int i = 0;
        while (true) {
            sb.append(keys[i]).append("=").append(values[i]);
            if (i == N - 1) {
                return sb.append("}").toString();
            } else {
                sb.append(", ");
            }
            i++;
        }
    }

    public static void main(String[] args) {
        BinarySearchST<Integer, Double> st = new BinarySearchST<>();
        st.put(1, 5567.5);
        st.put(5, 10000.0);
        st.put(3, 4535.5);
        st.put(7, 7000.0);
        st.put(12, 2500.0);
        st.put(10, 4500.0);
        st.put(17, 15000.5);
        st.put(15, 12000.5);
        st.deleteMax(); // 17
        st.deleteMin(); // 1
        st.delete(12); // 剩下[3, 5, 7, 10, 15]

        System.out.println("符號(hào)表的長度為" + st.size());
        System.out.println("[3, 6]之間有" + st.size(3, 6) + "個(gè)鍵");
        System.out.println("比9小的鍵的數(shù)量為" + st.rank(9));
        System.out.println("排在第4位置的鍵為" + st.select(4));
        System.out.println("大于等于8的最小鍵為" + st.ceiling(8));
        System.out.println("小于等于8的最大鍵為" + st.floor(8));

        System.out.println("符號(hào)表所有的鍵和對應(yīng)的值為:" + st.keys() + " -> " + st.values());
        System.out.println("鍵2和鍵8之間的所有鍵及對應(yīng)的值:" + st.keys(2, 8) + " -> " + st.values(2, 8));

        System.out.println(st);

    }

}

首先這個(gè)符號(hào)表是容量自適應(yīng)的删豺,實(shí)現(xiàn)見resize方法。這意味著我們不用擔(dān)心數(shù)組腳標(biāo)越界愧怜。思路是:當(dāng)鍵值對個(gè)數(shù)等于數(shù)組長度時(shí)候,將數(shù)組長度擴(kuò)充到原來的兩倍妈拌;當(dāng)鍵值對個(gè)數(shù)過少拥坛,等于數(shù)組長度的四分之一時(shí),將數(shù)組長度減小到原來的一半尘分。

然后是很重要的方法get / put猜惋,put之前需要判斷是否傳入了null值,以及是否需要增容培愁,之后使用int i = rank(key)方法定位key的位置著摔,如果key在符號(hào)表中存在,會(huì)用新的值取代舊的值定续;否則在位置i處插入新的鍵值對谍咆,為此需要將i及其之后的元素都往后移動(dòng)一個(gè)位置。get方法就很簡單了私股,首先要判斷是不是空表摹察,因?yàn)橄旅嬲{(diào)用了keys[i].compareTo(key),如果是空表倡鲸,則keys[i]是null供嚎,調(diào)用compareTo會(huì)出現(xiàn)異常。int i = rank(key)返回的i最小就是0了峭状,所以if里只需判斷i < N以防止keys[i]越界克滴。

delete方法其實(shí)就是刪除數(shù)組中某個(gè)元素,用int i = rank(key)快速定位到key的位置(如果key不在符號(hào)表中优床,刪除失敗返回null)劝赔,i之后的元素都往前移動(dòng)一個(gè)位置,最后原數(shù)組的最后一個(gè)鍵值對keys[N -1]values[N -1]需要置空(因?yàn)樗鼈儸F(xiàn)在都往前移動(dòng)一個(gè)位置了胆敞,這個(gè)位置已經(jīng)沒有作用了)望忆,隨之N減少1表示刪除一個(gè)鍵值對成功。

min / max返回最小的鍵和最大的鍵竿秆,由于鍵數(shù)組本身有序启摄,keys[0]keys[N - 1]就分別對應(yīng)著最小 / 最大鍵。

deleteMin / deleteMax幽钢,就是刪除最小 / 最大的鍵并返回對應(yīng)的值歉备。

select(int k)返回位置排在k的鍵。注意匪燕,細(xì)心觀察可以發(fā)現(xiàn)select和rank之間滿足如下關(guān)系蕾羊。

k = rank(select(k))
key = select(rank(key)

floor(Key key)返回小于等于key的最大鍵喧笔。還是先用int i = rank(key)定位key的位置,如果key在符號(hào)表中龟再,直接返回keys[i]书闸;否則,我們返回的應(yīng)該是keys[i - 1]利凑,這個(gè)在紙上畫畫就能理解了浆劲。有點(diǎn)要注意,如果rank(key)返回的是0哀澈,那么使用keys[i - 1]就會(huì)異常牌借,所以對i == 0單獨(dú)判斷下,此時(shí)應(yīng)該返回null割按,因?yàn)楸碇腥我怄I都沒有給出的key要小膨报。

ceiling(Key key)返回大于等于key的最小鍵,同樣如果key在符號(hào)表中适荣,直接返回keys[i]现柠;如果不在符號(hào)表中,還是應(yīng)該返回keys[i](紙上畫畫吧)弛矛。

最后我們來看看比較有意思的size(Key low, Key high) / keys(Key low, Key high) 晒旅,還有個(gè)values(Key low, Key high)原理和keys一樣,這里就說前述兩個(gè)汪诉。這些方法都有判斷Key high在不在符號(hào)表中废恋,這有影響嗎?我們結(jié)合幾個(gè)圖來看看扒寄。

無非是下面4種情況鱼鼓。

可見當(dāng)Key high存在于符號(hào)表中時(shí),計(jì)算size需要進(jìn)行+1操作该编,將high這個(gè)鍵也算進(jìn)去迄本;計(jì)算keys的時(shí)候也要將Key high算進(jìn)去。

由于是數(shù)組實(shí)現(xiàn)的课竣,插入和刪除都是相當(dāng)麻煩的嘉赎,不過查找效率很高。上面鏈表實(shí)現(xiàn)的順序查找于樟,在插入和刪除上有優(yōu)勢公条,但是查找效率挺低的。有沒有辦法整合這兩者的優(yōu)點(diǎn)呢迂曲?這就是接下來要學(xué)習(xí)的二叉查找樹(BST)靶橱。


by @sunhaiyu

2017.10.13

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子关霸,更是在濱河造成了極大的恐慌传黄,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件队寇,死亡現(xiàn)場離奇詭異膘掰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)佳遣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門识埋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苍日,你說我怎么就攤上這事〈吧” “怎么了相恃?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長笨觅。 經(jīng)常有香客問我拦耐,道長,這世上最難降的妖魔是什么见剩? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任杀糯,我火速辦了婚禮,結(jié)果婚禮上苍苞,老公的妹妹穿的比我還像新娘固翰。我一直安慰自己,他們只是感情好羹呵,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布骂际。 她就那樣靜靜地躺著,像睡著了一般冈欢。 火紅的嫁衣襯著肌膚如雪歉铝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天凑耻,我揣著相機(jī)與錄音太示,去河邊找鬼。 笑死香浩,一個(gè)胖子當(dāng)著我的面吹牛类缤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播邻吭,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼呀非,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岸裙,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤猖败,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后降允,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恩闻,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年剧董,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幢尚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翅楼,死狀恐怖尉剩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毅臊,我是刑警寧澤理茎,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站管嬉,受9級特大地震影響皂林,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚯撩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一础倍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胎挎,春花似錦沟启、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至项栏,卻和暖如春浦辨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沼沈。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工流酬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人列另。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓芽腾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親页衙。 傳聞我的和親對象是個(gè)殘疾皇子摊滔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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