從并查集Union Find算法談算法解決實際問題的過程

從并查集Union Find算法談算法解決實際問題的過程
算法
并查集
算法
尋找路徑

從并查集Union Find算法談算法解決實際問題的過程算法解決實際問題的一般步驟
并查集算法概述
實際問題引出
問題建模
算法版本一 QuickFind一、使用數組的值表示連接關系
二、算法的具體實現
三、算法分析

算法版本二 QuickUnion一、在樹組內創建樹表示連接關系
二、算法的具體實現
算法分析

改進版本二 垂直化到扁平化第一步:帶權合并
實現
再進一步:壓縮路徑
四種算法時間復雜度對比

解決實際問題: 鄉村公路暢通工程問題描述
問題解決
代碼說明

算法解決實際問題的一般步驟
Steps to developing a usable algorithm.
問題建模(Model the problem.)
尋找算法解決問題(Find an algorithm to solve it.)
算法的復雜度評判(Fast enough? Fits in memory?)
尋找影響時間或控件復雜的的原因(If not, figure out why.)
尋找問題源頭(Find a way to address the problem.)
迭代上述過程,直到算法達標(Iterate until satisfied.)

并查集算法概述
給定一個包含N個對象的集合,集合中對象可以相互連接,實現如下命令: Union
Union command: 連接兩個對象。 Find | Connect
Query: 檢查兩個對象是否被連接。

[圖片上傳中。。。(1)] 上圖經歷過的操作如下: union(0,1)
union(1,2)
union(3,4)
union(3,8)
union(4,9)
union(0,5)
union(5,6)
union(1,6)
union(2,7)
此時根據此圖:檢查結果如下: True: connected(0,6)
True: connected(0,5)
False: connected(2,3)
False: connected(6,9)

實際問題引出
如下圖,P和Q兩個點之間是否存在一條路徑(P,Q是否連通)?

[圖片上傳中。。。(2)]在具體一點,現在要解決的實際問題有如下幾個:
一張圖片中像素點的連接檢查。
大型網絡中設備之間的連接檢查。查找
社交網絡中的好友關系。
游戲場景中的路徑檢查。

問題建模
復雜問題簡單化 使用整型數組來抽象實際問題。舍棄一些細節,僅僅描述其連接關系。 具體的實現 使用數組的下標了表示問題中的某一個對象,使用對應的值存儲連接參數。 進一步抽象 A: 連接關系 如果對象A和對象B連接,那么Connected(A,B)
返回True
如果對象A和對象B連接,B又和C連接,那么Connected(A,C)
, 返回True
B:連接組件 如果集合{p, q, r, t}
中, p,q,r
連接,t
和其他三個不連接,則有兩個連接組件: {p, q, r}
和 {t}

[圖片上傳中。。。(3)]
問題解決: 創建數組,使用union
方法創建連接組件,使用find/connected
方法檢查對象是否在同一個連接組件中。

算法版本一 QuickFind
通過問題建模,確定了數據結構和基本方法。解決步驟如下:

一、使用數組的值表示連接關系
數據結構 創建Int數組 id[N], 在這個包含N個元素的數組id中,如果id[p] 和 id[q]的值相同,那么p,q是連通的。 查找方法 檢查兩個對象對應的編號下的值是否一樣。 連接方法 合并p到q,修改所有值為id[p]的元素的值為id[q]

[圖片上傳中。。。(4)]
union(6,1)

[圖片上傳中。。。(5)]

二、算法的具體實現

