數(shù)據(jù)結(jié)構(gòu)和算法(二) - 鏈表

鏈表簡(jiǎn)單的來(lái)說(shuō)就是一條鏈子, 鐵鏈子大家都知道, 環(huán)環(huán)相扣, 首尾的環(huán)扣空出來(lái), 中間的環(huán)扣, 一個(gè)一個(gè)的首位相連, 這就是鏈表. 官方的定義是, 一個(gè)結(jié)構(gòu)體里面有兩個(gè)值, 一個(gè)值(value)是存儲(chǔ)當(dāng)前節(jié)點(diǎn)的值, 一個(gè)值(p)是存儲(chǔ)下一個(gè)節(jié)點(diǎn)的位置(指針的地址), 最后一個(gè)節(jié)點(diǎn)的p沒(méi)有值.


鏈表是一種物理存儲(chǔ)單元上非連續(xù)欧瘪、非順序的存儲(chǔ)結(jié)構(gòu)數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的设哗。鏈表由一系列結(jié)點(diǎn)(鏈表中每一個(gè)元素稱為結(jié)點(diǎn))組成原环,結(jié)點(diǎn)可以在運(yùn)行時(shí)動(dòng)態(tài)生成谁帕。每個(gè)結(jié)點(diǎn)包括兩個(gè)部分:一個(gè)是存儲(chǔ)數(shù)據(jù)元素的數(shù)據(jù)域,另一個(gè)是存儲(chǔ)下一個(gè)結(jié)點(diǎn)地址的指針域。 相比于線性表順序結(jié)構(gòu)淋纲,操作復(fù)雜。由于不必須按順序存儲(chǔ)院究,鏈表在插入的時(shí)候可以達(dá)到O(1)的復(fù)雜度洽瞬,比另一種線性表順序表快得多,但是查找一個(gè)節(jié)點(diǎn)或者訪問(wèn)特定編號(hào)的節(jié)點(diǎn)則需要O(n)的時(shí)間业汰,而線性表和順序表相應(yīng)的時(shí)間復(fù)雜度分別是O(logn)和O(1)伙窃。

如圖所示,鏈表是一系列節(jié)點(diǎn)样漆。 節(jié)點(diǎn)有兩個(gè)職責(zé):


屏幕快照 2018-12-04 下午9.10.18.png

1.存儲(chǔ)一個(gè)值为障。
2.保持對(duì)下一個(gè)節(jié)點(diǎn)的引用。 nil值表示列表的結(jié)尾氛濒。

理論上我覺(jué)得理解起來(lái)還是不難的, 當(dāng)初老師教的時(shí)候也聽(tīng)懂了, 但是用C語(yǔ)言寫的時(shí)候, 那個(gè)是一臉的蒙圈, 現(xiàn)在想想, C語(yǔ)言的指針雖然靈活, 但是理解起來(lái)還是挺繞的, 無(wú)形的給學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)增加了難度.

Open

新建一個(gè)playground, 然后打開(kāi)source新建一個(gè)Helpers.swift文件, 鍵入一下代碼

public func example(of description: String, action: () -> Void) {
  print("---Example of \(description)---")
  action()
  print()
}

這里注意函數(shù)前面一定要加 public , 否則在playground中會(huì)找不到該方法.
該方法的參數(shù)是一個(gè)閉包, 是為了方便我們打印輸出log

Next

接下來(lái)在playground的source里面新建一個(gè)Node.swift, 這個(gè)文件是用來(lái)存儲(chǔ)節(jié)點(diǎn)數(shù)據(jù)的.

