《數(shù)據(jù)結(jié)構與算法之美》16~20筆記

關于我的倉庫

  • 這篇文章是我為面試準備的學習總結(jié)中的一篇
  • 我將準備面試中找到的所有學習資料,寫的Demo,寫的博客都放在了這個倉庫里iOS-Engineer-Interview
  • 歡迎star????
  • 其中的博客在簡書,CSDN都有發(fā)布
  • 博客中提到的相關的代碼Demo可以在倉庫里相應的文件夾里找到

前言

  • 該系列為學習《數(shù)據(jù)結(jié)構與算法之美》的系列學習筆記
  • 總結(jié)規(guī)律為一周一更,內(nèi)容包括其中的重要知識帶你,以及課后題的解答
  • 算法的學習學與刷題并進,希望能真正養(yǎng)成解算法題的思維
  • LeetCode刷題倉庫:LeetCode-All-In
  • 多說無益,你應該開始打代碼了

16講二分查找(下):如何快速定位IP對應的省份地址

  • 十個二分九個錯,確實感觸很深,一個字,學

  • 唐納德·克努特(Donald E.Knuth)在《計算機程序設計藝術》的第3卷《排序和查找》中說到:“盡管第一個二分查找算法于1946年出現(xiàn),然而第一個完全正確的二分查找算法實現(xiàn)直到1962年才出現(xiàn)。”

變體一:查找第一個值等于給定值的元素

503c572dd0f9d734b55f1bd12765c4f8
  • hard做法【簡潔】
// 變體一:查找第一個值等于給定值的元素[hard]
int BinarySearchXH(vector<int> &arr, int value) {
    
    int len = arr.size();
    int left = 0;
    int right = len - 1;
    int mid = left + ((right - left) >> 1);
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (arr[mid] >= value) {
            right = mid - 1;
        } else {
            left = mid + 1;
        } 
    }
    
    if (left < len && arr[left] == value) {
        return left;
    } else {
        return -1;
    }
}
// 其實這種做法也沒有那么難理解,只是對arr[mid] = value的情況將它視作沒找到,繼續(xù)進行二分
// 就和上一章總結(jié)的那樣,如果出現(xiàn)的是return right會返回最接近那個,這里使用的是return left返回的就是第一個了
  • easy做法:
// 變體一:查找第一個值等于給定值的元素[easy]
int BinarySearchXE(vector<int> &arr, int value) {
    
    int len = arr.size();
    int left = 0;
    int right = len - 1;
    int mid = left + ((right - left) >> 1);
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (arr[mid] > value) {
            right = mid - 1;
        } else if (arr[mid] < value) {
            left = mid + 1;
        } else {
            if ((mid == 0) || (arr[mid - 1] != value)) {
                return mid;
            } else {
                right = mid - 1;
            }
        }
    }
    
    return -1;
}

// 沒啥好說的,很簡單就是對與找到的mid不一定是第一個,遙往前找
// 這里其實找到一個一直往前推也可以,當然這樣慢了,沒有二分的優(yōu)越性了

變體二:查找最后一個值等于給定值的元素

  • 這題目好劃。。。
// 變體二:查找最后一個值等于給定值的元素
int BinarySearchY(vector<int> &arr, int value) {
    
    int len = arr.size();
    int left = 0;
    int right = len - 1;
    int mid = left + ((right - left) >> 1);
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (arr[mid] > value) {
            right = mid - 1;
        } else if (arr[mid] < value) {
            left = mid + 1;
        } else {
            if ((mid == len - 1) || (arr[mid + 1] != value)) {
                return mid;
            } else {
                left = mid + 1;
            }
        }
    }
    
    return -1;
}

變體三:查找第一個大于等于給定值的元素

  • 感覺老師確實有一手
  • 對于這類題目,我以前可以會在原二分基礎上修修改改碰碰運氣,但是老師給了個其實還是可以按照二分的思路一點點的看
