一、含义
贪心算法是指在对问题求解时,总是作出在当前看来是最好的选择。也就是不从整体最优解上加以考虑,只考虑局部最优解。
二、算法思路
- 建立数学模型描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题求解,得到子问题的局部最优解
- 把子问题的解局部最优解合成原来解问题的一个解
贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做依次贪心选择,就将所求问题简化为一个规模更小的子问题。通过每一步贪心选择,可得到问题的一个最优解。虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪心算法不用回溯。
三、算法特性
- 有一个以最优方式来解决的问题。为了构造问题的解决方法,有一个候选对象集合:比如不同面值的硬币。
- 随着算法的进行,将积累起其他两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。
- 有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。
- 还有一个函数检查是否一个候选对象的集合是否是可行的,即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。
- 选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。
- 最后,目标函数给出解的值。
四、使用条件
1、贪心选择性质:一个问题的最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。
2、最优子结构性质:当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
五、例题
分发糖果:n个孩子站成一排。给你一个整数数组ratings表示每个孩子的评分。需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到1个糖果。
- 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的最少糖果数量。
方法一:两次遍历
相邻两个孩子评分更高的孩子会获得更多的糖果 这句话可以拆分为两个规则分别处理:
(1)左规则:当ratings[i-1]
(2)右规则:当ratings[i]>ratings[i+1]时,i号学生的糖果数量将比i+1号孩子的糖果数量多。
遍历该数组两次,处理出每一个孩子分别满足左右规则时,最少需要被分得的糖果数量。最终每个人分的的糖果数量即为这两个数量的最大值。
var candy = function(ratings) {
for(let i=0;ilength;i++){
if(ratings[i]>ratings[i-1]){
for(let i=ratings.length-1;i>-1;i--){
if(ratings[i]>ratings[i+1]){
res+=Math.max(arr[i],right)
方法二:常数空间遍历
糖果要尽量少给,从1开始累计,每次要么仍然是1,要么比相邻的同学多给1个。从左到右枚举每一个同学,记前一个同学分得的糖果数量为pre:
- 如果当前同学比上一个同学评分高,说明我们就在最近的递增序列中,直接分配给该同学pre+1;否则就在递减序列中,直接分配给当前同学一个糖果,并把该同学所在的递减序列中所有的同学都再多分配一个糖果,以保证糖果数量还是满足条件。
- 无需显式地额外分配糖果,只需记录当前的递减序列长度,即可知道需要额外分配的糖果数量。
- 同时注意当当前的递减序列长度和上一个递增序列等长时,需要把最近的递增序列的最后一个同学也并进递减序列中。
这样,只要记录当前递减序列的长度dec,最近的递增序列长度inc和前一个同学分得的糖果数量pre即可。
var candy = function(ratings) {
const n = ratings.length;
let inc = 1, dec = 0, pre = 1;
for (let i = 1; i < n; i++) {
if (ratings[i] >= ratings[i - 1]) {
if (ratings[i] === ratings[i - 1]) pre = 1;