C++編寫算法(六)——查找問題,二叉查找樹

一藕甩、二叉查找樹

前面一章分析了二分查找這種算法施敢。

要支持高效的插入操作,我們需要鏈?zhǔn)降臄?shù)據(jù)結(jié)構(gòu)狭莱,但是鏈表不能二分查找僵娃,因?yàn)橹虚g值需要從鏈表頭部開始尋找。為了將二分查找的效率和鏈表的靈活性結(jié)合起來(lái)腋妙,需要一種新的數(shù)據(jù)結(jié)構(gòu)默怨。
這種數(shù)據(jù)結(jié)構(gòu)就是二叉樹查找,它能夠?qū)㈡湵聿迦氲撵`活性和有序數(shù)組查找的高效性結(jié)合起來(lái)的符號(hào)表實(shí)現(xiàn)骤素。

要實(shí)現(xiàn)二叉樹匙睹,首先樹的結(jié)構(gòu)需要了解清楚,一般一個(gè)樹可以分為兩個(gè)部分:

樹根 ---> 樹節(jié)點(diǎn)

樹節(jié)點(diǎn)中又存儲(chǔ)著幾個(gè)部分:

存儲(chǔ)的Key值 -- 存儲(chǔ)的value值 -- 節(jié)點(diǎn)左孩子 -- 節(jié)點(diǎn)右孩子

// header.h
#ifndef JUNZALGHEADER_H_
#define JUNZALGHEADER_H_
#include <iostream>
#include <queue>
template<class T>
struct TreeNode
{
    T key;
    int data;
    TreeNode* leftChild = nullptr;
    TreeNode* rightChild = nullptr;
};

template<class T>
class BinaryTree
{
protected:
    TreeNode<T> * root;
public:
    BinaryTree();
//  BinaryTree(T k, int d, int n);
    void SetTreeRoot(const TreeNode<T> * r);
    void SetTreeNnum(int n);
    TreeNode<T>* getRoot() { return root; }

    // Ergodic
    void midOrder();
    void midOrder_ac(TreeNode<T> * currentNode);

    void preOrder();
    void preOrder_ac(TreeNode<T> * currentNode);

    void postOrder();
    void postOrder_ac(TreeNode<T> * currentNode);

    void layerOrder();

    void Show(TreeNode<T> * currentNode) const;
    // insert
    bool treeInsert(TreeNode<T> * currentNode);

    // find
    int treeFind(TreeNode<T> * currentNode);
};
#endif // !JUNZALGHEADER_H_

template<class T>
BinaryTree<T>::BinaryTree()
{
    root = NULL;
}

template<class T>
void BinaryTree<T>::SetTreeRoot(const TreeNode<T> * r)
{
    root = r;
    if (num == 0)
        num += 1;
    else
        num = 1;
}

template<class T>
void BinaryTree<T>::SetTreeNnum(int n)
{
    num = n;
}

template<class T>
void BinaryTree<T>::Show(TreeNode<T> * currentNode) const
{
    std::cout << currentNode->key;
}

template<class T>
void BinaryTree<T>::midOrder()
{
    midOrder_ac(root);
}

template<class T>
void BinaryTree<T>::midOrder_ac(TreeNode<T> * currentNode)
{
    if (currentNode)
    {
        midOrder_ac(currentNode->leftChild);
        Show(currentNode);
        midOrder_ac(currentNode->rightChild);
    }
    else
    {
        return;
    }
}

template<class T>
void BinaryTree<T>::preOrder()
{
    preOrder_ac(root);
}

template<class T>
void BinaryTree<T>::preOrder_ac(TreeNode<T> * currentNode)
{
    if (currentNode)
    {
        Show(currentNode);
        preOrder_ac(currentNode->leftChild);
        preOrder_ac(currentNode->rightChild);
    }
    else
        return;
}

template<class T>
void BinaryTree<T>::postOrder()
{
    postOrder_ac(root);
}

template<class T>
void BinaryTree<T>::postOrder_ac(TreeNode<T>* currentNode)
{
    if (currentNode)
    {
        postOrder_ac(currentNode->leftChild);
        postOrder_ac(currentNode->rightChild);
        Show(currentNode);
    }
    else
        return;
}

template<class T>
void BinaryTree<T>::layerOrder()
{
    std::queue<TreeNode<T>*> q;
    TreeNode<T> *currentNode = root;
    while (currentNode)
    {
        Show(currentNode);
        if(currentNode->leftChild)
            q.push(currentNode->leftChild);
        if(currentNode->rightChild)
            q.push(currentNode->rightChild);
        if (q.empty()) break;
        currentNode = q.front();
        q.pop();
    }
}

template<class T>
bool BinaryTree<T>::treeInsert(TreeNode<T> *currentNode)
{
    if (root == NULL)
    {
        root = currentNode;
        return true;
    }
    else
    {
        TreeNode<T> * NowNode = root;
        while (NowNode)
        {
            if (currentNode->key < NowNode->key)
            {
                if (NowNode->leftChild)
                {
                    NowNode = NowNode->leftChild;
                }
                    
                else
                {
                    NowNode->leftChild = currentNode;
                    break;
                }
            }
            else if (currentNode->key > NowNode->key)
            {
                if (NowNode->rightChild)
                    NowNode = NowNode->rightChild;
                else
                {
                    NowNode->rightChild = currentNode;
                    break;
                }
            }
            else
            {
                NowNode->data = currentNode->data;
                break;
            }
        }
        return true;
    }
}

template<class T>
int BinaryTree<T>::treeFind(TreeNode<T> *currentNode)
{

    if (root == NULL)
    {
        return -1;
    }
    else
    {
        TreeNode<T> *NowNode = root;
        while (NowNode)
        {
            if (currentNode->key == NowNode->key)
            {
                NowNode->data = currentNode->data;
                return NowNode->data;
            }
            else if (currentNode->key > NowNode->key)
                NowNode = NowNode->rightChild;
            else if (currentNode->key < NowNode->key)
                NowNode = NowNode->leftChild;
        }
        return NowNode->data;
    }
}
// main.cpp
#include<iostream>
#include "JunzAlgHeader.h"
using namespace std;
const int NUM = 7;
int main()
{
    BinaryTree<char> tree;
    TreeNode<char> temp[NUM];
    for (int i = 0; i < NUM; i++)
    {
        cout << "Add the treenode: \n";
        cout << "Key: (char)";
        cin >> (temp[i].key);
        cout << "Value:(int)";
        cin >> (temp[i].data);
        // tree insert
        tree.treeInsert(&temp[i]);
    }

    cout << "Middle Order Ergodic: \n";
    tree.midOrder();
    cout << endl;
    cout << "Layer Order Ergodic: \n";
    tree.layerOrder();
    cout << endl;

    cout << "Tree find:\n";
    TreeNode<char> temp1;
    temp1.data = 4;
    temp1.key = 'A';
    cout << tree.treeFind(&temp1) << endl;
    TreeNode<char> temp2;
    temp2.data = 2;
    temp2.key = 'A';
    cout << tree.treeFind(&temp2) <<endl;
    system("pause");
    return 0;
}

編寫這個(gè)程序是遇到一個(gè)問題:就是使用常規(guī)的頭文件聲明類及方法济竹、.cpp文件定義類方法痕檬、main函數(shù)執(zhí)行功能這個(gè)套路是行不通的,程序會(huì)報(bào)Linker Tools Error LNK2019的錯(cuò)誤送浊。
原因:采用的是特殊的模板類進(jìn)行代碼的編寫梦谜。

編譯器處理模板類時(shí),如有一些模板類以及函數(shù)袭景,則會(huì)進(jìn)行如下操作唁桩。
1、模板函數(shù)和類在編譯時(shí)期間并未生成具體的二進(jìn)制代碼耸棒, 在main函數(shù)中也沒有嵌入這個(gè)類或函數(shù)的代碼朵夏,只是包含了一句 call +類名/函數(shù)名
2、編譯階段榆纽,在main函數(shù)中發(fā)現(xiàn)了模板類和的引用仰猖,但是main.obj


有序性相關(guān)方法和刪除操作

1捏肢、首先,我們可以獲取樹的最大鍵和最小鍵饥侵。

其思路非常簡(jiǎn)單鸵赫,最小鍵即處于樹最左邊的鍵,最大鍵即處于樹最右邊的鍵躏升。獲取最小鍵與最大鍵的方法我們可以采用遞歸來(lái)實(shí)現(xiàn)辩棒。

 // ac
    TreeNode<T> * Min_ac(TreeNode<T> *currentNode);
    TreeNode<T> * Max_ac(TreeNode<T> *currentNode);
// min & max find
    T & Min();
    T & Max();

// definition
//private Min methods
template<class T>
TreeNode<T>* BinaryTree<T>::Min_ac(TreeNode<T> * currentNode)
{
    if (currentNode->leftChild == NULL)
        return currentNode;
    return Min_ac(currentNode->leftChild);
}
//private Max methods
template<class T>
TreeNode<T>* BinaryTree<T>::Max_ac(TreeNode<T> * currentNode)
{
    if (currentNode->rightChild == NULL)
        return currentNode;
    return Max_ac(currentNode->rightChild);
}

// public Min
template<class T>
T & BinaryTree<T>::Min()
{
    return (Min_ac(root)->key);
}

//public Max
template<class T>
T & BinaryTree<T>::Max()
{
    return (Max_ac(root)->key);
}
2、向上取整和向下取整

在向下取整中膨疏,若需要判斷的鍵Key到達(dá)某個(gè)節(jié)點(diǎn)時(shí)一睁,它需要尋找該節(jié)點(diǎn)的左子樹,因?yàn)樾∮诘扔趉ey的鍵值就在該節(jié)點(diǎn)的左子樹中佃却。只有當(dāng)右子樹存在比Key小的鍵值時(shí)者吁,最大的小于等于鍵值的鍵才會(huì)出現(xiàn)在右子樹中。因此饲帅,利用這個(gè)思路复凳,可以寫出floor向下取整函數(shù)。當(dāng)然灶泵,向上取整就是把上述思路的左右對(duì)調(diào)一下育八。

// ac
    TreeNode<T> * floor_ac(TreeNode<T> *currentNode, T k);
    TreeNode<T> * ceiling_ac(TreeNode<T> *currentNode, T k);
    T floor(T k);
    T ceiling(T k);
//
// private floor method
template<class T>
TreeNode<T> * BinaryTree<T>::floor_ac(TreeNode<T> * currentNode, T k)
{
    if (currentNode == NULL)
        return NULL;
    if (k < currentNode->key)
        return floor_ac(currentNode->leftChild, k);
    if (k == currentNode->key)
        return currentNode;
    TreeNode<T> * temp;
    temp = floor_ac(currentNode->rightChild, k);
    if (temp != NULL)
        return temp;
    else
        return currentNode;
}

// private ceiling method
template<class T>
TreeNode<T> * BinaryTree<T>::ceiling_ac(TreeNode<T> *currentNode, T k)
{
    if (currentNode == NULL)
        return NULL;
    if (k > currentNode->key)
        return ceiling_ac(currentNode->rightChild, k);
    if (k == currentNode->key)
        return currentNode;
    TreeNode<T> *temp;
    temp = ceiling_ac(currentNode->leftChild, k);
    if (temp != NULL)
        return temp;
    else
        return currentNode;
}

// public floor methods
template<class T>
T BinaryTree<T>::floor(T k)
{
    TreeNode<T> * Node = floor_ac(root, k);
    if (Node == NULL)
        return NULL;
    return Node->key;
}

//public ceiling methods
template<class T>
T BinaryTree<T>::ceiling(T k)
{
    TreeNode<T>* Node = ceiling_ac(root, k);
    if (Node == NULL)
        return NULL;
    return Node->key;
}

3、選擇操作

選擇操作是將排名為r的節(jié)點(diǎn)的key值輸出赦邻。具體思路是:
①?gòu)母?jié)點(diǎn)開始髓棋,計(jì)算其左子樹的結(jié)點(diǎn)數(shù)。
②如果左子樹的結(jié)點(diǎn)數(shù)小于r惶洲,則繼續(xù)往左子樹尋找排名為r的結(jié)點(diǎn)仲锄。
③如果左子樹的結(jié)點(diǎn)數(shù)大于r,則向其右子樹中尋找排名為r-左子樹結(jié)點(diǎn)數(shù)-1的結(jié)點(diǎn)
④如果左子樹的結(jié)點(diǎn)數(shù)等于r湃鹊,則返回其左子樹的根結(jié)點(diǎn)儒喊。
⑤遞歸1~4過(guò)程,直至找到相應(yīng)結(jié)點(diǎn)币呵。

// private select method
template<class T>
TreeNode<T> * BinaryTree<T>::select_ac(TreeNode<T> *currentNode, int k)
{
    if (currentNode == NULL)
        return NULL;
    int size_leftChild = size(currentNode->leftChild);
    if (size_leftChild > k)
        return select_ac(currentNode->leftChild, k);
    else if (size_leftChild < k)
        return select_ac(currentNode->rightChild, k - size_leftChild - 1);
    else
        return currentNode;
}
//public select method
template<class T>
T BinaryTree<T>::select(int k)
{
    return select_ac(root, k)->key;
}

4怀愧、排名操作

排名操作其實(shí)是選擇操作的逆過(guò)程。即它是通過(guò)查找結(jié)點(diǎn)中的key值余赢,計(jì)算該結(jié)點(diǎn)有r個(gè)比它的key值小的結(jié)點(diǎn)芯义,從而得出排名r。 具體思路是:
①對(duì)比需要查找的key值與當(dāng)前根結(jié)點(diǎn)的key值妻柒。
②如果查找key值小于根結(jié)點(diǎn)key值扛拨,則繼續(xù)在根結(jié)點(diǎn)的左子樹尋找。
③如果查找的key值大于根結(jié)點(diǎn)的key值举塔,則將排名更新至1+當(dāng)前根節(jié)點(diǎn)左子樹的結(jié)點(diǎn)數(shù)绑警,再在右子樹尋找求泰。
④如果查找的key值等于根結(jié)點(diǎn)的key值,則排名即為其左子樹的結(jié)點(diǎn)數(shù)计盒。
⑤遞歸1~4過(guò)程渴频,直至找到相應(yīng)結(jié)點(diǎn),返回排名北启。

// private rank method
template<class T>
int BinaryTree<T>::rank_ac(TreeNode<T> *currentNode, T ke)
{
    int size_temp = size(currentNode->leftChild);
    if (ke == currentNode->key)
        return size_temp;
    else if (ke < currentNode->key)
        return rank_ac(currentNode->leftChild, ke);
    else
        return 1 + size_temp + rank_ac(currentNode->rightChild, ke);

}
template<class T>
int BinaryTree<T>::rank(T ke)
{
    return rank_ac(root, ke);
}

5卜朗、刪除最大鍵和刪除最小鍵

刪除最小(大)鍵首先需要找到最泄敬濉(大)鍵场钉。所用的思路與上述的Min()和Max()函數(shù)類似。
尋找到最行柑巍(大)鍵后逛万,需要對(duì)其進(jìn)行刪處,并將它的右子樹連接到最屑缒啤(大)鍵的父結(jié)點(diǎn)中泣港。

// private delete min method
template<class T>
TreeNode<T> * BinaryTree<T>::deleteMin_ac(TreeNode<T> * currentNode)
{
    if (currentNode->leftChild == NULL)
        return currentNode->rightChild;
    currentNode->leftChild = deleteMin_ac(currentNode->leftChild);
    return currentNode;
}
// public delete min
template<class T>
bool BinaryTree<T>::deleteMin()
{
    if (root)
    {
        root = deleteMin_ac(root);
        return true;
    }
    else
    {
        return false;
    }
}
// private delete max method
template<class T>
TreeNode<T> * BinaryTree<T>::deleteMax_ac(TreeNode<T> * currentNode)
{
    if (currentNode->rightChild == NULL)
        return currentNode->rightChild;
    currentNode->rightChild = deleteMax_ac(currentNode->rightChild);
    return currentNode;
}
// public delete max
template<class T>
bool BinaryTree<T>::deleteMax()
{
    if (root)
    {
        root = deleteMax_ac(root);
        return true;
    }
    else
        return false;
}

小記:尋找與刪除最性葜场(大)結(jié)點(diǎn)的思路是比較容易的价匠,唯一的難題在于如何將最小(大)結(jié)點(diǎn)的右子樹連接到最星好俊(大)結(jié)點(diǎn)的父結(jié)點(diǎn)上踩窖,保持樹的有序性。

currentNode->leftChild = deleteMin_ac(currentNode->leftChild);

這個(gè)句式巧妙地解決了這個(gè)難題晨横,(以刪除最小葉結(jié)點(diǎn)為例)通過(guò)每一層的遞歸洋腮,將遞歸的結(jié)果付給當(dāng)前遍歷結(jié)點(diǎn)的左指針。在未找到最小值前手形,deleteMin_ac()函數(shù)當(dāng)前層會(huì)返回該葉結(jié)點(diǎn)的原左子結(jié)點(diǎn)啥供,保護(hù)了未被操作的子結(jié)點(diǎn)。當(dāng)尋找到最小值時(shí)库糠,deleteMin_ac()函數(shù)返回最小葉結(jié)點(diǎn)的右子結(jié)點(diǎn)伙狐,返回上層之后自然能夠?qū)⒆钚∪~節(jié)點(diǎn)的父結(jié)點(diǎn)與最小葉結(jié)點(diǎn)的右子結(jié)點(diǎn)相連。此時(shí)瞬欧,最小葉結(jié)點(diǎn)輸入失連狀態(tài)贷屎,被系統(tǒng)自動(dòng)回收。
(刪除最大葉結(jié)點(diǎn)的原理也如此)


6艘虎、刪除樹中的某個(gè)結(jié)點(diǎn)

刪除樹中的某個(gè)結(jié)點(diǎn)是樹中最難的操作唉侄,根據(jù)理解,刪除樹中結(jié)點(diǎn)需要分為4個(gè)步驟:
①尋找需要?jiǎng)h除的節(jié)點(diǎn)野建。
②找到需要?jiǎng)h除的節(jié)點(diǎn)的位置后属划,查找其右子樹的最小葉節(jié)點(diǎn)t恬叹。
③t的右指針指向需要?jiǎng)h除的節(jié)點(diǎn)的右子樹根節(jié)點(diǎn),t的左指針指向需要?jiǎng)h除的節(jié)點(diǎn)的左指針榴嗅。
④將需要?jiǎng)h除的節(jié)點(diǎn)的父結(jié)點(diǎn)左/右指針指向t妄呕。

// private delete node assisted method
template<class T>
TreeNode<T> * BinaryTree<T>::delete_as(TreeNode<T> * currentNode, T ke)
{
    if (currentNode == NULL)
        return NULL;
    if (ke < currentNode->key)
        currentNode->leftChild = delete_as(currentNode->leftChild, ke);
    else if (ke > currentNode->key)
        currentNode->rightChild = delete_as(currentNode->rightChild, ke);
    else
    {
        if (currentNode->leftChild == NULL)
            return currentNode->rightChild;
        if (currentNode->rightChild == NULL)
            return currentNode->leftChild;
        TreeNode<T> *t = currentNode;
        currentNode = Min_ac(t->rightChild);
        currentNode->rightChild = deleteMin_ac(t->rightChild);
        currentNode->leftChild = t->leftChild;
    }
    return currentNode;
}
// public deleteNode
template<class T>
bool BinaryTree<T>::deleteNode(T ke)
{
    if (root)
    {
        delete_as(root, ke);
        return true;
    }

    else
        return false;
    
}

小記:編寫刪除方法的思路十分清晰。但是有一點(diǎn)困擾了我很久嗽测,思路③中左指針和右指針的分配是有順序的绪励。從程序上看,如果

// code1
currentNode->rightChild = deleteMin_ac(t->rightChild);
currentNode->leftChild = t->leftChild;

調(diào)換順序?yàn)?/p>

// code2
currentNode->leftChild = t->leftChild;
currentNode->rightChild = deleteMin_ac(t->rightChild);

那么唠粥,在樹圖疏魏。。晤愧。中大莫,code2會(huì)丟失結(jié)點(diǎn)A,導(dǎo)致遍歷程序無(wú)休止的運(yùn)行官份。其原因是本身結(jié)點(diǎn)H的左孩結(jié)點(diǎn)為NULL只厘,在上述程序中,為了刪除結(jié)點(diǎn)E舅巷,需要把結(jié)點(diǎn)A的父結(jié)點(diǎn)修改為結(jié)點(diǎn)H羔味。code2執(zhí)行第一行時(shí)是沒有問題的,但執(zhí)行第二行時(shí)钠右,因?yàn)镠的左子結(jié)點(diǎn)已經(jīng)賦為結(jié)點(diǎn)A赋元,則deleteMin_ac函數(shù)旨在尋找最小結(jié)點(diǎn),因此需要尋找E的右子樹的最左的子結(jié)點(diǎn)飒房。在原來(lái)的樹中搁凸,E的右子樹的最小結(jié)點(diǎn)為H,而執(zhí)行了code2第一行的操作后狠毯,E的右子樹的最小結(jié)點(diǎn)變?yōu)锳护糖,因此刪除A后,將C接到H的左指針處嚼松。H的右指針接到結(jié)點(diǎn)R嫡良,造成整個(gè)程序的混亂。因此惜颇,將code2代碼改為code1皆刺,才能實(shí)現(xiàn)正確刪除結(jié)點(diǎn)的功能。


7凌摄、范圍查找

范圍查找是輸入一個(gè)區(qū)間羡蛾,如[F,T]。遍歷整個(gè)樹锨亏,找到落入該范圍的節(jié)點(diǎn)痴怨。因此忙干,范圍查找與層序遍歷有一些類似,相類似的地方有兩點(diǎn):①兩者同樣需要遍歷整個(gè)樹浪藻,②遍歷整個(gè)樹都需要引入隊(duì)列的思想捐迫。范圍查找的具體步驟為:
(1)遍歷整個(gè)樹
(2)尋找隊(duì)列中落入?yún)^(qū)間的葉結(jié)點(diǎn)
(3)將這些落入?yún)^(qū)間的葉結(jié)點(diǎn)以隊(duì)列(類似與層序)的形式輸出

// public search method
template <class T>
void BinaryTree<T>::keys_ac(std::queue<TreeNode<T>*> &q, T lo, T hi)
{
    if (root == NULL)
        return;
    std::queue<TreeNode<T>*> temp_saver;
    TreeNode<T>* ptNode = root;
    while (ptNode)
    {
        if (ptNode->key >= lo && ptNode->key <= hi)
            q.push(ptNode);
        if (ptNode->leftChild)
            temp_saver.push(ptNode->leftChild);
        if (ptNode->rightChild)
            temp_saver.push(ptNode->rightChild);
        if (temp_saver.empty()) break;
        ptNode = temp_saver.front();
        temp_saver.pop();
    }
}

小記:范圍查找需要采用兩個(gè)隊(duì)列進(jìn)行實(shí)現(xiàn),其中一個(gè)隊(duì)列用于遍歷整個(gè)樹(temp_saver)爱葵,另一個(gè)隊(duì)列用于存儲(chǔ)落入?yún)^(qū)間的葉結(jié)點(diǎn)(q)施戴。采用函數(shù)引用隊(duì)列q (queue & q),便于修改隊(duì)列q的內(nèi)容萌丈。在函數(shù)調(diào)用之后輸出隊(duì)列q即能夠找到落入?yún)^(qū)間的葉結(jié)點(diǎn)赞哗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辆雾,隨后出現(xiàn)的幾起案子肪笋,更是在濱河造成了極大的恐慌,老刑警劉巖度迂,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藤乙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惭墓,警方通過(guò)查閱死者的電腦和手機(jī)坛梁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诅妹,“玉大人罚勾,你說(shuō)我怎么就攤上這事毅人】越疲” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵丈莺,是天一觀的道長(zhǎng)划煮。 經(jīng)常有香客問我,道長(zhǎng)缔俄,這世上最難降的妖魔是什么弛秋? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮俐载,結(jié)果婚禮上蟹略,老公的妹妹穿的比我還像新娘。我一直安慰自己遏佣,他們只是感情好挖炬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著状婶,像睡著了一般意敛。 火紅的嫁衣襯著肌膚如雪馅巷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天草姻,我揣著相機(jī)與錄音钓猬,去河邊找鬼。 笑死撩独,一個(gè)胖子當(dāng)著我的面吹牛敞曹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播综膀,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼异雁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了僧须?” 一聲冷哼從身側(cè)響起纲刀,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎担平,沒想到半個(gè)月后示绊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暂论,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年面褐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片取胎。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡展哭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闻蛀,到底是詐尸還是另有隱情匪傍,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布觉痛,位于F島的核電站役衡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏薪棒。R本人自食惡果不足惜手蝎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俐芯。 院中可真熱鬧棵介,春花似錦、人聲如沸吧史。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至逆巍,卻和暖如春及塘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锐极。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工笙僚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灵再。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓肋层,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親翎迁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栋猖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

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