您现在的位置是:首页 >技术教程 >算法篇——贪心算法大集合(js版)网站首页技术教程

算法篇——贪心算法大集合(js版)

低保和光头哪个先来 2024-06-17 10:31:58
简介算法篇——贪心算法大集合(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;
};
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。