數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)筆記(基礎(chǔ)班十)---并查集和圖

并查集

  • 有若干個(gè)樣本a仍劈、b厕倍、c、d…類型假設(shè)是V贩疙。
  • 在并查集中一開始認(rèn)為每個(gè)樣本都在單獨(dú)的集合里讹弯。
  • 用戶可以在任何時(shí)候調(diào)用如下兩個(gè)方法:
    1.boolean isSameSet(V x, V y) : 查詢樣本x和樣本y是否屬于一個(gè)集合。
    2.void union(V x, V y) : 把x和y各自所在集合的所有樣本合并成一個(gè)屋群。
  • isSameSet和union方法的代價(jià)越低越好闸婴。(時(shí)間復(fù)雜度O(1))。

并查集特征

1)每個(gè)節(jié)點(diǎn)都有一條往上指的指針芍躏。
2)節(jié)點(diǎn)a往上找到的頭節(jié)點(diǎn)邪乍,叫做a所在集合的代表節(jié)點(diǎn)。
3)查詢x和y是否屬于同一個(gè)集合对竣,就是看看找到的代表節(jié)點(diǎn)是不是一個(gè)庇楞。
4)把x和y各自所在集合的所有點(diǎn)合并成一個(gè)集合,只需要小集合的代表點(diǎn)掛在大集合的代表點(diǎn)的下方即可否纬。

并查集的優(yōu)化

1)節(jié)點(diǎn)往上找代表點(diǎn)的過程吕晌,把沿途的鏈變成扁平的。
2)小集合掛在大集合的下面临燃。
3)如果方法調(diào)用很頻繁睛驳,那么單次調(diào)用的代價(jià)為O(1),兩個(gè)方法都如此膜廊。

/**
 * 并查集
 * 提供兩個(gè)方法:
 * 1乏沸、查詢兩個(gè)元素是否在同一個(gè)集合中  isSameSet
 * 2、把兩個(gè)元素所在的集合合并成一個(gè)  union
 *
 * 假定用戶一次直接給出所有集合
 */
public class UnionSearch<V> {
    // 節(jié)點(diǎn)位置表
    Map<V,Node> nodes;
    // 查找當(dāng)前節(jié)點(diǎn)對應(yīng)的父節(jié)點(diǎn)表
    Map<Node,Node> parents;
    // 作為代表元素表 k代表元素 v 集合中有幾個(gè)元素
    Map<Node,Integer> size;
    public UnionSearch(List<V> list){
        nodes = new HashMap<>();
        parents = new HashMap<>();
        size = new HashMap<>();
        for(V v : list){
            // 出事時(shí)每個(gè)元素單獨(dú)作為一個(gè)結(jié)合
            Node node = new Node(v);
            nodes.put(v,node);
            parents.put(node,node);
            size.put(node,1);
        }
    }


    public  boolean isSameSet(V v1,V v2){
        if(!nodes.containsKey(v1)
                || !nodes.containsKey(v2)){
            return false;
        }
        Node node1 = findFather(v1);
        Node node2 = findFather(v2);
        if(node1 == node2){
            return true;
        }

        return false;
    }


    public void union(V v1,V v2){
        if(!nodes.containsKey(v1)
                || !nodes.containsKey(v2)){
            return;
        }
        Node node1 = findFather(v1);
        Node node2 = findFather(v2);
        // 兩個(gè)元素不在一個(gè)集合中才合并
        if(node1 != node2){
           // 看誰的元素多
            Node big = size.get(node1) > size.get(node2) ? node1 : node2;
            Node small = size.get(node1) <= size.get(node2) ? node1 : node2;
            // 小的掛在大的上面
            parents.put(small,big);
            size.put(big,size.get(big) + size.get(small));
            size.remove(small);
        }

    }

