算法分析与设计课程总结
来源:互联网 发布:编程实现一个冒泡排序 编辑:程序博客网 时间:2024/05/17 02:25
算法设计的整个过程,可以包含对问题需求的说明、数学模型的拟制、算法的详细设计、算法的正确性验证、算法的实现、算法分析、程序测试和文档资料的编制。
算法设计的先驱者唐纳德。E。克努特对算法的特征做了如下描述:
1、有穷性
算法在执行有限步之后必须终止。
2、确定性
算法的每一步骤必须有确切的定义。
3、输入
一个算法有0个或多个输入,作为算法开始执行前的初始值,或初始状态。
4、输出项
一个算法有一个或多个输出,以反映对输入数据加工后的结果。
5、可行性
在有限时间内完成计算过程。
算法大致分为基本算法、数据结构算法、数论与代数算法、计算几何算法、图论的算法、动态规划以及数值分析、加密算法、排序算法、检索算法、随机化算法和并行算法。算法大致分为以下三类:
(1)有限的确定性的算法。这类算法在有限的一段时间内终止。
(2)有限的、非确定性的算法。这类算法在有限的一段时间内终止。
(3)无限的算法。只那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止的算法。
经典的算法有很多,这里主要列举一下算法:
1、穷举搜索法。
穷举搜索法是对可能解的众多候选解按某种顺序进行逐一枚举和检验,并从中找出那些符合要求的候选解作为问题的解。
穷举法的特点是算法简单,但运行时间长。有些问题所列举出来的情况数目会大得惊人,就是用高速计算机运行,其等待时间也是使人无法忍受的。我们在运用穷举法解决问题时,应尽可能将明显不符合条件的情况排除在外,以尽快去的问题的解。
2、迭代法。
迭代法是应用于求方程或方程组近似根的常用的一种算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤操作:
(1)选一个方程的近似根,赋给变量x0。
(2)将x0的值保存于变量x1,然后计算g(x1),将结果存于变量x0.
(3)将x0与x1的差的绝对值还小于指定的精度要求时,重复步骤(2)的计算。
若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就是方程的根。
3、递推算法。
递推算法是利用问题本身所具有的的一种递推关系求问题解的一种方法。他把问题分成若干步,找出相邻几步的关系,从而达到目的。
4、递归算法。
递归算法是一种直接或间接的调用自身的算法。在计算机编写程序中,递归算法对解决一大类问题十分有效,他往往使算法简单而且易于理解。
能采用递归算法的问题一般有以下特征:为求解规模为n的问题,设法将他分解成规模较小的问题,然后从这些规模较小的问题的解构造出大问题的解,并且这些规模较小的问题也能采用相应的分解和综合方法,分解成规模更小的问题,从这些规模更小的问题的解构造出规模较大的问题的解。特别的,当规模n=0或1时,直接的出解。
递归算法解决问题的特点如下:
(1)递归就是在过程或函数里调用自身。
(2)在使用递归策略时,必须有一个明确的递归结束条件,成为递归出口。
(3)递归算法解题通常显得简洁,但递归法解题的运行效率较低。
(4)在递归调用的过程中系统为每层的返回点、局部变量等开辟堆栈来存储。递归次数过多容易造成队战満溢等。
5、分治算法。
分治算法是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到最后问题可以简单直接求解,原问题的解即子问题的解的合并。
6、贪心算法。
贪心算法也称贪婪算法。它对问题求解时,总是作出在当前看来最好的选择。也就是说,它不从整体最优上考虑,所得出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得出整体最优解,但对范围相对较广泛的许多问题它能产生整体最优解或整体最优解的近似解。
贪心算法的基本思路如下:
(1)建立数学模型描述问题。
(2)把求解的问题分为若干个子问题。
(3)对每一子问题求解,得到子问题的局部最优解。
(4)把子问题的局部最优解合成原来解问题的一个解。
7、动态规划。
动态规划是一种在数学和计算机科学中应用求解包含叠子问题的最优化问题的方法。基本思想是,将原问题分解为相似的子问题,在求解过程中通过子问题的解求出原问题得解。动态规划的思想是多种算法的基础,被广泛应用与计算机科学和工程领域。
8、回溯算法。
回溯算法是一种优选搜索法,安优选条件向前搜索,以达到目的。探索到某一步时,发现原先的选择并不优或达不到目标。就退回一步重新选择,这种走不通就退回在走的技术称为回溯法,而满足回溯条件的某一个状态的点称为“回溯点”。
回溯方法解决问题的过程是先选择某一可能的线索进行试探,每一步试探都有多种方法,每一方式都一一试探,如有问题就返回纠正,直到得出全部符合条件的答案或问题无解为止。回溯法的本质是深度优先,所以算法中都需要建立一个堆栈,用来保存搜索路径。一旦产生的部分解序列不符合要求,就要从堆栈中找出回溯的前一个位置,继续试探。
9、分支限界法。
分支限界法是一种在表示问题解空间的树上进行系统搜索的方法,回溯法使用了深度优先的策略,而分支限界法一般采用广度优先的策略,同时还采用最大收益策略来控制搜索的分支。
分支限界法的基本思想是对包含具有约束条件的最优化问题的所有可行的解空间进行搜索。该算法在具体执行时,把全部可行解空间不断分割成越来越小的子集,并为每一个子集内的姐计算一个下界。每次分支后,对所有界限超出已知可行解的那些子在做进一步分支,解的许多子集就可以不用考虑了。从而缩小了搜索范围。这一过程一直进行到找出可行解的值不大于任何子集的界限为止。因此,这种算法一般可以求得最优解。
递归与分治策略
递归一次用于表示直接或间接的调用自身的算法。特别的,用函数自身给出定义的函数被称为递归函数 。
递归函数举例:
1、斐波那契函数:
int fibonacci(){ if(n<=2) return 1; return fibonacci(n-1)+fibonacci(n-2); }从这个函数可以看出,递归函数具有方便简洁的特点,但是其在运行过程中效率较低。
一般用以下方法解fibonacci 函数:
int fibonacci() { if(n<=2)return 1; else{ int a=1,b=1,f; for(int i=3;i<=n;i++) {f=a+b; a=b;b=f; } return f ; }2、二叉树的遍历:
(1)前序遍历:
给出一棵二叉树,返回其节点值的前序遍历。
样例
给出一棵二叉树 {1,#,2,3}
,
1 \ 2 / 3
返回 [1,2,3]
.
代码:
class Solution {public: /* * @param root: A Tree * @return: Preorder in ArrayList which contains node values. */ vector<int>v; vector<int> preorderTraversal(TreeNode * root) { // write your code here if(root==NULL) return v; else{ v.push_back(root->val); preorderTraversal(root->left); preorderTraversal(root->right); } return v; }};(2)中序遍历:
代码:
class Solution {public: /* * @param root: A Tree * @return: Inorder in ArrayList which contains node values. */ vector<int>v; vector<int> inorderTraversal(TreeNode * root) { // write your code here if(root==NULL)return v; else{ inorderTraversal(root->left); v.push_back(root->val); inorderTraversal(root->right); } return v; }};(3)后序遍历:
class Solution {public: /* * @param root: A Tree * @return: Postorder in ArrayList which contains node values. */ vector<int>v; vector<int> postorderTraversal(TreeNode * root) { // write your code here if(root==NULL)return v; else{ postorderTraversal(root->left); postorderTraversal(root->right); v.push_back(root->val); } return v; }};3、把排序数组转换为高度最小的二叉搜索树
给一个排序数组(从小到大),将其转换为一棵高度最小的排序二叉树。
样例
给出数组 [1,2,3,4,5,6,7]
, 返回
4 / \ 2 6 / \ / \1 3 5 7
class Solution {public: /* * @param A: an integer array * @return: A tree node */ TreeNode *dfs(vector<int>&A,int a,int b) { if(a>b)return NULL; int c=(a+b)/2; TreeNode *root=new TreeNode(A[c]); root->left=dfs(A,a,c-1); root->right=dfs(A,c+1,b); return root; } TreeNode * sortedArrayToBST(vector<int> &A) { // write your code here if(A.size()==0)return NULL; int a=0,b=A.size()-1; return dfs(A,a,b); }};4、二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的距离。
样例给出一棵如下的二叉树:
1 / \ 2 3 / \ 4 5
这个二叉树的最大深度为3
class Solution {public: /** * @param root: The root of binary tree. * @return: An integer */ int maxx=-1; void dfs(TreeNode *root,int dep) { if(root->left==NULL&&root->right==NULL) { if(dep>maxx)maxx=dep; return; } if(root->left!=NULL) { dfs(root->left,dep+1); } if(root->right!=NULL) dfs(root->right,dep+1); } int maxDepth(TreeNode *root) { // write your code here if(root==NULL)return 0; dfs(root,1); return maxx; }};分治策略是对于一个规模为n的问题,若这个问题可以容易地解决则直接解决,否则将其分解为k个相互独立的小规模的问题,递归的节小规模问题,将他们的解合并得到原问题的解。
步骤:
(1)分解:将原问题分解为若干相互独立与原问题规模相同的小规模问题。
(2)解决:若小规模问题易于解决则直接解决,否则递归解决。
(3)合并:将子问题的解合并成原问题的解。
模板:
DC(p){if(|p|<=n0)return abhoc(p);divide p into smaller substances p1,p2~pk;for(i=1;i<=k;i++)yi=DC(pi);return merge(y1,y2,~yk);}5、二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的距离
样例给出一棵如下的二叉树:
1 / \ 2 3 / \ 4 5
这个二叉树的最大深度为3
.
class Solution {public: /** * @param root: The root of binary tree. * @return: An integer */ int maxx=-1; void dfs(TreeNode *root,int dep) { if(root->left==NULL&&root->right==NULL) { if(dep>maxx)maxx=dep; return; } if(root->left!=NULL) { dfs(root->left,dep+1); } if(root->right!=NULL) dfs(root->right,dep+1); } int maxDepth(TreeNode *root) { // write your code here if(root==NULL)return 0; dfs(root,1); return maxx; }};
Greedy(A){s={};while(not solution(s)){x=select(A);if(feasible(s,x)s=s+{x};A=A-{x};}return s;}1、主元素:
样例
给出数组[1,1,1,1,2,2,2],返回 1
class Solution {public: /* * @param nums: a list of integers * @return: find a majority number */ int majorityNumber(vector<int> &nums) { // write your code here int cnt=0; int result=0; for(int i=0;i<nums.size();i++) { if(cnt==0) { result=nums[i]; cnt++; } else if(result!=nums[i]) cnt--; else cnt++; } return result; }};2、落单的数
样例
给出 [1,2,2,1,3,4,3],返回 4
class Solution {public: /* * @param A: An integer array * @return: An integer */ int singleNumber(vector<int> &A) { // write your code here int ans=0; for(int i=0;i<A.size();++i) ans ^=A[i]; return ans; }};3、买卖股票的最佳时机
样例
给出一个数组样例 [3,2,3,1,2], 返回 1
class Solution {public: /* * @param prices: Given an integer array * @return: Maximum profit */ int maxProfit(vector<int> &prices) { // write your code here int res=0; if(prices.size()<2) return res; int lowest=prices[0]; for(int i=1;i<prices.size();i++) { int cur=prices[i]; res=max(res,cur-lowest); lowest=min(lowest,cur); } return res; }};4、硬币排线
有 n
个硬币排成一条线。两个参赛者轮流从右边依次拿走 1 或 2 个硬币,直到没有硬币为止。拿到最后一枚硬币的人获胜。
样例
n = 1
, 返回 true
.
n = 2
, 返回 true
.
n = 3
, 返回 false
.
n = 4
, 返回 true
.
n = 5
, 返回 true
.
class Solution {public: /* * @param n: An integer * @return: A boolean which equals to true if the first player will win */ bool firstWillWin(int n) { // write your code here return n%3!=0; }};5、分糖果:
有 N 个小孩站成一列。每个小孩有一个评级。
按照以下要求,给小孩分糖果:
每个小孩至少得到一颗糖果。
评级越高的小孩可以比他相邻的两个小孩得到更多的糖果。
需最少准备多少糖果?
样例给定评级 = [1, 2]
, 返回 3
.
给定评级 = [1, 1, 1]
, 返回 3
.
给定评级 = [1, 2, 2]
, 返回 4
. ([1,2,1]).
class Solution {public: /* * @param ratings: Children's ratings * @return: the minimum candies you must give */ int candy(vector<int> &ratings) { // write your code here vector<int>dp(ratings.size(),1); if(ratings.size()==1)return 1; for(int i=0;i<ratings.size()-1;i++) if(ratings[i]<ratings[i+1]) dp[i+1]=max(dp[i+1],dp[i]+1); for(int i=ratings.size()-1;i>=1;i--) if(ratings[i]<ratings[i-1]) dp[i-1]=max(dp[i-1],dp[i]+1); return accumulate(dp.begin(),dp.end(),0); }};
void backtrack(int t){if(t>n)update(x);else for(int i=0;i<=1;i++){x[t]=i;if(constraint(t)&&bound(t))backtrack(t+1);}}回溯算法搜索排列树的伪代码:
void backtrack(int t){if(t>n) update(x);else for(int i=t;i<=n;i++){swap(x[t],x[i]);if(constraint(t)&&bound(t))backtrack(t+1);swap(x[t],x[i]);}}
1、数字组合 II
给出一个例子,候选数字集合为[10,1,6,7,2,1,5] 和目标数字 8 ,
解集为:[[1,7],[1,2,5],[2,6],[1,1,6]]
class Solution {public: /* * @param num: Given the candidate numbers * @param target: Given the target number * @return: All the combinations that sum to target */ vector<vector<int>> combinationSum2(vector<int> &num, int target) { // write your code here set<vector<int>>r; vector<int>cur; sort(num.begin(),num.end()); combination(cur,num,0,0,target,r); vector<vector<int>>ret; copy(r.begin(),r.end(),back_inserter(ret)); return ret; } void combination(vector<int>cur,vector<int>&num,int index,int curSum,int target,set<vector<int>>&ret) { if(curSum>target) return; if(curSum==target) { ret.insert(cur); return; } if(index==num.size()) return; combination(cur,num,index+1,curSum,target,ret); cur.push_back(num[index]); combination(cur,num,index+1,curSum+num[index],target,ret); }};2、n皇后问题
n皇后问题是将n个皇后放置在n*n的棋盘上,皇后彼此之间不能相互攻击。《不同行,不同列,不同对角线》
给定一个整数n,返回所有不同的n皇后问题的解决方案。
每个解决方案包含一个明确的n皇后放置布局,其中“Q”和“.”分别表示一个女王和一个空位置。
对于4皇后问题存在两种解决的方案:
[
[".Q..", // Solution 1
"...Q",
"Q...",
"..Q."],
["..Q.", // Solution 2
"Q...",
"...Q",
".Q.."]
]
class Solution { vector<string> base; vector<vector<string> > ret; void search(vector<int> &c,int n,int cur){ if(n==cur){ ret.push_back(base); return; } for(int i=0;i<n;++i){ //cur表示当前行,i表示要选择的列 if(isValid(c,cur,i)){ //检验当前列是否合法 c[cur]=i; string s(n,'.'); s[i]='Q'; //当前位置摆放皇后,那么同一行其他位置全部是'.' base.push_back(s); search(c,n,cur+1); //搜寻下一行 base.pop_back(); //别忘了弹出,base用于存放下一组可能位置 } } } bool isValid(vector<int> &c,int row,int col){ for(int i=0;i<row;++i) if(c[i]==col||row-col==i-c[i]||row+col==i+c[i]) //分别检查纵向,主对角线,副对角线上是否有其他皇后 return false; //因为是一行一行摆放,所以不用检查同一行 return true; } public: vector<vector<string> > solveNQueens(int n) { if(n<=0) return ret; vector<int> c(n); search(c,n,0); return ret; } };
可证明,采用如下策略可以得到一个最优装载方案:先尽可能的将第一艘船装满,其次将剩余的集装箱装到第二艘船上。
template<class Type>void EnQueue(Queue<Type> &Q, Type wt, Type &bestw, int i, int n) { if(i == n) //可行叶结点 { if(wt>bestw) bestw = wt ; } else Q.Add(wt) ; //非叶结点}//装载问题先尽量将第一艘船装满//队列式分支限界法,返回最优载重量template<class Type>Type MaxLoading(Type w[],Type c,int n){ //初始化数据 Queue<Type> Q; //保存活节点的队列 Q.Add(-1); //-1的标志是标识分层 int i=1; //i表示当前扩展节点所在的层数 Type Ew=0; //Ew表示当前扩展节点的重量 Type bestw=0; //bestw表示当前最优载重量 //搜索子集空间树 while(true) { if(Ew+w[i]<=c) //检查左儿子 EnQueue(Q,Ew+w[i],bestw,i,n); //将左儿子添加到队列 //将右儿子添加到队列 即表示不将当前货物装载在第一艘船 EnQueue(Q,Ew,bestw,i,n); Q.Delete(Ew); //取下一个节点为扩展节点并将重量保存在Ew if(Ew==-1) //检查是否到了同层结束 { if(Q.IsEmpty()) return bestw; //遍历完毕,返回最优值 Q.Add(-1); //添加分层标志 Q.Delete(Ew); //删除分层标志,进入下一层 i++; } }}2、单源最短路径问题;
#include <iostream> #include<queue> using namespace std; typedef struct ArcCell{ int adj;//保存权值 int info;//存储最短路径长度 }ArcCell,AdjMaxtrix[100][100]; typedef struct{ int data; int length; }VerType; typedef struct{ VerType vexs[100];//顶点向量 AdjMaxtrix arcs; int vexnum;//顶点数 int arcnum;//弧数 }Graph; Graph G; queue<int> q; void CreateGraph() { int m,n,t; printf("输入顶点数和弧数:"); scanf("%d%d",&G.vexnum,&G.arcnum); printf("输入顶点:"); for(int i=1;i<=G.vexnum;i++) { scanf("%d",&G.vexs[i].data); G.vexs[i].length=10000; } for(int i=1;i<=G.vexnum;i++) for(int j=1;j<=G.vexnum;j++) { G.arcs[i][j].adj=0; } printf("输入弧及权重:\n"); for(int i=1;i<=G.arcnum;i++) { scanf("%d%d%d",&m,&n,&t); G.arcs[m][n].adj=1; G.arcs[m][n].info=t; } } int NextAdj(int v,int w) { for(int i=w+1;i<=G.vexnum;i++) if(G.arcs[v][i].adj) return i; return 0;//not found; } void ShortestPaths(int v) { int k=0;//从首个节点开始访问 int t; G.vexs[v].length=0; q.push(G.vexs[v].data); while(!q.empty()) { t=q.front(); k=NextAdj(t,k); while(k!=0) { if(G.vexs[t].length+G.arcs[t][k].info<=G.vexs[k].length)//减枝操作 { G.vexs[k].length=G.vexs[t].length+G.arcs[t][k].info; q.push(G.vexs[k].data); } k=NextAdj(t,k); } q.pop(); } } void Print() { for(int i=1;i<=G.vexnum;i++) printf("%d------%d\n",G.vexs[i].data,G.vexs[i].length); } int main() { CreateGraph(); ShortestPaths(1); Print(); return 0; }
1、最优化原理,也就是最有子结构性质。这指的是一个最优化策略具有这样的性质,无论过去状态和决策如何,对前面的决策所形成的状态而言,余下的决策必须构成最优策略,简单来说就是一个最优化策略的子策略总是最优的,如果一个问题满足最优化原理,就称其有最优子结构性质。
int fun(int n){ if (n==1||n==2) return n; /*判断n-1的状态有没有被计算过*/ if (!dp[n-1]) dp[n-1] = fun(n-1); if(!dp[n-2]) dp[n-2]=fun(n-2); return dp[n-1]+dp[n-2]; }2、矩阵:
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
代码:
int dp[4][4] = {}; int main(){ int arr[4][4] = {1,3,5,9,8,1,3,4,5,0,6,1,8,8,4,0}; //cout << fun(arr,4,4) << endl; const int oo = ~0U>>2; for (int i = 0;i<4;i++) for (int j = 0; j < 4;j++) dp[i][j] = oo; //dp[0][0] = oo; for (int i = 0; i < 4;i++){ for (int j = 0; j<4;j++){ if (dp[i][j] == oo){ if (i==0&&j==0) dp[i][j] = arr[i][j]; else if (i==0&&j!=0) dp[i][j] = arr[i][j] + dp[i][j-1]; else if(i!=0&&j==0) dp[i][j] = arr[i][j] + dp[i-1][j]; else{ dp[i][j] = arr[i][j]+min(dp[i-1][j],dp[i][j-1]); } } } } // cout << dp[3][3] << endl; for (int i = 0; i< 4;i++){ for (int j = 0; j<4;j++){ cout << dp[i][j] << " "; } cout << endl; } }3、爬楼梯:
样例
比如n=3,1+1+1=1+2=2+1=3,共有3中不同的方法
返回 3
class Solution {public: /** * @param n: An integer * @return: An integer */ int climbStairs(int n) { // write your code here int res; if(n<2)return 1; int first=1; int second=1; while(n>1){ res=first+second; first=second; second=res; n--; } return res; }};
- 算法分析与设计课程总结
- 算法分析与设计课程总结
- 算法分析与设计课程总结
- 算法设计与分析课程总结
- 《算法分析与设计》课程作业
- 算法设计与分析课程机试题
- 设计Huffman编码(算法设计与分析课程实验)
- 算法分析与设计总结
- 算法分析与设计总结
- 算法设计与分析课程Part1笔记(1)
- 算法设计与分析课程Part1笔记(2)
- 算法设计与分析课程Part1笔记(3)
- 算法设计与分析课程Part1笔记(4)
- 算法设计与分析课程Part1笔记(5)
- 算法设计与分析课程Part1笔记(6)
- 算法分析与设计课程(1):Add Two Numbers
- 算法分析与设计课程(3):【leetcode】Permutations
- 算法分析与设计课程(4):【leetcode】Wildcard Matching
- Word中插入表格与柱状图饼状图技术经验分享
- webpack+vue初始化项目基本
- Matlab 自定义自己的 colormap(注意其中的一个坑)
- set path in cmd in windows
- Laravel 精选资源大全
- 算法分析与设计课程总结
- AADL模式
- 一个核物理学霸为何两次收到BlackHat的邀请
- spring容器内建的profile功能实现开发环境、测试环境、生产环境配置自动切换
- solr7.0.0使用ikanalyzer中文分词器
- 【面向JS--闭包】
- 日期格式yyyy/MM/dd格式转为yyyy-MM-dd格式
- 虚拟机centos7 调用service network restart报错
- hadoop2.x安装文件配置