面向前端的算法——贪心算法
贪心算法是一种求局部最优解的算法。因此,应用方面,贪心算法只是追求某个范围内的最优解。
贪心算法思想
例如有若干1元,5元,10元的零钱。将手中的N兑换成以上1,5,10的零钱,怎么样的兑换次数最少?
这里假设N是24
很显然,210 + 41 = 24 这样兑换的次数最少。
从上面可以看出,上面的问题就是先用局部最优的10元零钱去兑换也就是N对10做除法然后余数对5做除法(此时的局部最优就是5),在然后余数对1做除法(此时的局部最优是1)
从上面就可以看出贪心算法的一般步骤:
- 建立数学模型描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题进行求解,得到子问题的局部最优解
- 把子问题的局部最优解合并成原来解问题的一个解就是其结果
实战体验
1. 盛最多水的容器
第一种解法: 看到这个题,我首先想到就是枚举法,将他们的结果全部枚举比较选出最大的,实现如下:
function solution(nums: number[]){
let value = 0
const _nums:number[] = [...nums];
let shiftLg = 0
nums.forEach( (item,index) =>{
_nums.shift()
shiftLg ++;
_nums.forEach( (its,idx)=>{
const height =item>its? its:item;
const width = (idx + shiftLg)- index;
const result = height * width
value = result > value ? result :value
})
})
return value
}
其算法缺陷就是时间复杂度为N*N,因此测试用例的时候会出现超出时间限制
第二种解法: 贪心思维下的双指针法
思路概述:
- 第一步给两个指针一个指向数组第一个成员,一个指向数组的最后一个成员
- 其容器的高就是两个指针指的元素值最小的那个
- 其容器的宽就是两个指针的位置之差的绝对值
- 然后这两个指针谁指的值小谁就向前/向后移动一位
- 循环2,3,4当两个指针指向同一个元素时结束循环
function solution(nums: number[]){
if(nums.length === 0 || nums.length === 1) {
return 0
}
let value = 0;
let start = 0;
let end = nums.length -1
while (start!==end){
const height = Math.min(nums[start],nums[end]);
const width = end - start;
const res = height * width;
value = Math.max(res,value)
// 贪心思维体现就在于指针的移动,总是最小的去移动,以期望遇到大的
if(nums[start]<nums[end]){
start ++
}else{
end --
}
}
return value
}
2. 分发糖果
n
个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
来源:力扣(LeetCode) 链接:
const candy = function(ratings) {
let total = 0
const record = ratings.map((item,index)=>{
return {
index:index,
score: item,
candy:1,
}
})
let _pre = record[0]
record.forEach( item =>{
if(item.index !==0){
// 前一个小孩的得分小于当前小孩的得分
if(_pre.score < item.score){
item.candy = _pre.candy +1
}else if(_pre.score > item.score){
// 前一个小孩得分大于当前小孩,
// 比较,1 前一小孩的糖果大于当前小孩的糖果,不做变动
// 2. 前一小孩的糖果小于等于当前小孩的糖果,做出变动
if(_pre.candy <= item.candy){
// 同时向前搜索
let _point = item.index
while(_point!==0 && record[_point -1].score > record[_point].score){
if(record[_point-1].candy <=record[_point].candy){
record[_point-1].candy = record[_point-1].candy +1
}
_point --
}
}
}
}
_pre = item
})
record.forEach( item =>{
total +=item.candy
})
return total
};