填空题(10分、10个)
(第一章)算法的基本概念
(5分)算法的特点、算法的描述方法、算法的表达方式
(5分)时间复杂度、空间复杂度
选择、判断题(5分、5个和5分、5个)
贪心法着色问题——穷解还是无穷解、局部还是全局
基本概念上的理解
动态规划有几个步骤(哪几项是或者不是里面的)
程序时间复杂度
简答题(80分、4-5个)
每一种算法都有涉及、基于作业、习题改变、思想一样
给出算法描述、时间复杂度、填表操作(动态规划)、解空间(回溯法)
算法
(1)蛮力法
蛮力法是指采用遍历(扫描)技术,即采用一定的策略将待求解问题的所有元素依次处理一次,从而找出问题的解。依次处理所有元素是蛮力法的关键,为了避免陷入重复试探,应保证处理过的元素不再被处理。
(2)减治、分治、变治
原问题的解只存在于其中一个较小规模的子问题中;原问题的解与其中一个较小规模的解之间存在某种确定的对应关系。
(3)动态规划
(4)贪心法
(5)回溯法
问题
组合问题
0/1背包、(任务分配)
给定n个重量为{w1, w2, … ,wn}、价值为{v1, v2, … ,vn}的物品和一个容量为C的背包,求这些物品中的一个最有价值的子集,且要能够装到背包中。
蛮力法
时间复杂度——2^n
动态规划
0/1背包问题可以看作是决策一个序列(x1, x2, …, xn),对任一变量xi的决策是决定xi=1还是xi=0。设V(n, C)表示将n个物品装入容量为C的背包获得的最大价值,显然,初始子问题是把前面i个物品装入容量为0的背包和把0个物品装入容量为j的背包,得到的价值均为0,即:
V(i, 0)= V(0, j)=0 (0≤i≤n, 0≤j≤C)
考虑原问题的一部分,设V(i, j)表示将前i(1≤i≤n)个物品装入容量为j(1≤j≤C)的背包获得的最大价值,在决策xi时,可采用递推式:
为了确定装入背包的具体物品,从V(n,C)的值向前推,如果V(n,C)>V(n-1,C),表明第n个物品被装入背包,前n-1个物品被装入容量为C-wn的背包中;否则,第n个物品没有被装入背包,前n-1个物品被装入容量为C的背包中。依此类推,直到确定第1个物品是否被装入背包中为止。由此,得到如下函数:
有5个物品,其重量分别是{2, 2, 6, 5, 4},价值分别为{6, 3, 5, 4, 6},背包的容量为10,动态规划法求解0/1背包问题。
贪心法
设背包容量为W,共有n个物品,物品重量存放在数组w[n]中,价值存放在数组v[n]中,问题的解存放在数组x[n]中。
算法——背包问题
输入:背包容量W,物品重量w[n],物品价值v[n]
输出:解向量数组x[n]
将n个物品按单位重量价值v[i]/w[i]降序排列;
将数组x[n]初始化为0;
i=0;
循环直到(w[i]>W)
4.1 将第i个物品放入背包:x[i]=1;
4.2 W=W-w[i];
4.3 i++;
x[i]=W/w[i];
排序的时间复杂性为O(nlog2n),while循环的时间为O(n),所以本算法的时间复杂度为O(nlog2n)。
最大连续子段
给定一个有n(n≥1)个整数的序列,要求求出其中最大连续子序列的和。
蛮力法
穷举所有连续子序列来得到。
设含有n个整数的序列a[0..n-1],穷举所有的连续子序列a[i..j],求出它的所有元素之和thisSum,并通过比较将最大值存放在maxSum中,最后返回maxSum。
时间复杂度——O(n2)
分治法
对于含有n个整数的序列a[0..n-1]
若n=1,表示该序列仅含一个元素,如果该元素大于0,则返回该元素;否则返回0。
若n>1,采用分治法求解最大连续子序列时,取其中间位置mid=(n-1)/2,该子序列只可能出现3个地方。
该子序列完全落在左半部即a[0..mid]中。采用递归求出其最大子段和maxLeftSum。
该子序列完全落在右半部即a[mid+1..n-1]中。采用递归求出其最大子段和maxRightSum。
该子序列跨越序列a的中部而占据左右两部分。
T(n)=1 当n=1
T(n)=2T(n/2)+n 当n>1
时间复杂度——O(nlogn)
在线处理法
从头开始扫描数组a,用sum(初值为0)记录当前子序列之和,用max(初值为0)记录最大连续子序列和。
如果扫描中遇到负数,当前子序列和sum将会减小,若sum为负数,表明前面已经扫描的那个子序列可以抛弃了,则放弃这个子序列,重新开始下一个子序列的分析,并置sum为0。
若这个子序列和sum不断增加,那么最大子序列和max也不断增加。
时间复杂度——O(n)
棋盘覆盖问题
有一个2k×2k(k>0)的棋盘,恰好有一个方格与其他方格不同,称之为特殊方格,并且称该棋盘为一特殊棋盘。现在要用图(b)的4种不同形状的三格骨牌覆盖除了特殊方格外的其他全部方格,并且任何两个三格骨牌不能重叠。请给出一种覆盖方案。
分治法
T(K)=O(1) K=0
T(K)=4T(K-1)+O(1) K>0
T(K)=O(4k)
数塔问题
动态规划
求解初始子问题:底层的每个数字可以看作1层数塔,则最大数值和就是其自身;
再求解下一阶段的子问题:第4层的决策是在底层决策的基础上进行求解,可以看作4个2层数塔,对每个数塔进行求解;
再求解下一阶段的子问题:第3层的决策是在第4层决策的基础上进行求解,可以看作3个2层的数塔,对每个数塔进行求解;
以此类推,直到最后一个阶段:第1层的决策结果就是数塔问题的整体最优解。
最长递增子序列
问题描述:在数字序列A={a1, a2, …, an}中按递增下标序列(i1, i2,…, ik)(1≤i1< i2<…< ik≤n)顺序选出一个子序列B,如果子序列B中的数字都是严格递增的,则子序列B称为序列A的递增子序列。最长递增子序列问题就是要找出序列A的一个最长的递增子序列。
动态规划
设L(n)为数字序列A={a1, a2, …, an}的最长递增子序列的长度,显然,初始子问题是{a1},即L(1)=1。考虑原问题的一部分,设L(i)为子序列A={a1, a2, …, ai}的最长递增子序列的长度,则满足如下递推式:
A={5, 2, 8, 6, 3, 6, 9, 7}
首先计算初始子问题,可以直接获得:
L(1)=1({5})
然后依次求解下一个阶段的子问题,有:
L(2)=1({2})
L(3)=max{L(1)+1, L(2)+1}=2({5, 8}, {2, 8})
L(4)= max{L(1)+1, L(2)+1}=2({5, 6}, {2, 6})
L(5)=L(2)+1=2({2, 3})
L(6)=max{L(1)+1, L(2)+1, L(5)+1)}=3({2, 3, 6})
L(7)=max{L(1)+1, L(2)+1, L(3)+1, L(4)+1, L(5)+1, L(6)+1}=4({2, 3, 6, 9})
L(8)=max{L(1)+1, L(2)+1, L(4)+1, L(5)+1, L(6)+1}=4({2, 3, 6, 7})
序列A的最长递增子序列的长度为4,有两个最长递增子序列,分别是{2, 3, 6, 9}和{2, 3, 6, 7})。
图着色
贪心法
任选一顶点着颜色1,在图中寻找尽可能多的顶点用颜色1着色;
选取不能用颜色1着色的顶点,用颜色2着色,在图中寻找尽可能多的顶点用颜色2着色;
直到所有顶点都被着色停止算法。
算法——图着色问题
输入:无向连通图G=(V,E)
输出:最小色数k
所有顶点置未着色状态;
颜色k初始化为0;
循环直到所有顶点均着色
3.1 取下一种颜色:k++;
3.2 依次考察所有顶点:
3.2.1 若顶点i已着色,则转步骤3.2,考察下一个顶点;
3.2.2 若顶点i着颜色k不冲突,则color[i]=k;
输出色数k;
回溯法
由于用m种颜色为无向图G=(V,E)着色,其中,V的顶点个数为n,可以用一个n元组X=(x1, x2, …, xn)描述图的一种可能着色,其中,xi∈{1, 2, …, m}(1≤i≤n)表示赋予顶点i的颜色。
例如,7元组x(1,2,1,3,1,3,4)表示对具有7个顶点的无向图的一种4着色,顶点1着颜色1,顶点2着颜色2,顶点3着颜色1,如此等等
如果在n元组X中,所有相邻顶点都不会着相同颜色,就称此n元组为答案解,否则为无效解。
图中的顶点编号为1~n,着色编号为1~m。对于图G中的每一个顶点,可能的着色为1~m,所以对应的解空间是一棵m叉树,高度为n,层次i从1开始。
回溯法求解图着色问题,首先把所有顶点的颜色初始化为0,然后依次为每个顶点着色。在图着色问题的解空间树中,如果从根结点到当前结点对应一个部分解,也就是所有的颜色指派都没有冲突,则在当前结点处选择第一棵子树继续搜索,也就是为下一个顶点着颜色1,否则,对当前子树的兄弟子树继续搜索,也就是为当前顶点着下一个颜色,如果所有m种颜色都已尝试过并且都发生冲突,则回溯到当前结点的父结点处,上一个顶点的颜色被改变,依此类推。
该算法中的每个顶点试探1~m种着色,共n个顶点,对应的解空间树是一棵m叉树(子集树),因此算法最坏情况下的时间复杂度为O(mn)。
图问题
TSP问题
指旅行家要旅行n个城市然后回到出发城市,要求各个城市经历且仅经历一次,并要求所走的路程最短。该问题又称为货郎担问题、邮递员问题、售货员问题,是图问题中最广为人知的问题。
蛮力法
时间复杂度——(n-1)!/2
动态规划
对于图G=(V, E),假设从顶点i出发,令V'=V-i,则d(i, V')表示从顶点i出发经过V'中各个顶点一次且仅一次,最后回到出发点i的最短路径长度, 显然,初始子问题是d(k, { }),即从顶点i出发只经过顶点k回到顶点i。现在考虑原问题的一部分,d(k, V'-{k})表示从顶点k出发经过V'-{k}中各个顶点一次且仅一次,最后回到出发点i的最短路径长度,则:
d(k, { })=cki
d(i, V')=min{cik + d(k, V'-{k})}(k∈V')
∞ 3 6 7
5 ∞ 2 3
6 4 ∞ 2
3 7 5 ∞
首先计算初始子问题,可以直接获得:d(1, { })= c10 =5(1→0);
d(2, { })= c20 =6(2→0);d(3, { })= c30 =3(3→0)
再求解下一个阶段的子问题,有:
d(1, {2})= c12+d(2, {})=2+6=8(1→2); d(1, {3})= c13+d(3, {})=3+3=6(1→3)
d(2, {1})= c21+d(1, {})=4+5=9(2→1); d(2, {3})= c23+d(3, {})=2+3=5(2→3)
d(3, {1})= c31+d(1, {})=7+5=12(3→1);d(3, {2})= c32+d(2, {})=5+6=11(3→2)
再求解下一个阶段的子问题,有:
d(1, {2, 3})=min{c12+d(2, {3}), c13+ d(3, {2})}=min{2+5, 3+11}=7(1→2)
d(2, {1, 3})=min{c21+d(1, {3}), c23+ d(3, {1})}=min{4+6, 2+12}=10(2→1)
d(3, {1, 2})=min{c31+d(1, {2}), c32+ d(2, {1})}=min{7+8, 5+9}=14(3→2)
直到最后一个阶段,有:
d(0, {1, 2, 3})=min{c01+ d(1, { 2, 3}), c02+ d(2, {1, 3}), c03+ d(3, {1, 2})}
=min{3+7, 6+10, 7+14}=10(0→1)
所以,从顶点0出发的TSP问题的最短路径长度为10,再将状态进行回溯,得到最短路径是0→1→2→3→0。
贪心法
最近邻点策略:从某城市出发,每次在没有到过的城市中选择最近的一个,直到经过了所有的城市,最后回到出发城市。
算法——最近邻点策略求解TSP问题
1. P={};
2. V=V-{u0}; u=u0; //从顶点u0出发
3. 循环直到集合P中包含n-1条边
3.1 查找与顶点u邻接的最小代价边(u, v)并且v属于集合V;
3.2 P=P+{(u, v)};
3.3 V=V-{v}; //保证了不产生回路
3.4 u=v; //从顶点v出发继续求解
算法的时间性能为O(n2)
最短链接策略:每次在整个图的范围内选择最短边加入到解集合中,但是,要保证加入解集合中的边最终形成一个哈密顿回路。因此,当从剩余边集E'中选择一条边(u, v)加入解集合中,应满足以下条件:
① 边(u, v)是边集E'中代价最小的边;
② 边(u, v)加入解集合S后,S中不产生回路;
③ 边(u, v) 加入解集合S后,S中不产生分枝(就是没有节点度数超过3);
如果操作“在E’中选取最短边(u, v)“用顺序查找,则算法的时间性能是O(n2),如果采用堆排序的方法将集合E‘中的边建立堆,则选取最短边的操作可以是O(log2n),对于两个顶点是否连通以及是否会产生分枝,可以用并查集的操作将其时间性能提高到O(n),此时算法的时间性能为O(nlog2n)。
普利姆算法
算法描述: Prim(G)
//输入:加权连通图G=<V,E>
//输出:最小生成树集合ET
VT = {v0},任意顶点初始化顶点集合
ET= ∅
for i=1:V-1 do
所有边(v,u)中最小权重边e*=(v*,u*)
v在VT中,u在V-VT中
VT=VT∪{u*}
ET=ET∪{e*}
return ET
- 找出结点1与其他结点相连的边{(1,2),(1,4),(1,6)}找出最短长度为1的边(1,2);ET={1,2}
- 找出与结点{1,2}与其他结点相连的边{(1,4),(1,6),(2,4),(2,6)},找出最短长度为2的边(1,6);ET={1,2,6}
- 找出结点{1,2,6}与其他结点相连的边{(1,4),(2,4),(6,4)},找到最短长度为3的边(2,4);ET={1,2,4,6}
- 找出结点{1,2,4,6}与其他结点相连的边{(4,3),(4,5)},找出最短长度为3的边(4,3);ET={1,2,3,4,6}
- 找出结点{1,2,3,4,6}与其他结点相连的边{(4,5)},找到最短长度为4的边(4,5);ET={1,2,3,4,5,6}
- 找出结点{1,2,3,4,5,6}与其他结点相连的边{(5,7)},找到最短长度为5的边(5,7);ET={1,2,3,4,5,6,7}
最小生成树为:
最小长度为18
多段图最短路径
问题描述:设图G=(V, E)是一个带权有向图,如果把顶点集合V划分成k个互不相交的子集Vi (2≤k≤n, 1≤i≤k),使得E中的任何一条边(u, v),必有u∈Vi,v∈Vi+m (1≤i≤k, 1<i+m≤k),则称图G为多段图,称s∈V1为源点,t∈Vk为终点。多段图的最短路径问题求从源点到终点的最小代价路径。
动态规划
设cuv表示多段图的有向边<u, v>上的权值,将从源点s到终点t的最短路径长度记为d(s, t),考虑原问题的部分解d(s, v),显然有下式成立:
d(s, v) =csv (<s, v>∈E)
d(s, v) = min{d(s, u) + cuv} (<u, v>∈E)
输入:多段图的代价矩阵
输出:最短路径长度及路径
1. 循环变量j从1~n-1重复下述操作,执行填表工作:
1.1 考察顶点j的所有入边,对于边(i, j)∈E:
1.1.1 cost[j]=min{cost[i]+cij};
1.1.2 path[j]=使cost[i]+cij最小的i;
1.2 j++;
2. 输出最短路径长度cost[n-1];
3. 循环变量i=path[n-1],循环直到path[i]=0:
3.1 输出path[i];
3.2 i=path[i];
排序问题
为无序序列排序
蛮力法
选择排序——选择排序法的元素之间的比较次数与原始序列中元素的分布状态无关。
第i趟排序从序列的后n-i+1个元素中一个值最小的元素,将其置于该n-i+1个元素的最前面。
时间复杂度——O(n2)
非稳定性排序
冒泡排序——冒泡排序法的排序趟数与原始序列中数据元素的排列有关,因此,排序的趟数为一个范围,即[1..n-1]。
第i趟排序对序列的前n-i+1个元素从第一个元素开始依次作如下操作:相邻的两个元素比较大小,若前者大于后者,则两个元素交换位置,否则不交换位置。
时间复杂度——O(n2)
稳定性排序方法
减治法
堆排序
将原始序列转换为第一个堆。
将堆的第一个元素与堆的最后那个元素交换位置。(即“去掉”最大值元素)。
将“去掉”最大值元素后剩下的元素组成的子序列重新转换一个新的堆。
重复上述过程的第2至第3步n-1次。
时间复杂度——O(nlogn)
分治法
算法——归并排序MergeSort
输入:待排序序列r[s..t]
输出:升序序列r[s..t]
如果s=t,则待排序区间只有一个记录,递归终止;
划分:将序列r[s..t]一分为二,即求mid=(s+t)/2;
求解:
3.1 对前半子序列r[s..mid]进行归并排序;
3.2 对后半子序列r[mid+1..t]进行归并排序;
合并:将已排序的两个子序列r[s..mid]和r[mid+1..t]合并为一个有序序列r[s..t]。
T(n)=1 当n=1
T(n)=2T(n/2)+O(n) 当n>1
时间复杂度——O(nlogn)
空间复杂度——O(n)
查找问题
查找某一个数在无序序列中的位置
蛮力法
顺序查找
顺序查找从表的一端向另一端逐个将元素与给定值进行比较,若相等,则查找成功,给出该元素在表中的位置;若整个表检测完仍未找到与给定值相等的元素,则查找失败,给出失败信息。
时间复杂度——O(n2)
减治法
二叉查找树
建立二叉查找树
root是空树,则查找失败;
若k=根结点的值,则查找成功;
若k<根结点的值,则在root的左子树上查找;
若k>根结点的值,则在root的右子树上查找;
上述过程一直持续到查找成功或者待查找的子树为空,如果待查找的子树为空,则查找失败。
时间复杂度——O(logn)
查找两个等长有序(升序)序列的中位数
减治法
分别求中位数,进行比较,分为三种情况,再处理,重复这一过程
1. 循环直到序列A和序列B均只有一个元素
1.1 a = 序列A的中位数;
1.2 b = 序列B的中位数;
1.3 比较a和b,执行下面三种情况之一:
1.3.1 若a=b,则返回a,算法结束;
1.3.2 若a<b,则在序列A中舍弃a之前的元素,在序列B中舍弃b之后的元素,转步骤1;
1.3.3 若a>b,则在序列A中舍弃a之后的元素,在序列B中舍弃b之前的元素,转步骤1;
2. 序列A和序列B均只有一个元素,返回较小者;
时间复杂度——O(logn)
查找某一个数在有序序列中的位置
减治法
折半查找:取中间记录,分三种情况进行处理,再依次处理
1. low=1;high=n; //设置初始查找区间
2. 测试查找区间[low,high]是否存在,若不存在,则查找失败;否则
3. 取中间点mid=(low+high)/2; 比较k与r[mid],有以下三种情况:
3.1 若k<r[mid],则high=mid-1;查找在左半区进行,转2;
3.2 若k>r[mid],则low=mid+1;查找在右半区进行,转2;
3.3 若k=r[mid],则查找成功,返回记录在表中位置mid;
时间复杂度——O(logn)
查找无序序列中按升序排列后,第K小元素
减治法
借鉴快速排序思想
算法——选择问题
输入:无序序列{r1, r2, …, rn},位置k
输出:返回第k小的元素值
1.设置初始查找区间:i=1,j=n;
2.以ri为轴值对序列ri~rj进行一次划分,得到轴值的位置s;
3.将轴值位置s与k比较
3.1 如果k=s,则将rs作为结果返回;
3.2 如果k<s,则j=s-1,转步骤2;
3.3 如果k>s, 则i=s+1,转步骤2。
时间复杂度——O(n)