常見排序算法:

插入排序

(defun insertion-sort (arr symbol)
  "
arr is of type LIST
symbol must be `>` or `<`
                                      執行時間          執行次數
for j = 1 until arr.length            c1               n
    key = arr[j]                      c2               n - 1
    i = j - 1                         c4               n - 1

                                                       n
    while i >= 0 and arr[i] > key     c5               Σ Tj
                                                       j=1
                                                       n
        arr[i + 1] = a[i]             c6               Σ (Tj - 1)
                                                       j=1
                                                       n
        i = i - 1                     c7               Σ (Tj - 1)
                                                       j=1
    a[i + 1] = key                    c8               n - 1
"
  (let ((len (length arr)))
    (do ((j 1 (1+ j)))
    ((>= j len) arr)
      (let ((key (nth j arr)))
    (do ((i (- j 1) (1- i)))
        ((not (and (>= i 0) (funcall symbol (nth i arr) key))) (setf (nth (+ i 1) arr) key))
      (setf (nth (+ i 1) arr) (nth i arr)))))))

    /**
      * insert sort
      *
      * @param arr
      * @return sorted array
      */
    def `insert-sort` (arr: Array[Int]): Array[Int] = {
        for (i <- 0 until arr.length) {
            var j = i
            val k = arr(j)
            while (j > 0 && arr(j) > arr(j - 1)) {
                arr(j) = arr(j - 1)
                arr(j - 1) = k
                j = j - 1
            }
        }
        arr
    }

歸并排序

; 分治法思想:
; 將原問題分解為幾個規模較小但類似于原問題的子問題
; 遞歸求解這些子問題
; 最后合并這些子問題的解來簡歷原問題的解

; 并歸排序算法完全遵守分治模式,即
; 分解:分解待排序的n個元素的序列成各具n/2個元素的兩個子序列
; 解決:使用并歸排序遞歸地排序兩個子序列
; 合并:合并兩個已排序的子序列以產生已排序的答案

; 而它的關鍵操作就是“合并”:兩個'已排序'序列的合并
; 下面,可以定義一個輔助函數merge(arr, l, m , r)來完成合并

(defun ~merge (arr l m r)
  "
`包CL已經含有merge名稱的函數 此處需要換一個名稱`
arr(ay) - 數組
l - left 下標
m - mid 下標
r - right 下標

MERGE(array, l, m, r)
    n1 = m - l + 1
    n2 = r - m
    let al[n1] and ar[n2] be new arrays

    for i = 0 until n1
        al[i] = array[l + i]

    for j = 0 until n2
        ar[j] = array[m + j + 1]

    i = 0, j = 0

    for k = l to r
        case i < n1 and j < n2
            if al[i] <= ar[j]
                array[k] = al[i]
                i =  i + 1
            else
                array[k] = ar[j]
                j =  j + 1
        case i < n1
            array[k] = al[i]
            i = i + 1
        case j < n2
            array[k] = ar[j]
            j = j + 1
"
  (let* ((n1 (+ 1 (- m l)))
     (n2 (- r m))
     (n3 (+ n1 n2))
     (al (make-array `(,n1) :initial-element nil))
     (ar (make-array `(,n2) :initial-element nil)))
    (dotimes (i n1)
      (setf (svref al i) (aref arr (+ i l))))
    (dotimes (j n2)
      (setf (svref ar j) (aref arr (+ j m 1))))
    (let ((i 0)
      (j 0))
      (do ((k l (1+ k)))
      ((>= k (+ n3 l)) arr)
    (cond ((and (< i n1) (< j n2))
           (if (<= (aref al i) (aref ar j))
           (progn
             (setf (aref arr k) (aref al i))
             (setf i (+ i 1)))
           (progn
             (setf (aref arr k) (aref ar j))
             (setf j (+ j 1)))))
          ((< i n1)
           (progn
         (setf (aref arr k) (aref al i))
         (setf i (+ i 1))))
          ((< j n2)
           (progn
         (setf (aref arr k) (aref ar j))
         (setf j (+ j 1)))))))))

