2、棧和隊列

一、棧

1.1 棧的實現

棧(Stack)是限制僅在表的一端進行插入和刪除運算的線性表。java沒有棧這樣的數據結構,如果想利用先進后出(FILO)這樣的數據結構,就必須自己實現。要實現Stack,至少應該包括:

  • pop() 出棧操作,彈出棧頂元素。
  • push(E e) 入棧操作
  • peek() 查看棧頂元素
  • isEmpty()棧為空

另外,實現一個棧,還應該考慮到幾個問題:

  • 棧的初始大小以及棧滿以后如何新增棧空間
  • 對棧進行更新時需要進行同步
    (轉自:https://segmentfault.com/a/1190000002516799

有三種實現:
實現一:數組實現

package cn.list;
import java.util.EmptyStackException;

public class MyArrayStack {
    private int[] array;// 用數組實現
    private int top; // 棧頂指針
    private final static int size = 100;

    public MyArrayStack() {
        array = new int[size];
        top = -1; // 棧空的時候
    }

    // 壓棧
    public void push(int element) {
        if (top == size - 1) {
            resize();//這里進行擴容
        } else
            array[++top] = element;
    }

    // 彈棧
    public int pop() {
        if (top == -1) {
            throw new EmptyStackException();
        }
        return array[top--];
    }

    // 判斷是否為空
    public boolean isEmpty() {
        return top == -1;
    }

    // 返回棧頂元素
    public Integer peek() {
        if (top == -1) {
            throw new EmptyStackException();
        }
        return array[top];
    }
    //返回棧中元素個數
    public int size(){
        return top + 1 ;
    }
    private void resize(){
        assert size == array.length;//表示當條件成立時程序立即中止并返回一個錯誤消息
        int[] newArr = new int[size * 2 + 1];
        System.arraycopy(array, 0, newArr, 0, size);
        array = newArr;
    }
}

實現二:容器實現

package cn.list;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.List;

public class MyArrayListStack<T> implements Stack<T> {
    private List<T> list; // 用容器實現

    MyArrayListStack() {
        list = new ArrayList<T>();
    }

    // 彈棧
    public T pop() {
        if (this.isEmpty() == true) {
            throw new EmptyStackException();
        }
        return list.remove(list.size() - 1);
    }

    // 壓棧
    public void push(T element) {
        list.add(element);
    }

    // 判斷是否為空
    public boolean isEmpty() {
        return list.size() == 0;
    }

    // 返回棧頂元素
    public T peek() {
        if (this.isEmpty() == true) {
            throw new EmptyStackException();
        }
        return list.get(list.size() - 1);
    }
    public int size(){
        return list.size();
    }
}

interface Stack<T> {
    public T pop();

    public void push(T element);

    public boolean isEmpty();

    public T peek();
    public int size();
}

實現三:鏈表實現

package cn.list;
import java.util.LinkedList;

//自己實現棧,使用LinkedList實現
public class MyLinkedListStack<T> {
    private LinkedList<T> list = new LinkedList<T>();

    public MyLinkedListStack() {
    }

    public void clear() {
        list.clear();
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }

    public T peek() {
        if (isEmpty()) {
            throw new java.util.EmptyStackException();
        }
        return list.getLast();
    }

    public T pop() {
        if (isEmpty()) {
            throw new java.util.EmptyStackException();
        }
        return list.removeLast();
    }

    public void push(T element) {
        list.addLast(element);
    }

    public int size(){
        return list.size();
    }
}

1.2 棧的應用

(轉自:http://blog.csdn.net/hengjie2009/article/details/8478863

1.2.1 平衡符號

問題描述: 在編寫代碼并且編譯時,難免會因為少寫了一個')'和被編譯器報錯。也就是說,編譯器會去匹配括號是否匹配。當你輸入了一個'(',很自然編譯器回去檢查你是否有另一個')'符號與之匹配。如果所有的括號都能夠成對出現,那么編譯器是能夠通過的。否則編譯器會報錯。例如字符序列“(a+b)”是匹配的, 而字符序列"(a+b]"則不是。

算法描述如下: 創建一個空棧,讀取字符序列直到結尾。如果字符是開放符號'(''[''{',將其入棧;如果是一個封閉符號')'']''}',則當棧為空時報錯。否則,將棧頂元素彈出。如果彈出的符號不是對應的開放符號,則報錯。當字符序列結束,判斷棧是否為空,為空則報錯

package cn.list;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Stack;

//棧的應用:平衡符號
public class BalanceSigned {

    public static boolean isBalanceChar() {
        Stack<Character> stack = new Stack<Character>();
        String path = "D:/a.txt";
        File file = new File(path);
        try {
            FileInputStream fis = new FileInputStream(file);
            InputStreamReader isr = new InputStreamReader(fis);
            BufferedReader br = new BufferedReader(isr);
            String line = "";
            while ((line = br.readLine()) != null) {
                for (int i = 0; i < line.length(); i++) {
                    switch (line.charAt(i)) {
                    case '[':
                        stack.push('[');
                        break;
                    case '(':
                        stack.push('(');
                        break;
                    case '{':
                        stack.push('{');
                        break;
                    case '/':
                        if (i < line.length() - 1 && line.charAt(i + 1) == '*') {
                            stack.push('/');
                            stack.push('*');
                        }
                        break;
                    case ']':
                        if (stack.size() == 0 || stack.pop() != '[') {
                            System.out.print("illigal character" + "[]");
                            return false;
                        }
                        break;
                    case ')':
                        if (stack.size() == 0 || stack.pop() != '(') {
                            System.out.print("illigal character" + "()");
                            return false;
                        }
                        break;
                    case '}':
                        if (stack.size() == 0 || stack.pop() != '{') {
                            System.out.print("illigal character" + "{}");
                            return false;
                        }
                        break;
                    case '*':
                        if ((i < line.length() - 1 && line.charAt(i + 1) == '/')
                                && (stack.size() < 2 || (stack.pop() != '*' || stack
                                        .pop() != '/'))) {
                            System.out.print("illigal character" + "");
                            return false;
                        }
                        break;
                    default:
                        break;
                    }
                }
            }
            if (stack.size() != 0) {
                System.out.print("error");
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    public static void main(String[] args) {
        boolean flag = isBalanceChar();
        if (flag) {
            System.out.println("是平衡符號");
        } else {
            System.out.println("不是平衡符號");
        }
    }
}

1.2.2 中綴表達式轉后綴表達式

package cn.list;
/**
 * 工具類:
 * 1、中綴表達式轉化為后綴表達式
 * 2、給出一個算術表達式(中綴表達式),直接得到計算結果
 */
import java.util.Stack;
import java.util.regex.Pattern;

//中綴表達式到后綴表達式的轉換
public class Infix2Suffix {
    
    private Infix2Suffix() {}

    // 方法:給出一個算術表達式(中綴表達式),得到計算結果。 例如 (5+8+10)*1,返回23
    public static double stringToArithmetic(String string) {
        return suffixToArithmetic(infixToSuffix(string));
    }

    /**
     * 中綴表達式轉后綴表達式 只處理了+,-,*,/和括號,沒有處理負號及其它運算符,也沒對前綴表達式驗證。*/
    // 方法:中綴表達式轉成后綴表達式
    public static String infixToSuffix(String infix) {
        Stack<Character> stack = new Stack<Character>();
        String suffix = "";
        int length = infix.length();
        for (int i = 0; i < length; i++) {
            Character temp;
            char c = infix.charAt(i);
            switch (c) {
            // 忽略空格
            case ' ':
                break;
            // 碰到'(',push到棧
            case '(':
                stack.push(c);
                break;
            // 碰到'+''-',將棧中所有運算符彈出,送到輸出隊列中
            case '+':
            case '-':
                while (stack.size() != 0) {
                    temp = stack.pop();
                    if (temp == '(') {
                        stack.push('(');
                        break;
                    }
                    suffix += " " + temp;
                }
                stack.push(c);
                suffix += " ";
                break;
            // 碰到'*''/',將棧中所有乘除運算符彈出,送到輸出隊列中
            case '*':
            case '/':
                while (stack.size() != 0) {
                    temp = stack.pop();
                    if (temp == '(' || temp == '+' || temp == '-') {
                        stack.push(temp);
                        break;
                    } else {
                        suffix += " " + temp;
                    }
                }
                stack.push(c);
                suffix += " ";
                break;
            // 碰到右括號,將靠近棧頂的第一個左括號上面的運算符全部依次彈出,送至輸出隊列后,再丟棄左括號
            case ')':
                while (stack.size() != 0) {
                    temp = stack.pop();
                    if (temp == '(')
                        break;
                    else
                        suffix += " " + temp;
                }
                // suffix += " ";
                break;
            // 如果是數字,直接送至輸出序列
            default:
                suffix += c;
            }
        }

        // 如果棧不為空,把剩余的運算符依次彈出,送至輸出序列。
        while (stack.size() != 0) {
            suffix += " " + stack.pop();
        }
        return suffix;
    }

    // 方法:通過后綴表達式求出算術結果
    public static double suffixToArithmetic(String postfix) {

        Pattern pattern = Pattern.compile("\\d+||(\\d+\\.\\d+)"); // 使用正則表達式匹配數字
        String strings[] = postfix.split(" "); // 將字符串轉化為字符串數組
        for (int i = 0; i < strings.length; i++){
            strings[i].trim(); // 去掉字符串首尾的空格
        }
        Stack<Double> stack = new Stack<Double>();

        for (int i = 0; i < strings.length; i++) {

            if (strings[i].equals(""))
                continue;

            // 如果是數字,則進棧
            if ((pattern.matcher(strings[i])).matches()) {

                stack.push(Double.parseDouble(strings[i]));
            } else {
                // 如果是運算符,彈出運算數,計算結果。
                double y = stack.pop();
                double x = stack.pop();
                stack.push(caculate(x, y, strings[i])); // 將運算結果重新壓入棧。
            }
        }
        return stack.pop(); // 彈出棧頂元素就是運算最終結果。

    }

    private static double caculate(double x, double y, String simble) {
        if (simble.trim().equals("+"))
            return x + y;
        if (simble.trim().equals("-"))
            return x - y;
        if (simble.trim().equals("*"))
            return x * y;
        if (simble.trim().equals("/"))
            return x / y;
        return 0;
    }
}

說明:中綴表達式如a+b*c+(d*e+f)*g會轉換成abc*+de*f+g*+

二、隊列

(摘自:http://www.cnblogs.com/smyhvae/p/4793339.html
隊列和棧的實現其實差不多,但是要注意一個問題,隊列是從尾部添加(入隊),從頭部刪除(出隊),有兩種實現方式(數組實現和鏈表實現),對于數組實現有“假溢出”的問題,即經過一些列的出隊和入隊,導致隊列頭尾標記都指向了數組的尾部,此時雖然數組未滿,但是卻不能再插入數據了,于是我們需要實現循環,即當尾部標記指向數組的尾部且數組未滿的情況下,讓尾部標記在下一次插入的時候指向數組的頭位置。

這里我們給出兩種實現方式:
方式一:數組實現

package cn.list;

//循環順序隊列,數組實現
public class MyArrayQueue implements Queue {

    static final int defaultSize = 10; // 默認隊列的長度
    int front; // 隊頭
    int rear; // 隊尾
    int count; // 統計元素個數的計數器
    int maxSize; // 隊的最大長度
    Object[] queue; // 隊列

    public MyArrayQueue() {
        init(defaultSize);
    }

    public MyArrayQueue(int size) {
        init(size);
    }

    public void init(int size) {
        maxSize = size;
        front = rear = 0;
        count = 0;
        queue = new Object[size];
    }

    public void append(Object obj) throws Exception {
        if (count > 0 && front == rear) {
            throw new Exception("隊列已滿!");
        }
        queue[rear] = obj;
        //當隊列滿時讓尾部標記重新指向數組開始位置,達到循環的目的
        rear = (rear + 1) % maxSize;
        count++;
    }

    public Object delete() throws Exception {
        if (isEmpty()) {
            throw new Exception("隊列為空!");
        }
        Object obj = queue[front];
        //開始標記在到達數組最后一個位置時也需要循環
        front = (front + 1) % maxSize;
        count--;
        return obj;
    }
    
    //取得隊列頭
    public Object getFront() throws Exception {
        if (!isEmpty()) {
            return queue[front];
        } else {
            return null;
        }
    }

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

}

// 隊列接口
interface Queue {

    // 入隊
    public void append(Object obj) throws Exception;

    // 出隊
    public Object delete() throws Exception;

    // 獲得隊頭元素
    public Object getFront() throws Exception;

    // 判斷對列是否為空
    public boolean isEmpty();
}

方式二:鏈表實現

package cn.list;


//實現鏈式隊列
public class MyListQueue implements Queue {

    Node front; //隊頭
    Node rear;  //隊尾
    int count; //計數器

    public MyListQueue() {
        init();
    }

    public void init() {
        front = rear = null;
        count = 0;
    }

    public void append(Object obj) throws Exception {
        Node node = new Node(obj, null);

        //如果當前隊列不為空。
        if (rear != null) {
            rear.next = node; //隊尾結點指向新結點
        }
        rear = node; //設置隊尾結點為新結點

        //說明要插入的結點是隊列的第一個結點
        //此時頭尾指向同一個節點
        if (front == null) {
            front = node;
        }
        count++;
    }

    public Object delete() throws Exception {
        if (isEmpty()) {
            new Exception("隊列已空!");
        }
        Node node = front;
        front = front.next;
        count--;
        return node.getElement();
    }

    public Object getFront() throws Exception {
        if (!isEmpty()) {
            return front.getElement();
        } else {
            return null;
        }
    }

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

//結點類
class Node {

  Object element; //數據域
  Node next;  //指針域

  //頭結點的構造方法
  public Node(Node nextval) {
      this.next = nextval;
  }

  //非頭結點的構造方法
  public Node(Object obj, Node nextval) {
      this.element = obj;
      this.next = nextval;
  }

  //獲得當前結點的后繼結點
  public Node getNext() {
      return this.next;
  }

  //獲得當前的數據域的值
  public Object getElement() {
      return this.element;
  }

  //設置當前結點的指針域
  public void setNext(Node nextval) {
      this.next = nextval;
  }

  //設置當前結點的數據域
  public void setElement(Object obj) {
      this.element = obj;
  }

  public String toString() {
      return this.element.toString();
  }
}

三、棧和隊列的應用

(摘自:http://www.cnblogs.com/smyhvae/p/4795984.html

3.1 應用一:判斷回文

隊列有一個很常見的應用就是判斷字符串是否是回文字符串(從頭看和從尾看是一樣的,如soros),這可以將此字符串分別加入到棧和隊列中,然后分別出棧和出隊進行比較,如果都是一樣的則表示是回文字符串。

3.2 棧實現隊列

package cn.list;
import java.util.Stack;
//使用兩個棧實現一個隊列
//說的通俗一點,現在把數據1、2、3分別入棧一,然后從棧一中出來(3、2、1),放到棧二中,
//那么,從棧二中出來的數據(1、2、3)就符合隊列的規律了,即負負得正。

public class MyStackQueue {

    private Stack<Integer> stack1 = new Stack<Integer>();// 執行入隊操作的棧
    private Stack<Integer> stack2 = new Stack<Integer>();// 執行出隊操作的棧

    // 方法:給隊列增加一個入隊的操作
    public void push(int data) {
        stack1.push(data);
    }

    // 方法:給隊列正價一個出隊的操作
    public int pop() throws Exception {
        // stack1中的數據放到stack2之前,先要保證stack2里面是空的(要么一開始就是空的,
        //要么是stack2中的數據出完了),不然出隊的順序會亂的,這一點很容易忘
        if (stack2.empty()) {
            while (!stack1.empty()) {
                // 把stack1中的數據出棧,放到stack2中【核心代碼】
                stack2.push(stack1.pop());
            }
        }
        // stack2為空時,有兩種可能:1、一開始,兩個棧的數據都是空的;2、stack2中的數據出完了
        if (stack2.empty()) { 
            throw new Exception("隊列為空");
        }
        //如果stack2不為空,則直接出棧
        return stack2.pop();
    }
}

3.3 隊列實現棧

package cn.list;
import java.util.ArrayDeque;
import java.util.Queue;

//使用隊列實現棧
public class MyQueueStack {

    Queue<Integer> queue1 = new ArrayDeque<Integer>();
    Queue<Integer> queue2 = new ArrayDeque<Integer>();

    // 方法:入棧操作
    public void push(int data) {
        queue1.add(data);
    }

    // 方法:出棧操作
    /*將1、2、3依次入隊列一, 然后最上面的3留在隊列一,
     * 將下面的1、2入隊列二,將3出隊列一,此時隊列一空了,然后把隊列二中的所有數據入隊列一;
     * 將最上面的2留在隊列一,將下面的3入隊列二。。。依次循環。
     * */
    public int pop() throws Exception {
        int data;
        if (queue1.size() == 0) {
            throw new Exception("棧為空");
        }

        while (queue1.size() != 0) {
            if (queue1.size() == 1) {
                data = queue1.poll();
                while (queue2.size() != 0) { // 把queue2中的全部數據放到隊列一中
                    queue1.add(queue2.poll());
                }
                return data;
            }
            queue2.add(queue1.poll());
        }
        throw new Exception("棧為空");// 不知道這一行的代碼是什么意思
    }
}

3.4 設計含最小函數min()的棧

(摘自:http://blog.csdn.net/sgbfblog/article/details/7752878
要求min、push、pop的時間復雜度都是O(1)
分析:
很剛開始很容易想到一個方法,那就是額外建立一個最小堆保存所有元素,這樣每次獲取最小元素只需要O(1)的時間。但是這樣的話,PUSHPOP操作就需要O(lgn)的時間了(假定棧中元素個數為n),不符合題目的要求。可以使用一個輔助棧。
解法:
使用一個輔助棧來保存最小元素,這個解法簡單不失優雅。設該輔助棧名字為minStack,其棧頂元素為當前棧中的最小元素。這意味著要獲取當前棧中最小元素,只需要返回minStack 的棧頂元素即可。每次執行push操作,檢查push的元素是否小于或等于minStack棧頂元素。如果是,則也push該元素到minStack 中。當執行pop操作的時候,檢查pop的元素是否與當前最小值相等。如果相同,則需要將改元素從minStackpop出去。
實例:
假定有元素3, 2, 5, 4, 2, 1依次入棧,則原始棧中元素為(1), 輔助棧中元素為(2)

(1) (2)
1 null
2 null
4 1
5 2
2 2
3 3

這樣,第1次pop時,1從兩個棧都pop出去;第2次pop時,2從兩個棧都pop出去;第3次pop,元素4從原始棧pop出去,輔助棧不用pop;第4次pop,元素5從原始棧pop出去,輔助棧不需pop;第5次pop,元素2從兩個棧pop出去;第6次pop,元素3從兩個棧都pop出去。我們可以發現,每次push或者pop后,輔助棧的棧頂元素總是當前棧的最小元素。

package cn.list;
import java.util.Stack;

public class GetMinStack {

    private Stack<Integer> stack = new Stack<Integer>();
  //輔助棧:棧頂永遠保存stack中當前的最小的元素
    private Stack<Integer> minStack = new Stack<Integer>(); 


    public void push(int data) {
        stack.push(data);  //直接往棧中添加數據

        //在輔助棧中需要做判斷
        if (minStack.size() == 0 || data <= minStack.peek()) {
            minStack.push(data);
        } else {
            minStack.add(minStack.peek());   //【核心代碼】peek方法返回的是棧頂的元素
        }
    }

    public int pop() throws Exception {
        if (stack.size() == 0) {
            throw new Exception("棧中為空");
        }

        int data = stack.pop();
        minStack.pop();  //核心代碼
        return data;
    }

    public int min() throws Exception {
        if (minStack.size() == 0) {
            throw new Exception("棧中空了");
        }
        return minStack.peek();
    }
}

3.5 判斷棧的push和pop序列是否一致

已知一組數據1、2、3、4、5依次進棧,那么它的出棧方式有很多種,請判斷一下給出的出棧方式是否是正確的?
例如:
數據:
  1、2、3、4、5
出棧1:
  5、4、3、2、1(正確)
出棧2:
  4、5、3、2、1(正確)
出棧3:
  4、3、5、1、2(錯誤)

package cn.list;
import java.util.Stack;
/*
 * 判斷棧的push和pop序列是否一致
 *  通俗一點講:已知一組數據1、2、3、4、5依次進棧,
 *  那么它的出棧方式有很多種,請判斷一下給出的出棧方式是否是正確的?
 *  
 *  例如:
數據:
  1、2、3、4、5
出棧1:
  5、4、3、2、1(正確)
出棧2:
  4、5、3、2、1(正確)
出棧3:
  4、3、5、1、2(錯誤)
 * */

public class StackTest {

    //方法:data1數組的順序表示入棧的順序。現在判斷data2的這種出棧順序是否正確
    public static boolean sequenseIsPop(int[] data1, int[] data2) {
        Stack<Integer> stack = new Stack<Integer>(); //這里需要用到輔助棧

        for (int i = 0, j = 0; i < data1.length; i++) {
            stack.push(data1[i]);
            while (stack.size() > 0 && stack.peek() == data2[j]) {
                stack.pop();
                j++;
            }
        }
        return stack.size() == 0;
    }

    public static void main(String[] args) {

        Stack<Integer> stack = new Stack<Integer>();

        int[] data1 = {1, 2, 3, 4, 5};
        int[] data2 = {4, 5, 3, 2, 1};
        int[] data3 = {4, 5, 2, 3, 1};

        System.out.println(sequenseIsPop(data1, data2));
        System.out.println(sequenseIsPop(data1, data3));
    }
}

說明:下面我們看此題的原理:

  • 我們以前面的序列4、5、3、2、1為例。第一個希望被pop出來的數字是4,因此4需要先push到棧里面。由于push的順序已經由push序列確定了,也就是在把4 push進棧之前,數字1,2,3都需要push到棧里面。此時棧里的包含4個數字,分別是1,2,3,4,其中4位于棧頂。把4 pop出棧后,剩下三個數字1,2,3。接下來希望被pop的是5,由于仍然不是棧頂數字,我們接著在push序列中4以后的數字中尋找。找到數字5后再一次push進棧,這個時候5就是位于棧頂,可以被pop出來。接下來希望被pop的三個數字是3,2,1。每次操作前都位于棧頂,直接pop即可。

  • 再來看序列4、3、5、1、2。pop數字4的情況和前面一樣。把4 pop出來之后,3位于棧頂,直接pop。接下來希望pop的數字是5,由于5不是棧頂數字,我們到push序列中沒有被push進棧的數字中去搜索該數字,幸運的時候能夠找到5,于是把5 push進入棧。此時pop5之后,棧內包含兩個數字1、2,其中2位于棧頂。這個時候希望pop的數字是1,由于不是棧頂數字,我們需要到push序列中還沒有被push進棧的數字中去搜索該數字。但此時push序列中所有數字都已被push進入棧,因此該序列不可能是一個pop序列。

  • 也就是說,如果我們希望pop的數字正好是棧頂數字,直接pop出棧即可;如果希望pop的數字目前不在棧頂,我們就到push序列中還沒有被push到棧里的數字中去搜索這個數字,并把在它之前的所有數字都push進棧。如果所有的數字都被push進棧仍然沒有找到這個數字,表明該序列不可能是一個pop序列。
    通過此原理我們可以手工進行判斷。

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

推薦閱讀更多精彩內容