    // 調(diào)用此方法是一定保證集合中有此元素
    private Node findFather(V node){
        // 做一個(gè)扁平化優(yōu)化,把從當(dāng)前節(jié)點(diǎn)網(wǎng)上找的節(jié)點(diǎn)都加入容器中
        Stack<Node> stack = new Stack<>();
        Node cur = nodes.get(node);
        while (parents.get(cur) != cur){
            // 路徑上經(jīng)過的節(jié)點(diǎn)加入容器
            stack.push(cur);
            // 只要此時(shí)節(jié)點(diǎn)不等于父節(jié)點(diǎn)爪瓜,說明不是代表節(jié)點(diǎn)就一直往上找
            cur = parents.get(cur);
        }

        // 把所有經(jīng)過節(jié)點(diǎn)直接和代表節(jié)點(diǎn)相連蹬跃,下次查找是O(1)
        while (!stack.isEmpty()){
            parents.put(stack.pop(),cur);
        }
        //跳出來說明已經(jīng)找到了
        return cur;
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList();

        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("f");
        list.add("g");
        UnionSearch<String> unionSearch = new UnionSearch<>(list);
        unionSearch.union("a","b");
        unionSearch.union("c","b");
        System.out.println(unionSearch.isSameSet("a", "g"));

    }
}

1)由點(diǎn)的集合和邊的集合構(gòu)成。
2)雖然存在有向圖和無向圖的概念铆铆,但實(shí)際上都可以用有向圖來表達(dá)蝶缀。
3)邊上可能帶有權(quán)值丹喻。

圖結(jié)構(gòu)的表達(dá)

1)鄰接表法
2)鄰接矩陣法
3)除此之外還有其他眾多的方式

圖的數(shù)據(jù)結(jié)構(gòu)

  • 點(diǎn)
// 點(diǎn)
public class Node {
    // 結(jié)點(diǎn)值
    public int value;
    // 入度
    public int in;
    // 出度
    public int out;
    // 鄰居
    List<Node> nexts;
    // 存一份邊
    List<Edge> edges;

    public Node(int value){
        this.value = value;
        // 新結(jié)點(diǎn)入度為0
        this.in = 0;
        // 新結(jié)點(diǎn)出度為0
        this.out = 0;
        // 新結(jié)點(diǎn)鄰居為空
        this.nexts = new ArrayList<>();
        this.edges = new ArrayList<>();
    }
}
// 邊
public class Edge {
    // 權(quán)重
    public int weight;
    // 起點(diǎn)邊
    public Node from;
    // 終點(diǎn)邊
    public Node to;
    public Edge(Node from,Node to,int weight){
        this.from = from;
        this.to = to;
        this.weight = weight;
    }
}
// 圖
public class Graph {
    // 點(diǎn)的集合
    HashMap<Integer,Node> nodes;
    // 邊的集合
    HashSet<Edge> edges;
    public Graph(){
        this.nodes = new HashMap<>();
        this.edges = new HashSet<>();
    }
}

  • 生成圖
public class GraphGenerator {
    // matrix 所有的邊
    // N*3 的矩陣
    // [weight, from節(jié)點(diǎn)上面的值,to節(jié)點(diǎn)上面的值]
    public static Graph graphGenerator(Integer[][] matrix){
        Graph graph = new Graph();
        for (int i = 0;i < matrix.length;i++){
            // 每一行代表一個(gè)點(diǎn)
            // 權(quán)重
            int weight = matrix[i][0];
            // 起始點(diǎn)
            int from = matrix[i][1];
            // 終點(diǎn)
            int to = matrix[i][2];
            // 如果沒有起始點(diǎn)就新建
            if(graph.nodes.containsKey(from)){
                graph.nodes.put(from,new Node(from));
            }
            if(graph.nodes.containsKey(to)){
                graph.nodes.put(to,new Node(to));
            }

            // 取出點(diǎn)構(gòu)建圖
            Node fromNode = graph.nodes.get(from);
            Node toNode = graph.nodes.get(to);
            // 構(gòu)建邊
            Edge edge = new Edge(fromNode,toNode,weight);
            // fromNode 幾點(diǎn)出度加一翁都,鄰居加一
            fromNode.out ++;
            fromNode.nexts.add(toNode);
            fromNode.edges.add(edge);
            // toNode入度加一
            toNode.in ++;
            // 圖中邊集加一
            graph.edges.add(edge);
        }
        return graph;
    }
}

寬度優(yōu)先遍歷