// 變體三:查找第一個大于等于給定值的元素
int BinarySearchZ(vector<int> &arr, int value) {
    
    int len = arr.size();
    int left = 0;
    int right = len - 1;
    int mid = left + ((right - left) >> 1);
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (arr[mid] >= value) {
            if ((mid == 0) || (arr[mid - 1] < value)) {
                return mid;
            } else {
                right = mid - 1;
            }
        } else {
            left = mid + 1;
        }
    }   
    return -1;
}

變體四:查找最后一個小于等于給定值的元素

  • 劃水呀 劃水呀
// 變體四:查找最后一個小于等于給定值的元素
int BinarySearchZA(vector<int> &arr, int value) {
    
    int len = arr.size();
    int left = 0;
    int right = len - 1;
    int mid = left + ((right - left) >> 1);
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (arr[mid] > value) {
            right = mid - 1;
        } else {
            if ((mid == len - 1) || (arr[mid + 1] > value)) {
                return mid;
            } else {
                left = mid + 1;
            }
        }
    }
    
    return -1;
}

開篇題目:如何快速定位出一個IP地址的歸屬地?

  • 當我們想要查詢202.102.133.13這個IP地址的歸屬地時,我們就在地址庫中搜索,發(fā)現(xiàn)這個IP地址落在[202.102.133.0, 202.102.133.255]這個地址范圍內(nèi),那我們就可以將這個IP地址范圍對應的歸屬地“山東東營市”顯示給用戶了。
[202.102.133.0, 202.102.133.255]  山東東營市 
[202.102.135.0, 202.102.136.255]  山東煙臺 
[202.102.156.34, 202.102.157.255] 山東青島 
[202.102.48.0, 202.102.48.255] 江蘇宿遷 
[202.102.49.15, 202.102.51.251] 江蘇泰州 
[202.102.56.0, 202.102.56.255] 江蘇連云港
  • 當我們要查詢某個IP歸屬地時,我們可以先通過二分查找,找到最后一個起始IP小于等于這個IP的IP區(qū)間,然后,檢查這個IP是否在這個IP區(qū)間內(nèi),如果在,我們就取出對應的歸屬地顯示;如果不在,就返回未查找到。

課后題:LeetCode 33 搜索旋轉(zhuǎn)有序數(shù)組

假設按照升序排序的數(shù)組在預先未知的某個點上進行了旋轉(zhuǎn)。

( 例如,數(shù)組 [0,1,2,4,5,6,7] 可能變?yōu)?[4,5,6,7,0,1,2] )。

搜索一個給定的目標值,如果數(shù)組中存在這個目標值,則返回它的索引,否則返回 -1 。

你可以假設數(shù)組中不存在重復的元素。

你的算法時間復雜度必須是 O(log n) 級別。

示例 1:

輸入: nums = [4,5,6,7,0,1,2], target = 0
輸出: 4
示例 2:

輸入: nums = [4,5,6,7,0,1,2], target = 3
輸出: -1

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) return mid;
            else if (nums[mid] < nums[right]) {
                if (nums[mid] < target && nums[right] >= target) left = mid + 1;
                else right = mid - 1;
            } else {
                if (nums[left] <= target && nums[mid] > target) right = mid - 1;
                else left = mid + 1;
            }
        }
        return -1;
    }
};
  • 其實也很簡單,就是找有序的那段進行判斷,不在里面就分另一段

17講跳表:為什么Redis一定要用跳表來實現(xiàn)有序集合

  • 這個很新鮮啊,跳表,不說了哦,學
  • 但是它確實是一種各方面性能都比較優(yōu)秀的動態(tài)數(shù)據(jù)結(jié)構,可以支持快速的插入、刪除、查找操作,寫起來也不復雜,甚至可以替代(Red-black tree)

理解跳表

  • 先看原始單鏈表數(shù)據(jù)
e18303fcedc068e5a168de04df956f6d
  • 下一步我們是每兩個節(jié)點中提取一個座位索引放在索引層
14753c824a5ee4a976ea799727adc78e
  • 現(xiàn)在假如我們要查找一個16,在索引層發(fā)現(xiàn)在13 17之間,我們就會從13下降到原始鏈表層,在原始鏈表層繼續(xù)查找,就找到16了

  • 當然一層看到不夠,我們會繼續(xù)往上建,直到索引層只剩2,3個索引值

