拓扑到动态规划的时间复杂度汇总:
拓扑排序 | O(顶点数+边数) |
折半查找 | O(log2n) |
贪心算法 | O(n^2) |
归并排序 | O(n+nlogn) |
分治法 | O(nlogn) |
二分搜索 | 最坏:O(logn) |
大整数乘法 | O(n^2) |
Strassen矩阵乘法 | O(n^3) |
B树 | O(logm(n)) |
动态规划 | O(n) |
-
拓扑排序
拓扑排序的方法:
① 输入网图。令 n 为顶点个数。
② 在网图中选一个没有直接前驱(即入度为0)的顶点, 并输出之;
③ 从图中删去该顶点, 同时删去所有它发出的有向边(出边);
④ 重复以上 ②、③步, 直到全部顶点均已输出,则拓扑序列形成
AOE网:带权的有向无环图
关键路径-由关键活动构成的从开始到完成的路径,也是路径长度最长的路径
路径长度-路径上各活动持续时间之和
关键活动-延迟就会影响整个工期的活动
Delayv,w = Latest[w] - Earlieat[v] - Cv,w ,Delayv,w = 0 活动就是关键活动,
Earlieat[v]最早完成时间,Latest[w] 最晚完成时间
-
查找算法
折半查找(顺序存储结构储存的有序表):效率比顺序查找高
有n个结点的判定树的深度为 【log2n】+1,时间复杂度O(log2n)
//非递归:
int Search_Bin (SSTable ST, keyType key)
{
int low=1, high=ST.length, mid;
while (low<=high) {
mid=(low+high)/2;
if (key==ST.elem[mid].key)
return mid; //查找成功
else if (key<ST.elem[mid].key)
high=mid-1;
else
low=mid+1;
}
return 0; //查找失败返回0
}
//递归算法:
int Search_Bin (SSTable ST, keyType key, int low, int high)
{ // low 和 high 的初值分别是 1 和 n
int mid;
if (low<=high) {
mid=(low+high)/2;
if (key==ST.elem[mid].key)
return mid; //查找成功
else if (key<ST.elem[mid].key)
return Search_Bin( ST, key, low, mid-1);
else
return Search_Bin( ST, key, mid+1, high);
}
else
return 0; //查找失败返回0
}
-
贪心算法
贪心方法是作出在当前看来最好的选择,即所作的选择只是局部最优选择。
希望从局部的最优选择得到整体最优解。
背包问题 时间上界 O(nlogn)
贪心算法求解的问题具有两个重要的性质:
①最优子结构性质:整体最优解包含它的子问题的最优解,是一个问题可用动态规划算法或贪心算法求解的一个关键特征
②贪心选择性质:所求问题的整体最优解可以通过一系列局部最优的选择来完成即贪心选择来达到。这是贪心算法可行的要素。
多机调度问题(NP完全问题)
要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。对于这一类问题,用贪心选择策略有时可以设计出较好的近似算法。
当 n =< m 时,只要将机器i的[0, ti]时间区间分配给作业i即可,算法只需要O(1)时间。
当 n > m 时,设置合理的贪心策略求解
step1.把作业处理所需时间按从小到大顺序依次分给每台机器
机器1 | 2 | 5 | 16 |
机器2 | 3 | 6 | |
机器3 | 4 | 14 |
step2.把作业处理所需时间按照从大到小的顺序依次分给每台
机器1 | 16 | 5 | 2 |
机器2 | 14 | 4 | |
机器3 | 6 | 3 |
-
排序
归并排序:时间复杂度 O(n+nlogn),需要线性的额外储存,经常复制临时数组导致效率低
常用于外部排序很少用于内部排序
/* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/
void Merge(ElementType A[], ElementType TmpA[], int L, int R, int RightEnd)
{ /* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */
int LeftEnd, NumElements, Tmp;
int i;
LeftEnd = R - 1; /* 左边终点位置 */
Tmp = L; /* 有序序列的起始位置 */
NumElements = RightEnd - L + 1;
while( L <= LeftEnd && R <= RightEnd ) {
if ( A[L] <= A[R] ) TmpA[Tmp++] = A[L++]; /* 将左边元素复制到TmpA */
else TmpA[Tmp++] = A[R++]; /* 将右边元素复制到TmpA */
}
while( L <= LeftEnd )
TmpA[Tmp++] = A[L++]; /* 直接复制左边剩下的 */
while( R <= RightEnd )
TmpA[Tmp++] = A[R++]; /* 直接复制右边剩下的 */
for( i = 0; i < NumElements; i++, RightEnd -- )
A[RightEnd] = TmpA[RightEnd]; /* 将有序的TmpA[]复制回A[] */
}
void Msort( ElementType A[], ElementType TmpA[], int L, int RightEnd )
{ int Center;
if ( L < RightEnd ) {
Center = (L+RightEnd) / 2;
Msort( A, TmpA, L, Center ); /* 递归解决左边 */
Msort( A, TmpA, Center+1, RightEnd ); /* 递归解决右边 */
Merge( A, TmpA, L, Center+1, RightEnd ); /* 合并两段有序序列 */
}
}
void MergeSort( ElementType A[], int N )
{ ElementType *TmpA;
TmpA = (ElementType *)malloc(N*sizeof(ElementType));
if ( TmpA != NULL ) {
Msort( A, TmpA, 0, N-1 );
free( TmpA );
}
else printf( "空间不足" );
}
-
分治法 时间复杂度O(nlogn)
求解:①划分②求解子问题③合并
可以使用分治法求解的一些经典问题:
最大子段和问题
给定由 n 个整数(可能为负整数)组成的序列 a1 , a2 , …, an ,求该序列的子段和的最大值。当所有整数均为负整数时定义其最大子段和为 0。 (注意:子段是连续的)
例如, 当(a1 , a2 , a3 , a4 , a5 , a6 ) =(-2, 11, -4, 13, -5, -2)时,最大子段和为:20。
下面将分别给出用简单的穷举法和分治法求解最大子问题的算法。
int MaxSubSum(int *a, int left, int right){
int sum=0;
if (left==right)
sum=a[left] >0?a[left]:0;
int center=(left+right)/2;
int leftsum=MaxSubSum(a, left, center);
int rightsum=MaxSubSum(a,center+1,right );
int s1=0; int lefts=0;
for(int i=center; i>=left; i--){
lefts+=a[i];
if (lefts>s1)
s1=lefts;
}
int s2=0; int rights=0;
for(int i=center+1; i<=right; i++){
rights+=a[i];
if (rights>s2)
s2= rights;
}
sum=s1+s2;
if(sum<leftsum) sum=leftsum;
if (sum< rightsum) sum=rightsum;
return sum;
-
动态规划
大问题自上而下分解为子问题,时间复杂度O(n)
动态规划法的基本要素:
①最优子结构:矩阵连乘计算次序问题的最优解包含着其子问题的最优解
②重叠子问题:递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次
-
B树:平衡的多路查找树
根非叶子节点至少有2棵子树
除根结点外的所有非叶结点至少有[m/2]个子节点,最多m个;至少t-1个关键字,至多2t-1个
具有 N 个子节点的非叶子节点拥有 N-1 个键。
所有叶子节点必须处于同一层上。
一个包含x个关键字的节点有x+1个孩子
第h层至少有 2[m/2]h-2
关键码有n个则失败节点数为n+1,n>=2[m/2]h-2-1
N个关键字的m阶B数,有n+1叶节点
查找包含两种操作:①找结点(磁盘)②在结点中找关键字(内存)
时间复杂度 O(logmn),深度h<=logm/2[(n+1)/2]+1