1碍论,利用隊(duì)列實(shí)現(xiàn)
2,從源節(jié)點(diǎn)開始依次按照寬度進(jìn)隊(duì)列荐吵,然后彈出
3骑冗,每彈出一個(gè)點(diǎn)赊瞬,把該節(jié)點(diǎn)所有沒有進(jìn)過隊(duì)列的鄰接點(diǎn)放入隊(duì)列
4先煎,直到隊(duì)列變空

  • 注意要點(diǎn),不能重復(fù)遍歷巧涧,便利過的點(diǎn)應(yīng)該排除薯蝎。
// 圖的寬度有限便利
public class GraphBfs {
    public static void graphBfs(Node node){
        if(node == null){
            return;
        }
        HashSet<Node> set = new HashSet<>();
        Queue<Node> queue = new LinkedList<>();
        queue.offer(node);
        set.add(node);
        while (!queue.isEmpty()){
            Node cur = queue.poll();
            System.out.print(cur.value + " ");
            // 全部鄰居加入隊(duì)列
            for (Node next : cur.nexts){
                if(!set.contains(next)){
                    queue.offer(next);
                    set.add(next);
                }
            }
        }
    }
}

深度優(yōu)先遍歷

1.利用棧實(shí)現(xiàn)
2.從源節(jié)點(diǎn)開始把節(jié)點(diǎn)按照深度放入棧,然后彈出
3.每彈出一個(gè)點(diǎn)谤绳,把該節(jié)點(diǎn)下一個(gè)沒有進(jìn)過棧的鄰接點(diǎn)放入棧
4.直到棧變空

- 圖的深度優(yōu)先遍歷主要注意重復(fù)結(jié)點(diǎn)不遍歷
// 圖的寬度優(yōu)先遍歷
public class GraphDfs {
    public static void  graphDfs(Node node){
        if(node == null){
            return;
        }
        HashSet<Node> set = new HashSet<>();
        // 利用棧
        Stack<Node> stack = new Stack<>();
        // 入棧就打印
        stack.push(node);
        System.out.print(node.value + " ");
        while (!stack.isEmpty()){
            Node cur = stack.pop();
            for(Node item : cur.nexts){
                if(!set.contains(item)){
                    stack.push(cur);
                    stack.push(item);
                    set.add(item);
                    System.out.print(item.value + " ");
                    break;
                }
            }
        }  
    }
}

圖的拓?fù)渑判蛩惴?/h1>

1)在圖中找到所有入度為0的點(diǎn)輸出
2)把所有入度為0的點(diǎn)在圖中刪掉占锯,繼續(xù)找入度為0的點(diǎn)輸出,周而復(fù)始
3)圖的所有點(diǎn)都被刪除后缩筛,依次輸出的順序就是拓?fù)渑判?br> 要求:有向圖且其中沒有環(huán)
應(yīng)用:事件安排消略、編譯順序

// 拓?fù)渑判?public class TopologySort {

    public static List<Node> topologySort(Graph graph){
        if(graph == null){
            return null;
        }
        // 入度為0的點(diǎn)進(jìn)入此隊(duì)列
        Queue<Node> zeroQueue = new LinkedList<>();
        // k :結(jié)點(diǎn)  v :入度數(shù)
        HashMap<Node,Integer> map = new HashMap<>();
        List<Node> res = new ArrayList<>();
        for(Node item : graph.nodes.values()){
            map.put(item,item.in);
            if(item.in == 0){
                zeroQueue.offer(item);
            }
        }
        while (!zeroQueue.isEmpty()){
            Node cur = zeroQueue.poll();
            res.add(cur);
            for(Node temp : cur.nexts){
                map.put(temp,map.get(temp)-1);
                if(map.get(temp) == 0){
                    zeroQueue.offer(temp);
                }
            }
            
        }
        return res;
    }
}

最小生成樹K算法

/**
 * 最小生成樹算法之Kruskal
 * 1)總是從權(quán)值最小的邊開始考慮,依次考察權(quán)值依次變大的邊(優(yōu)先級(jí)隊(duì)列)
 * 2)當(dāng)前的邊要么進(jìn)入最小生成樹的集合瞎抛,要么丟棄
 * 3)如果當(dāng)前的邊進(jìn)入最小生成樹的集合中不會(huì)形成環(huán)艺演,就要當(dāng)前邊
 * 4)如果當(dāng)前的邊進(jìn)入最小生成樹的集合中會(huì)形成環(huán),就不要當(dāng)前邊
 * 5)考察完所有邊之后桐臊,最小生成樹的集合也得到了
 */