/// 這個(gè)是一個(gè)泛型類型, value 存儲(chǔ)值, next 存儲(chǔ)指向下一個(gè)節(jié)點(diǎn), 為可選類型, 因?yàn)樽詈笠粋€(gè)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)為nil
public class Node<Value> {
    public var value: Value 
    public var next: Node?
    public init(value: Value, next: Node? = nil) {
        self.value = value
        self.next = next
    }
}
///  CustomStringConvertible 是Swift 標(biāo)準(zhǔn)庫(kù)的一個(gè)協(xié)議, 這個(gè)協(xié)議的作用就是自定義打印輸出的內(nèi)容
extension Node: CustomStringConvertible {
    public var description: String {
        ///  使用 guard 進(jìn)行 next為空值的判斷, 如果為空, 則不指向下一個(gè)節(jié)點(diǎn)
        guard let next = next else {
            return "\(value)"
        }
        return "\(value) -> " + String(describing: next) + " "
    }
}

回到playground, 創(chuàng)建節(jié)點(diǎn)

example(of: "creating and linking nodes") {
    let node1 = Node(value: 1)
    let node2 = Node(value: 2)
    let node3 = Node(value: 3)
    
    node1.next = node2
    node2.next = node3
    
    print(node1)

}
/// ---Example of creating and linking nodes---
/// 1 -> 2 -> 3  
屏幕快照 2018-12-04 下午9.31.34.png

這個(gè)鏈表使用起來(lái)不夠方便, 每添加一個(gè)節(jié)點(diǎn), 還需要手動(dòng)的去指向下一個(gè)節(jié)點(diǎn), 正常的因該是, 不關(guān)心內(nèi)部怎么關(guān)聯(lián), 使用者只需要添加刪除就可以了, 就像數(shù)組一樣.

提供管理Node對(duì)象的接口产场。 向鏈表添加值有三種方法,每種方法都有自己獨(dú)特的性能特征:

  1. push:在列表的前面添加一個(gè)值舞竿。
  2. append:在列表末尾添加一個(gè)值京景。
  3. insert(after :):在列表的特定節(jié)點(diǎn)之后添加一個(gè)值。

push

public struct LinkedList<Value> {
    public var head: Node<Value>?
    public var tail: Node<Value>?
    public init() {
        
    }
    /// 判斷鏈表是否為空
    public var isEmpty: Bool {
        return head == nil
    }
    
    /// 如果鏈表是空的話, 第一個(gè)節(jié)點(diǎn)既是頭部也是尾部, push 操作是往頭部插入的, 每次新插入的一個(gè)元素都會(huì)被放在頭部
    /// struct 和enum 的值在內(nèi)部是不能修改的, 如果要修改需要在方法前面添加 mutating 修飾符
    public mutating func push(_ value: Value) {
        head = Node(value: value, next: head)
        /// 當(dāng)tail 為nil是, 頭部和尾部是同一個(gè), 這樣當(dāng)鏈表里面再進(jìn)來(lái)一個(gè)值的時(shí)候, 新值被賦值給了我head節(jié)點(diǎn), 舊值被賦值給了tail節(jié)點(diǎn), 并且新值的next指向了舊值, 依次類推
        if tail == nil {
            tail = head
        }
    }
}

extension LinkedList: CustomStringConvertible {
    public var description: String {
        guard let head = head else {
            return "Empty list"
        }
        return String(describing: head)
    }
}

playground 中輸入

example(of: "push") {
    var list = LinkedList<Int>()
    list.push(3)
    list.push(2)
    list.push(1)
    print(list)  
}
/// ---Example of push---
/// 1 -> 2 -> 3  

append

public mutating func append(_ value: Value) {
        // 1 如果鏈表為空, 先進(jìn)行push操作, 創(chuàng)建一個(gè)新的節(jié)點(diǎn)
        if isEmpty {
            push(value)
            return
        }
        let node = Node(value: value)
        // 2 由于第一步執(zhí)行了push操作, tail 一定可以強(qiáng)制解包成功, 鏈表中的尾部節(jié)點(diǎn)的next賦值為添加的新節(jié)點(diǎn)
        tail!.next = node
        
        // 3 把新的節(jié)點(diǎn)的值再賦值給鏈表中的尾部節(jié)點(diǎn)
        tail = node
    }

playground 輸入

