1.算法设计的基石
作者:互联网
一.几种基本的算法思想
首先,我们先认识几个词:分治法,动态规划法,回溯法,分支限界法,贪心法。
我们必须知道,上面五种思想将贯穿我们的算法学习的全过程,这五种思想并没有啥深度和技术含量,他们更像是一种基石。
在我们以后写任何算法的时候,我们都要思考,我们写的这个算法是用的那种思想实现的。
1.分治法
1)分治法概述
分治法是计算机领域中最为常用的算法类型,其核心思想是 “分而治之”,原理是:
首先将一个复杂混合的问题拆解为两个或两个以上类型相同的子问题,然后将子问题依然进行分治法处理,直到子问题本身简单到可以求解,
最终按照问题的拆解顺序,从子问题逐步向大问题结果汇总,层层向上,最终得到原问题的解。
它的典型的应用就是递归。
2)分治法问题实例学习
分治法解决的最简单的问题是:阶乘问题 ,求解N的阶乘(1234…n)
这个问题看起来很简单,但是当N特别大时,是不是就会感到很无助?
下面我们应用分治法来解决这个问题:
首先我们尝试将这个问题拆分,开始我们可以想到,我们可以将其拆为(123…(n/2))和((n/2+1)(n/2+2)*…*n)的乘积。
拆到最后其实就是两个数相乘的结果。 所以子问题就是:两个数相乘
好了,现在子问题找到了,下面我们要思考如何将子问题的结果汇总成大问题的结果?
其实想想很简单,我们可以保留每个子问题的结果,然后将这个结果以下个子问题的入参进行求解,以此类推,可以得到最后的结果;
简单来说就是,先算出n*(n-1)的结果,然后将这个结果*(n-2),然后将这个结果*(n-3)依此类推。
写成代码就是:
int jiecheng(int n){
if(n==0) return 1;
return jiecheng(n-1)*n;
}
2.动态规划法
1)动态规划法概述
动态规划法又叫中间结果记录法,它的原理是:
将问题拆分为多个简单的子问题,对于相对重叠的子问题的结果记录在中间结果表中,当遇到相同的问题时直接查询中间结果表的结果即可。
它有效的避免了重复计算所带来的性能缺陷。
2)动态规划问题实例学习
动态规划法解决的典型的问题有:斐波那契数
问题描述:给出一个数,求出它的斐波那契数
1,1,2,3,5,8,13是一个斐波那契数列,当n=5时,它的斐波那契数为8,依次类推。
很明显我们可以用递归的方式进行问题解决:
public int Fib(int n){
if(n<=1)
return 1;
else
return Fib(n-1)+Fib(n-2);
}
但是递归存在重复计算的缺陷,我们先分析下用递归进行计算n=6时的计算过程:
我们可以看到,为了算F6,递归法重复算了F1或F0 最小子问题13次。造成这个问题的很明显的原因是大问题之间的重复计算。
如果我们新建一个中间结果表,在计算的过程中将为计算的大问题结果存入,并在求解新大问题之前先查询这个结果集,看有没有能匹配上的,如果有直接返回,
这样我们可以节省大量时间!!! 这就是动态规划的精髓所在!!
代码表示如下:
public int FibonacciBetter(int n){
int last=1;//存储前一个数
int nextToLast=1;//存储后一个数
int answer;//存储结果
for(int i=1;i<n;i++){
answer=last+nextToLast;
nextToLast=last;
last=answer;
}
return answer;
}
上面的算法,在计算过程中存储需要用到的中间结果,然后使用,是动态规划的典型应用,在这个问题的解法中,中间结果表是:last,nextToLast,answer组成的集合。(请细细体会)
3.回溯法
1)回溯法概述
回溯法是一种通过不断尝试来获得最终解的方法,但并不是一个漫无目的的过程,它在选择求解目标的过程中会利用当前条件与目标的最优匹配选择进行尝试,
即使当前失败,也能回溯到上一步继续进行最优匹配。(深度优先) 换句话说,回溯法相当于穷举搜索的巧妙实现。
2)回溯法问题实例学习
用回溯法解决的一个典型的问题就是:背包问题
问题描述:背包问题简单来说就是给定背包最大承重量,在一堆物品中怎样拿才能尽可能使得拿的物品总价值最大。
例如,有一个背包承重量15kg
有如下物品
目标:在不超过背包称重量的情况下拿到最大价值物品集。
下面我们用回溯法来解决这个问题,回溯法相当于穷举的巧妙实现!!
所以下面我们先用普通的穷举方法穷举一下可能的结果:
1) A+B 110
2) A+C 150
3) A+D 120
4) B+C 160
5) B+D 130
6) C+D 170
注意:这里的穷举法省略了不符合要求的组合,在实际的算法中,其实它也是会算出来的,然后再筛掉。
最后我们可以得到,拿物品C ,D是最佳的选择。 但是如果我们按照穷举的方法,计算量为6,直到最后一次我们才能得到正确的结果。
下面我们用回溯法来进行:
使用回溯法的第一步就是选择起点,通过上面的穷举我们可以知道,一个好的起点可以帮我们节省大量时间(假如开始就选择了C作为起点或D作为起点)
如何选择起点我个人认为这要根据具体问题进行选择,例如这个问题我们可以选择单位价值最大的物品作为起点:
现在我们以C作为起点,使用回溯法的过程为:
1)C+A 150
2) C+B 160
3) C+D 170
注意:这里的穷举法省略了不符合要求的组合,在实际的算法中,其实它也是会算出来的,然后再筛掉。
得到结果!!! 只需三次!!
4.分支限界法
1)分支界限法概述
分支界限法,是以最小代价进行尝试(广度优先),它与回溯法稍有不同。
2)分支界限法解决问题
用分支界限法法解决的一个典型的问题就是:背包问题
问题描述:背包问题简单来说就是给定总价值量,在一堆物品中怎样拿才能尽可能使得拿的物品单位价值最大。
例如,有一个背包承重量15kg
这里的标准不再是最大单位价值,而是以质量为维度来进行选择。以最小的代价获取,也就是 100/总质量最大 的组合。
他其实和回溯法的思想类似,就是维度变了,这里直接给出结果: A+C是最佳选择。
5.贪心法
1)贪心法概述
顾名思义,贪心法是指当前的条件下的最好选择,贪心法没有一个全局对比,也就是说走一步算一步,每步只选择当前最好的一步。
每一步为局部最优解,所以它只适用于保证每一步选择的最优解能通向下一步最优解的问题,否则,这个方法会出现时灵,时不灵的效果。
2)贪心法解决问题实例
贪心法的精髓是每次都要最好的。
典型的应用场景是,玩扑克时,我们抓牌,连续抓五张,只要保证每次抓的点数是最大的,就能保证最后的五张牌的点数最大。
标签:结果,int,问题,算法,回溯,穷举,设计,我们,基石 来源: https://blog.csdn.net/c1776167012/article/details/122197204