public class Kruskal {
    public static Set<Edge> kruskal(Graph graph){
        if(graph == null){
            return null;
        }
        Set<Edge> res = new HashSet<>();
        // 先把所有點(diǎn)都加入并查集
        List<Node> nodeList = (List<Node>)graph.nodes.values();
        UnionSearch<Node> unionSearch = new UnionSearch<>(nodeList);
        // 獲取所有邊
        PriorityQueue<Edge> queue = new PriorityQueue<>(new MyComparator());
        for(Edge item : graph.edges){
            queue.offer(item);
        }
        while (!queue.isEmpty()){
            Edge cur = queue.poll();
            Node from = cur.from;
            Node to = cur.to;
            if(!unionSearch.isSameSet(from,to)){
               res.add(cur);
                // 加入并查集
                unionSearch.union(from,to);
            }
        }
        
        return res;

    }
    
    private void makeUnion(List<Node> list,UnionSearch<Node> unionSearch){
        for(Node item : list){
            
        }
    }

    static class MyComparator implements Comparator<Edge>{

        @Override
        public int compare(Edge o1, Edge o2) {
            return o1.weight - o2.weight;
        }
    }


     static class UnionSearch<V> {
        // 節(jié)點(diǎn)位置表
        Map<V,Node2> nodes;
        // 查找當(dāng)前節(jié)點(diǎn)對應(yīng)的父節(jié)點(diǎn)表
        Map<Node2,Node2> parents;
        // 作為代表元素表 k代表元素 v 集合中有幾個(gè)元素
        Map<Node2,Integer> size;
        public UnionSearch(List<V> list){
            nodes = new HashMap<>();
            parents = new HashMap<>();
            size = new HashMap<>();
            for(V v : list){
                // 出事時(shí)每個(gè)元素單獨(dú)作為一個(gè)結(jié)合
                Node2 node = new Node2(v);
                nodes.put(v,node);
                parents.put(node,node);
                size.put(node,1);
            }
        }


        public  boolean isSameSet(V v1,V v2){
            if(!nodes.containsKey(v1)
                    || !nodes.containsKey(v2)){
                return false;
            }
            Node2 node1 = findFather(v1);
            Node2 node2 = findFather(v2);
            if(node1 == node2){
                return true;
            }

            return false;
        }


        public void union(V v1,V v2){
            if(!nodes.containsKey(v1)
                    || !nodes.containsKey(v2)){
                return;
            }
            Node2 node1 = findFather(v1);
            Node2 node2 = findFather(v2);
            // 兩個(gè)元素不在一個(gè)集合中才合并
            if(node1 != node2){
                // 看誰的元素多
                Node2 big = size.get(node1) > size.get(node2) ? node1 : node2;
                Node2 small = size.get(node1) <= size.get(node2) ? node1 : node2;
                // 小的掛在大的上面
                parents.put(small,big);
                size.put(big,size.get(big) + size.get(small));
                size.remove(small);
            }

        }

        // 調(diào)用此方法是一定保證集合中有此元素
        private Node2 findFather(V node){
            // 做一個(gè)扁平化優(yōu)化,把從當(dāng)前節(jié)點(diǎn)網(wǎng)上找的節(jié)點(diǎn)都加入容器中
            Stack<Node2> stack = new Stack<>();
            Node2 cur = nodes.get(node);
            while (parents.get(cur) != cur){
                // 路徑上經(jīng)過的節(jié)點(diǎn)加入容器
                stack.push(cur);
                // 只要此時(shí)節(jié)點(diǎn)不等于父節(jié)點(diǎn)胎撤,說明不是代表節(jié)點(diǎn)就一直往上找
                cur = parents.get(cur);
            }

            // 把所有經(jīng)過節(jié)點(diǎn)直接和代表節(jié)點(diǎn)相連,下次查找是O(1)
            while (!stack.isEmpty()){
                parents.put(stack.pop(),cur);
            }
            //跳出來說明已經(jīng)找到了
            return cur;
        }

        
    }
}

最小生成樹p算法

