Leetcode | Javascript每日刷题

持续更新...

338. 比特位计数

  1. 循环遍历[0, num],按位与得出1的个数,复杂度是O(n * k),k是每个数字的二进制位数。
var countBits = function(num) {
    const res = []
    const getBinaryOne = (n) => {
        let sum = 0
        while(n) {
            sum += n & 1
            n >>= 1
        }
        return sum
    }
    for(let i=0;i<=num;i++) {
        res.push(getBinaryOne(i))
    }
    return res
};
  1. 在Leetcode的官方题解中提及到三种动态规划方法,都是利用差不多的状态转移方程进行的,这里记录兼具代码实现简单和思想简洁的动态规划+最低有效位方法。方便在面试之类的情况下快速回忆。这些动态规划代码的复杂度都是O(n)。
    算法思想:
    十进制 -> 二进制
    605 -> 1001011101
    302 -> 100101110
    非常直观的看出:605的"1"的个数比302多1,而1是605的最低位数字。而且:
    605 = 302 * 2 + 1
    而且根据二进制特性302 * 2^n,1的个数是不变的,所以可以得出状态转移方程:
    P(x) = P(x/2) + x%2
var countBits = function(num) {
    const res = [0]
    for(let i=1;i<=num;i++) {
        res[i] = res[Math.floor(i/2)] + i%2
    }
    return res
};

46. 全排列

直观的回溯法,没有任何优化,时间复杂度和空间复杂度都是O(N!)

var permute = function(nums) {
    const resArr = []
    const per = (arr, res) => {
        if(!arr.length) {
            resArr.push(res)
            return
        }
        for(let i=0;i<arr.length;i++) {
            let arrCopy = [...arr]
            let resCopy = [...res]
            resCopy.push(arrCopy.splice(i,1))
            per(arrCopy,resCopy)
        }
    }
    per(nums, [])
    return resArr
};

22. 括号生成

  1. 借助了之前用栈检验括号有效性的思想的回溯法。边想边写的,时间空间复杂度不好,但是容易理解,贴上来给大家看看。
var generateParenthesis = function(n) {
    /**
     * 回溯剪枝
     */
    const resArr = []
    const func = (arr, stack, left, right) => {
        // 收入条件 - 左右为空
        if(left === 0 && right === 0) {
            resArr.push(arr.join(''))
            return
        }
        // 左括号进入
        if(left > 0) {
            func([...arr,'('], [...stack, '('], left-1, right)
        }
        // 右括号进入
        if(right > 0) {
            // 栈内为空不能进栈
            if(stack.length === 0) return
            stack.pop()
            func([...arr, ')'], [...stack], left, right-1)
        }
    }
    func([],[],n,n)
    return resArr
};
  1. 用下标来优化的回溯法。并且把上面的进栈出栈操作简化为一个逻辑:只要左括号数量比右括号多就可以进右括号。
var generateParenthesis = function(n) {
    const resArr = []
    const backtrack = (cur, left, right) => {
        if(left===0 && right===0) {
            resArr.push(cur)
            return
        }
        if(left>0) {
            backtrack(cur+'(', left-1, right)
        }
        if(right>left){
            backtrack(cur+')', left, right-1)
        }
    }
    backtrack('',n,n)
    return resArr
}

94. 二叉树的中序遍历

递归

var inorderTraversal = function(root) {
    const res = []
    const mid = (node) => {
        if(!node) {
            return
        }
        node.left && mid(node.left)
        res.push(node.val)
        node.right && mid(node.right)
    }
    mid(root)
    return res
};

改为迭代法

var inorderTraversal = function(root) {
    const res = []
    const stack = []
    let node = root
    while(node !== null || stack.length) {
        while(node !== null) {
            stack.push(node)
            node = node.left
        }
        node = stack.pop()
        res.push(node.val)
        node = node.right
    }
    return res
};

39. 组合总和

var combinationSum = function(candidates, target) {
    const res = []
    const back = (start, arr, sum) => {
        if(sum === target) {
            res.push(arr)
            return
        }
        for(let i=start;i<candidates.length;i++) {
            if(sum+candidates[i]<=target) {
                let arrCopy = [...arr, candidates[i]]
                back(i, arrCopy, sum+candidates[i])
            }
        }
    }
    back(0, [], 0)
    return res
};

114. 二叉树展开为链表

参考的是第一个题解的做法。

var flatten = function(root) {
    while(root) {
        if(!root.left) {
            root = root.right
        } else {
            let prev = root.left
            while(prev.right) {
                prev = prev.right
            }
            prev.right = root.right
            root.right = root.left
            root.left = null
        }
    }
};

