僞代碼以下:git
INSERTION_SORT(A) for j = 2 to length[A] do key = A[j] i = j-1 // 將其與已經排好序的數組進行挨個比較 while i>0 and A[i] > key do A[i+1] = A[i] i = i-1 A[i+1] = key
原理算法
其實就是在每次進入一個元素key後,將其與已經排好序的序列進行比較,若是key值小於序列的第一個值(默認爲從小到大排序),那麼就代表該key應該置入這個序列當中,而且將比較過的元素自動向後移動,以騰出空間放置key。最後將key值置入空位中。segmentfault
現實中就是在接牌時,插入和調整撲克牌順序的問題了,算法複雜度爲O(n^2)數組
插入排序採用的是增量方法(incremental),採用分治法的合併排序時間複雜度爲O(nlgn)緩存
分治法
- 分解
- 解決
- 合併數據結構
主要在於合併算法MERGE(A, p, q, r)
僞代碼以下:dom
MERGE(A, p, q, r) n1 = q-p+1 n2 = r-q for i = 1 to n1 do L[i] = A[p+i-1] for j = 1 to n2 do R[j] = A[q+j] L[n1+1] = -oo R[n2+1] = -oo i = j = 1 for k = p to r do if L[i] <= R[j] then A[k] = L[i] i = i+1 else A[k] = R[j] j = j+1
原理函數
首先將輸入數組分割,而後在對分割後的數組進行一一比較,若是L的元素小,就將其輸出至目的數組A,若是R小,則輸出R至A.性能
此時MERGE-SORT算法就清晰了,若是將一個數組經過遞歸不斷將其分割,最終分割爲每一個數組只有一個元素,那麼使用MERGE時,就可以將此二元素排序,而後對4元素排序,而後是8元素....
僞代碼以下:ui
MERGE-SORT(A, p, r) if p<r then q = (p+r)/2 MERGE-SORT(A, p ,q) MERGE-SORT(A, q+1, r) MERGE(A, p , q, r)
冒泡排序
僞代碼以下:
BUBBLE-SORT(A) for i = 1 to length[A] do for j = length[A] downto i+1 do if A[j] < A[j-1] then exchange A[j], A[j-1]
經過不斷交換相鄰的兩個反序元素達到排序的目的,時間複雜度爲O(n^2)
1----n-1
2----n-2
3----n-4
....
n-1----1
即
1+2+3+4+....+n = n(n+1)/2,因此時間複雜度爲O(n^2)
遞歸式 T(n) = aT(n/b) + f(n), 其中a>=1, b>1, f(n) 是給定函數
仔細畫出遞歸樹
是求解遞歸式T(n)的食譜方法。
主方法依賴於定理4.1主定理 設a>=1 和 b >1爲常數,設f(n)爲一函數,T(n)由遞歸式
T(n) = aT(n/b) + f(n)
對非負整數定義,其中n/b 指上頂或者下底,那麼T(n)可能有以下的漸進界
...
主要是針對f(n),a,b等值。
機率分析,隨機算法。
使用機率分佈預測輸入值的分佈,以此來幫助分析算法平均狀況行爲的。
I(A) = 1,若是A發生的話;0,若是A不發生的話。
沒法獲得輸入分佈,考慮使用隨機算法。
隨機排列數組
許多隨機算法經過排列給定的輸入數組來使輸入隨機化。
二叉堆數據結構能夠被視爲一顆徹底二叉樹。樹的每一層都是被填滿的,最後一層可能除外。
父節點
PARENT(i) return [i/2] LEFT(i) return 2i RIGHT(i) return 2i+1
有兩種:最大堆和最小堆,在這兩種堆中,節點內的數組都要知足堆特性,在最大堆中,除了根之外的每一個節點,有
A[PARENT(i)] >= A[i]
根元素爲最大值。
最小堆恰好相反。
MAX-HEAPIFY. 保持堆的性質,最大堆性質,根或者子根都是樹最大元素,O(lgn)
對A[i]的這顆字數執行最大堆化。
算法以下:
MAX-HEAPIFY( A, i ) l = LEFT(A[i]) r = RIGHT(A[i] If l <= heap-size(A) and A[l] > A[i] then largest = l else largest = i If r <= heap-size(A) and A[r] > A[largest] then largest = r If largest != i then exchange(A[i], A[largest]) MAX-HEAPIFY(A, largest)
建堆 Build-MAX-HEAP, 對每一顆非葉子節點樹執行最大堆化,直至根元素。
自n/2 處開始遞減,循環執行Max-HEAPIFY,運行時間的界爲O(n)
BUILD-MAX-HEAP(A) heap-size[A] = length[A] for i = length[A]/2 downto 1 do MAX-HEAPIFY(A, i)
堆排序 HEAPSORT 自葉節點向跟元素遞減,交換跟元素和葉節點,接着調用MAX-HEAPIFY, 保持該子樹的最大堆性質,便可完成節點自小到大排序,時間複雜度爲O(nlgn)
原理
每次都將最大的根元素換至末尾的葉節點,這一操做可以將最大元素調到最後,而且排除出堆,而後再一次構造最大堆,次最大元素又被調節到根元素位置,而後再一次將其換至葉節點,這樣,不斷循環,就可以將元素在數組中從小到大排列。
HEAPSORT(A)
BUILD_MAX_HEAP(A) for i = length[A] downto 2 do exchange(A[1], A[i]) heap-size[A] = heap-size[A] - 1 MAX-HEAPIFY(A,1)
最大優先級隊列,最小優先級隊列的應用如圖所示。
QUICKSORT(A,p, r) If p < r Then q = PARTITION(A, p, r ) QUICKSORT(A, p, q-1) QUICKSORT(A, q+1, r)
最初調用時,QUICKSORT(A, 1, length(A) )
其中PARTITION(A, q, r )算法以下
x = A[r] i = p-1 For j = p to r-1 Do If A[j] <= x Then i += 1 Exchange(A[i], A[j]) Exchange(A[i+1], A[r]) Return i + 1
分區算法 不斷將數組分爲四個區:
- 第一區 小於 x的區間 A[p, i]
- 第二區 大於x的區間 A[i+1, j]
- 第三區間 還沒有比較的區間 A[j+1, r-1]
- 第四區間 主元 A[r]
每一次運行都是O(n)
總共須要遞歸調用O(log n),所以算法時間複雜度爲O(n log n)
有時咱們須要對樣本加入一些隨機化數據,以便對於全部輸入,都能獲得較好的平均性能,所以,採用隨機選擇主元的快速排序隨機化版本。
主要是對分區操做進行修改
RAMDOMIZED-PARTITION(A, p, r) i = random(p, r) Exchange(i , r) Return PARTITION(A, p, r)
新的排序算法修改以下:
RANDOMIZED-QUICKSORT(A, p, r ) If p < r then q = RANDOMIZED-PARTITION(A, p, r) RANDOMIZED-QUICKSORT(A, p, q-1) RANDOMIZED-QUICKSORT(A, q+1, r)
合併排序和堆排序在最壞狀況下,達到上界O(nlogn),快速排序在平均狀況下達到此上界,最壞O(n^2)
比較排序,非比較排序
比較排序能夠抽象爲決策樹模型,好比三個值的比較決策結果又3!= 6
定理8.1 任意一個比較排序的算法在最壞狀況下,都須要作O(nlgn)次的比較
推論8.2 堆排序和合並排序都是漸進最優的比較排序算法。
比較有趣的一個排序算法,使用三個數組空間A,B,C。
統計各個值出現的次數.
C[A[j]] = C[A[j]] +1
可見對於C的空間要求將會比較大,只適用於數值比較小的空間。
這樣的排序將會致使各個值A[j]的出現次數按照A[j]值的大小在C中依次由小到大排列。而後計算出C中每一個值以前出現多少個值,進而計算出該值所佔的位置。
算法以下:
COUNTING-SORT(A, B, k) for i=0 to k do C[i] = 0 // 計算A[i] 各個值出現的次數 for j=1 to lenght[A] do C[A[j]] = C[A[j]]+1 // 計算小於或者等於C[i]值出現的次數 for i=1 to k do C[i] = C[i]+C[i-1] // 將A[j]的值按照出現的位置置入B中,並將該值的出現次數減一,使與之相同的值自動排在A[j]的前一個位置 for j = length[A] downto 1 do B[C[A[j]]] = A[j] C[A[j]] = C[A[j]]-1
該算法很繞,但比較穩定,運行時間爲O(n)
基數排序是對具備多個關鍵字域的記錄進行排序,好比年月日。
RADIX-SORT(A,d) for i = 1 to d do use a stable sort to sort array A to digit i
能夠將一個32位的字視爲4個8位的數字,因而就能夠將k 縮小至255,數位d變爲4.
一樣的,利用計數排序做爲中間穩定排序的基數排序不是原地排序,佔用空間較大。儘管基數排序執行的遍數可能比快速排序少,但每一遍所需的時間要長得多。所以通常仍是使用快排,由於快排可以有效的利用硬件緩存,同時也是個原地排序。
輸入須要符合均勻分佈,便可以以線性時間O(n)運行。計數排序的假設輸入由小範圍內整數構成,而桶排序假設輸入由一個隨機過程產生,該過程均勻分佈在區間(0,1]
算法
BUCKET-SORT(A)
n = length(A) for i = 1 to n do insert A[i] into list B[nA[i]] for i = 0 to n-1 do sort list B[i] with insertion sort concatenate the lists B[0],B[1],.... B[n-1] together in order
約定
取下中位數
本章討論從一個由n個不一樣數值構成的集合中選擇第i個順序統計量的問題。選擇問題的定義以下:
輸入:一個包含n個不一樣的數的集合A和一個數i, 1=<i<=n
輸出: 元素x屬於A,它恰大於A中其餘的i-1個元素。
好比MINIMUM(A)
min = A[1] for i = 2 to length[A] do if min > A[i] min = A[i] return min
經過n-1比較得出,時間複雜度爲O(n)
最大值也是如此。
同時計算最大值和最小值,如使用獨立比較須要比較2(n-1)次。
可經過每次輸入一對數據,而後將其中大的於最大值比較,小的與最小值比較,每一個元素比較三次,但只需運行n/2次,因此總的比較次數是3(n/2)次。
使用RANDOMIZED-SELECT 算法,以第七章快速排序算法爲模型,也是對輸入數組進行劃分,但只處理劃分的一邊,該邊經過與須要選擇的位計算而出,指望運行時間爲O(n)
RANDOMIZED-SELECT算法利用RADOMIZED-PARTITION程序,返回數組A[p..r]中第i小的元素
RANDOMIZED-SELECT(A, p, r, i) if p = r then return A[p] q = RANDOMIZED-PARTITION(A, p, r) k = q - p + 1 if i = k then return A[q] elseif i < k then return RANDOMIZED-SELECT(A, p, q-1, i) //注意i的位置將會作相應的調整,i始終只是相對位置,而q,r都是指在整個數組中的位置 //所以在比較i和k的位置時,是經過計算k的相對位置來與i比較的。 else return RANDOMIZED-SELECT(A, q+1, r, i-k)
按照個人理解,彷佛不須要使用i的相對位置,原始位置應該就能夠,感受上沒有限制。
結論,在平均狀況下,任何順序統計量特別是中位數,均可以在線性時間內O(n)獲得。
最壞狀況運行時間爲O(n)的SELECT算法。採用快速排序的肯定性劃分算法PARTITION,並作了相應的修改。
- 將輸入數組劃分爲n/5個組,每組5個
- 尋找n/5個組中的每一組中位數,首先對每組中的元素進行插入排序(插入排序在元素數量較小的狀況下有着O(n)的複雜度)而後從排序過的數組中選出中位數。
- 在第二步中選出的中位數,遞歸調用SELECT選出一箇中位數x
- 利用修改過的PARTITION過程,按照中位數的中位數x,對輸入數組進行劃分,讓k比劃分地區的元素數目多1,因此x就是第k小元素,而且有n-k個元素在劃分的高區。
- 若是i = k ,則返回x,不然,若是i<k, 則在低區遞歸調用SELECT以找出第i小的元素,若是i>k, 則在高區遞歸調用尋找第(i-k)個最小元素。
本章中選擇算法之因此具備線性時間,是由於這些算法並無進行排序。線程時間的行爲並非由於對輸入作假設所獲得的結果。在比較模型中,即便是在平均狀況下,排序仍然須要O(nlgn)的時間,因此從漸進意義上來看,排序和索引方法的效率是不高的。
在有限集合S上構造一個長度爲k的串稱爲K串。直觀上,爲了在n元集上構造一個k串,有n種方法選擇第一個元素,一樣地,也有N種方法選擇第二個元素,這樣一次進行K次,從而K串的數目爲nnn*...n = n^k.
最形象的就是車牌號了。
有限集S的排列是S中全部元素的有序序列,且每一個元素僅出現一次。一個N元集的集合有n!種排列,第一個元素有n種選擇,第二個有n-1種選擇,第三個有n-2種選擇...
集合S的k排列是S中k個元素的有序排列,且每一個元素出現一次。所以N元集合的K排列數目是
n(n-1)(n-2)(n-3)(n-4)...(n-k+1) = n!/(n-k)!
n元集的K組合就是S的k子集,n元集的k組合的數目能夠用排列的數目來表示,對每一個k組合,它的元素剛好有k!中排列,每一個都是n元集的一個不一樣的k排列。所以,n元集的k組合數目是其k排列的數目除以k!。這個數量爲
n!/(k!(n-k)!)
組合與排列的區別是,組合中k元祖是無序的,而排列是有序的,意味着組合的元祖對應着k!種不一樣的排列元祖。
所以在計算時須要除去每一個元祖的其餘的有序排列。
二項式係數
二項式界
下界
= n!/k!(n-k)! 展開>= (n/k)^k
再利用斯特林近似式得上界
<= n^k/k! <= (en/k)^k
條件機率Pr{A|B} 讀做在事件B發生的條件下,事件A發生的機率
Pr{A|B} = Pr{A交B}/ Pr{B}
A交B是時間A發生,B也發生的事件,所以能夠認爲這是B中的一個基本事件,所以該事件佔整個B事件的機率就是Pr{A交B}/ Pr{N},也即Pr{A|B}
若是Pr{A交B} = Pr{A} Pr{B},則稱連個事件獨立。
貝葉斯定理
Pr{A交B} = Pr{B} Pr{A|B} = Pr{A} Pr{B|A}
經過交換律,可得以下貝葉斯定理
Pr{A|B} = Pr{A} Pr{B|A}/Pr{B}
隨機變量X的機率密度函數
f(x) = Pr{X=x}
隨機變量的指望值
E[X] = ∑xPr{X=x}
方差
Var[X] = E[(X-E[X]^2)]
= E[X^2]-E^2[X]
幾何分佈
假設進行一系列的伯努利實驗,每次實驗成功的機率是P,失敗的機率是q=1-p,在取得一次成功前要進行多少次實驗?由於在取得一次成功前有k-1次失敗,從而有
Pr{X=k} = q^(k-1)p
指望 1/p
二項分佈
由於有(n k) 種方法從n次實驗中選取k次成功的試驗,且每次發生的機率是p^k*q^(n-k)
Pr{X=k} = (n k)p^k*q^(n-k)
指望E[X] = np, 方差 Var[X] = npq 發自個人 iPad