第一章 算法概述
1.算法渐进性态:当问题规模(n)趋近无穷大,称为O(n)= 表达式,取表达式的最高阶无穷大,这个就是运行时间的上界。
问题的上界
问题准确界
问题的下界
问题一:知问题复杂度,求计算机速度提升一定倍数,求同样时间内处理问题规模变为原来多少?
问题:解决某问题有三种算法,复杂性分别为1000N、10N的2次方、2的N次方,在一台机器上可处理问题的规模分别为S1、S2、S3 。若机器速度提高到原来的10倍,问在同样时间内可处理问题的大小如何?
解:设计算机原来速度为v,则提升速度后10v,
问题规模/速度=时间,利用两次处理时间相等但问题规模不同列方程。
原问题规模/原速度 = 新问题规模 / 新速度
则,1000S1 / v = 1000n / ( 10v)
问题二:知问题复杂度,改进复杂度,求提升后,原来问题解决时间。
问题:例2:问题P的算法复杂度为T(n)=n的3次方(毫秒),现改善为T(n)=n的2次方(毫秒)。问原来运行1小时的问题实例,现在要运行多少时间?
解:设计算机速度为v,原来用时为t, 问题规模为n,新用时为t1。
问题规模/速度=时间,利用两次处理速度相等、问题规模相同列方程。
同时利用原来的用时求出问题规模n的大小。
n^3 / t = n^2 / t1
n^3 = 1 hour
n=153
t1 = t / n = 23.5
第二章 递归与分治
阶乘函数
int Factorial(int n)
{
if(n==0) return 1;
return n*Factorial(n-1);
}
Fibonacci数列
无穷数列1,1,2,3,5,8,13,21,34,55,……。
int fibonacci(int n)
{
if (n <= 1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
排列问题
整数划分问题
一般递归问题会用非递归方法代替:
1. 可化为非递归方法。
2. 栈模拟。
分治法的基本思想
分治法的设计思想:
1) 将一个难以直接解决的大问题,分割成一些规模较小的子问题;这些子问题互相独立且与原问题相同
2)递归地解子问题
3) 将各个子问题的解合并得到原问题的解
Divide-and-Conquer(P)
{
if ( |P|<=n0) Adhoc(P);
divide P into smaller subinstances P1 ,P2,... ,Pk;
for (i = 1;i <= k; i++)
yi=Divide-and-Conquer(Pi);
return Merge( yl ,..., yk);
}
算法时间复杂度
主方法
二分搜索技术
int BinarySearch(Type a[ ], const Type &x,int n)
{
int left=0; int right=n-1;
while(left<=right){
int middle=(left+right)/2;
if(x==a[middle])
return middle;
else if(x>a[middle])
left=middle+1;
else
right=middle-1;
}
return -1;
}
棋盘覆盖问题
合并排序
temlplate <class type>
void MergeSort(Type a[], int left, int right)
{ if (1eft < right) //至少有2个元素
{
int i = (left + right ) /2; //取中点
MergeSort(a, left, i);
MergeSort(a, i+1, right);
Merge(a, b, left, i, right);//从a合并到数组b
copy(a, b, left, right);//复制回数组a
}
}
快速排序算法
//将>=x的元素交换到右边区域
//将<=x的元素交换到左边区域
int Partition(Type a[ ],int p , int r )
{
int i=p;
j=r+1;
type x=a[p];
while(true)
{
while(a[++i] < x&&i<r);
while(a[--j] > x);
if (i>=j ) break;
swap(a[i],a[j]);
}
a[p] = a[j];
a[ j] = x;
return j }
template <class Type>
void QuickSoft(Type a[], int p, int r)
{
if(p<r){
int q=Partition(a, p, r)
QuickSort(a, p, q-1); //对左半段排序
QuickSoft(a, q+1, r); //对右半段排序
}
}
//随机选择基准点、
template <class Type>
void randomizedQuickSoft(Type a[], int p, int r)
{
if (p<r) {
int q=randomizedPartition(a, p, r)
randomizedQuickSort(a, p, q-1); //对左半段排序
randomizedQuickSoft(a, q+1, r); //对右半段排序
}
}
template <class Type>
int randomizedPartition(Type a[], int p, int r)
{
int i=random( p, r)
swap( a[i], a[p] )
return Partition (a,p,r)
}
最接近点对问题
给定平面上n个点,找其中的一对点,使得
在n个点组成的所有点对中,该点对间的距离最小。
线性时间选择
大整数的乘法
Strassen矩阵乘法
第三章 动态规划
矩阵连乘
最大子段和
int MaxSum(int n, int * a)
{
int sum=0, b=0;
for (int i=1; i<=n; i++) {
if(b>0) b+=a[i];
else b=a[i];
if (b>sum) sum=b;
}
return sum
} //时间和空间复杂度:O(n)
0-1背包
最长公共子序列
最大子段和
int MaxSum(int n, int * a)
{
int sum=0, b=0;
for (int i=1; i<=n; i++) {
if(b>0) b+=a[i];
else b=a[i];
if (b>sum) sum=b;
}
return sum
} //时间和空间复杂度:O(n)
流水作业调度
图像压缩
第四章 贪心算法
贪心算法的基本要素:
(1) 最优子结构
问题的最优解包含子问题的最优解
(2) 贪心选择性质
问题的整体最优解可以通过一系列局部最优选择达到
背包问题
最优装载
哈夫曼编码
构成赫夫曼树的步骤:
1)从小到大进行排序, 将每一个数据,每个数据都是一个节点 , 每个节点可以看成是一颗最简单的二叉树
2)取出根节点权值最小的两颗二叉树
3)组成一颗新的二叉树, 该新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和
4)再将这颗新的二叉树,以根节点的权值大小 再次排序, 不断重复 1-2-3-4 的步骤,直到数列中,所有的数据都被处理,就得到一颗赫夫曼树
哈夫曼编码就是根节点到树叶的编码,左子树为0,右子树为1
第五章 回溯法
背包问题
图的着色:
已知无向图G=(V,E)和m种不同的颜色,如果只允许使用这m种颜色对图G的结点着色,每个结点着一种颜色。问是否存在一种着色方案,使得图中任意相邻的两个结点都有不同的颜色。整数m称为图G的着色数。
第六章 分支限界法概述
背包问题
优先级队列
优先级定义:当前价值+剩余能装的最大价值(上界函数)
分支限界法求解0/1背包问题动画演示(Implementation of 0/1 Knapsack using Branch and Bound)_哔哩哔哩_bilibili
单源最短路径
约束函数:c[i][j]<inf dist[i]+c[i][j] <dist[j]
优先级:节点所对应的当前路长
装载问题