example(of: "append") {
    
    var list = LinkedList<Int>()
    list.append(1)
    list.append(2)
    list.append(3)
    
    print(list)
    
}
/// ---Example of append---
/// 1 -> 2 -> 3  

insert(after:)

添加值的操作是insert(after :)骗奖。 此操作在列表中的特定位置插入值确徙,并且需要兩個(gè)步驟:
1.在列表中查找特定節(jié)點(diǎn)醒串。
2.插入新節(jié)點(diǎn)。
首先鄙皇,實(shí)現(xiàn)代碼以查找要插入值的節(jié)點(diǎn)芜赌。

node(at :)將嘗試根據(jù)給定的索引檢索列表中的節(jié)點(diǎn)。 由于您只能從頭節(jié)點(diǎn)訪問(wèn)列表的節(jié)點(diǎn)伴逸,因此您必須進(jìn)行迭代遍歷缠沈。

/// 查找某個(gè)位置的node
    public func node(at index: Int) -> Node<Value>? {
        // 1 由于目前只能從頭結(jié)點(diǎn)開(kāi)始訪問(wèn), 先獲取頭結(jié)點(diǎn)的值, 并記錄下標(biāo)
        var currentNode = head
        var currentIndex = 0
        // 2 使用while循環(huán),將引用向下移動(dòng)到列表中错蝴,直到達(dá)到所需的索引洲愤。 空列表或越界索引將導(dǎo)致nil返回值。
        while currentNode != nil && currentIndex < index {
            currentNode = currentNode!.next
            currentIndex += 1
        }
        return currentNode
    }

插入一個(gè)節(jié)點(diǎn)

    // 1 @discardableResult讓調(diào)用者忽略此方法的返回值顷锰,而編譯器不會(huì)向上和向下跳過(guò)警告柬赐。
    @discardableResult
    public mutating func insert(_ value: Value, after node: Node<Value>) -> Node<Value> {
        // 2 如果是尾部節(jié)點(diǎn), 調(diào)用append方法。 這將負(fù)責(zé)更新尾部官紫。
        guard tail !== node else {
            append(value)
            return tail!
        }
        // 3 否則肛宋,只需將新節(jié)點(diǎn)與列表的其余部分鏈接起來(lái),然后返回新節(jié)點(diǎn)束世。
        node.next = Node(value: value, next: node.next)
        return node.next!
    }

playground輸入

example(of: "inserting at index") {
    var list = LinkedList<Int>()
    list.push(3)
    list.push(2)
    list.push(1)
    print("Before inserting: \(list)")
    var middleNode = list.node(at: 1)!
    middleNode = list.insert(10, after: middleNode)
    print("After inserting: \(list)")
}
/// Before inserting: 1 -> 2 -> 3  
/// After inserting: 1 -> 2 -> 10 -> 3

從鏈表中移除節(jié)點(diǎn)

刪除節(jié)點(diǎn)有三個(gè)主要操作:

  1. pop:刪除列表前面的值酝陈。
  2. removeLast:刪除列表末尾的值。
  3. remove(at :):刪除列表中任何位置的值毁涉。

1. pop:刪除列表前面的值后添。

/// 1.
    @discardableResult
    //2.
    public mutating func pop() -> Value? {
        // 5. 
        defer {
            //3. 
            head = head?.next
            // 4. 
            if isEmpty {
                tail = nil
            }
        }
        return head?.value
    }
  1. 刪除列表最前面的值稱為pop
  2. 返回值為一個(gè)可選值, 因?yàn)? 如果鏈表為空, 則返回值為nil

3.如果刪除成功, 則將頭部指向下一個(gè)節(jié)點(diǎn)
4.如果為空, 為節(jié)點(diǎn)置為nil
5.defer 為推遲執(zhí)行, defer 里面的block會(huì)在方法體內(nèi)的代碼執(zhí)行完畢之后, 最后執(zhí)行

playground 輸入