492206afe5e2fef9f683c7cff83afa65
  • 現(xiàn)在,我們來看下假如查找一個64個節(jié)點的鏈表的,我們只需要經(jīng)過幾個節(jié)點呢?
46d283cd82c987153b3fe0c76dfba8a9
  • 好快!

跳表的優(yōu)越性

  • 第k級索引的結(jié)點個數(shù)是第k-1級索引的結(jié)點個數(shù)的1/2,那第k級索引結(jié)點的個數(shù)就是n/(2k)。
  • 在每一級,注意是每一級,都最多只要經(jīng)過三個節(jié)點就能遍歷完
d03bef9a64a0368e6a0d23ace8bd450c
  • 此時我們實現(xiàn)了查找任意數(shù)據(jù)的時間復雜度為O(logn),在單鏈表中實現(xiàn)了二分查找,空間換時間計劃通?
  • 空間復雜度O(n)

跳表的操作

  • 我們看一個插入數(shù)據(jù)的操作,插入一個數(shù)據(jù)6
65379f0651bc3a7cfd13ab8694c4d26c
  • 這里會出現(xiàn)一個問題,我們在兩集索引之間插入過多節(jié)點會導致其搜索效率降低,這就還涉及到了跳表索引的動態(tài)更新
  • 跳表通過隨機函數(shù)的方式進行動態(tài)更新,比如每次能生成一個隨機數(shù)k,在插入數(shù)據(jù)后,就在第1到k層索引層中插入該數(shù)據(jù)
a861445d0b53fc842f38919365b004a7
  • 不要問,問就是已經(jīng)懂了

解答開篇:為什么Redis中的有序集合是通過跳表來實現(xiàn)的

  • 其中,插入、刪除、查找以及迭代輸出有序序列這幾個操作,紅黑樹也可以完成,時間復雜度跟跳表是一樣的。但是,按照區(qū)間來查找數(shù)據(jù)這個操作,紅黑樹的效率沒有跳表高。
  • 對于按照區(qū)間查找數(shù)據(jù)這個操作,跳表可以做到O(logn)的時間復雜度定位區(qū)間的起點,然后在原始鏈表中順序往后遍歷就可以了。這樣做非常高效。

課后題:在今天的內(nèi)容中,對于跳表的時間復雜度分析,我分析了每兩個結(jié)點提取一個結(jié)點作為索引的時間復雜度。如果每三個或者五個結(jié)點提取一個結(jié)點作為上級索引,對應的在跳表中查詢數(shù)據(jù)的時間復雜度是多少呢?

  • 如果每三個或者五個節(jié)點提取一個節(jié)點作為上級索引,那么對應的查詢數(shù)據(jù)時間復雜度,應該也還是 O(logn)。
  • 假設每 5 個節(jié)點提取,那么最高一層有 5 個節(jié)點,而跳表高度為 log5n,每層最多需要查找 5 個節(jié)點,即 O(mlogn) 中的 m = 5,最終,時間復雜度為 O(logn)。
  • 空間復雜度也還是 O(logn),雖然省去了一部分索引節(jié)點,但是似乎意義不大。

18講散列表(上):Word文檔中的單詞拼寫檢查功能是如何實現(xiàn)的

  • 散列即是哈希
  • 散列表用的是數(shù)組支持按照下標隨機訪問數(shù)據(jù)的特性,所以散列表其實就是數(shù)組的一種擴展,由數(shù)組演化而來。可以說,如果沒有數(shù)組,就沒有散列表。
  • 假如我們用十個數(shù)字0到9要放到長度為10的數(shù)組里面,那我們只要把對應數(shù)字放在相應下標上即可
  • 但是如果我們同樣是存放十個數(shù)據(jù),可它的值不是0到9,那就需要我們通過哈希函數(shù)去計算散列值
92c89a57e21f49d2f14f4424343a2773

散列函數(shù)

  • 散列函數(shù)計算得到的散列值是一個非負整數(shù)

  • 如果key1 = key2,那hash(key1) == hash(key2)

  • 如果key1 ≠ key2,那hash(key1) ≠ hash(key2)

