一 某些典型的问题
1.1 稳定匹配问题
1.2 排序
- 归并排序
void mergeSort(int a[],int start,int end){ if(start>=end){ return; } //分治中,中间段的选取一定要注意 int mid=start+(end-start)/2;//与下面赋值的时候对 mergeSort(a,start,mid); mergeSort(a,mid+1,end); merge(a,start,mid,end);//为什么要传入mid,这里只是*逻辑形式上的划分段*,由mid进行区分。 } void merge(int a[],int start,int mid,int end){ int *num=(int*)malloc((end-start+1)*sizeof(int));//申请空间来存放两个有序区归并后的临时区域,很重要。 int i=start;//指向前一段sorted数组的头指针 int j=mid+1;//指向后一段sorted数组的头指针 int n=0;//指向所申请的数组空间的第一个位置 while(i<=mid && j<=end){ if(a[i]<=a[j]){ num[n++]=a[i++];//放入数组后,两数组指针向后移动 } else{ num[n++]=a[j++]; } } //当某段数组遍历完之后,将未遍历完的数组直接加到申请的数组中 while(i<=mid){ num[n++]=a[i++];//放入数组后,两数组指针向后移动 } while(j<=end){ num[n++]=a[j++]; } for(int i=0;i<n;i++){ a[start+i]=num[i];//这里从start开始 } //记得释放申请空间 free(num); }
1.4 树
1.5 图
(1) 基本分类
- 有向图
- 无向图
(2) 表示方法
- 邻接表
- 邻接矩阵
(3) 性质
- 环
- 连通性
- 树
(4) 图的遍历
- 连通性
- BFS
- DFS
(5) 图中最短路径
- BFS(广度优先搜素)
适合无向图中的最短路径搜索,时间复杂度O(V+E)。 - Dijsltra算法
适用于边权为正的无向和有向图,不适用于 有负边权的图!(为什么?)- 基本算法思想:
- 将初始点s看作集合S,将其他点看作集合T。
- 从初始点出发,找到初始点到达各节点的距离d[i] (若无法到达,则将距离记作无穷大,记作INF)
- 选取d[i]中最小的距离d[x],找到初始点能够到达的距离中最小的节点x,将其放入集合S中。
- 进行松弛操作,通过x所能到达的各节点的距离,去更新初始点s到达各点的最小距离。d[i]=min{ d[i] , d[x]+d[x][i]}
- 重复3,4步直到所有的点都归入集合S。
(注:重复第三步的时候,应该从所有的d[i]中寻找最小值,而不是只从与x点相邻的点中寻找。想想为什么?
- 时间复杂度
为什么Dijsktra算法的时间复杂度是O(n^2)。
首先我们需要遍历所有的顶点,这个时间复杂度已经是O(n),再根据遍历点去更新初始节点到所有节点的最小距离所以为O(n^2)。
- Bellman-Ford算法
适用于带负权边的图,但不适用于带负权环的图 时间复杂度:O(n^2)O(VE)
1.3 分治算法
(1)求逆序数(明显逆序数)
count-and-sort(A){
if(A中只有一个数){
return 0;//注意递归边界条件
}else{
A1=A左半边的数;
A2=A右半边的数;
r1=count-and-sort(A1);
r2=count-and-sort(A2);
r=merge-and-count(A1,A2);
return r1+r2+r3;
//r1表示左边数组的逆序数,r2表示右边数组的逆序数
//r表示两边结合起来的逆序数,最后返回两边的逆序数
}
merge-and-count(A1,A2){
先定义两个指针p1,p2指向两个数组的首元素
count表示逆序数个数,初始化为0
初始化一个数组temp[]能容下A1,A2;
while(A1和A2都不为空){
if(p1<p2){
直接将p1所指向的数据放入temp数组中
并移动p1
}else{
if(p1>p2+5){
为明显逆序对,count加上p1后面剩余元素
}else{
沿着p1向后移动
}
将p2所指向的数据放入temp数组中
p2向后移动
}
}
while(p1或者p2不为空){
将p1或p2所指向的后面的数组元素放在temp后面
}
for(i=0 to end){
将temp中的元素全部放入A中
}
free(temp);
释放临时申请的空间
}
(2)二分查找
(3)快速排序
(4)输出前m大的数
(5)快速傅里叶变换
1.4 贪心算法
1.6 动态规划
(1)酒店租房问题
酒店给出了未来一年中某间客房每天的单价C1,C2,…,Cn,并支持两种结算方式,一种按照每天的单价支付,另一种是没连续30天可以按照常住价t30支付。如果该租户向租一年,求最低的结算方案。
- 算法思想
记OPT(j)为第j天内的最小结算价格,有两种结算方式,一种按每天的单价支付,一种直接付款t30直接住30天,但前几天按照t30肯定不划算,只有在按照每天价格住一定天数后总价格大于按常住价t30则选择t30。
所以可以列出递归式:
OPT(j)=min { OPT(j-1)+Cj , t30}, 1<j<=30;
OPT(j)=min {OPT(j-1)+Cj , OPT(j-30)+t30},j>30;
(2)基因序列比对问题(实际上字符串比对问题)
给定两个字符串,对其相同字符的程度及位置进行比对。求两个字符串的最小错位程度。
错位程度(想要对齐两个字符串的代价):将两个字符串相同字符对齐在一起,不齐的地方用空格补充;最终无法对齐的部分被称为错位(mismatch),找出两个字符串的最小mismatch。
- 算法思想
- 第一步:画格子
在使用动态规划方法时,第一步依旧是画格子。
需要声明的是第二行和第二列空着的位置表示gap,边界条件
- 第一步:画格子
- 第二步:填表
第一个网格位置的值表示gap对上gap(无意义),得分为零。
分析前面的几种情况。
(3) 最大字符子串和问题
要点:将OPT[i]记为以i字符结尾的连续最大子串和,Ci(i=1,2,…,n)为第i个数值
递归方程式:
OPT[i]=max {OPT[i-1]+Ci,Ci}
(4) 股票买入卖出问题
一只股票,在n个连续交易日中每天都有固定的成交价格p(i); i=1,2,…,n.允许有一次买入操作和卖出操作,问如何选择买入日期j和卖出日期k使得收益最大化p[k]-p[j]
- 算法思想
依次让后面的成交价格减去前面的成交价格,得到一个n-1的数组记录当天买入第二天卖出的收益。
然后再采用最大连续子串和的算法思想求解得到第i天买入第j天卖出的最大收益。 - 拓展
用分治法也能够去解。最大子串有三种情况:- 1<=i<j<=n/2,最大子串在数组的左半部分;
- n/2<=i<j<=n,最大子串在数组的右半部分;
- 1<=i<n/2<j<=n,最大子串部分来自于左边,部分来自于右边。
1.5 搜索算法
1.7 字符串匹配
1.8 网络流问题
- 最大流问题
Ford-Fulkerson 算法是一种解决最大流的方法,其依赖于三种重要思想:
- 残留网络(Residual networks)
- 增广路径(Augmenting paths)
- 割(Cut)
这些思想是最大流最小割定理的精髓,该定理用流网络的割来描述最大流的值。
伪代码:
1 FORD-FULKERSON-METHOD(G, s, t)
2 initialize flow f to 0
3 while there exists an augmenting path p
4 do augment flow f along p
5 return f
- 给定流网络图G=(V,E),每条边e上的容量Ce都是证书,假设G中存在多个大小相同的最小割,这些割集中包含一个最小的边数,找出变数最小的割集。
1 先求出网络图中的最大流
2 将其中可能的关键边(即满流的边)权值设为1,其余边的权值设为0
3 再使用一次最大流算法,则求出来的最大流即是最小割的最小边集
1.9 NP问题
- 给定一个新的问题,证明他是NP完全的基本策略是:
1. 先证明X属于NP问题
2. 选择一个已知的NP完全问题Y
3. 证明问题Y可以被归约到问题X - 用哈密顿回路问题证明旅行商问题
(1)旅行路径可以看作是所有城市的一个排列,我们能够在多项式的时间内核实这个排列确实恰好包含每个顶点一次,所以这是一个NP问题。
(2)哈密顿回路是一个NPC问题
(3)现在我们证明哈密顿回路能够归约到旅行商问题;给定一个有向图G(V,E),规定在旅行商问题的实例中,对图中每一个节点Vi,有一个城市Vi‘,如果图中两节点之间的距离为d,则两城市之间的距离同样为d,这样我们如果如果在图G中能找到相应的哈密顿回路,则我们就可以通过计算回路的权值判断是否小于特定的值。即我们可以看出,哈密顿回路问题可以归约为旅行商问题。即证。 - 独立集问题证明区间调度问题(课程安排)
(1)课程安排问题是NP问题:给定K门课程,我们可以在多项式时间内检查他们之间有没有冲突。
(2)给定一个独立集实例,有图G=(V,E)和数K。其中节点Vi表示课程i,边E表示授课时段,其中与节点Vi边邻接的边集就是课程i的授课时间集Ci。这个规约过程在多项式时间内可以完成。
(3)证明:至少开设K个课程且任何两门课程之间没有时间冲突当且仅当至少存在大小为K的独立集。如果至少能开设K们课程且课程之间没有时间冲突。存在与课程对应的K个节点,他们彼此之间不存在边,即存在大小至少为K的独立集。所以课程安排问题是一个NP完全问题。 - 子集和问题证明课程安排问题
(1)课程安排问题是NP问题:给定K门课程,我们可以在多项式时间内检查他们之间没有冲突且能不能够在截止时间内完成。