example(of: "pop") {
    
    var list = LinkedList<Int>()
    list.push(3)
    list.push(2)
    list.push(1)
    
    print("pop 之前: \(list)")
    let poppedValue = list.pop()
    print("pop 之后: \(list)")
    print("Popped 值: " + String(describing: poppedValue))
    
}
pop 之前: 1 -> 2 -> 3  
pop 之后: 2 -> 3 
Popped 值: Optional(1)

2. removeLast:刪除列表末尾的值。

刪除列表的最后一個(gè)節(jié)點(diǎn)有點(diǎn)不方便薪丁。 盡管您有對(duì)尾節(jié)點(diǎn)的引用遇西,但如果沒(méi)有引用它之前的節(jié)點(diǎn),則無(wú)法將其刪除严嗜。 因此粱檀,你將不得不進(jìn)行遍歷。

 //#removeLast
    @discardableResult
    public mutating func removeLast() -> Value? {
        // 1
        guard let head = head else {
            return nil
        }
        // 2
        guard head.next != nil else {
            return pop()
        }
        
        // 3
        var prev = head
        var current = head
        
        while let next = current.next {
            prev = current
            current = next
        }
        // 4
        prev.next = nil
        tail = prev
        return current.value
        
    }

1.如果頭部為nil漫玄,則無(wú)需移除任何東西茄蚯,因此您返回nil。

2.如果列表只包含一個(gè)節(jié)點(diǎn)睦优,則removeLast在功能上等同于pop渗常。 由于pop將處理更新頭部和尾部引用,因此您只需將此工作交給它汗盘。

3.您繼續(xù)搜索下一個(gè)節(jié)點(diǎn)皱碘,直到current.next為nil。這表示當(dāng)前是列表的最后一個(gè)節(jié)點(diǎn)隐孽。

4.由于current是最后一個(gè)節(jié)點(diǎn)癌椿,因此只需使用prev.next引用將其斷開(kāi)即可健蕊。 還要確保更新尾部參考。

example(of: "removing the last node") {
    var list = LinkedList<Int>()
    list.push(3)
    list.push(2)
    list.push(1)
    
    print("移除最后一個(gè)節(jié)點(diǎn)之前: \(list)")
    let removedValue = list.removeLast()
    print("移除最后一個(gè)節(jié)點(diǎn)之后: \(list)")
    print("移除的值: " + String(describing: removedValue))
    
}

輸出值

移除最后一個(gè)節(jié)點(diǎn)之前: 1 -> 2 -> 3  
移除最后一個(gè)節(jié)點(diǎn)之后: 1 -> 2 
移除的值: Optional(3)

removeLast需要一直遍歷列表踢俄。 這樣做性能極地, 后面改進(jìn)

3. remove(at :):刪除列表中任何位置的值缩功。

刪除操作是刪除列表中特定節(jié)點(diǎn)。 這很像在任意位置插入一個(gè)節(jié)點(diǎn); 首先在要?jiǎng)h除的節(jié)點(diǎn)之前找到該節(jié)點(diǎn)都办,然后取消引用嫡锌。


屏幕快照 2018-12-09 下午8.47.06.png
@discardableResult
    public mutating func remove(after node: Node<Value>) -> Value? {
        defer {
            if node.next === tail {
                tail = node
            }
            node.next = node.next?.next
        }
        return node.next?.value
    }

playground 輸入

example(of: "removing a node after a particular node") {
    var list = LinkedList<Int>()
    list.push(3)
    list.push(2)
    list.push(1)
    
    print("Before removing at particular index: \(list)")
    let index = 1
    let node = list.node(at: index - 1)!
    let removedValue = list.remove(after: node)
    print("After removing at index \(index): \(list)")
    print("Removed value: " + String(describing: removedValue))
    
}

Before removing at particular index: 1 -> 2 -> 3
After removing at index 1: 1 -> 3 
Removed value: Optional(2)

Swift 集合協(xié)議 在鏈表中的使用

