一、贪心算法理论基础
贪心的本质是选择每一个阶段的局部最优,从而达到全局最优
贪心没有套路,就是常识性推到加上举反例
二、分发饼干
局部最优为大饼干给胃口大的或者小饼干给胃口小的,充分利用饼干,全局最优就是喂饱尽可能多的小孩
/**
* @param {number[]} g
* @param {number[]} s
* @return {number}
*/
var findContentChildren = function(g, s) {
let res = 0
let i = 0
let j = 0
g.sort((a, b) => a - b)
s.sort((a, b) => a - b)
while(i < g.length && j < s.length) {
if(s[j] >= g[i]) {
i++
j++
res++
} else {
j++
}
}
return res
};
三、摆动序列
局部最优,删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度上就可以有两个局部峰值
/**
* @param {number[]} nums
* @return {number}
*/
var wiggleMaxLength = function(nums) {
if(nums.length < 2) {
return nums.length
}
let preDiff = 0
let curDiff = 0
let index = 1
let res = 1
while(index < nums.length) {
curDiff = nums[index] - nums[index - 1]
if((preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)) {
res++
preDiff = curDiff
}
index++
}
return res
};
动态规划思路
对于位于i位置的数,可能为波峰或者波谷,
dp[i][0]表示前i个数,第i个数作为波峰的摆动子序列的最长长度
dp[i][1]表示前i个数,第i个数作为波谷的摆动子序列的最长长度
状态转移方程
dp[i][0] = max(dp[i][0], dp[j][1] + 1) , 其中 0 < j < i 且 nums[j] < nums[i]
dp[i][1] = max(dp[i][1], dp[j][0] + 1) , 其中 0 < j < i 且 nums[j] > nums[i]
初始状态
dp[0][0] = dp[0][1] = 1
var wiggleMaxLength = function(nums) {
let dp = new Array(nums.length).fill(0).map(_ => [0, 0])
dp[0][0] = 1 // 波峰
dp[0][1] = 1 // 波谷
for(let i = 1; i< nums.length;i++) {
dp[i][0] = 1
dp[i][1] = 1
for(let j = 0;j < i; j++) {
if(nums[j] > nums[i]){
dp[i][1] = Math.max(dp[i][1], dp[j][0] + 1)
}
}
for(let j = 0;j < i; j++) {
if(nums[j] < nums[i]){
dp[i][0] = Math.max(dp[i][0], dp[j][1] + 1)
}
}
}
return Math.max(dp[nums.length - 1][0], dp[nums.length - 1][1])
};
四、最大子序和
贪心思路
局部最优为 当前连续和为负数的时候立刻放弃,从下个元素重新计算连续和,因为负数会拉低总和
只要连续和为正,对后面的元素起到增大总和的作用
只要有更大的连续和出现,result就会更新
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
let result = -Infinity
let sum = 0
for(let i = 0; i < nums.length;i++) {
sum += nums[i]
if(sum > result) {
result = sum
}
// 当和小于等于0的时候,对结果对总和是拖累的
if(sum <= 0) {
sum = 0
}
}
return result
};