; 在~merge函數中,3個for循環運行時間都跟數組長度相關
; 其余步驟可用常量c代替
; 所以次函數時間復雜度可以簡化成
; c1 * n1 + c2 * n2 + c3 * n3 + c ==> 線性階 ==> O(n)


(defun merge-sort (arr l r)
  "
歸并排序數組A[l ... r]
如果l >= r,則表示子數組最多只有一個元素,可以當作已排列數組序列

MERGE-SORT(arr, l, r)
    if l < r
        m = |(l + r) / 2|
        MERGE-SORT(arr, l, m)
        MERGE-SORT(arr, (m + 1), r)
        MERGE(arr, l, m, r)
"
  (if (< l r)
      (let ((m (floor (+ l r) 2)))
    (merge-sort arr l m)
    (merge-sort arr (+ m 1) r)
    (~merge arr l m r))))
    /**
      * 數組arr以下標m為分界點,[1 - m](m - r],必須有序一致
      * @param arr
      * @param l
      * @param m
      * @param r
      * @return sorted array
      */
    def merge (arr: Array[Int], l: Int, m: Int, r: Int): Array[Int] = {
        val n1 = m - l + 1
        val n2 = r - m
        val left = new Array[Int](n1)
        val right = new Array[Int](n2)

        for (i <- 0 until n1)
            left(i) = arr(l + i)

        for (j <- 0 until n2)
            right(j) = arr(m + 1 + j)

        var i = 0
        var j = 0
        var end = 0
        for (k <- l to r) {
            end match {
                case 1 => {
                    arr(k) = right(j)
                    j = j + 1
                }
                case 2 => {
                    arr(k) = left(i)
                    i = i + 1
                }
                case _ => {
                    if (i > n1 - 1) {
                        arr(k) = right(j)
                        j = j + 1
                        end = 1
                    } else if (j > n2 - 1) {
                        arr(k) = left(i)
                        i = i + 1
                        end = 2
                    } else if (left(i) < right(j)) {
                        arr(k) = left(i)
                        i = i + 1
                    } else {
                        arr(k) = right(j)
                        j = j + 1
                    }
                }
            }
        }
        arr
    }

    def `merge-sort` (arr: Array[Int], l: Int, r: Int): Array[Int] = {
        if (l < r) {
            val m: Int = math.floor((l + r) / 2).toInt
            `merge-sort`(arr, l, m)
            `merge-sort`(arr, m + 1, r)
            merge(arr, l, m, r)
        }
        arr
    }

堆排序

;;;; 6 堆排序

;;;; 6.1 堆

; 如果輸入數組中僅有常數個元素需要在排序過程中存儲在數組外
; 則稱排序算法是原址的(in place)

; 插入排序是一種原址排序
; 而歸并排序中, merge過程并不是原址的

; 堆排序是原址的,時間復雜度為O(nlgn),它具有插入排序和歸并排序的優點

(defun parent (i)
  "根據節點下標求父節點 i [0 ...]
"
  (if (= i 0)
      0
      (floor (/ (- i 1) 2))))

(defun left (i)
  "i [0 ...]"
  (+ (* 2 i) 1))

(defun right (i)
  "i [0 ...]"
  (* 2 (+ i 1)))

; 二叉堆的兩種形勢:
; 最大堆:除了根以外的所有節點i都滿足 A[parent(i)] >= A[i]
; 最小堆:除了根以外的所有節點i都滿足 A[parent(i)] <= A[i]

; 堆排序算法中,一般使用最大堆,最小堆通常用于有限隊列

; 如果把堆看成一棵樹,如果包含n個元素的堆可以看成一棵完全二叉樹
; 那么該堆的高度就是lgn,堆結構上的一些基本操作的運行時間至多與樹的高度成正比
; 即時間復雜度為O(lgn)


;;;; 6.2 維護堆的性質

