算法设计与分析复习提纲-仅供笔者考试复习用

本届题型:
一、单选题(25=10分)
二、多选题(4
5=20分)
三、填空题(25=10分)
四、算法补充题(2
5=10分)
五、算法理解题(3题,28分)
六、算法设计与分析题(2题,22分)
仅供个人复习用,如有疑问请私信我

须知

此文章为笔者为考试复习准备,参考意义不大。
此文章参考了很多大佬的文章,和自己本人的一些理解。
因链接众多,未能及时一一附上。参考了有51篇文章,及算法设计与分析这本书,再有就是青岛大学《算法设计与分析》课程视频。

在这里插入图片描述

算法设计与分析复习提纲知识点整理

第一章

1.算法的定义

算法是指解决问题的一种方法 或一个过程。更严格地讲,算法是由若干条指令组成的有序序列。

2.算法时间复杂度的分类,计算及规则

在这里插入图片描述

3.算法的性质

  • 输入
  • 输出
  • 有限性
  • 确定性
  • (可行性)

4.时间复杂度和空间复杂度的计算

第二章

1.分治的基本思想

分治算法的主要思想是将原问题递归地分成若干个子问题,直到子问题满足边界条件,停止递归。将子问题逐个击破(一般是同种方法),将已经解决的子问题合并,最后,算法会层层合并得到原问题的答案。

2.分治的基本步骤、特点

基本步骤

step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。

特点

分治法所能解决的问题一般具有以下几个特征:

  1. 该问题的规模缩小到一定的程度就可以容易地解决
  2. 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
  3. 利用该问题分解出的子问题的解可以合并为该问题的解;
  4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;
第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。

3.递归公式的推导(过程、结论)

在这里插入图片描述
图(a)表示T(n)。
图(b)表示对T(n)进行扩展,形成与递归方程等价的一棵树。cn2表示树的根,即递归顶层的开销。根的三棵子树为小一级的递归方程T(n/4)。
图( c )表示对T(n/4)的进一步展开。根据递归方程,继续展开树中的每个结点,直到问题规模变成1,每个开销为T(1)。
图(d)表示最终结果树。树的高度是log4n,深度为log4n+1。
详情参考原文链接:算法设计与分析期末不挂科

4.应用:二分搜索(算法、时间复杂度、递归关系)、快速排序(方法、时间复杂度)、大整数乘法、合并排序(方法法、时间复杂度)、棋盘覆盖问题、循环赛日程表

二分算法

>	#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
//时间复杂度:O(logn)
const int N=1000;
int yz[N],n;
int x;

>int BSearch(int q[],int x,int n)
{
int l=0,r=n-1;
while(l<=r)
{
    int mid=(l+r)/2;
    if(x==q[mid])
    return mid+1;
    if(x>q[mid])
    l=mid+1;
    else
    r=mid-1;
}

 return -1;
}

int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>yz[i];

cin>>x;



cout<<BSearch(yz,x,n);

return 0;
}

时间复杂度:O(logn)
递归关系
在这里插入图片描述

快速排序

>#include<iostream>

using namespace std;

//时间复杂度:O(nlogn),若分得不对称则最坏情况是O(n^2)
const int N=10000000;
int yz[N], n;

void quick_sort(int q[],int l,int r)
{
 	 if(l>=r)
	 	return ;

	 int i=l-1,j=r+1,x=q[l+r>>1];

 	while(i<j)
	{
    	do i++; while(q[i]<x);
   	    do j--; while(q[j]>x);

   		 if(i<j)
  	    {
    	swap(q[i],q[j]);
   		}
	 }
	 
		quick_sort(q,l,j);
		quick_sort(q,j+1,r);

	}

int main()
{
 	cin>>n;
	for(int i=0;i<n;i++)
 	cin>>yz[i];

 	quick_sort(yz,0,n-1);

 	for(int i=0;i<n;i++)
		cout<<yz[i];

 	return 0;

}