散列沖突

開放尋址法

  • 線性探測(Linear Probing)通過散列函數(shù)求出的散列值如果上面已經(jīng)有數(shù)據(jù)了,就繼續(xù)往下搜索,直到找到空閑位置,進行插入
5c31a3127cbc00f0c63409bbe1fbd0d5
  • 查找時比較數(shù)組中下標為散列值的元素和要查找的元素,如果一直查找到空閑位置,還是沒找到該元素,說明它不在表里。這里我覺得你要分清楚,一般我們哈希表是用來進行記錄的,查找是為了判斷該元素是否在表里,比如C++ map里面的find()函數(shù),也就是說雖然我們已經(jīng)有這個數(shù)據(jù)了,還要去查找看起來很蠢,但這并不矛盾,我們在意的,僅僅是記錄本身而已
9126b0d33476777e7371b96e676e90ff
  • 但這樣還是有個問題,由于我們會進行線性探測,所以的當我們刪除數(shù)據(jù)的時候,如果我們刪除的數(shù)據(jù)時通過線性探測得出來的,這樣就會出現(xiàn)數(shù)據(jù)本身應該算在空閑位置那一塊
  • 什么意思呢?就是說如果在刪除數(shù)據(jù)時,我們簡單的清空元素會導致后面進行線性探測的時候,在查看到該位置時就認做找不到該元素【因為已經(jīng)到空閑位置了】,所以我們要對后面刪除的位置設置deleted標志位
fe7482ba09670cbe05a9dfe4dd49bd1d
  • 我們可以將刪除的元素,特殊標記為deleted。當線性探測查找的時候,遇到標記為deleted的空間,并不是停下來,而是繼續(xù)往下探測。
  • 線性探測法其實存在很大問題。當散列表中插入的數(shù)據(jù)越來越多時,散列沖突發(fā)生的可能性就會越來越大,空閑位置會越來越少,線性探測的時間就會越來越久。極端情況下,我們可能需要探測整個散列表,所以最壞情況下的時間復雜度為O(n)。同理,在刪除和查找時,也有可能會線性探測整張散列表,才能找到要查找或者刪除的數(shù)據(jù)
  • 二次探測(Quadratic probing):線性探測每次探測的步長是1,那它探測的下標序列就是hash(key)+0,hash(key)+1,hash(key)+2……而二次探測探測的步長就變成了原來的“二次方”,也就是說,它探測的下標序列就是hash(key)+0,hash(key)+12,hash(key)+22……
  • 雙重散列(Double hashing):僅要使用一個散列函數(shù)。我們使用一組散列函數(shù)hash1(key),hash2(key),hash3(key)……我們先用第一個散列函數(shù),如果計算得到的存儲位置已經(jīng)被占用,再用第二個散列函數(shù),依次類推,直到找到空閑的存儲位置
  • 裝載因子(load factor):表示空位的多少,填入表中的元素個數(shù)/散列表的長度,裝載因子越大,說明空閑位置越少,沖突越多,散列表的性能會下降

鏈表法

  • 每個“桶(bucket)”或者“槽(slot)”會對應一條鏈表,所有散列值相同的元素我們都放到相同槽位對應的鏈表中。
a4b77d593e4cb76acb2b0689294ec17f
  • 當插入的時候,我們只需要通過散列函數(shù)計算出對應的散列槽位,將其插入到對應鏈表中即可,所以插入的時間復雜度是O(1)。當查找、刪除一個元素時,我們同樣通過散列函數(shù)計算出對應的槽,然后遍歷鏈表查找或者刪除
  • 這兩個操作的時間復雜度跟鏈表的長度k成正比,也就是O(k)。對于散列比較均勻的散列函數(shù)來說,理論上講,k=n/m,其中n表示散列中數(shù)據(jù)的個數(shù),m表示散列表中“槽”的個數(shù)

解答開篇:Word文檔中單詞拼寫檢查功能是如何實現(xiàn)的?

  • 當用戶輸入某個英文單詞時,我們拿用戶輸入的單詞去散列表中查找。如果查到,則說明拼寫正確;如果沒有查到,則說明拼寫可能有誤,給予提示。借助散列表這種數(shù)據(jù)結(jié)構,我們就可以輕松實現(xiàn)快速判斷是否存在拼寫錯誤