(defun max-heapify (arr i &optional (root-index 0))
  "
用于維護最大堆性質的重要過程
arr - 數組
i - 下標(相對數組, 以起始下標為準的新數組)
root-index - 起始下標(整個數組)

輸入數組arr和下標i
調用該函數時,假設父節點left(i)和right(i)的二叉樹都是最大堆
但A[i]可能小于其孩子,這就違背了最大堆的性質
MAX-HEAPIFY通過讓A[i]的值在最大堆中逐漸降級
從而使得以下標i為根節點的子樹重新遵守最大堆的性質

MAX-HEAPIFY (A, i, root-index)
    l = left(i) + root-index
    r = right(i) + root-index
    heap-size = A.heap-size - root-index
    new-i = i + root-index

    if l - root-index < heap-size and A[l] > A[new-i]
        largest = l
    else
        largest = new-i

    if r - root-index < heap-size and A[r] > A[largest]
        largest = r

    if largest != new-i
        exchange A[new-i] with A[largest]
        MAX-HEAPIFY (A, largest, root-index)

對于一個以i為根節點,大小為n的子樹,MAX-HEAPIFY的時間代價包括:
調整A[i]/A[left(i)]/A[right(i)]的關系的時間代價O(1)
在加上一棵以i的一個孩子節點為根節點的子樹運行MAX-HEAPIFY的時間代價(假設遞歸調用會發生)
因為每個孩子子樹的大小至多為2n/3(最壞情況發生在樹的最底層恰好半滿的時候)
T(n) <= T(2n/3) + O(1) ==> T(n) = O(lgn)
"
  (let ((heap-size (- (length arr) root-index))
    (l (+ (left i) root-index))
    (r (+ (right i) root-index))
    (new-i (+ i root-index))
    (largest (+ i root-index))
    (dummy nil))
    (if (and (< (- l root-index) heap-size) (> (aref arr l) (aref arr new-i)))
    (setf largest l))
    (if (and (< (- r root-index) heap-size) (> (aref arr r) (aref arr largest)))
    (setf largest r))
    (if (/= largest new-i)
    (progn
      (setf dummy (aref arr new-i))
      (setf (aref arr new-i) (aref arr largest))
      (setf (aref arr largest) dummy)
      (max-heapify arr largest root-index))
    arr)))

#+test
(let ((arr (vector 16 4 10 14 7 9 3 2 8 1)))
  ; arr result vector(16 14 10 8 7 9 3 2 4 1)
  (max-heapify arr 1))

#+test
(let ((arr (vector 16 4 10 14 7 9 3 2 8 1)))
  ; arr result vector(16 4 10 14 7 9 3 8 2 1)
  (max-heapify arr 0 7))

;;;; 6.3 建堆
(defun build-max-heap (arr root-index)
  ;i為相對數組里的下標,取值范圍[0 ... ]
  (do ((i (- (length arr) 1) (1- i)))
      ((< i 0) arr)
    (max-heapify arr i root-index)))

#+test
(let ((arr (vector 4 1 3 2 16 9 10 14 8 7)))
  ; result: (vector 16 14 10 8 7 9 3 2 4 1)
  (build-max-heap arr))

#+test
(let ((arr (vector 4 1 3 2 16 9 10 14 8 7)))
  ; result: (vector 4 1 3 2 16 9 14 10 8 7)
  (build-max-heap arr 5))

; 每次調用 max-heapify的時間復雜度是lgn
; build-max-heap需要O(n)次這樣的調用
; 因此總的時間復雜度就是O(nlgn)
; 這個上界雖然正確 但不是漸進緊確的
; 但是根據如下性質可以得到一個更緊確的界:
; 包含n個元素的堆的高度為floor(lgn),高度為h的堆最多包含celing(n/2^(h+1))個節點
; 最終可以推導出 build-max-heap為O(n)
; 目前不太清楚 build-max-heap => O(n) --!

;;;; 6.4 堆排序算法