递归版的采用了后序遍历,以后补充

48. 旋转图像

O(N^2) 时间复杂度的转置+逆序方法。

var rotate = function(matrix) {
    // 转置+逆序
    const n = matrix.length
    for(let i=0;i<n;i++) {
        for(let j=i+1;j<n;j++) {
            let tmp = matrix[i][j]
            matrix[i][j] = matrix[j][i]
            matrix[j][i] = tmp
        }
    }
    matrix.forEach(arr => arr.reverse())
};

287. 寻找重复数

第一反应是哈希表,空间O(n)时间O(n)

var findDuplicate = function(nums) {
    const map = new Map()
    for(let n of nums) {
        if(map.get(n)) {
            return n
        }
        map.set(n, true)
    }
};

残念的是空间复杂度要求O(1),时间复杂度小于O(N^2),并且要求不能更改原数组【排序也被排除O(nlogn),如果使用了非in-place排序,空间复杂度也不合格】

这里用到的方法其实是之前的环形链表题目!因为数组大小位于区间[1,n],数组长度为n+1,所以以数组值为数组的下标,可以进行类似链表的跳转。如果存在重复元素,会形成一个环,采用快慢指针来解就很舒服了。

var findDuplicate = function(nums) {
    let slow = nums[0]
    let fast = nums[nums[0]]
    while(slow!==fast) {
        slow = nums[slow]
        fast = nums[nums[fast]]
    }
    let prev1 = 0
    let prev2 = slow
    while(prev1!==prev2) {
        prev1 = nums[prev1]
        prev2 = nums[prev2]
    }
    return prev1
};

96. 不同的二叉搜索树

var numTrees = function(n) {
    /**
     * 非常好的动态规划题,并且可以当卡特兰数的入门题目,详见官方题解
     * G(n) = SUM i:0->n (F(i,n))
     * F(i,n) = G(i-1) * G(n-i)
     * G(n) = SUM i:0->n (G(i-1)*G(n-i)) 
     * G(0) = 1
     * G(1) = 1
     */
    const dp = Array(n+1).fill(0)
    dp[0] = 1
    dp[1] = 1
    for(let i=2;i<=n;i++) {
        // 为什么采用二层循环?由于G(n-i)的结果可能超出动态规划辅助数组,因此我们先限制n的范围,由小到大,从G(2)求到G(n)。
        for(let j=1;j<=i;j++) {
            dp[i] += dp[j-1]*dp[i-j]
        }
    }
    return dp[n]
};
var numTrees = function(n) {
    /**
     * 卡特兰数解法
     * 有关卡特兰数的题目和适用范围:https://blog.csdn.net/zkp_987/article/details/94565351
     * C(n+1) = 2(2n+1)/n+2 * C
     * C(0) = 1
     */
    let c = 1
    for(let i=0;i<n;i++) {
        c = 2*(2*i+1)*c /(i+2)
    }
    return c
};

64. 最小路径和

var minPathSum = function(grid) {
    const n = grid.length
    const m = grid[0].length
    for(let i=n-1;i>=0;i--){
        for(let j=m-1;j>=0;j--) {
            if(i === n-1 && j !== m-1) {
                grid[i][j] = grid[i][j+1] + grid[i][j]
            } else if(i !== n-1 && j === m-1) {
                grid[i][j] = grid[i+1][j] + grid[i][j]
            } else if(i !== n-1 && j !== m-1) {
                grid[i][j] = grid[i][j] + Math.min(grid[i+1][j], grid[i][j+1])
            }
        }
    }
    return grid[0][0]
}

208. 实现 Trie (前缀树)

class TrieNode {
    constructor () {
        this.isEnd = false
        this.links = Array(26)
    }
    containsKey(ch) {
        return Boolean(this.links[ch.charCodeAt() - 'a'.charCodeAt()])
    }
    getNode(ch) {
        return this.links[ch.charCodeAt() - 'a'.charCodeAt()]
    }
    putNode(ch, node) {
        this.links[ch.charCodeAt() - 'a'.charCodeAt()] = node
    }
    setEnd() {
        this.isEnd = true
    }
}


class Trie {
    constructor () {
        this.root = new TrieNode()
    }
    insert(word) {
        let node = this.root
        for(const c of word) {
            if(!node.containsKey(c)) {
                node.putNode(c, new TrieNode())
            }
            node = node.getNode(c)
        }
        node.setEnd()
    }
    searchPrefix(word) {
        let node = this.root
        for(const c of word) {
            if(node.containsKey(c)) {
                node = node.getNode(c)
            } else {
                return null
            }
        }
        return node
    }
    search(word) {
        let node = this.searchPrefix(word)
        return node !== null && node.isEnd
    }
    startsWith(prefix) {
        let node = this.searchPrefix(prefix)
        return node !== null
    }
}

