时间复杂度的第一性原理:
- 渐进分析
- 数学期望
分治与排序
渐进分析是时间复杂度分析的第一性原理,依托数学的渐进分析形成计算机程序复杂度分析系统。
渐进分析
计算机算法复杂度分析主要考虑最坏的运行时间
数学公式:
- 渐进上界
- 渐进下界
- 渐进紧确界
分治策略
- 分解:将一个问题划分成一些子问题
- 解决:递归解决这些子问题
- 合并:将子问题的解组合成原问题的解
乘法子问题
- 分解乘法子问题
-
伪代码
multiplication(x, y): if (n==1): return x*y make x as a*10^(n/2) + b make y as c*10^(n/2) + d a*c = multiplication(a,c) a*d = multiplication(a,d) b*c = multiplication(b,c) b*d = multiplication(b,d) return ac*10^n + (ad+bc)*10^(n/2) + bd -
主定理
LEVEL 子问题个数 子问题大小 求解子问题操作次数 该层总计操作次数 0 1 n c*nd 1cnd 1 a n/b c*(n/b)d ac(n/b)d ... ... ... ... ... t at n/(bt) c*(n/bt)d atc*(n/bt)d ... ... ... ... ... log2 n alog2 n n/(blogb n) = 1 c*(n/blogb n)d alog2 n c*(n/blogb n)d 总计
-
性能分析
LEVEL 子问题个数 子问题大小 求解子问题操作次数 该层总计操作次数 0 1 n 1 O(1) 1 4 n/2 1 O(4) ... ... ... ... ... t 4t n/(2t) 1 O(4t) ... ... ... ... ... log2 n 4log2 n =n2 1 1 O(n2) 总计 = O(1) + O(4) + ... O(n2) ==> O(n2)
表达式:
KAMTSUBA乘法子问题
- 子问题
-
伪代码
multiplication(x, y): if (n==1): return x*y make x as a*10^(n/2) + b make y as c*10^(n/2) + d a*c = multiplication(a,c) b*d = multiplication(b,d) (a+b)*(c+d) = multiplication((a+b),(c+d)) return ac*10^n + ((a+b)*(c+d)-ac-bd)*10^(n/2) + bd -
性能分析
LEVEL 子问题个数 子问题大小 求解子问题操作次数 该层总计操作次数 0 1 n 1 O(1) 1 3 n/2 1 O(3) ... ... ... ... ... t 3t n/(2t) 1 O(3t) ... ... ... ... ... log2 n 3log2 n =n1.6 1 1 O(n1.6) 总计 = O(1) + O(3) + ... O(n1.6) ==> O(n2)
表达式:
排序
插入排序
-
维护一个不断增长的排序序列,每次循环将元素插入到指定正确的地方
-
伪代码
InsertSort(Z): for i in range(1,len(Z)): curValue = Z[i] j = i-1 while j>=0 and Z[j] > curValue: Z[j+1] = Z[j] j-- Z[j+1] = curValue -
时间复杂度分析
- for循环n次,while循环最多n次,所有最差为O(n2)
归并排序
-
使用了分治的思想
- 每次将数组排序分为两个长度相等的数组排序的子问题
- 递归求解子问题
- 合并子问题的解(遍历两个数组,依次将最小值放入新的大数组中),并返回排好序的数组
-
伪代码
mergerSort(Z): n = len(Z) if n <= 1: return Z L = mergerSort(Z[0:n/2]) R = mergerSort(Z[n/2:n]) return merger(L,R) merger(L,R): newArray = array[len(L)+len(R)] i=0,j=0 for t in newArray: if L[i] < R[j]: newArray[t] = L[i] i++ else: newArray[t] = R[i] j++ return newArray; -
性能分析
LEVEL 子问题个数 子问题大小 求解子问题操作次数 该层总计操作次数 0 1 n c*n O(n) 1 2 n/2 c*(n/2) O(n) ... ... ... ... ... t 2t n/(2t) c*(n/2t) O(n) ... ... ... ... ... log2 n 2log2 n =n 1 c*1 O(n) 总计 = O(n) + O(n) + ... O(n) ==> O(nlogn)
表达式:
线性时间寻找第t大小数字
-
思路
- 使用分治策略
- 选择一个主元,围绕它划分子数组
- 递归
- 合并结果集
- 使用分治策略
-
伪代码
query (Z,t): //获取排序第t位数 if len(Z) == 1: return Z[0] pivot = medianOfmedian(Z) //获取中位数的中位数 L,R = grouping(Z,pivot) //将其进行分组 if len(L) == t-1: //尺判断过程会排除一部分(至少排除7/10),当层时间复杂度T(7n/10) return pivot; else if len(L) > t-1: return query(L,t); else if len(L) < t-1: return query(R,t); grouping(Z,pivot)://按pivot将其进行分组,小在左,大在右,时间复杂度O(n) L,R = [],[] for i in Z: if Z[i] == pivot: continue else if Z[i] < pivot: add Z[i] to L else : add Z[i] to R medianOfmedian(Z)://递归获取中位数的中位数,每次分五组,当层时间复杂度T(5/n) n = len(Z) if n <= 5: return getMedian[0] one = medianOfmedian(Z[0:n/5]) two = medianOfmedian(Z[n/5:2n/5]) three = medianOfmedian(Z[2n/5:3n/5]) four = medianOfmedian(Z[3n/5:4n/5]) five = medianOfmedian(Z[4n/5:n]) getMedian(one,two,three,four,five); getMedian(A)://获取中位数 sort(A) n = len (A) if n > 3 return A[2]; else if n > 1 return A[1]; else if n > 0 return A[0];- 性能分析
- 性能分析
快速排序
统计基础知识
X是一个伯努利随机变量,其为1的概率为1/100,为0的概率为99/100
-
X的数学期望是多少
-
抽取n个独立的随机变量,和的期望是多少?
-
抽取n个随机变量,在看到第一个1的时候停止,令N为最后一次抽取的下标N的期望是多少?
随机算法
- 算法中的某一部分涉及随机化操作
- 运行时间分析
- A写了一个算法,B选择执行的输入,A执行这个算法
- 运行时间是一个随机变量(依赖于算法执行过程中的随机性),我们可以求解期望运行时间
- A写了一个算法,B选择执行的输入,同时B也决定算法运行过程中的随机性(每次都给同一个数)
- 运行时间不是随机的(B每次都输入最坏的情况),我们可以求解最坏运行时间
- A写了一个算法,B选择执行的输入,A执行这个算法
快速排序
-
思路
-
使用分治策略
-
随机选择一个主元,围绕它划分子数组
-
递归
-
合并结果集
-
-
-
伪代码
quickSort(Z):
if len(Z) = 1:
return
pivot = random(Z)
L,R = grouping(Z,pivot) //将其进行分组
Z = [L,pivot,R]
quickSort(L)
quickSort(R)
return Z;
random(Z)://随机获取主元
n = random(0,len(Z)-1)
return Z[n];
grouping(Z,pivot)://按pivot将其进行分组,小在左,大在右,时间复杂度O(n)
L,R = [],[]
for i in Z:
if Z[i] == pivot:
continue
else if Z[i] < pivot:
add Z[i] to L
else :
add Z[i] to R
- 性能分析
- 最坏
- T(n) = T(n-1) + O(n) = O(n2)
- 最好
- T(n) = T(n/2) + T(n/2) + O(n) = O(nlogn)
- 最坏
排序算法下界
比较排序
- 归并排序 最坏情况 O(nlogn)
- 快速排序 最好情况 O(nlogn)
非比较排序
- 计数排序O(n)
- 1、提前知道可能出现的值;2、输入的可能值数量有限
- 1、按可能出现的值创建桶;2、遍历数组将值放入指定桶中;3、按顺序将指定桶中的值拿出来依次放入数组
- 基数排序O(n)
- 1、待排序的是整数或者字符串;2、长度不超过一定的范围;
- 1、使用计数排序按最低位开始放入指定的0~9的10个队列中;2、按顺序依次拿出所有的数;3、重复1和2直到最大位结束。