算法设计的整个过程,可以包含对问题需求的说明、数学模型的拟制、算法的详细设计、算法的正确性验证、算法的实现、算法分析、程序测试和文档资料的编制。
算法设计的先驱者唐纳德。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、
把排序数组转换为高度最小的二叉搜索树