105. 从前序与中序遍历序列构造二叉树

用了很多数组的API,性能不行,但是方便快速理解和写出代码。

var buildTree = function(preorder, inorder) {
    /**
     * 先序数组元素 - 中间节点
     * 中序数组元素用中序数组元素可以分为两半,左为左子树,右为右子树
     */
    const recBuild = (po,io) => {
        if(!po.length) {
            return null
        }
        const mid = po.shift()
        if(!po.length) {
            return new TreeNode(mid)
        }
        const midPos = io.indexOf(mid)
        const leftIo = io.slice(0,midPos)
        const rightIo = io.slice(midPos+1)
        const leftPo = po.slice(0,midPos)
        const rightPo = po.slice(midPos)
        delete po
        delete io
        const node = new TreeNode(mid)
        node.left = recBuild(leftPo, leftIo)
        node.right = recBuild(rightPo, rightIo)
        return node
    }
    return recBuild(preorder, inorder)
};

使用参数下标,性能较好

var buildTree = function(preorder, inorder) {
    /**
     * 其实 这个build的过程本身就是一个先序遍历,因此只需要设置一个全局自增的Index就能走先序遍历的过程了
     * 所以 目前需要的下标就是中序遍历的left和right部分
     */
    let index = 0
    const inorderMap = new Map()
    for(let i=0;i<inorder.length;i++) {
        inorderMap.set(inorder[i], i)
    }
    const recBuild = (inLeft, inRight) => {
        if(inLeft === inRight) {
            return null
        }
        const rootVal = preorder[index]
        const root = new TreeNode(rootVal)
        const inorderIndex = inorderMap.get(rootVal)
        index++
        root.left = recBuild(inLeft, inorderIndex)
        root.right = recBuild(inorderIndex+1, inRight)
        return root
    }
    return recBuild(0, inorder.length)
};

148. 排序链表

归并排序的用在链表这里刚刚好,因为它只需要中点坐标就可以。

var sortList = function(head) {
    if(!head || !head.next) return head
    let slow = head, fast = head.next
    while(fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
    }
    let mid = slow.next // 就是中点的右侧,右部分的头指针
    slow.next = null
    let left = sortList(head)
    let right = sortList(mid)
    const h = new ListNode('head')
    let p = h
    while(left && right) {
        if(left.val < right.val) {
            p.next = left
            left = left.next
        } else if(left.val >= right.val) {
            p.next = right
            right = right.next
        }
        p = p.next
    }
    p.next = left?left:right
    return h.next
};

406. 根据身高重建队列

题解的思路挺巧妙的,可以直接看下

var reconstructQueue = function(people) {
    const res = []
    // 先按height逆序排列 相等则按照k进行顺序排列
    people = people.sort((a,b)=>{
        const v = b[0]-a[0]
        if(v===0) {
            return a[1] - b[1]
        }
        return v
    })
    // 再根据k进行插入
    for(const p of people) {
        res.splice(p[1],0,p)
    }
    return res
};

11. 盛最多水的容器

var maxArea = function(height) {
    let maxArea = 0
    let left = 0, right = height.length-1
    /**
     * 两条边长中最短的那一条总是限制住矩形的高,为了寻求突破,最短那一条需要往内移动。
     */
    while(left !== right) {
        let area = Math.min(height[left],height[right]) * (right - left)
        maxArea = Math.max(area, maxArea)
        height[left] > height[right] ? right-- : left++
    }
    return maxArea
};

102. 二叉树的层次遍历

var levelOrder = function(root) {
    /**
     * 队列实现层次遍历
     */
    const res = []
    if(!root) {
        return res
    }
    let q = [root]
    while(q.length) {
        res.push(q.map(item=>item.val))
        let nextQ = []
        while(q.length) {
            let node = q.shift()
            node.left && nextQ.push(node.left)
            node.right && nextQ.push(node.right)
        }
        if(!nextQ.length) {
            break
        }
        q = nextQ
    }
    return res
};

215. 数组中的第K个最大元素

有三种经典做法:(1)排序 - O(nlogn)(2)topK小根堆 - O(nlogk)(3)快速选择算法 - O(n)

排序
var findKthLargest = function(nums, k) {
    return nums.sort((a,b)=>b-a)[k-1]
};
小根堆

这个topk算法从大二的时候老师就叫我们学了,非常经典的算法,建议背熟,可以和堆排序一起记忆。