課后題

假設我們有10萬條URL訪問日志,如何按照訪問次數(shù)給URL排序?

  • 遍歷 10 萬條數(shù)據(jù),以 URL 為 key,訪問次數(shù)為 value,存入散列表,同時記錄下訪問次數(shù)的最大值 K,時間復雜度 O(N)
  • 如果 K 不是很大,可以使用桶排序,時間復雜度 O(N)。如果 K 非常大(比如大于 10 萬),就使用快速排序,復雜度 O(NlogN)

有兩個字符串數(shù)組,每個數(shù)組大約有10萬條字符串,如何快速找出兩個數(shù)組中相同的字符串?

  • 以第一個字符串數(shù)組構建散列表,key 為字符串,value 為出現(xiàn)次數(shù)。再遍歷第二個字符串數(shù)組,以字符串為 key 在散列表中查找

19講散列表(中):如何打造一個工業(yè)級水平的散列表

  • 給力哦,全面邁向工業(yè)化,多快好省搞起來
  • 在極端情況下,有些惡意的攻擊者,還有可能通過精心構造的數(shù)據(jù),使得所有的數(shù)據(jù)經(jīng)過散列函數(shù)之后,都散列到同一個槽里。如果我們使用的是基于鏈表的沖突解決方法,那這個時候,散列表就會退化為鏈表,查詢的時間復雜度就從O(1)急劇退化為O(n)
  • 厲害,還有這樣的攻擊手段
  • 這一節(jié)就當開開眼吧

散列函數(shù)設計

  • 散列函數(shù)的設計不能太復雜
  • 散列函數(shù)生成的值要盡可能隨機并且均勻分布
  • 直接尋址法、平方取中法、折疊法、隨機數(shù)法等

裝載因子過大

  • 當裝載因子過大,也就是說表里面基本裝滿的時候,我們就要考慮動態(tài)擴容的問題
  • 申請一個更大的空間,把老數(shù)據(jù)搬進來
67d12e07a7d673a9c1d14354ad029443
  • 插入一個數(shù)據(jù),最好情況下,不需要擴容,最好時間復雜度是O(1)。最壞情況下,散列表裝載因子過高,啟動擴容,我們需要重新申請內(nèi)存空間,重新計算哈希位置,并且搬移數(shù)據(jù),所以時間復雜度是O(n)。用攤還分析法,均攤情況下,時間復雜度接近最好情況,就是O(1)
  • 但在擴容時,不能一次完成,因為將老數(shù)據(jù)搬入新的表需要耗費大量時間,讓用戶等待顯然不合理,所以我們選擇每次執(zhí)行新的插入時,搬運一次老數(shù)據(jù),將操作均攤下去
6d6736f986ec4b75dabc5472965fb9cb

解決沖突的方法

  • 當數(shù)據(jù)量比較小、裝載因子小的時候,適合采用開放尋址法。這也是Java中的ThreadLocalMap使用開放尋址法解決散列沖突的原因
  • 鏈表法最差的時間復雜度也就O(logn),并且可以結(jié)合跳表,二叉樹這類數(shù)據(jù)結(jié)構
103b84d7173277c5565607b413c40129
  • 基于鏈表的散列沖突處理方法比較適合存儲大對象、大數(shù)據(jù)量的散列表,而且,比起開放尋址法,它更加靈活,支持更多的優(yōu)化策略,比如用紅黑樹代替鏈表

