算法分析复习.docx
《算法分析复习.docx》由会员分享,可在线阅读,更多相关《算法分析复习.docx(14页珍藏版)》请在冰点文库上搜索。
![算法分析复习.docx](https://file1.bingdoc.com/fileroot1/2023-6/21/42f3fbfb-61d0-4c42-851d-2f211299b799/42f3fbfb-61d0-4c42-851d-2f211299b7991.gif)
算法分析复习
算法的定义和特征?
算法:
是解某一特定问题的一组有穷规则的集合。
什么是算法?
算法的特征有哪些?
1)算法是解决某类问题的一系列运算的集合。
具有有穷行、可行性、确定性、0个或者多个输入、1个或者多个输出。
什么是P类问题?
什么是NP类问题?
请描述集合覆盖问题的近似算法的基本思想?
用确定的图灵机可以在多项式实间内可解的判定问题称为P类问题。
用不确定的图灵机在多项式实间内可解的判定问题称为NP类问题。
算法的特征:
•输入:
有零个或多个外部量作为算法的输入。
•输出:
算法产生至少一个量作为输出。
•确定性:
组成算法的每条指令清晰、无歧义。
•有限性:
算法中每条指令的执行次数有限,执行每条指令的时间也有限。
•能行性:
算法中有待实现的运算,都是基本的运算。
原则上可以由人们用纸和笔,在有限的时间里精确地完成。
用计算机解决一个现实问题的步骤?
1问题分析
2数学模型建立
3算法设计与选择
4算法表示
5算法分析
6算法实现
7程序调试
8结果整理文档编制
0-1背包问题用动态规划与回溯法求解的比较
动态规划算法求解问题,可依据其递归式以自底向上的方式进行计算。
在计算过程中,保存已解决的子问题的答案。
每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量重复计算,最终得到多项式时间算法。
回溯法在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。
回溯算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。
如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。
否则,进入该子树,继续按深度优先的策略进行搜索。
分支限界法与回溯法的不同
(1)求解目标:
回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。
(2)搜索方式的不同:
回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。
分支限界法基本思想
v1、在结点估算沿着它的各儿子结点搜索时,目标函数可能取得的“界”;
v2、把儿子结点和目标函数可能取得的“界”,保存在优先队列或堆中;
v3、从队列或堆中选取“界”最大或最小的结点向下搜索,直到叶子结点;
v4、若叶子结点的目标函数的值,是结点表中的最大值或最小值,则沿叶子结点到根结点的路径所确定的解,就是问题的最优解,由该叶子结点所确定的目标函数的值,就是解这个问题所得到的最大值或最小值。
分支限界算法可解问题:
0-1背包问题;货郎担问题;作业分配问题
分治法的基本思想
求解一个输入规模为n,且n的取值非常大的问题时,直接求解非常困难。
分治法是把问题划分成多个子问题来进行处理。
这些子问题在结构上与原来的问题一样,但在规模上比原来的小。
如果得到的子问题相对来说还是太大,可以反复地使用分治策略,把这些问题划分成更小的、结构相同的子问题。
这样,就可以使用递归的方法,分别求解这些子问题,并把这些子问题的解结合起来,从而获得原来问题的解。
分治法所能解决的问题一般具有以下几个特征:
•该问题的规模缩小到一定的程度就可以容易地解决;
•该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质
•利用该问题分解出的子问题的解可以合并为该问题的解;
•该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
分治法的应用
•二分搜索法
•大整数的乘法
•Strassen矩阵乘法
•棋盘覆盖
•合并排序
•快速排序
•最接近点对问题
•循环赛日程表
递归的概念
•递归算法:
直接或间接地调用自身的算法
•递归函数:
用函数自身给出定义的函数。
•特点:
•把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
•只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量
•写出的程序十分简洁易懂。
贪心算法
顾名思义,贪心算法总是作出在当前看来最好的选择。
也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
贪心算法的基本要素:
贪心选择性质、最优子结构性质
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法与动态规划算法的差异?
贪心算法和动态规划算法都要求问题具有最优子结构性质,这是2类算法的一个共同点。
2个经典的组合优化问题:
0-1背包问题和背包问题。
这2类问题都具有最优子结构性质,极为相似,但背包问题可以用贪心算法求解,而0-1背包问题却不能用贪心算法求解
贪心算法可解的问题:
活动安排问题;最优装载问题;哈夫曼编码;单源最短路径;最小生成树;多机调度问题;
最小生成树.Prim算法:
最小生成树Kruskal算法:
动态规划算法思想?
•动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
•与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是独立的。
子问题中存在大量的公共子问题,在分治求解过程中被多次重复计算,保存计算结果,为后面的计算直接引用,减少重复计算次数这就是动态规划的基本思想
•用动态规划算法求解问题,可依据其递归式以自底向上的方式进行计算。
在计算过程中,保存已解决的子问题的答案。
每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量重复计算,最终得到多项式时间算法。
在动态规划算法中,很多问题的求解过程是滚动的,即解决k层问题,不仅只涉及k-1层,可能会涉及k-1以下各层的计算结果,因此其求解的其基本思想表示如下。
最优子结构是问题能用动态规划算法求解的前提。
动态规划基本步骤
1.找出最优解的性质,并刻划其结构特征。
2.递归地定义最优值。
3.以自底向上的方式计算出最优值。
4.根据计算最优值时得到的信息,构造最优解。
动态规划算法可求解问题:
货郎担问题;多段图问题;矩阵连乘问题;图像压缩;电路布线;流水作业调度;0-1背包问题;
动态规划算法与分治算法?
动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,动态规划法用一个表来记录所有已解决的子问题的答案。
不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
需要时从表中找出已求得的答案,避免大量重复计算从而得到多项式时间算法。
动态规划算法适用于解最优化问题。
快速排序采用的思想是分治思想。
快速排序是找出一个元素(理论上可以随便找一个)作为基准(pivot),然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值都不小于基准值,如此作为基准的元素调整到排序后的正确位置。
递归快速排序,将其他n-1个元素也调整到排序后的正确位置。
最后每个元素都是在排序后的正确位置,排序完成。
所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
举例说明一下吧,这个可能不是太好理解。
假设要排序的序列为
224936715首先用2当作基准,使用ij两个指针分别从两边进行扫描,把比2小的元素和比2大的元素分开。
首先比较2和5,5比2大,j左移
224936715比较2和1,1小于2,所以把1放在2的位置
214936715比较2和4,4大于2,因此将4移动到后面
214936745比较2和7,2和6,2和3,2和9,全部大于2,满足条件,因此不变
经过第一轮的快速排序,元素变为下面的样子
[1]2[493675]
之后,在把2左边的元素进行快排,由于只有一个元素,因此快排结束。
右边进行快排,递归进行,最终生成最后的结果。
尽管快速排序的最坏时间为O(n2),它的平均时间复杂度为O(nlgn)。
intquicksort(vector&v,intleft,intright){
if(leftintkey=v[left];
intlow=left;
inthigh=right;
while(lowwhile(lowkey){
high--;
}
v[low]=v[high];
while(lowlow++;
}
v[high]=v[low];
}
v[low]=key;
quicksort(v,left,low-1);
quicksort(v,low+1,right);
}
}
一、设数组A有n个元素,需要找出其中的最大最小值。
(20分)
(1)请给出一个解决方法,并分析其复杂性。
(2)把n个元素等分为两组A1和A2,分别求这两组的最大值和最小值,然后分别将这两组的最大值和最小值相比较,求出全部元素的最大值和最小值。
如果A1和A2中的元素多于两个,则再用上述方法各分为两个子集。
直至子集中元素至多两个元素为止。
这是什么方法的思想?
请给出该方法的算法描述,并分析其复杂性。
一、
(1)基本思想:
从头到尾逐个扫描,纪录最大和最小元素。
【2分】
输入:
具有n个元素的数组【1分】
输出:
最大值和最小值【1分】
步骤:
【4分】
voidFindMaxMin(intA[],intn,intmax,intmin)
{
max=min=A[0];
for(i=1;i{
if(A[i]>max)max=A[i];
if(A[i]}
}
复杂性分析:
由于是从头到尾扫描各个元素,而每个元素都要与max和min比较一遍,从而时间复杂性为:
O(n)。
【2分】
(2)【10分】voidFindMaxMin(intleft,intright,intmax,intmin){
if(left==right)max=min=A[left];
elseif(left=right-1){
max=(A[left]A[right]:
A[left]);
min=(A[left]A[left]:
A[right]);
}
else{
mid=(left+right)/2;
FindMaxMin(left,mid,gmax,gmin);
FindMaxMin(mid+1,right,hmax,hmin);
max=(gmaxhmax:
gmax);
min=(gmingmin:
hmin]);
}
}
合并排序
基本思想:
将待排序元素分成大小大致相同的2个子集合,分别对2个子集合进行排序,最终将排好序的子集合合并成为所要求的排好序的集合。
&最坏时间复杂度:
O(nlogn)
&平均时间复杂度:
O(nlogn)
&辅助空间:
O(n)
&稳定性:
稳定
1、假设合并排序算法的抽象定义为:
PublicstaticvoidmergeSort(Comparablea[],intleft,intright)
{}
其中使用的合并及拷贝方法分别定义为:
Publicstaticvoidmerge(Comparable[]c,Comparable[]d,intLintm,intr)
{inti=L;
Intj=m+1;
Intk=1;
While((i<=m)&&(j<=r))
If(c[i]<=c[j])
d[k++]=c[i++];
else
d[k++]=c[j++];
if(i>m)
for(intq=j;q<=r;q++)
d[k++]=c[q];
else
for(intq=i;q<=m;q++)
d[k++]=c[q];
}
Publicstaticvoidcopy(Comparable[]c,Comparable[]d,inti,intj)
{}
请补充完善递归方式实现的合并排序的细节。
publicstaticvoidmergeSort(Comparablea[],intleft,intright)
{
if(leftinti=(left+right)/2;//取中点
mergeSort(a,left,i);
mergeSort(a,i+1,right);
merge(a,b,left,i,right);//合并到数组b
copy(a,b,left,right);//复制回数组a
}
}
快速排序
intsplit(TypeA[],intlow,inthigh)
3.{
4.intk,i=low;
5.Typex=A[low];
6.for(k=low+1;k<=high;k++){
7.if(A[k]<=x){
8.i=i+1;
9.if(i!
=k)
10.swap(A[i],A[k]);
11.}
12.}
13.swap(A[low],A[i]);
14.returni;
15.}
voidquick_sort(TypeA[],intlow,inthigh)
3.{
4.intk;
5.if(low6.k=split(A,low,high);
7.quick_sort(A,low,k-1);
8.quick_sort(A,k+1,high);
9.}
10.}
&最坏时间复杂度:
O(n2)
&平均时间复杂度:
O(nlogn)
&辅助空间:
O(n)或O(logn)
&稳定性:
不稳定
杨辉三角
#include
main()
{inti,j,n=0,a[17][17]={0,1};
while(n<1||n>16)
{printf("请输入杨辉三角形的行数:
");
scanf("%d",&n);
}
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
{
a[i][j]=a[i-1][j-1]+a[i-1][j];/*每个数是上面两数之和*/
printf("%5d",a[i][j]);/*输出杨辉三角*/
}
printf("\n");
}
}
#if0
#include
main()
{inti,j,n=0,a[17][17]={1};
while(n<1||n>16)
{printf("请输入杨辉三角形的行数:
");
scanf("%d",&n);
}
for(i=1;i{
a[i][0]=1;/*第一列全置为一*/
for(j=1;j<=i;j++)
a[i][j]=a[i-1][j-1]+a[i-1][j];/*每个数是上面两数之和*/
}
for(i=0;i{for(j=0;j<=i;j++)
printf("%5d",a[i][j]);
printf("\n");
}
}
#endif