(defun heap-sort (arr)
  "
build-max-heap初始化整體數組為最大堆
在相對數組里,首元素為最大,每次維護首元素下標后的元素的最大堆性質

HEAP-SORT(A)
    BUILD-MAX-HEAP(A, 0)
    for i = 0 until A.length
        MAX-HEAPIFY(A, 0, i)
"
  ;(do ((i 0 (1+ i)))
  ;    ((>= i (length arr)) arr)
  ;  (build-max-heap arr i)) ; 時間復雜度 O(n^2),棄用
  (build-max-heap arr 0) ;O(n) or O(nlgn),但最終對heap-sort的時間復雜度無影響
  (do ((i 1 (1+ i)))
      ((>= i (length arr)) arr)
    (max-heapify arr 0 i)) ;O(nlgn)
  )
    /*
     * 用數組存儲二叉堆,用下標計算表示出對應的父結點,左孩子,右孩子
     */

    def parent(i: Int): Int = {
        i match {
            case i: Int if i <= 0 => 0
            case _ => math.floor((i - 1) / 2).toInt
        }
    }

    def left(i: Int): Int = (i * 2) + 1

    def right(i: Int): Int = (i + 1) * 2

    def exchange (arr: Array[Int], i: Int, j: Int) {
        val tmp = arr(i)
        arr(i) = arr(j)
        arr(j) = tmp
    }

    /**
      * 最大堆
      * @param arr      數組
      * @param i        相對下標,以根下標為準的新數組
      * @param root     根下標
      * @return
      */
    def `max-heapify` (arr: Array[Int], i: Int, root: Int = 0): Array[Int] = {
        val l = left(i) + root
        val r = right(i) + root
        val heapsize = arr.length - root
        val index = i + root

        var largest = index

        if (l - root < heapsize && arr(l) > arr(largest))
            largest = l

        if (r - root < heapsize && arr(r) > arr(largest))
            largest = r

        if (largest != index) {
            exchange(arr, index, largest)
            `max-heapify`(arr, largest, root)
        }
        arr
    }

    def `build-max-heap` (arr: Array[Int], root: Int = 0): Array[Int] = {
        for (i <- arr.length - 1 to 0 by -1) {
            `max-heapify`(arr, i)
        }
        arr
    }

    def `heap-sort` (arr: Array[Int]): Array[Int] = {
        `build-max-heap`(arr)
        for (i <- 0 to arr.length - 1) {
            `max-heapify`(arr, 0, i)
        }
        arr
    }

快速排序

;;;; 快速排序

; 快速排序的最壞時間復雜度為O(n^2),雖然最壞時間復雜度很差
; 但在實際排序應用中是最好的選擇
; 它的期望復雜度是nlgn,而且也是原址排序


;;;; 7.1 快速排序描述

; 與歸并排序一樣,它采用了分治思想,下面是快速排序對數組A[l ... r]三步分治過程:

; 分解:數組A[l...r]被劃分成為兩個(可能為空)子數組A[l...m-1]和A[m+1...r]
; 使得A[l...m-1]中的任一元素都小于等于A[m],而A[m]也小于等于A[m+1...r]中的任一元素
; 其中,計算下標m也是劃分的一部分

; 解決:通過遞歸調用快速排序,對子數組A[l...m-1]和A[m+1...r]進行排序

; 合并:因為子數組都是原址排序的,所以不需要合并操作:數組A[l...r]已經有序

(defun quick-sort (arr l r)
  "
QUICK-SORT(A, l, r)
    if l < r
        m = PARTITION(A, l, r)
        QUICK-SORT(A, l, m - 1)
        QUICK-SORT(A, m + 1, r)
"
  (let ((m nil))
    (if (< l r)
    (progn
      (setf m (partition arr l r))
      (quick-sort arr l (- m 1))
      (quick-sort arr (+ m 1) r)))
    arr))