/**
 * 最小生成樹断凶,p算法
 * 1)可以從任意節(jié)點(diǎn)出發(fā)來尋找最小生成樹
 * 2)某個(gè)點(diǎn)加入到被選取的點(diǎn)中后伤提,解鎖這個(gè)點(diǎn)出發(fā)的所有新的邊
 * 3)在所有解鎖的邊中選最小的邊,然后看看這個(gè)邊會(huì)不會(huì)形成環(huán)
 * 4)如果會(huì)认烁,不要當(dāng)前邊肿男,繼續(xù)考察剩下解鎖的邊中最小的邊,重復(fù)3)
 * 5)如果不會(huì)却嗡,要當(dāng)前邊舶沛,將該邊的指向點(diǎn)加入到被選取的點(diǎn)中,重復(fù)2)
 * 6)當(dāng)所有點(diǎn)都被選取稽穆,最小生成樹就得到了
 */
public class Prim {
    public static Set<Edge> prim(Graph graph){
        if(graph == null){
            return null;
        }
        // 生成優(yōu)先級(jí)隊(duì)列
        PriorityQueue<Edge> queue = new PriorityQueue<>(new MyComparator());
        Set<Edge> edgeSet = new HashSet<>();
        Set<Node> nodeSet = new HashSet<>();
        Set<Edge> res = new HashSet<>();
        List<Node> nodeList = (List<Node>)graph.nodes.values();
        for(Node node : nodeList){ // 防止森林冠王,如果明確沒有森林可不要此循環(huán)。
            if(!nodeSet.contains(node)){
                // 解鎖點(diǎn)的所有邊
                nodeSet.add(node);
                for(Edge temp : node.edges){
                    if(!edgeSet.contains(temp)){
                        queue.offer(temp);
                        edgeSet.add(temp);
                    }
                }
                while (!queue.isEmpty()){
                    // 權(quán)重最小的邊
                    Edge cur = queue.poll();
                    // 此時(shí)from節(jié)點(diǎn)已經(jīng)在nodeSet結(jié)合中解鎖出來了舌镶,只需判斷to節(jié)點(diǎn)是否解鎖了
                    if(!nodeSet.contains(cur.to)){
                        // 要這條邊
                        res.add(cur);
                        // 加入點(diǎn)的集合
                        nodeSet.add(cur.to);
                        // 解鎖cur.to的所有邊
                        for (Edge e : cur.to.edges){
                            if(!edgeSet.contains(e)){
                                queue.offer(e);
                                edgeSet.add(e);
                            }
                        }
                    }
                }
            }
        }
        return res;
    }
   static class MyComparator implements Comparator<Edge>{
       @Override
       public int compare(Edge o1, Edge o2) {
           return o1.weight - o2.weight;
       }
   }
}

Dijkstra算法

1)Dijkstra算法必須指定一個(gè)源點(diǎn)
2)生成一個(gè)源點(diǎn)到各個(gè)點(diǎn)的最小距離表柱彻,一開始只有一條記錄豪娜,即原點(diǎn)到自己的最小距離為0,源點(diǎn)到其他所有點(diǎn)的最小距離都為正無窮大
3)從距離表中拿出沒拿過記錄里的最小記錄哟楷,通過這個(gè)點(diǎn)發(fā)出的邊瘤载,更新源點(diǎn)到各個(gè)點(diǎn)的最小距離表,不斷重復(fù)這一步
4)源點(diǎn)到所有的點(diǎn)記錄如果都被拿過一遍卖擅,過程停止鸣奔,最小距離表得到了

  • 優(yōu)化點(diǎn),在distanceMap中查找起點(diǎn)到其他點(diǎn)的最短距離是用遍歷的方式時(shí)間復(fù)雜度是O(N),如果用小根堆惩阶,可以讓時(shí)間復(fù)雜度降到O(logn),但是應(yīng)為會(huì)更新加入到小根堆中的距離數(shù)據(jù)挎狸,一般的小根堆不能自動(dòng)調(diào)整,所以要自己手動(dòng)改寫小根堆断楷。
