伪代码以下: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