(defun partition (arr l r)
  "
PARTITION(A, L, R)
    k = A[R]
    i = L
    for j = L to R - 1
        if A[j] <= x
            exchange A[i] with A[j]
            i = i + 1

    exchange A[i] with A[R]
    return i

大致思路就是這樣,數組A[L...I...J...R]
區間[L...I]存放比key小或者等于的元素
區間[I...J]存放比key大的元素
區間[J...R]是未比較元素
當J接近到R時,由于最終有一步是i = i + 1
所以下標i此時的元素是大于key值的,交換他們之后
數組就變成這樣的情況:
以下標i為界,前面部分都是比它小或者等的,后面部分都是大于它的
"
  (let ((k (aref arr r))
    (i l)
    (dummy nil))
    (do ((j l (1+ j)))
    ((>= j r))
      (if (< (aref arr j) k)
      (progn
        (setf dummy (aref arr i))
        (setf (aref arr i) (aref arr j))
        (setf (aref arr j) dummy)
        (setf i (+ i 1)))))
    (setf dummy (aref arr i))
    (setf (aref arr i) (aref arr r))
    (setf (aref arr r) dummy)
    (values i arr)))


;;;; 7.3 快速排序隨機化版本

; 前面的快速排序中,輸入數據的所有排列都是等概率的,但在實際工作中
; 這種情況不會總成立,所以在算法中引入隨機化性,使得所有輸入輸入都能獲得較好的期望性能
; 下面是隨機版本的快速排序的偽代碼實現:

; RANDOMIZED-PARTITION(A, p, r)
;     i = RANDOM(p, r)
;     exchange A[r] with A[i]
;     return PARTION(A, p, r)
; 
; RANDOMIZED-QUICKSORT(A, p, r)
;     if p < r
;         q = RANDOMIZED-PARTITION(A, p, r)
;         RANDOMIZED-QUICKSORT(A, p, q - 1)
;         RANDOMIZED-QUICKSORT(A, q + 1, r)
    def partition (arr: Array[Int], left: Int, right: Int): Int = {
        val k = arr(right)
        var i = left
        for (j <- left to right - 1) {
            if (arr(j) <= k) {
                /*exchange arr(i) and arr(j)*/
                val tmp = arr(i)
                arr(i) = arr(j)
                arr(j) = tmp

                i = i + 1
            }
        }
        arr(right) = arr(i)
        arr(i) = k
        i
    }

    def `quick-sort`(arr: Array[Int], left: Int, right: Int): Array[Int] = {
        if (left < right) {
            val mid = partition(arr, left, right)
            `quick-sort`(arr, left, mid - 1)
            `quick-sort`(arr, mid + 1, right)
        }
        arr
    }

計數排序

; 在排序的最終結果中,各元素之間的次序依賴于它們的比較關系,這樣的排序算法稱為`比較排序`
; 在之前筆記中涉及到的算法,包括`插入,歸并,快速,堆`排序,都屬于比較排序

; 計數排序運用的是運算而不是比較來確定排序順序的

;;;; 8.2 計數排序

; 計數排序基本思想:對于每一個輸入元素,確定小于x的元素的個數
; 利用這一信息,可以直接把元素放在數組對應的下標中

