您现在的位置是:首页 >技术教程 >算法篇——贪心算法大集合(js版)网站首页技术教程
算法篇——贪心算法大集合(js版)
455.分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
链接:https://leetcode.cn/problems/assign-cookies
var findContentChildren = function(g, s) {
// 匹配到小孩就+1
var res = 0;
// 把数组转变为升序,从后往前遍历
s = s.sort((a, b) => (a - b));
g = g.sort((a, b) => (a - b));
// 先满足胃口大的
for(var i = g.length-1; i >= 0; --i) {
for(var j = s.length-1; j >= 0; --j) {
if(s[j] >= g[i]) {
res++;
s[j] = -1;
break;
}
}
}
return res;
};
376. 摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。
链接:https://leetcode.cn/problems/wiggle-subsequence
var wiggleMaxLength = function(nums) {
// 仅有一个元素
if(nums.length == 1) return 1;
// 或者含两个不等元素的序列
else if(nums.length == 2 && nums[0] != nums[1]) return 2;
// 作为 摆动序列 的 最长子序列的长度
var res = 1;
// 前一个差值
var preRes = 0;
// 当前差值
var curRes = 0;
for(var i = 0; i < nums.length-1; i++) {
curRes = nums[i+1] - nums[i];
if((curRes > 0 && preRes <= 0) || (curRes < 0 && preRes >= 0)) {
// 向前赋值
preRes = curRes;
res++;
}
}
return res;
};
53. 最大子序和
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
链接:力扣
var maxSubArray = function(nums) {
var sum = 0;
// max 是小于任何数
var max = -Infinity;
if(nums.length == 1) return nums[0];
for(var i = 0; i < nums.length; i++) {
sum += nums[i];
if(max < sum) max = sum;
// 如果加上当前的元素以后,sum 变成0,应该清零重新求和
if(sum < 0) sum = 0;
}
return max;
};
53. 最大子序和
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii
var maxProfit = function(prices) {
var res = 0;
// 计算正利润
var odd = 0;
for(var i = prices.length-1; i >= 0; i--) {
// 进行差值计算
odd = prices[i] - prices[i-1];
// 如果是正利润就放入结果中
if(odd > 0) res += odd;
else odd = 0;
}
return res;
};
55. 跳跃游戏
给定一个非负整数数组 nums
,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
链接:力扣
var canJump = function(nums) {
if(nums.length == 1) return true;
// 初始位置下标
var cover = 0;
// 最后一个值
var target = nums.length-1;
for(var i = 0; i <= cover; i++) {
cover = Math.max(cover, i + nums[i])
if(cover >= target) return true;
}
return false;
};
45.跳跃游戏II
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
链接:https://leetcode.cn/problems/jump-game-ii
var jump = function(nums) {
// 需要看这一步和下一步覆盖的范围
var cur = 0, next = 0, res = 0;
for(var i = 0; i < nums.length-1; i++) {
// 取最大覆盖范围
next = Math.max(nums[i] + i, next);
if(i == cur) {
cur = next;
res++;
}
}
return res;
};
1005.K次取反后最大化的数组和
给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:
选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
重复这个过程恰好 k 次。可以多次选择同一个下标 i 。
以这种方式修改数组后,返回数组 可能的最大和 。
链接:https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations
var largestSumAfterKNegations = function(nums, k) {
var res = 0;
sortItem(nums);
for(var i = 0; i < nums.length; i++) {
// 负数取反 且 K 还有剩余次数
if(nums[i] < 0 && k > 0) {
nums[i] = -nums[i];
k--;
}
res += nums[i];
}
sortItem(nums);
// 此时k有剩余,说明数组内都是正数
if(k % 2 > 0) {
// 因为上面的循环中把所有的元素都加过了
// 此时应该在取反k次的基础上,再减掉刚刚加的一次
res -= 2 * nums[0];
}
return res;
};
// 转变为升序
var sortItem = (nums) => {
nums = nums.sort((a, b) => (a-b));
return nums;
};
134. 加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
链接:https://leetcode.cn/problems/gas-station
var canCompleteCircuit = function(gas, cost) {
// 得到两个数组各自的元素和
var gasSum = getSum(gas);
var costSum = getSum(cost);
var min = Infinity, curSum = 0;
// 如果gas的总和小于cost的总和,返回-1
if(gasSum < costSum) return -1;
// 计算出发节点是0节点还是非0节点
else {
var oddList = getOdd(gas, cost);
// 遍历每个差值找到最小的差值和
for(var i of oddList) {
curSum += i;
if(min > curSum) min = curSum;
}
// 如果差值和的最小值是负数,说明从非0节点开始
if(min < 0) {
// 如果有节点能填平那个最小差值,就从用来填平的节点开始
for(var j = gas.length-1; j >= 0; j--) {
min += oddList[j];
if(min >= 0) return j;
}
// 否则无法实现
return -1;
}
// 如果差值都大于0,说明不缺油,就从0出发
return 0;
}
};
// 计算数组各元素的和
var getSum = (list) => {
var sum = 0;
for(var i of list) {
sum += i;
}
return sum;
}
// 计算油量差值
var getOdd = (gas, cost) => {
var oddSum = [];
for(var i = 0; i < gas.length; i++) {
var odd = gas[i] - cost[i];
oddSum.push(odd);
}
return oddSum;
}
135. 分发糖果
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
链接:https://leetcode.cn/problems/candy
var candy = function(ratings) {
var resList = new Array(ratings.length).fill(1);
// 从左到右遍历,右边评分比左边大
for (var i = 1; i < ratings.length; i++) {
if(ratings[i] > ratings[i-1])
// 比左边的糖果数大 1
resList[i] = resList[i-1] + 1;
}
// 从右到左遍历,左边评分比右边大
for (var i = ratings.length-2; i >= 0; i--) {
if(ratings[i] > ratings[i+1])
// 如果本身的糖果数已经比右边的大,则取自身,否则比右边大 1
resList[i] = Math.max(resList[i+1] + 1, resList[i]);
}
var res = 0;
for(var i of resList) res += i;
return res;
};
860.柠檬水找零
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
链接:https://leetcode.cn/problems/lemonade-change
var lemonadeChange = function(bills) {
var five = 0, ten = 0;
for(var i of bills) {
if(i == 5) five++;
else if(i == 10) {
if(five <= 0) return false;
five--;
ten++;
}
else if(i == 20) {
if(ten > 0 && five > 0) {
ten--;
five--;
}
else if(five >= 3) five -= 3;
else return false;
}
}
return true;
};
406.根据身高重建队列
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
链接:https://leetcode.cn/problems/queue-reconstruction-by-height
var reconstructQueue = function(people) {
// 如果身高高则排前面,如果身高相同则看k值小的在前面
var people = people.sort((a, b) =>
(a[0] == b[0]) ? a[1] - b[1] : b[0] - a[0]
)
var res = [];
for(let i = 0; i < people.length; i++) {
// 按身高高的k来插入
res.splice(people[i][1], 0, people[i]);
}
return res;
};
452. 用最少数量的箭引爆气球
有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。
一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。
给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。
链接:https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons
var findMinArrowShots = function(points) {
// 对数组进行升序排序
points.sort((a, b) => {
return a[0] - b[0];
})
// 至少需要一只箭
var res = 1;
// 找最小右边界
for(var i = 1; i < points.length; i++) {
// 说明没有重合的半径
if(points[i][0] > points[i-1][1]) {
res++;
}
// 如果后一个气球的x[start] 比前一个气球的x[end] 小, 说明是最小右边界
// 它之前的区间需要一只箭
else {
points[i][1] = Math.min(points[i][1], points[i-1][1]);
}
}
return res;
};
435. 无重叠区间
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
链接:https://leetcode.cn/problems/non-overlapping-intervals
var eraseOverlapIntervals = function(intervals) {
intervals.sort((a, b) => {
return a[1] - b[1];
})
// 非移除元素计数器
var res = 1;
var end = intervals[0][1];
for(var i = 1; i < intervals.length; i++) {
// 说明不是重叠区间
if(intervals[i][0] >= end) {
res++;
// end 也要向下一个元素移动
end = intervals[i][1];
}
}
return intervals.length - res;
};