以Java中的HashMap為例看工業(yè)級別的散列表實現(xiàn)

  • HashMap默認的初始大小是16,當然這個默認值是可以設置的,如果事先知道大概的數(shù)據(jù)量有多大,可以通過修改默認初始大小,減少動態(tài)擴容的次數(shù),這樣會大大提高HashMap的性能
  • 最大裝載因子默認是0.75,當HashMap中元素個數(shù)超過0.75*capacity(capacity表示散列表的容量)的時候,就會啟動擴容,每次擴容都會擴容為原來的兩倍大小
  • ashMap底層采用鏈表法來解決沖突。即使負載因子和散列函數(shù)設計得再合理,也免不了會出現(xiàn)拉鏈過長的情況,一旦出現(xiàn)拉鏈過長,則會嚴重影響HashMap的性能。
  • 于是,在JDK1.8版本中,為了對HashMap做進一步優(yōu)化,我們引入了紅黑樹。而當鏈表長度太長(默認超過8)時,鏈表就轉(zhuǎn)換為紅黑樹。我們可以利用紅黑樹快速增刪改查的特點,提高HashMap的性能。當紅黑樹結(jié)點個數(shù)少于8個的時候,又會將紅黑樹轉(zhuǎn)化為鏈表。因為在數(shù)據(jù)量較小的情況下,紅黑樹要維護平衡,比起鏈表來,性能上的優(yōu)勢并不明顯

20講散列表(下):為什么散列表和鏈表經(jīng)常會一起使用

LRU緩存淘汰算法

  • LRU是在講鏈表那一節(jié)就提到的算法
    • 如果此數(shù)據(jù)之前已經(jīng)被緩存在鏈表中了,我們遍歷得到這個數(shù)據(jù)對應的結(jié)點,并將其從原來的位置刪除,然后再插入到鏈表的頭部
    • .如果此數(shù)據(jù)沒有在緩存鏈表中
      • 如果此時緩存未滿,則將此結(jié)點直接插入到鏈表的頭部
      • 如果此時緩存已滿,則鏈表尾結(jié)點刪除,將新的數(shù)據(jù)結(jié)點插入鏈表的頭部
  • 我們使用散列表鏈表相結(jié)合的方式解決這個問題
eaefd5f4028cc7d4cfbb56b24ce8ae6e
  • 這個問題其實翻譯過來就是:我雖然懂散列表是個什么東西,但我就是想要能有序輸出里面的東西,這就需要我們在散列表+雙向鏈表之外在加一個拉鏈【hnext】
  • 當我們查找數(shù)據(jù)的時候,我們是通過散列表,在順序查找,但當我們是添加數(shù)據(jù)的時候,需要對鏈的頭尾進行操作,所以要對鏈進行操作
  • 添加操作:我們需要先看這個數(shù)據(jù)是否已經(jīng)在緩存中。如果已經(jīng)在其中,需要將其移動到雙向鏈表的尾部;如果不在其中,還要看緩存有沒有滿。如果滿了,則將雙向鏈表頭部的結(jié)點刪除,然后再將數(shù)據(jù)放到鏈表的尾部;如果沒有滿,就直接將數(shù)據(jù)放到鏈表的尾部

Redis有序集合

  • 這一塊感覺么得意思,鴿了

Java LinkedHashMap

  • 這一塊和上面的LRU算法一樣,同樣是通過雙向鏈表+散列來實現(xiàn)的
  • 插入的2會出現(xiàn)在鏈表的末尾
17ac41d9dac454e454dcb289100bf198
  • 此時如果我們要插入新的3數(shù)據(jù),會把舊的刪除,新的放最后
fe313ed327bcf234c73ba738d975b18c
  • 照訪問時間排序的LinkedHashMap本身就是一個支持LRU緩存淘汰策略的緩存系統(tǒng)
  • LinkedHashMap是通過雙向鏈表和散列表這兩種數(shù)據(jù)結(jié)構組合實現(xiàn)的。LinkedHashMap中的“Linked”實際上是指的是雙向鏈表,并非指用鏈表法解決散列沖突

課后題

今天講的幾個散列表和鏈表結(jié)合使用的例子里,我們用的都是雙向鏈表。如果把雙向鏈表改成單鏈表,還能否正常工作呢?為什么呢?

  • 在刪除一個元素時,雖然能 O(1) 的找到目標結(jié)點,但是要刪除該結(jié)點需要拿到前一個結(jié)點的指針,遍歷到前一個結(jié)點復雜度會變?yōu)?O(N),所以用雙鏈表實現(xiàn)比較合適。

獵頭系統(tǒng)設計

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