(defun counting-sort (arr)
  "
COUNTING-SORT(A, B, k)
    //數組B存放排序的輸出
    let C[0...k] be a new array //提供臨時存儲空間
    for i = 0 until k
        C[i] = 0                //初始化C的元素

    for j = 0 until A.length
        C[A[j]] = C[A[j]] + 1   //C[i]代表A[n]元素個數(A[n] == i,i = 0, 1 ... k)

    for i = 0 until k
        C[i+1] = C[i+1] + C[i]  //對每一個i=0,1...k,統計多少輸入元素是小于或等于i的

    for j = A.length - 1 downto 0
        B[C[A[j]] - 1] = A[j]
        C[A[j]] = C[A[j]] - 1   //把每個元素A[j]放到它在輸出數值B中的正確位置
                                //如果所有元素互異,對于每一個A[j]值來說
                                //C[A[j]]就是A[j]在輸出數值中的最終正確位置
                                //這是因為有C[A[j]]個元素小于或等于A[j]
                                //但有可能所有元素不是互異的
                                //所以將每一個值A[j]放入B中后,都要將C[A[j]]值減一
                                //這樣,如果遇到下一個值等于A[j]的元素時
                                //可以直接放在輸出數組B中A[j]的前一個位置

計數排序的一個重要性質是它是穩定的:具有相同值的元素在輸出數組中的相對次序與它們在輸入數組中的相對次序相同
代碼實現上用到了比較性質,用于確定k值
而且由于編程語言的特性
加上對偏移的計算
實現上跟偽代碼略微不同

顯然,每重循環都是線性時間,最終時間復雜度表示為O(n)
但不是原址排序,空間換時間

如果計算出來的k值過大,也就是說存在過大的輸入元素,就需要對空間和時間進行考慮了
"
  (labels ((maximum (arr max i)
         (if (< i 0)
         max
         (maximum arr
              (if (or (null max) (> (aref arr i) max))
                  (aref arr i)
                  max)
              (1- i))))
       (minimum (arr min i)
         (if (< i 0)
         min
         (minimum arr
              (if (or (null min) (< (aref arr i) min))
                  (aref arr i)
                  min)
              (1- i))))
       (fn-offset (num)
         (if (< num 0)
         (abs num)
         (- 0 num))))
    (let* ((len (length arr))
       (offset (fn-offset (minimum arr nil (1- len))))
       (k (+ 1 (+ (maximum arr nil (1- len)) offset)))
       (arr-tmp (make-array `(,k) :initial-element 0))
       (arr-out (make-array len)))
      (dotimes (j len)
    (setf (aref arr-tmp (+ offset (aref arr j))) (+ (aref arr-tmp (+ offset (aref arr j))) 1)))
      (dotimes (i (1- k))
    (setf (aref arr-tmp (+ i 1)) (+ (aref arr-tmp (+ i 1)) (aref arr-tmp i))))
      (do ((j (1- len) (- j 1)))
      ((< j 0) arr-out)
    (setf (aref arr-out (1- (aref arr-tmp (+ offset (aref arr j))))) (aref arr j))
    (setf (aref arr-tmp (+ offset (aref arr j))) (- (aref arr-tmp (+ offset (aref arr j))) 1))))))

or

    def `counting-sort` (a: Array[Int]): Array[Int] = {
        /*
         * 假設數據分布在[0 - 9]中,且數據個數不超過區間長度
         */
        val min = 0
        val max = 9

        val c = new Array[Int](max - min + 1)
        val b = new Array[Int](a.length)

        for (i <- 0 until c.length) c(i) = 0
        for (i <- 0 until a.length) c(a(i)) = c(a(i)) + 1
        for (i <- 1 until c.length) c(i) = c(i) + c(i - 1)
        for (i <- a.length - 1 to 0 by -1) {
            b(c(a(i)) - 1) = a(i)
            c(a(i)) = c(a(i)) - 1
        }
        b
    }

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

推薦閱讀更多精彩內容

  • 一、冒泡排序 基本思想:兩個數比較大小,較大的數下沉,較小的數冒起來。過程:①、比較相鄰的元素。如果第一個比第二個...
    xiaoqunzi233閱讀 248評論 0 0
  • 算法分類 十種常見排序算法可以分為兩大類:1. 比較類排序:通過比較來決定元素間的相對次序,由于其時間復雜度不能突...
    RyanGongLN閱讀 351評論 0 0
  • 一、直接插入排序 直接插入排序(Insertion Sort)的基本思想是:每次將一個待排序的元素記錄,按其關鍵字...
    kevin16929閱讀 572評論 0 0
  • 本書講述的是日本第五大上市出版社社長健成澈的40多年的做人做事的原則。以及他利用這些原則的成功之道。 ...
    Garry_626c閱讀 320評論 0 0
  • 安裝步驟如下: 1、獲取github最新的Git安裝包下載鏈接,進入Linux服務器,執行下載 2、壓縮包解壓 3...
    貳shu閱讀 337評論 0 0