Swift標(biāo)準(zhǔn)庫(kù)有一組協(xié)議,可幫助定義特定的類型琳钉。 這些協(xié)議中的每一個(gè)都對(duì)特性和性能提供了某些保證世舰。 在這些協(xié)議集中,有四個(gè)被稱為集合協(xié)議槽卫。

  1. Sequence : 序列類型提供對(duì)其元素的順序訪問(wèn)
  2. Collection: 集合類型是一種提供額外保證的序列類型。集合類型是有限的胰蝠,允許重復(fù)的非破壞性順序訪問(wèn)歼培。
  3. Bidirectional Colllection 雙向集合: 集合類型可以是雙向集合類型,可以允許在序列中上下雙向移動(dòng)茸塞。 這對(duì)于鏈表是不可能的躲庄,因?yàn)槟阒荒軓念^到尾,而不是相反钾虐。
  4. RandomAccessCollection: 如果它可以保證訪問(wèn)特定索引處的元素將花費(fèi)與訪問(wèn)任何其他索引處的元素一樣長(zhǎng)的時(shí)間噪窘。該雙向集合類型就是隨機(jī)訪問(wèn)集合類型, 這對(duì)于鏈表來(lái)說(shuō)是不可能的效扫,因?yàn)樵L問(wèn)列表前面附近的節(jié)點(diǎn)比列表下方的節(jié)點(diǎn)快得多倔监。

so, 鏈表可以適用于Swift集合協(xié)議中的兩個(gè)。
首先菌仁,由于鏈表是一組序列浩习,采用Sequence協(xié)議是可以的。 其次济丘,由于鏈表是有限序列谱秽,因此采用Collection協(xié)議是可以的。

變成Swift集合

如何實(shí)現(xiàn)Collection協(xié)議摹迷。 集合類型是有限序列疟赊,并提供非破壞性順序訪問(wèn)。 Swift Collection還允許通過(guò)下標(biāo)進(jìn)行訪問(wèn), 使用索引可以映射到集合中的值峡碉。

自定義集合的索引(Custom collection indexes)

衡量Collection協(xié)議方法性能的指標(biāo)是Index映射到值的速度近哟。 與其他存儲(chǔ)類型(如Swift Array)不同,鏈表無(wú)法使用整數(shù)索引實(shí)現(xiàn)O(1)下標(biāo)操作鲫寄。 因此椅挣,自定義下標(biāo)是定義包含對(duì)其各自節(jié)點(diǎn)的引用的索引头岔。

實(shí)現(xiàn)集合協(xié)議

extension LinkedList: Collection {

    public struct Index: Comparable {

        public var node: Node<Value>?
        
        // 1. 自定義結(jié)構(gòu)體不能進(jìn)行==操作, 接受Comparable協(xié)議, 并實(shí)現(xiàn)方法
        static public func ==(lhs: Index, rhs: Index) -> Bool {
            switch (lhs.node, rhs.node) {
            case let (left?, right?):
                return left.next === right.next
            case (nil, nil):
                return true
            default:
                return false
            }
        }