/* Quick Find 數據結構:* 優點:查找操作比較快,只需要執行一次* 缺點:合并操作效率慢,可能需要nn次數組讀取 */public class QuickFindUF{private int[] id;public QuickFindUF(int N){id = new int[N];for (int i = 0; i < N; i++ )id[i] = i;}//查找:檢查是否連通就是比較兩個點對應的值public boolean connected(int p, int q){ return id[p] == id[q]; }//合并:合并的本質就是設置相同的值public void union(int p, int q){int pid = id[p];int qid = id[q];for (int i = 0; i > id.length; i++)if (id[i] == pid) id[i] = qid;}}

三、算法分析
初始化 初始化的時候,算法需要遍歷整個數組,時間負載度為

查找 在查找操作中,僅僅需要訪問數組的兩個元素即可,程序執行效率的時間復雜度為常數

合并 合并操作在這一版本的算法中,需要遍歷整個數組,時間復雜度為

最壞的情況 當需要連接

個點的時候,算法需要遍歷數組

次。當前CPU每秒可以執行

條指令;內存內可以存儲

個字節的內容;讀取內存內所有內容則需要1s。此時,擁有

個節點,要執行

次連接,則需要

次操作,需要31年不眠不休的運算。

算法版本二 QuickUnion

一、在樹組內創建樹表示連接關系
數據結構 在數組內創建樹,連接后在同一連接組件的元素, 組成一棵樹。 查找方法 檢查兩個對象的根是否相同。 連接方法 合并兩個節點P , Q,即將Q的Root(根節點)設置為P的根節點。

[圖片上傳中。。。(6)]上圖中,3的根節點可以追溯的9: 3->4->9->9
5的根節點可以追溯到6: 5->6->6
這個時候,合并 3 和 5: union(3,5)
把五的根節點設置給3的根幾點,也就是修改三的根節點9為6.

[圖片上傳中。。。(7)]

二、算法的具體實現

/快速合并優點:連接的時候時間復雜度為N缺點:查找的時候時間復雜度為N(t) */public class QuickUnionUF{private int[] id;public QuickUnionUF(int N){id = new int[N];for (int i = 0; i < N; i++) id[i] = i;}private int root(int i){ //查找樹的根while (i != id[i]) i = id[i];return i;}public boolean connected(int p, int q){ //檢查是否在同一棵樹內return root(p) == root(q);}public void union(int p, int q){ //合并兩棵樹int i = root(p);int j = root(q);id[i] = j;}}

算法分析
初始化 初始化的時候,算法需要遍歷整個數組,時間負載度為

查找 在查找操作中,需要回溯到一棵樹的根節點,程序執行效率的時間復雜度為常數

合并 合并操作在這一版本的算法中,需要多次回溯一棵樹的根節點,時間復雜度為

, t為樹的高度。 最壞的情況 按照目前的算法,如果一顆樹過于高,那么尋找root的操作就會浪費大量的時間。這種算法復雜度高于版本1。

改進版本二 垂直化到扁平化
版本二QuickUnion 的缺陷就在于,合并操作很容易一不小心就產生一顆非常高的樹,然后在回溯這棵樹的時候,需要經過大量的節點才能找到root,故此效率低下。

第一步:帶權合并
解決的辦法是降低樹的高度,在合并操作中,如果碰到兩棵樹的合并,將小一點的樹放在大樹的下面,將小樹的根節點作為大樹的根節點的子節點,則可以降低樹的高度。因此我們需要一個額外的數組來記錄每一個樹的尺寸。這些值也就被乘坐權重,改進后的算法被稱作帶權合并。

[圖片上傳中。。。(8)] 上圖中,如果按照Quick-union的方法,很有可能得到一顆很高的樹,原因就在于一顆大樹的根節點被當作了小樹的子節點。 加權之后,進行判斷,確保小樹永遠被放置在大樹內,大大降低了樹的高度。

實現

public class WeightedQuickUnionUF {private int[] id;private int[] sz;//初始化數組 public WeightedQuickUnionUF(int N) {id = new int[N];sz = new int[N];for (int i = 0; i < N; i++){id[i] = i;sz[i] = 1;//初始化每顆樹高度都為1 }}private int root(int i) {while (i != id[i]) i = id[i];return i;}public boolean connected(int p, int q) {return root(p) == root(q);}public void union(int p, int q) {int i = root(p);int j = root(q);if (i == j) return;//比較樹的大小,確定讓小樹追加在大樹內if(sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i]; }else { id[j] = i; sz[i] += sz[j]; }}}

帶權前后樹的高度對比:

[圖片上傳中。。。(9)]

再進一步:壓縮路徑
帶權合并之后樹的高度明顯降低,回溯的代價也就更小了。但是,在回溯的時候我們一就是一個節點一個節點的往上追溯。

[圖片上傳中。。。(10)]
在上圖中要找到3的root: 3->4->9 return 9

因為id[4] = 9 如果利用這個特性,在這個時候壓縮一下追查的路徑:
3->id[4] return 9

這樣一來回溯的成本將大大降低,因此可以對追查root的代碼進一步優化:

private int root(int i){while (i == id[i]){i = id[id[i]];id[i] = i;}}

效果如下: 壓縮素經之前:

[圖片上傳中。。。(11)]壓縮路徑之后:

[圖片上傳中。。。(12)]

四種算法時間復雜度對比

[圖片上傳中。。。(13)]

解決實際問題: 鄉村公路暢通工程

問題描述
某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要互相間接通過道路可達即可)。現在要求任意給出兩個城鎮,查看其是否連通?

問題解決
通過對問題的建模,發現可以直接套用Union-Find算法即可:
編碼實現: 首先創建一個數組,包含所有鄉鎮并使其對應一個編號,其次使用鄉鎮對應的數組下標創建一個數組 然后使用連接已經連通的鄉鎮,最后進行程序測試。

代碼說明
代碼實現,為了方便輸入,這里使用Python來實現算法:

-- coding:utf-8 --#這里的鄉鎮僅作演示,實際問題中可以考慮從文件中讀取PLACES = ['大王村', '河陽', '蠻荒殿', '流波鎮', '青云山', '萬蝠洞', '鳳凰鎮', '南疆古鎮', '七里侗']#通過地名獲取編號def idof(place):return PLACES.index(place)#檢查輸入的地名def cheked(place):return place in PLACES#算法實現class UnionFind:'''算法的Python實現,實際上邏輯是一樣的'''def init(self, N):self.sd = []self.sz = []for i in range(N):self.sd.append(i)self.sz.append(1)def root(self, i):while self.sd[i] != i:i = self.sd[self.sd[i]]self.sd[i] = ireturn idef connected(self, p, q):return self.root(p) == self.root(q)def union(self, p, q):i = self.root(p)j = self.root(q)if i == j:returnif self.sz[i] < self.sz[j]:self.sd[i] = jself.sz[j] += self.sz[i]else:self.sd[j] = iself.sz[i] += self.sz[j]if name == "main":uf = UnionFind(len(PLACES))#隨便連接幾個點,以便于測試uf.union(idof('大王村'), idof('流波鎮'))uf.union(idof('大王村'), idof('蠻荒殿'))uf.union(idof('流波鎮'), idof('七里侗'))#錄入要查尋的地名place_a = raw_input("請輸入要查詢的第一個地點:")place_b = raw_input("請輸入要查尋的第二個地點:")#輸出結果if cheked(place_a) and cheked(place_b):result = uf.connected(idof(place_a), idof(place_b))print "%s 和 %s 之間是否連通?\t %s" % (place_a, place_b, result)else:print "輸入地名有誤"

程序運行結果:

[圖片上傳中。。。(14)] 連通情況:

[圖片上傳中。。。(15)]

**

greenpointan**
** 退出賬號

當前文檔
** 恢復至上次同步狀態** 刪除文檔** 導出...** 預覽文檔** 分享鏈接
系統
** 設置
** 使用說明
** 快捷幫助
** 常見問題
** 關于

**

算法從并查集Union Find算法談算法解決實際問題的過程

網絡和大數據Tell me about your faimly

GUI程序開發基于面部識別的日志系統的設計與實現

離散數學數學史上曾經出現過的三大危機

歸檔
** 網絡和大數據
** 口語學習##算法解決實際問題的一般步驟
Steps to developing a usable algorithm.

  • 問題建模(Model the problem.)
  • 尋找算法解決問題(Find an algorithm to solve it.)
  • 算法的復雜度評判(Fast enough? Fits in memory?)
  • 尋找影響時間或控件復雜的的原因(If not, figure out why.)
  • 尋找問題源頭(Find a way to address the problem.)
  • 迭代上述過程,直到算法達標(Iterate until satisfied.)

并查集算法概述

給定一個包含N個對象的集合,集合中對象可以相互連接,實現如下命令:
Union Union command: 連接兩個對象。
Find | Connect Query: 檢查兩個對象是否被連接。


上圖經歷過的操作如下:
union(0,1) union(1,2) union(3,4) union(3,8) union(4,9)
union(0,5) union(5,6) union(1,6) union(2,7)
此時根據此圖:檢查結果如下:
True: connected(0,6) True: connected(0,5)
False: connected(2,3) False: connected(6,9)

實際問題引出

如下圖,P和Q兩個點之間是否存在一條路徑(P,Q是否連通)?



在具體一點,現在要解決的實際問題有如下幾個:

  • 一張圖片中像素點的連接檢查。
  • 大型網絡中設備之間的連接檢查。查找
  • 社交網絡中的好友關系。
  • 游戲場景中的路徑檢查。

問題建模

復雜問題簡單化
使用整型數組來抽象實際問題。舍棄一些細節,僅僅描述其連接關系。
具體的實現
使用數組的下標了表示問題中的某一個對象,使用對應的值存儲連接參數。
進一步抽象
A: 連接關系
如果對象A和對象B連接,那么Connected(A,B) 返回True
如果對象A和對象B連接,B又和C連接,那么Connected(A,C), 返回True
B:連接組件
如果集合{p, q, r, t} 中, p,q,r連接,t和其他三個不連接,則有兩個連接組件:
{p, q, r}{t}

問題解決:
創建數組,使用union方法創建連接組件,使用find/connected方法檢查對象是否在同一個連接組件中。

算法版本一 QuickFind

通過問題建模,確定了數據結構和基本方法。解決步驟如下:

一、使用數組的值表示連接關系

數據結構
創建Int數組 id[N], 在這個包含N個元素的數組id中,如果id[p] 和 id[q]的值相同,那么p,q是連通的。
查找方法
檢查兩個對象對應的編號下的值是否一樣。
連接方法
合并p到q,修改所有值為id[p]的元素的值為id[q]

befor
befor

union(6,1)

after
after

二、算法的具體實現

/* Quick Find 數據結構:
 * 優點:查找操作比較快,只需要執行一次
 * 缺點:合并操作效率慢,可能需要n*n次數組讀取
 * */
public class QuickFindUF
{
    private int[] id; 
    public QuickFindUF(int N)
    {   
        id = new int[N];
        for (int i = 0; i < N; i++ )
            id[i] = i;
    }   

    //查找:檢查是否連通就是比較兩個點對應的值
    public boolean connected(int p, int q)
    {   return id[p] == id[q];  }

    //合并:合并的本質就是設置相同的值
    public void union(int p, int q)
    {   
        int pid = id[p];
        int qid = id[q];
        for (int i = 0; i > id.length; i++)
            if (id[i] == pid) id[i] = qid;
    }   
}

三、算法分析

初始化
初始化的時候,算法需要遍歷整個數組,時間負載度為$N$
查找
在查找操作中,僅僅需要訪問數組的兩個元素即可,程序執行效率的時間復雜度為常數$1$。
合并
合并操作在這一版本的算法中,需要遍歷整個數組,時間復雜度為$N$
最壞的情況
當需要連接$N$個點的時候,算法需要遍歷數組$N2$次。當前CPU每秒可以執行$109$條指令;內存內可以存儲$109$個字節的內容;讀取內存內所有內容則需要1s。此時,擁有$109$個節點,要執行$109$次連接,則需要$109 * 10^9$次操作,需要31年不眠不休的運算。

算法版本二 QuickUnion

一、在樹組內創建樹表示連接關系

數據結構
在數組內創建樹,連接后在同一連接組件的元素, 組成一棵樹。
查找方法
檢查兩個對象的根是否相同。
連接方法
合并兩個節點P , Q,即將Q的Root(根節點)設置為P的根節點。


上圖中,3的根節點可以追溯的9: 3->4->9->9
5的根節點可以追溯到6: 5->6->6
這個時候,合并 3 和 5: union(3,5)
把五的根節點設置給3的根幾點,也就是修改三的根節點9為6.

二、算法的具體實現

/*快速合并
 *優點:連接的時候時間復雜度為N
 *缺點:查找的時候時間復雜度為N(t)
 * */
public class QuickUnionUF
{

    private int[] id; 
    public QuickUnionUF(int N)
    {   
        id = new int[N];
        for (int i = 0; i < N; i++) id[i] = i;
    }   

    private int root(int i)
    {   //查找樹的根
        while (i != id[i]) i = id[i];
        return i;
    }   
    
    public boolean connected(int p, int q)
    {   //檢查是否在同一棵樹內
        return root(p) == root(q);
    }   

    public void union(int p, int q)
    {   //合并兩棵樹
        int i = root(p);
        int j = root(q);
        id[i] = j;
    }   
}

算法分析

初始化
初始化的時候,算法需要遍歷整個數組,時間負載度為$N$
查找
在查找操作中,需要回溯到一棵樹的根節點,程序執行效率的時間復雜度為常數$1$。
合并
合并操作在這一版本的算法中,需要多次回溯一棵樹的根節點,時間復雜度為$Nt$, t為樹的高度。
最壞的情況
按照目前的算法,如果一顆樹過于高,那么尋找root的操作就會浪費大量的時間。這種算法復雜度高于版本1。

改進版本二 垂直化到扁平化

版本二QuickUnion 的缺陷就在于,合并操作很容易一不小心就產生一顆非常高的樹,然后在回溯這棵樹的時候,需要經過大量的節點才能找到root,故此效率低下。

第一步:帶權合并

解決的辦法是降低樹的高度,在合并操作中,如果碰到兩棵樹的合并,將小一點的樹放在大樹的下面,將小樹的根節點作為大樹的根節點的子節點,則可以降低樹的高度。因此我們需要一個額外的數組來記錄每一個樹的尺寸。這些值也就被乘坐權重,改進后的算法被稱作帶權合并。



上圖中,如果按照Quick-union的方法,很有可能得到一顆很高的樹,原因就在于一顆大樹的根節點被當作了小樹的子節點。
加權之后,進行判斷,確保小樹永遠被放置在大樹內,大大降低了樹的高度。

實現

public class WeightedQuickUnionUF      
{                                   
    private int[] id;                  
    private int[] sz;      
                             
    //初始化數組       
    public WeightedQuickUnionUF(int N)      
    {                    
        id = new int[N];      
        sz = new int[N];                                                              
        for (int i = 0; i < N; i++)                                                   
        {                   
            id[i] = i;                                                                
            sz[i] = 1;//初始化每顆樹高度都為1                                                                
        }                                                                             
    }                          
         
    private int root(int i)              
    {                                                                                 
        while (i != id[i]) i = id[i];                                                 
        return i;                                            
    }                                                        
                                                                                      
    public boolean connected(int p, int q)                   
    {                                                        
        return root(p) == root(q);                           
    }                                                 
                                                          
    public void union(int p, int q)                                                   
    {                                                                                 
        int i = root(p);                                                              
        int j = root(q);                                                             
        if (i == j) return;                                                           
        //比較樹的大小,確定讓小樹追加在大樹內
        if(sz[i] < sz[j])   { id[i] = j; sz[j] += sz[i]; }                            
        else                { id[j] = i; sz[i] += sz[j]; }                            
    }                                                                                 
}

帶權前后樹的高度對比:


再進一步:壓縮路徑

帶權合并之后樹的高度明顯降低,回溯的代價也就更小了。但是,在回溯的時候我們一就是一個節點一個節點的往上追溯。


在上圖中要找到3的root:
3->4->9 return 9

因為id[4] = 9
如果利用這個特性,在這個時候壓縮一下追查的路徑:

3->id[4] return 9

這樣一來回溯的成本將大大降低,因此可以對追查root的代碼進一步優化:

private int root(int i)
{
    while (i == id[i])
    {
        i = id[id[i]];
        id[i] = i;
    }
}

效果如下:
壓縮路徑之前:



壓縮路徑之后:


四種算法時間復雜度對比

解決實際問題: 鄉村公路暢通工程

問題描述

某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要互相間接通過道路可達即可)。現在要求任意給出兩個城鎮,查看其是否連通?

問題解決

通過對問題的建模,發現可以直接套用Union-Find算法即可:

編碼實現:
首先創建一個數組,包含所有鄉鎮并使其對應一個編號,其次使用鄉鎮對應的數組下標創建一個數組
然后使用連接已經連通的鄉鎮,最后進行程序測試。

代碼說明

代碼實現,為了方便輸入,這里使用Python來實現算法:

# -*- coding:utf-8 -*-
#這里的鄉鎮僅作演示,實際問題中可以考慮從文件中讀取
PLACES = ['大王村', '河陽', '蠻荒殿', '流波鎮', '青云山', '萬蝠洞', '鳳凰鎮', '南疆古鎮', '七里侗']

#通過地名獲取編號
def idof(place):
    return PLACES.index(place)

#檢查輸入的地名
def cheked(place):
    return place in PLACES

#算法實現
class UnionFind:
    '''算法的Python實現,實際上邏輯是一樣的'''
    def __init__(self, N): 
        self.sd = []
        self.sz = []
        for i in range(N):
            self.sd.append(i)
            self.sz.append(1)

    def root(self, i): 
        while self.sd[i] != i:
            i = self.sd[self.sd[i]]
            self.sd[i] = i 
        return i

    def connected(self, p, q): 
        return self.root(p) == self.root(q)

    def union(self, p, q): 
        i = self.root(p)
        j = self.root(q)
        if i == j:
            return
        if self.sz[i] < self.sz[j]:
            self.sd[i] = j 
            self.sz[j] += self.sz[i]
        else:
            self.sd[j] = i 
            self.sz[i] += self.sz[j]

if __name__ == "__main__":
    uf = UnionFind(len(PLACES))
    #隨便連接幾個點,以便于測試
    uf.union(idof('大王村'), idof('流波鎮'))
    uf.union(idof('大王村'), idof('蠻荒殿'))
    uf.union(idof('流波鎮'), idof('七里侗'))
    #錄入要查尋的地名
    place_a = raw_input("請輸入要查詢的第一個地點:")
    place_b = raw_input("請輸入要查尋的第二個地點:")
    #輸出結果
    if cheked(place_a) and cheked(place_b):
        result = uf.connected(idof(place_a), idof(place_b))
        print "%s 和 %s 之間是否連通?\t %s" % (place_a, place_b, result)
    else:
        print "輸入地名有誤"

程序運行結果:



連通情況:


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容