var findKthLargest = function(nums, k) {
  // 维护一个k大小的小根堆,从K开始到len-1结束,如果比堆顶大就进入堆,再调整堆,类似堆排序的变形
  const arr = nums
  const len = arr.length
  const swap = (i, j) => {
    let tmp = arr[i]
    arr[i] = arr[j]
    arr[j] = tmp
  }
  const minHeapify = (start, end) => {
    let dad = start
    let son = dad * 2 + 1
    if (son >= end) return
    if (son + 1 < end && arr[son] > arr[son + 1]) son++
    if (arr[dad] > arr[son]) {
      swap(dad, son)
      minHeapify(son, end)
    }
  }
  const maintainK = () => {
    for (let i = (k >> 1) - 1; i >= 0; i--) minHeapify(i, k) // 维护这个k堆
  }
  maintainK() // 初始化K堆
  for (let i = k; i < len; i++) {
    if (arr[i] > arr[0]) {
      swap(i, 0)
      maintainK()
    }
  }
  return arr[0]
};
快速选择算法

择日补充

49. 字母异位词分组

const groupAnagrams = function(strs) {
    const map = new Map()
    const hashStr = (str) => {
        const arr = Array(26).fill(0)
        const aIdx = 'a'.charCodeAt()
        for(const c of str) {
            arr[c.charCodeAt()-aIdx]++
        }
        return arr.join(',')
    }
    for(const str of strs) {
        const hash = hashStr(str)
        !map.has(hash) && map.set(hash, [])
        map.get(hash).push(str)
    }
    return Array.from(map.values())
};

347. 前 K 个高频元素

这不,小根堆适用的算法又来来

var topKFrequent = function(nums, k) {
    // 先整一个哈希表
    const map = new Map()
    for(const n of nums) {
        !map.has(n) && map.set(n, 0)
        map.set(n, map.get(n)+1)
    }
    // 小根堆,比较标准是哈希表的值
    const arr = Array.from(map.keys())
    const len = arr.length
    const swap = (i, j) => {
        let tmp = arr[i]
        arr[i] = arr[j]
        arr[j] = tmp
    }
    const minHeapify = (start, end) => {
        let dad = start
        let son = dad * 2 + 1
        if (son >= end) return
        if (son + 1 < end && map.get(arr[son]) > map.get(arr[son + 1])) son++
        if (map.get(arr[dad]) > map.get(arr[son])) {
            swap(dad, son)
            minHeapify(son, end)
        }
    }
    const maintainK = () => {
        for (let i = (k >> 1) - 1; i >= 0; i--) minHeapify(i, k) // 维护这个k堆
    }
    maintainK()
    for (let i = k; i < len; i++) {
        if (map.get(arr[i]) > map.get(arr[0])) {
            swap(i, 0)
            maintainK()
        }
    }
    for (let i = k - 1; i >= 0; i--) {
        swap(0, i)
        minHeapify(0, i)
    }
    return arr.slice(0, k)
};

236. 二叉树的最近公共祖先

  1. 哈希表存储祖先节点。
var lowestCommonAncestor = function(root, p, q) {
    /**
     * 哈希表存储祖先节点
     * 1. 先用哈希表存储祖先节点
     * 2. 每个节点的祖先搭配哈希表可以形成一个链表类似物。
     * 3. 最近公共祖先 = 链表的交点,可以使用双指针进行查找
     */
    const map = new Map()
    // 哈希表存储祖先节点
    const travel = (node) => {
        if(!node) {
            return
        }
        if(node.left) {
            map.set(node.left, node)
            travel(node.left)
        }
        if(node.right) {
            map.set(node.right, node)
            travel(node.right)
        }
    }
    travel(root)
    // 双指针找链表的交点
    let np = p, nq = q
    while(np !== nq) {
        np = map.get(np) ? map.get(np) : q
        nq = map.get(nq) ? map.get(nq) : p
    }
    return np
};
  1. 递归方法
const lowestCommonAncestor = function(root, p, q) {
    if (!root || root===p || root===q) {
        return root
    }

    const left = lowestCommonAncestor(root.left, p, q)
    if(left !== null && left !== p && left !== q) {
        return left
    }

    const right = lowestCommonAncestor(root.right, p, q) 
    if(right !== null && right !== p && right !==q) {
        return right
    }

    if(right && left) return root

    return left ? left : right
};

62. 不同路径