合并排序

>#include<iostream>

using namespace std;
//时间复杂度O(nlogn)

const int N=1000000;
int yz[N],n;
int temp[N];

void merge_sort(int q[],int l,int r)
{
	if(l>=r)
	return;//只有一个数或者没有数的话,就不用排序了

	int mid=(l+r)/2 ;//取中点
	//先分左边和右边
 	merge_sort(q,l,mid);
	merge_sort(q,mid+1,r);

	 int k=0,i=l,j=mid+1;//k为temp中数据的个数
	//i为左半边有序数列的起点,j为右半边有序数列的起点

	//取左半边和右半边中更小的那个值存入temp
	while(i<=mid&&j<=r)
		if(q[i]<q[j]) temp[k++]=q[i++];
		else temp[k++]=q[j++];

	 //如果左半边或右半边有没排序完的,直接放入temp中
	while(i<=mid) temp[k++]=q[i++];
	while(j<=r) temp[k++]=q[j++];

	  //将temp中数据粘贴到原函数中
	 for(i=l,j=0;i<=r;i++,j++)
		q[i]=temp[j];
}

int main()
{
	 cin>>n;
	 for(int i=0;i<n;i++)
	 cin>>yz[i];

	 merge_sort(yz,0,n-1);

	 for(int i=0;i<n;i++)
		cout<<yz[i];

	    return 0;
}

其他算法需掌握算法思想,根据书上的代码进行复习

第三章

1.贪心法的基本思想

在这里插入图片描述

2.贪心法的特点、基本要素

在这里插入图片描述在这里插入图片描述

3.应用:活动安排(算法、算法时间复杂度、算法正确性证明)、最小生成树、单源最短路径、最优装载问题(算法、算法时间复杂度、算法正确性证明)、分数背包问题(算法、算法时间复杂度、算法正确性证明)

活动安排

>#include<bits/stdc++.h>
#include<iostream>
using namespace std;    
//如果活动已按非减序排好,时间复杂度为O(n),反之,时间复杂度为O(nlogn)

/*
算法可以得到最优解的证明:
 证明贪心算法可以得到最优解要求:

1.证明第一次选择是正确的
2.证明第一次选择后,问题输入变为与第一个活动相容的活动的子问题(因为第二个选择的活动 i 是 E’ 中结束时间最早的,所以活动 i 是正确的依次类推所有的选择都是正确的)

*/


struct activity //活动结构体 
{
 	 int start;
	 int end;
};

bool cmp(activity a,activity b)  //cmp参数为sort函数的排序准则,设置为按照活动的结束时间由小到大排序 
{  
	return a.end<b.end;  
} 

void activity_select(int n,int selected[],activity act[])  
{  
 	int i=1;
	selected[1]=1; 

	for(int j=2;j<=n;j++)  //从结束时间倒数第二小的活动开始遍历 
	 {  
   	 if(act[j].start>=act[i].end)  
    	{  
       		 i=j;   
			selected[j]=1; 
    	}  
    else
   		 {
    		selected[j]=0;
		}
	 }  
}

int main()  
{
	cout<<"请输入活动的个数:";
	int n;
 	cin>>n;
	 int selected[n]; //若活动i被选择,则selected[i]置1,否则置0
	activity act[n];

	cout<<"请输入活动的开始和结束时间:"<<endl;   
 for(int i=1;i<=n;i++)
	{
  		  cin>>act[i].start>>act[i].end;
	}
    
	 act[0].start=-1;
	 act[0].end=-1;
    
	 sort(act+1,act+n+1,cmp); //act+1表示第2个元素(i=1,第1个活动) 

		activity_select(n,selected,act);  

		cout<<"有如下活动被选择:"<<endl;
		for (int i=1; i<=n; i++)
		{
			if (selected[i]==1)
			{
				cout<<i<<" "; 
			}
		}	
		 return 0;
}  
算法正确性证明:

在这里插入图片描述

最优装载问题(算法、算法时间复杂度、算法正确性证明)

#include<iostream>
#include<algorithm>
using namespace std;
/*时间复杂度: 首先按照物品体积进行排序,调用sort() 函数,其最优时间复杂度为O(n),
最差时间复杂度为 O(n logn),平均时间复杂度为O(n logn), 
其中按照 贪心策略寻找最优解的 for 语句的时间复杂度为均为 O(n),因此时间复杂度为 O(n+nlogn)。
*/
int main(){
	int n,c,i;//n个数据,载重量为c 
	int cnt=0,sum=0; 
	int w[100];
	cin>>n>>c;
	for(i=0;i<n;i++){
		cin>>w[i];
	}
	sort(w,w+n);
	for(i=0;i<n;i++){
		if(sum+w[i]<c){
			sum+=w[i];
			cnt++;
		}
		else break;
	}
	cout<<"集装箱的个数是"<<cnt<<endl;
	cout<<"装载的重量是"<<sum<<endl;
	return 0;
}

算法正确性证明:

在这里插入图片描述

分数背包问题(算法、算法时间复杂度、算法正确性证明)

#include<bits/stdc++.h>
using namespace std;
//时间复杂度O(nlogn)
/**/
struct node
{
    int w,v;//重量,价值
    double p;
}a[1005];
 
int cmp(node a,node b)
{
    return a.p>b.p;
}
 
int main()
{
    int n,c;
    double ans=0;
    cin>>n>>c;
    if(n==0 || c==0) return 0;
    for(int i=0;i<n;i++)
        cin>>a[i].v;
    for(int i=0;i<n;i++)
        cin>>a[i].w;
    for(int i=0;i<n;i++)
        a[i].p=(double)a[i].v/a[i].w;//物品的价值比上重量
    sort(a,a+n,cmp);
    for(int i=0;i<n;i++)
    {
        if(a[i].w<=c)
        {
            ans+=a[i].v;
            c-=a[i].w;
        }
        else
        {
            ans+=a[i].p*c;
            break;
        }
    }
    cout<<ans;
}
算法正确性证明:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第四章

1.动态规划的基本思想

在这里插入图片描述

2.动态规划解题的四个步骤

在这里插入图片描述

3.动态规划的两个基本要素

在这里插入图片描述

4.备忘录法的特点

在这里插入图片描述

5.应用:矩阵连乘(递归关系、算法、重叠子问题)、

参考连接:矩阵连乘

0-1背包问题(递归关系、算法、最优子结构)、

参考链接1
参考链接2

最长公共子序列(递归关系、算法、最优子结构、重叠子问题)、

参考链接

投资问题(递归关系、算法、最优子结构)

参考链接1
参考链接2

第五章

1.回溯法的基本思想

回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。为避免无效搜索,采用限界/剪枝函数;用约束函数(条件)在扩展结点处剪去不满足约束的子树,即剪去得不到可行解的子树;用目标函数剪去得不到最优解的子树。

2.回溯法解题步骤 (与下题合并)

3.剪枝函数的作用及分类

在这里插入图片描述

4.子集树和排列树:0-1背包和旅行商问题

参考链接

5.应用:0-1背包问题(解空间树,画解空间树中的搜索过程,解的形式,约束,目标、算法)、n后问题(解的形式,约束,算法)、图的M着色(解的形式,约束,算法)、装载问题(解的形式,约束)。

0-1背包问题(解空间树,画解空间树中的搜索过程,解的形式,约束,目标、算法)

参考链接

n后问题(解的形式,约束,算法)

参考链接1
参考链接2

第六章

参考链接

1.分支限界法的基本思想及搜索方式

2.分支限界法与回溯法的比较

3.常见的两种分支限界法

4.应用:0-1背包问题、旅行商问题,装载问题

注:复习中注意对比各类算法策略的对比和综合应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值