/**
 * 1)Dijkstra算法必須指定一個(gè)源點(diǎn)
 * 2)生成一個(gè)源點(diǎn)到各個(gè)點(diǎn)的最小距離表锨匆,一開始只有一條記錄,即原點(diǎn)到自己的最小距離為0冬筒,源點(diǎn)到其他所有點(diǎn)的最小距離都為正無窮大
 * 3)從距離表中拿出沒拿過記錄里的最小記錄恐锣,通過這個(gè)點(diǎn)發(fā)出的邊,更新源點(diǎn)到各個(gè)點(diǎn)的最小距離表舞痰,不斷重復(fù)這一步
 * 4)源點(diǎn)到所有的點(diǎn)記錄如果都被拿過一遍土榴,過程停止,最小距離表得到了
 */
public class Dijkstra {
    
    public static Map<Node2,Integer> dijkstra1(Node2 node){
        if(node == null){
            return null;
        }
        // 距離表,一開始只有出發(fā)點(diǎn)到出發(fā)點(diǎn)的距離為0
        Map<Node2,Integer> distanceMap = new HashMap<>();
        distanceMap.put(node,0);
        // 去重黑名單响牛,每次選舉最小記錄時(shí)排除此表中的數(shù)據(jù)
        Set<Node2> noSelect = new HashSet<>();
        // 獲取最小記錄點(diǎn)(出發(fā)點(diǎn)到某一點(diǎn)的最短距離)
        Node2 minDistance = getMinAndNoSelect(distanceMap,noSelect);
        while (minDistance != null){ // 把表中數(shù)據(jù)都遍歷完
            for (Edge temp : minDistance.edges){
                // 這個(gè)點(diǎn)往能出去的所有邊能不能更新表中的距離
                Node2 to = temp.to;
                if(!distanceMap.containsKey(to)){
                    // 如果表中不包含這個(gè)點(diǎn)玷禽,說明還沒找到過重from點(diǎn)到這個(gè)距離,直接加入表中
                    distanceMap.put(to,distanceMap.get(minDistance) + temp.weight);
                }else{
                    distanceMap.put(to,Math.min(distanceMap.get(to),distanceMap.get(minDistance) + temp.weight));
                }
            }
            minDistance = getMinAndNoSelect(distanceMap,noSelect);
            
        }
        
        return distanceMap;
    }
    
    
    private static Node2 getMinAndNoSelect(Map<Node2,Integer> distanceMap,Set<Node2> noSelcet){
        // 由于Dijkstra算法中邊都是非負(fù)的用0就可以了
        int min = 0;
        Node2 res = null;
        // 遍歷距離表
        for (Map.Entry<Node2, Integer> item : distanceMap.entrySet()){
            Node2 key = item.getKey();
            if(noSelcet.contains(key)){
                continue;
            }
            // 比較大小
            if(item.getValue() < min){
                min = item.getValue();
                res = item.getKey();
            }
        }
        noSelcet.add(res);
        return res;
    }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娃善,一起剝皮案震驚了整個(gè)濱河市论衍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聚磺,老刑警劉巖坯台,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瘫寝,居然都是意外死亡蜒蕾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門焕阿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咪啡,“玉大人,你說我怎么就攤上這事暮屡〕访” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長准夷。 經(jīng)常有香客問我钥飞,道長,這世上最難降的妖魔是什么衫嵌? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任读宙,我火速辦了婚禮,結(jié)果婚禮上楔绞,老公的妹妹穿的比我還像新娘结闸。我一直安慰自己,他們只是感情好酒朵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布桦锄。 她就那樣靜靜地躺著,像睡著了一般耻讽。 火紅的嫁衣襯著肌膚如雪察纯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天针肥,我揣著相機(jī)與錄音,去河邊找鬼香伴。 笑死慰枕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的即纲。 我是一名探鬼主播具帮,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼低斋!你這毒婦竟也來了蜂厅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤膊畴,失蹤者是張志新(化名)和其女友劉穎掘猿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唇跨,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稠通,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了买猖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片改橘。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖玉控,靈堂內(nèi)的尸體忽然破棺而出飞主,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布碌识,位于F島的核電站讽挟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏丸冕。R本人自食惡果不足惜耽梅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胖烛。 院中可真熱鬧眼姐,春花似錦、人聲如沸佩番。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趟畏。三九已至贡歧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赋秀,已是汗流浹背利朵。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猎莲,地道東北人绍弟。 一個(gè)月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像著洼,于是被迫代替她去往敵國和親樟遣。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355