var uniquePaths = function(m, n) {
    /**
     * 每个格子的值取决于 dp[i+1][j] + dp[i][j+1]
     * j=0:m-1 dp[n-1][j] = 1
     * i=0:n-1 dp[i][m-1] = 1
     * [10,6,3,1]
     * [4,3,2,1]
     * [1,1,1,0/1没关系]
     */
    // init
    if(n === 0 || m === 0) return 0
    if(n === 1 || m === 1) return 1
    const dp = Array(n).fill(Array(m).fill(0))
    for(let j=0;j<m;j++) dp[n-1][j]=1
    for(let i=0;i<n;i++) dp[i][m-1]=1
    for(let i=n-2;i>=0;i--) {
        for(let j=m-2;j>=0;j--) {
            dp[i][j] = dp[i+1][j] + dp[i][j+1]
        }
    }
    return dp[0][0]
};

739. 每日温度

const dailyTemperatures = function(T) {
    const stack = []
    const len = T.length
    const res = Array(len).fill(0)
    for(let i=len-1;i>=0;i--) {
        while(stack.length && T[i] >= T[stack[stack.length-1]]) stack.pop()
        if(stack.length) res[i] = stack[stack.length-1] - i
        stack.push(i)
    }
    return res
};

337. 打家劫舍 III

const map = new Map()

var rob = function(root) {
    if(!root) return 0
    if(map.has(root)) return map.get(root)
    let doIt = root.val + (root.left?rob(root.left.left)+rob(root.left.right):0) + (root.right?rob(root.right.right)+rob(root.right.left):0)
    let notDo = rob(root.left) + rob(root.right)
    let res = Math.max(doIt, notDo)
    map.set(root, res)
    return res
}

75. 颜色分类

var sortColors = function(nums) {
    /**
     * 初始化0的最右边界:p0 = 0。在整个算法执行过程中 nums[idx < p0] = 0.
     * 
     *  初始化2的最左边界 :p2 = n - 1。在整个算法执行过程中 nums[idx > p2] = 2.
     * 
     *  初始化当前考虑的元素序号 :curr = 0.
     * 
     *  While curr <= p2 :
     * 
     *  若 nums[curr] = 0 :交换第 curr个 和 第p0个 元素,并将指针都向右移。
     * 
     *  若 nums[curr] = 2 :交换第 curr个和第 p2个元素,并将 p2指针左移 。
     * 
     *  若 nums[curr] = 1 :将指针curr右移。
     */
    const n = nums.length
    let p0 = 0, p2 = n - 1, curr = 0
    const swap = (i,j) => {
        let tmp = nums[i]
        nums[i] = nums[j]
        nums[j] = tmp
    }
    while(curr <= p2) {
        // curr所在的为0时,为什么curr可以+1呢?因为此时的curr与之交换的是curr扫描过的位置,如果是0or2都已经被发配到正确的位置了
        if(nums[curr] === 0) {
            swap(curr, p0)
            p0++
            curr++
        }
        // curr所在的为2时,为什么curr可以不增加呢?因为此时的curr与之交换的是curr没扫描过的位置,需要再规划一下。
        else if(nums[curr] === 2) {
            swap(curr, p2)
            p2--
        }
        else if(nums[curr] === 1) {
            curr++
        }
    }
};

279. 完全平方数

var numSquares = function(n) {
    const dp = Array(n+1).fill(0)
    for(let i=1;i<=n;i++) {
        dp[i] = i
        for(let j=1;j*j<=i;j++) {
            dp[i] = Math.min(dp[i], dp[i-j*j]+1)
        }
    }
    return dp[n]
};

17. 电话号码的字母组合

var letterCombinations = function(digits) {
    /**
     * 类似全排列
     * 2 -> ['a','b','c']
     */
    if(digits.length===0) {
        return []
    }
    const res = []
    const letterGroup = [
        ['a','b','c'],
        ['d','e','f'],
        ['g','h','i'],
        ['j','k','l'],
        ['m','n','o'],
        ['p','q','r','s'],
        ['t','u','v'],
        ['w','x','y','z']
    ]
    const getCombinations = (arr, i) => {
        if(i === digits.length) {
            res.push(arr.join(''))
            return
        } 
        for(let c of letterGroup[Number(digits[i])-2]) {
            getCombinations([...arr, c], i+1)
        }
    }
    getCombinations([],0)
    return res
};

394. 字符串解码

var decodeString = function(s) {
    let stack = []
    let res = ''
    let time = 0
    for(let c of s) {
        if(c === '[') {
            stack.push([time, res])
            res = ''
            time = 0
        } else if(c === ']') {
            let [curr_time, last_res] = stack.pop()
            res = last_res + res.repeat(curr_time)
        } else if(c >= '0' && c <= '9') {
            time = time * 10 + Number(c)
        } else {
            res += c
        }
    }
    return res
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容