算法设计与分析总结

一、 算法分析
1.1 去年试卷
1.1.1 填空题
1.1.1.1 最长公共子序列算法采用的是动态规划
1.1.1.2 在对问题的解空间树进行搜索的方法中,一个活节点最多有一次机会成为或节点的是分支限界法。
1.1.1.3 实现最大子段和利用的算法是动态规划法
1.1.1.4 广度优先是分支界限法的一种搜索方式
1.1.1.5 衡量一个算法的好坏标准是时间复杂度低
1.1.1.6 Strassen矩阵乘法是利用分治策略实现的算法
1.1.1.7 使用分治法求解不需要满足的条件是子问题必须是一样的。需要满足的条件是子问题不能够重复,子问题的解可以合并,原问题和子问题使用相同的方法解。
1.1.1.8 用动态规划算法解决最大字段和问题,其时间复杂性为N
1.1.1.9 解决活动安排问题,最好用贪心算法
1.1.1.10 剪枝函数是回溯法中为避免无效搜索采取的策略。
1.1.1.11 从活结点表中选择下一个拓展节点的不同方式将导致不同的分支限界法,除栈式分支限界法以外,都是最常见的方式。
1.1.1.12 回溯算法和分支限界法的问题的解空间树不会是无序树。会是有序数,子集树,排列树。
1.1.1.13 优先队列式分支限界法选取拓展节点的原则是-节点的优先级。
1.1.1.14 贪心算法的基本要素是贪心选择性质。
1.1.1.15 回溯法在解空间树T上的搜索方式是深度优先
1.2 填空题
1.2.1 算法由若干条指令组成的有穷序列,且满足输入、输出、确定性、有限性四个特性。
1.2.2 分支限界法的两种搜索方式由队列是先进先出、优先队列分支限界法、用一个队列来存储节点的表叫活节点表。
1.2.3 直接或间接调用自身的方法叫递归算法
1.2.4 大整数乘积算法是用分治算法来设计的
1.2.5 以广度优先或以最小耗费方式搜索问题解的算法为分支限界法
1.2.6 动态规划的子问题重叠
1.2.7 贪心算法的选择性质是贪心选择性质、动态规划法的选择性质是最优子结构
1.2.8 问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
1.2.9 以深度优先方式搜索问题解的算法称为回溯法
1.2.10 快速排序法的三个步骤:分解、递归求解、合并。
1.2.11 贪心算法的基本要素是贪心选择和最优子结构性质
1.3 问答题
1.3.1 函数的渐进表达式
1.3.1.1 n2/10+2的n方
O(2的n的方)
1.3.1.2 10log3n
O(n)/0(logn)
1.3.1.3 21+1/n
O(1)
1.3.2 解释什么是NP类问题
NP问题是指还未被证明是否存在多项式算法能够解决的问题,而其中NP完全问题又是最有可能不是P问题的问题类型。所有的NP问题都可以用多项式时间规划到他们中的一个。所以显然NP完全的问题具有如下性质:它可以在多项式时间内求解,当且仅当所有的其他的NP-完全问题也可以在多项式时间内求解。
1.3.1 动态规划基本步骤(必考)
找出最优解的性质,并刻画其结构特征
递归地定义最优值。
以自底向上的方式计算最优值。
根据计算最优值时得到的信息,构造最后解。
1.3.1 贪心或哈弗慢编码出一个(必考)
1.3.1.1 贪心
1.3.1.2 哈夫曼
1.3.2 回溯法解题包含几个步骤

  1. 针对所给问题,定义问题的解空间。
  2. 确定易于搜索的解空间结构。
  3. 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
    1.3.3 简述分支限界法和回溯法的异同。
    相同点:两者都是一种在问题的解空间树T上搜索问题解的算法。
    不同点:在一般情况下,分支限界法与回溯法的求解目标不同。
    回溯法的求节目表是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。
    回溯法与分支限界法对解空间的搜索方式不同,回溯法通常用优先搜索,分支用广度优先搜索。
    对节点存储的常用数据结构和存储特性也不同,除搜索方式决定的存储结构外,分支界限法通常需要存储一些额外的信息以利于进一步展开搜索。
    1.4 算法设计与分析
    1.4.1 用动态规划策略求解最长公共子序列问题
    1.4.2 给出计算最优值的递归方程
    引入一个二维数组C[][],用C[i][j]记录X[i]与Y[j]的LCS的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
    自底向上进行递推计算,那么在计算c[i][j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]都已局算出来,此时根据X[i]=Y[j]还是X[i]!=Y[j]就可以计算出c[i][j]
    递归方程:

1.4.3 给定两个序列X={B,C,D,A}Y={A,B,C,B}请用动态规划策略求出其最长公共子序列。

1.4.4 整数划分
package com.ysy;
public class NumDivide {
public static void main(String[] args) {
System.out.println(divide(6, 6));
}
private static int divide(int n, int m) {
// 当划分的数是1的时候就是最后一种方案。
if (m == 1) {
return 1;
}
if (n == m) {
return 1 + divide(n, n - 1);
}
if (n < m) {
return divide(n, n);
} else {
// 分成两个子问题,一个含m一个不含m
return divide(n - m, m) + divide(n, m - 1);
}
}
}
1.4.1 回溯法求解八皇后问题
package com.ysy;

import java.util.Scanner;

public class NQuen {
static int n; // 皇后个数
static int[] x; // 当前解
static long sum; // 当前已找到的可行方案

public static long nQueen(int nn) {
	n = nn;
	sum = 0;
	x = new int[n + 1];
	for (int i = 0; i <= n; i++) {
		x[i] = 0;
	}
	backtrack(1);
	return sum;
}

private static void backtrack(int t) {
	if (t > n)
		sum++;
	else
		for (int i = 1; i <= n; i++) {
			x[t] = i;
			if (place(t))
				backtrack(t + 1);
		}
}

private static boolean place(int k) {
	for (int j = 1; j < k; j++)
		if ((Math.abs(k - j) == Math.abs(x[j] - x[k])) || (x[j] == x[k]))
			return false;
	return true;
}

public static void main(String[] args) {
	Scanner input = new Scanner(System.in);
	System.out.println("请输入皇后的个数:");
	int nn = input.nextInt();
	System.out.println(nQueen(nn));

}

}
1.5 平时整理
1.5.1 时间复杂度分析
系数不变,找大头。
n2+10n=O(n2), 
n2/10+2n=O(2^n), 
21+1/n=O(1), 
logn^3=O(logn), 
10log3^n=O(n). 
1.5.2 动态规划与分治法的联系,区别,共同点。
都是基本事项将待求问题分解成若干个子问题,先求子问题,最后得出原问题解。
与分治不同的是:适合用于动态规划的问题的子问题不是相互独立的。
适用要点:最优子结构,重复子问题。最优子结构的定义。
1.5.3 01背包问题(去年考)
1.5.4 判断算法正确性
1.5.4.1 二分查找
public static int binarySearch (int[] a,int x,int n){
//在a[0] <=a[a] <=…<=a[n-1]
//找到x时返回其在数组中的位置,否则返回-1
int left= 0;
int right = n-1;
while(left <= right){
int middle = (left + right)/2;
if(x== a[middle])
return middle;
if(x> a[middle])
left=middle+1;
else right = middle-1;
}
return -1;
}
public static int binarySearch1(int[] a,int x,int n){
int left= 0;
int right = n-1;
while(left <= right){
int middle = (left + right)/2;
if(x== a[middle])
return middle;
// left和right的调整不正确,导致死循环;
if(x> a[middle])
left=middle;
else right = middle;
}
return -1;
}
算法binarySeratch2,left 和right的调整不正确,导致x=a[n-1]时返回错误;
算法binarySeratch3,left 和right的调整不正确,导致x=a[n-1]时返回错误;
算法binarySeratch4,left和right的调整不正确,导致死循环;
算法binarySeratch5,正确,当数组中有重复元素时,返回满足条件最右元素;
算法binarySeratch6,left和right的调整不正确,导致x=a[n-1]时返回错误;
算法binarySeratch7,left和right的调整不正确,导致x=a[0]时导致死循环;
1.5.5 设计一个O(n2)时间的算法,找出n个数组组成的序列的最长单调递增子序列。
public static int A(){
int i,j,k;
for(i = 1, b[0] = 1; i for(j = 0,k = 0;j<1; j++)
if(a[j]<=a[i] && k b[i] = k+1;
}
return maxA(n);
}
static int maxA(int n){
int temp = 0;
for(int i = 0;i if(b[i]>temp)temp = b[i];
return temp;
}
1.5.6 用分支限界法求解背包问题。要求有 1算法思想分析说明,2代码或伪码实现
1、主要思想
分支限界法常以广度优先或以最小消耗(最大效益)优先的方式搜索问题的解空间树。问题的解空间树是表示问题解空间的一颗有序树,常见的有子集树和排列树。分支限界法和回溯法的区别还有一点,它们对于当前扩展结点所采用的扩展方式也是不相同的。分支限界法中,对于每一个活结点只有一次机会成为扩展结点。活结点一旦成为了扩展结点,就一次性产生其所有的子结点,子结点中,不符合要求的和非最优解的子结点将会被舍弃,剩下的子结点将加入到活结点表中。再重复上面的过程,直到没有活结点表中没有结点,至此完成解决问题的目的。
代码实现:
队列中添加结点的方式是先进先出,这里将左节点放到前面就是将优先的结点先出,其中每个判断的判断条件都是要注意的点,还有就是结点的索引和物品数组中下标相差一
package com.test;
import java.util.*;
public class Main {
static double c;
static int n;
static double w[];
static double p[];
static double cw;
static double cp;
static int bestX[];
static MaxHeap heap;
//上界函数bound计算结点所相应价值的上界
private static double bound(int i){
double cleft=c-cw;
double b=cp;
while(i<=n&&w[i]<=cleft){
cleft=cleft-w[i];
b=b+p[i];
i++;
}
//装填剩余容量装满背包
if(i<=n)
b=b+p[i]/w[i]*cleft;
return b;
}
//addLiveNode将一个新的活结点插入到子集树和优先队列中
private static void addLiveNode(double up,double pp,double ww,int lev,BBnode par,boolean ch){
//将一个新的活结点插入到子集树和最大堆中
BBnode b=new BBnode(par,ch);
HeapNode node =new HeapNode(b,up,pp,ww,lev);
heap.put(node);
}
private static double MaxKnapsack(){
//优先队列式分支限界法,返回最大价值,bestx返回最优解
BBnode enode=null;
int i=1;
double bestp=0;//当前最优值
double up=bound(1);//当前上界
while(i!=n+1){//非叶子结点
//检查当前扩展结点的左儿子子结点
double wt=cw+w[i];
if(wt<=c){
if(cp+p[i]>bestp)
bestp=cp+p[i];
addLiveNode(up,cp+p[i],cw+w[i],i+1,enode,true);
}
up=bound(i+1);
if(up>=bestp)
addLiveNode(up,cp,cw,i+1,enode,false);
HeapNode node =(HeapNode)heap.removeMax();
enode=node.liveNode;
cw=node.weight;
cp=node.profit;
up=node.upperProfit;
i=node.level;
}
for(int j=n;j>0;j–){

bestX[j]=(enode.leftChild)?1:0;
enode=enode.parent;
}
return cp;
}
public static double knapsack(double pp[],double ww[],double cc,int xx[]){
//返回最大值,bestX返回最优解
c=cc;
n=pp.length-1;
//定义以单位重量价值排序的物品数组
Element q[]=new Element[n];
double ws=0.0;
double ps=0.0;
for(int i=0;i q[i]=new Element(i+1,pp[i+1]/ww[i+1]);
ps=ps+pp[i+1];
ws=ws+ww[i+1];
}
if(ws<=c){
return ps;
}
p=new double[n+1];
w=new double[n+1];
for(int i=0;i p[i+1]=pp[q[i].id];
w[i+1]=ww[q[i].id];
}
cw=0.0;
cp=0.0;
bestX = new int[n+1];
heap = new MaxHeap(n);
double bestp = MaxKnapsack();
for(int j=0;j xx[q[j].id]=bestX[j+1];

return bestp;

}
public static void main(String [] args){
double w[]=new double[4];
w[1]=16;w[2]=15;w[3]=15;
double v[]=new double[4];
v[1]=45;v[2]=25;v[3]=25;
double c=30;
int x[] = new int[4];
double m = knapsack(v,w,c,x);
// System.out.println(“请输入物器数:”);
// Scanner sc=new Scanner(System.in);
// n=sc.nextInt();
// System.out.println(“请输入包容容量:”);
// c=sc.nextDouble();
// System.out.println(“请输入物品数组:3个int”);
// w[0]=sc.nextDouble();
// w[1]=sc.nextDouble();
// w[2]=sc.nextDouble();
// System.out.println(“请输入价值数组3个int:”);
// v[0]=sc.nextDouble();
// v[1]=sc.nextDouble();
// v[2]=sc.nextDouble();

System.out.println(“分支限界法”);
System.out.println("*****物品个数:n="+n);
System.out.println("*****背包容量:c="+c);
System.out.println("*****物品重量数组:w= {"+w[0]+" “+w[1]+” “+w[2]+”}");
System.out.println("*****物品价值数组:v= {"+v[0]+" “+v[1]+” “+v[2]+”}");
System.out.println("*****最优值:="+m);
System.out.println("*****选中的物品是:");
for(int i=1;i<=3;i++)
System.out.print(x[i]+" ");
}
}
//子空间中节点类型
class BBnode{
BBnode parent;//父节点
boolean leftChild;//左儿子节点标志
BBnode(BBnode par,boolean ch){
parent=par;
leftChild=ch;
}
}
class HeapNode implements Comparable{
BBnode liveNode; // 活结点
double upperProfit; //结点的价值上界
double profit; //结点所相应的价值
double weight; //结点所相应的重量
int level; // 活结点在子集树中所处的层次号
//构造方法
public HeapNode(BBnode node, double up, double pp , double ww,int lev){
liveNode = node;
upperProfit = up;
profit = pp;
weight = ww;
level = lev;
}
public int compareTo(Object o) {
double xup = ((HeapNode)o).upperProfit;
if(upperProfit < xup)
return -1;
if(upperProfit == xup)
return 0;
else
return 1;
}
}
class Element implements Comparable{
int id;
double d;
public Element(int idd,double dd){
id=idd;
d=dd;
}
public int compareTo(Object x){
double xd=((Element)x).d;
if(d if(dxd)return 0;
return 1;
}
public boolean equals(Object x){
return d
((Element)x).d;
}
}
class MaxHeap{
static HeapNode [] nodes;
static int nextPlace;
static int maxNumber;
public MaxHeap(int n){
maxNumber = (int)Math.pow((double)2,(double)n);
nextPlace = 1;//下一个存放位置
nodes = new HeapNode[maxNumber];
}
public static void put(HeapNode node){
nodes[nextPlace] = node;
nextPlace++;
heapSort(nodes);

}
public static HeapNode removeMax(){
HeapNode tempNode = nodes[1];
nextPlace–;
nodes[1] = nodes[nextPlace];
heapSort(nodes);
return tempNode;
}
private static void heapAdjust(HeapNode [] nodes,int s,int m){
HeapNode rc = nodes[s];
for(int j=2s;j<=m;j=2){

if(j

}
nodes[s] = rc;
}
private static void heapSort(HeapNode [] nodes){
for(int i=(nextPlace-1)/2;i>0;–i){

heapAdjust(nodes,i,nextPlace-1);

}
}
}
1.5.7 123

设c[j]是安排单词1~j时的最小费用,则所要求的最小费用是c[n].
c[j],j=1~n可递归地计算如下:
当j=0时,c[j] = 0;
当j>0时,c[j] = min{c[i-1] + lc[i][j]}
算法所需的计算时间为O(nM).算法所需的空间为O(n).
1.5.8 强整理
P22 分治法基本项 设计模式。
31 快排
p50
算法设计的总体思想:如果能够保存已解决的子问题的答案,而在需要的时候在找出已得到的答案,就可恶意避免大量重复计算,从而得到最短时间算法,
p53
m[i][j] 必考。公式。
p58 最长公共子序列
大问题化小问题 一刀两端和切黄瓜,区别 规模n大小
p76 递归关系。 01 背包
p130
p132
p133
p152 分支限界
p171

你可能感兴趣的:(期末,算法)