        // 2. 第一個(gè)參數(shù)是否小于第二個(gè)參數(shù)
        static public func <(lhs: Index, rhs: Index) -> Bool {
            // 如果相等, 返回false, 否則繼續(xù)
            guard lhs != rhs else {
                return false
            }
            // 3.  從鏈表的一個(gè)節(jié)點(diǎn)移動(dòng)到根節(jié)點(diǎn)
            /**
             * sequence函數(shù)式以first值為開(kāi)始, 依次返回每一個(gè)元素, 方法體里面是對(duì)這個(gè)元素操作之后的結(jié)果, 每次返回的nodes都會(huì)執(zhí)行contains操作
             * 雖然contains是在sequence方法之后, 但是執(zhí)行順序是沒(méi)執(zhí)行一次sequence方法, 緊接著執(zhí)行contains, 再執(zhí)行sequence, 再執(zhí)行contains, 以此類推, 直到contains滿足條件, 退出序列
             */
            // 從lhs節(jié)點(diǎn)開(kāi)始一直遍歷到尾部節(jié)點(diǎn)
            let nodes = sequence(first: lhs.node) {
                $0?.next
            }
            // 4. 循環(huán)遍歷所有的節(jié)點(diǎn), 判斷是否包含rhs.node (=== 等價(jià), 檢測(cè)兩個(gè)常量或者變量是否引用同一個(gè)實(shí)例)
            // 因?yàn)轭愂且妙愋停锌赡苡卸鄠€(gè)常量和變量在幕后同時(shí)引用同一個(gè)類實(shí)例鼠证。(對(duì)于結(jié)構(gòu)體和枚舉來(lái)說(shuō)峡竣,這并不成立。因?yàn)樗鼈冏鳛橹殿愋土烤牛诒毁x予到常量适掰、變量或者傳遞到函數(shù)時(shí),其值總是會(huì)被拷貝荠列。)
            // 判斷從lhs開(kāi)始一直到尾部節(jié)點(diǎn), 是否包含 === rhs的節(jié)點(diǎn)值, 如果存在, 則說(shuō)明, lhs > rhs, 否則 lhs < rhs
            return nodes.contains {
                $0 === rhs.node
            }
        }
    }
    // ## --- 以下四個(gè)是實(shí)現(xiàn)Collection協(xié)議

    // 1 頭結(jié)點(diǎn)
    public var startIndex: Index {
        return Index(node: head)
    }

    // 2 尾部節(jié)點(diǎn)
    public var endIndex: Index {
        return Index(node: tail?.next)
    }

    // 3  提供下一個(gè)節(jié)點(diǎn)的索引
    public func index(after i: Index) -> Index {
        return Index(node: i.node?.next)
    }

    // 4 下標(biāo)用于將索引映射到集合中的值类浪。 已經(jīng)創(chuàng)建了自定義索引,因此可以通過(guò)引用節(jié)點(diǎn)的值輕松地在恒定時(shí)間內(nèi)實(shí)現(xiàn)此目的肌似。
    public subscript(position: Index) -> Value {
        return position.node!.value
    }
}

playground 輸入

example(of: "using collection") {
    var list = LinkedList<Int>()
    for i in 0...9 {
        list.append(i)
    }
    
    print("鏈表: \(list)")
    print("第一個(gè)元素: \(list[list.startIndex])")
    print("前三個(gè)元素: \(Array(list.prefix(3)))")
    print("后三個(gè)元素: \(Array(list.suffix(3)))")
    
    let sum = list.reduce(0, +)
    print("求和: \(sum)")
}
鏈表: 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9         
第一個(gè)元素: 0
前三個(gè)元素: [0, 1, 2]
后三個(gè)元素: [7, 8, 9]
求和: 45
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末费就,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子川队,更是在濱河造成了極大的恐慌力细,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件固额,死亡現(xiàn)場(chǎng)離奇詭異眠蚂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)斗躏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門逝慧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人啄糙,你說(shuō)我怎么就攤上這事笛臣。” “怎么了隧饼?”我有些...
    開(kāi)封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵捐祠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我桑李,道長(zhǎng)踱蛀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任贵白,我火速辦了婚禮率拒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘禁荒。我一直安慰自己猬膨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著勃痴,像睡著了一般谒所。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沛申,一...
    開(kāi)封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天劣领,我揣著相機(jī)與錄音,去河邊找鬼铁材。 笑死尖淘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的著觉。 我是一名探鬼主播村生,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼饼丘!你這毒婦竟也來(lái)了趁桃?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肄鸽,失蹤者是張志新(化名)和其女友劉穎卫病,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贴捡,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年村砂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烂斋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡础废,死狀恐怖汛骂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情评腺,我是刑警寧澤帘瞭,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蒿讥,受9級(jí)特大地震影響蝶念,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芋绸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一媒殉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摔敛,春花似錦廷蓉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)刹悴。三九已至,卻和暖如春攒暇,著一層夾襖步出監(jiān)牢的瞬間土匀,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工扯饶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恒削,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓尾序,卻偏偏與公主長(zhǎng)得像钓丰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子每币,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

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