贪心: 遵循某种规律,不断贪心的选取当前最优策略的算法设计方法。
一、预备知识:贪心算法找钱
有1,5,10,20,100,200元的钞票无穷多张,现使用这些钞票支付x元,最少需要多少张?
思路:都是倍数关系,尽可能多的使用面值最大的钞票。
//贪心算法找钱
var findMoney = function(x){
let moneyArr = [200,100,20,10,5,1];
let count = 0;
for(let i = 0;i<moneyArr.length;i++){
count += Math.floor(x/moneyArr[i]);
x = x%moneyArr[i]
}
return count
}
console.log(findMoney(200))
二、分糖果455
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
思路: 让更多孩子满足。某个孩子可以用多个糖果满足,用更小的满足他。某个糖果可以满足多个孩子,把糖果分给需求更多的孩子。排序后,某个糖果如果不能满足某个孩子的需求,则该糖果也不能满足需求更大的孩子。
var findContentChildren = function(g, s) {
let child = 0;
let cookie = 0;
g.sort((a,b)=>a-b)
s.sort((a,b)=>a-b)
while(cookie < s.length){
if(g[child] <= s[cookie]){
child++;
cookie++;
}else{
cookie++;
}
}
return child
};
三、摇摆序列376
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。
示例 1:
输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。
思路:(贪心算法,自己写出来之后发现是贪心)
用count计数,用op代表nums[i+1]-nums[i]的结果为正数或者负数,如果结果为负数,op=0;如果结果为正数,op=1;遍历nums,如果后-前>0&&op=0,count就++,把op设为1;如果后-前<0&&op=1,count就++,把op设置为0。注意给op赋初始值!!!遍历Nums,碰到第一组后-前>0的,把op设为0;出现第一组后-前<0的,把op设为1.(这样防止碰到[3,3,3,5,4]这种类型的数据)
var wiggleMaxLength = function(nums) {
let count = 1;
let op = 0;//1代表结果为正数,0代表结果为负数
for(let i = 0;i<nums.length - 1;i++){
if(nums[i+1]!==nums[i]){
if(nums[i+1]-nums[i]>0){op = 0;break;};
if(nums[i+1]-nums[i]<0){op = 1;break;};
}
}
for(let i = 0;i<nums.length - 1;i++){
if(nums.length < 2){
return nums.length;
}else if(nums[i+1] - nums[i] > 0&&op===0){
count++;
op = 1;
}else if(nums[i+1] - nums[i] < 0&&op===1){
count++;
op = 0;
}
}
return count;
};
四、移除k个数字402
给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
示例 1 :
输入:num = "1432219", k = 3
输出:"1219"
解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。
思路: 从高位到低位遍历,结合栈。如果栈顶大于下一位数字,就弹出。注意要处理掉数字的前导0.
var removeKdigits = function(num, k) {
let stack = [];//定义一个栈
let number = '';
if(num.length<=k){
return '0'
}
for(let i = 0;i<num.length;i++){
while(stack.length!==0&&stack[stack.length-1]>num[i]&&k>0){
stack.pop();
k--;
}
if(num[i]!==0||stack.length!==0){
stack.push(num[i])
}
}
while(stack.length!==0&&k>0){
stack.pop();
k--;
}
for(let i = 0;i<stack.length;i++){
number += stack[i]
}
number = number.replace(/\b(0+)/gi,"")
if(number===''){
return '0'
}
return number
};
五、跳跃游戏55
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
思路: 从第i个位置,最远可以跳到nums[i],在这个范围内,贪心选择跳到步数更大的那一个。用jump代表当前所处位置,max_index代表最大的。直到Jump到达index数组尾部或者max_index<nums.length&&jump>=max_index时,返回false.扫描过程中要更新max_index.
var canJump = function(nums) {
let jump = 0;
let max_index = 0;
let index = [];
for(let i = 0;i < nums.length;i++){
index[i] = i + nums[i];
jump = i;
if(index[i] > max_index){
max_index = index[i];
}
if(jump >= max_index&&max_index<nums.length-1){
return false;
}
}
return true;
};
六、跳跃游戏2
七、射击气球452
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
示例 1:
输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
-在x = 11处发射箭,击破气球[10,16]和[7,12]。
思路: 先按左节点排序,维持一个可以射击到最多气球的射击区间,左节点比较该节点和射击区间的左节点谁大,并且不能大过射击区间的右节点;右节点比较该节点和射击区间的右节点谁小。如果该节点的左节点大于射击节点的右节点,更新射击区间,并且让shoot_num++.
var findMinArrowShots = function(points) {
let shoot_num = 1;//弓箭次数
points.sort((a,b)=>{
return a[0] - b[0]
})
let range = points[0]
for(let i = 1;i<points.length;i++){
if(points[i][0]>range[0]&&points[i][0]<range[1]){
range[0] = points[i][0]
}
if(points[i][1] < range[1]){
range[1] = points[i][1]
}
if(points[i][0] > range[1]){
shoot_num++;
range = points[i]
}
}
return shoot_num
};