算法----贪心算法

279 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

一、什么是贪心算法

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择**,从而希望导致结果是最好或最优的算法。

贪心算法在有最优子结构的问题中尤为有效。最优子结构的意思是局部最优解能决定全局最优解。简单地说,问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。

贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。

贪心算法考虑的要点

  1. 创建数学模型来描述问题。
  2. 把求解的问题分成若干个子问题
  3. 对每一子问题求解,得到子问题的局部最优解
  4. 把子问题的解局部最优解合成原来解问题的一个解

二、贪心算法相关应用

2.1 盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i,0) 和 (i,height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例:

image.png

输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49

分析: 局部最优解:即图中阴影部分的面积,公式为:S(i,j)=min(h[i],h[j])×(j−i),此时需要循环比较左侧和右侧不同位置面积的大小

相关实现:

var maxArea = function(height) {
    const n = height.length
    let left = 0,right = n-1,res = 0 
    while(left < right){
        res = Math.max(res,Math.min(height[left],height[right])*(right - left))
        height[right] > height[left]? left++:right--
    }
    return res
};

2.2 跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

输入: nums = [2,3,1,1,4]
输出: true
解释: 可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

分析: 对于数组中的每一个位置,我们要如何去判断是否能到达呢? 假设我们在位置x,能到达位置y,则x+nums[x] >= y,则我们即可到达y位置。

通过上述的分析,我们一次遍历数组中的每一个位置,并维护可以到达的最远的距离。

实现方式:

var canJump = function(nums) {
    if(nums.length === 1) return true
    let cover = 0
    for(let i = 0; i <= cover; i++) { //注意这里是cover,在cover范围的位置内找到更远的跳跃位置
        cover = Math.max(cover, i + nums[i])
        if(cover >= nums.length - 1) {
            return true
        }
    }
    return false
};

若不像上述方案,循环遍历时以cover为结束条件,则需要改为:

var canJump = function (nums) {
    const n = nums.length
    if (n === 1) return true
    let ret = 0
    for (let i = 0; i < n; i++) {
        if (ret < i) return false
        if (ret >= n - 1) return true
        ret = Math.max(i + nums[i], ret)
    }
};

参考与感谢

  • leetcode官网

相关算法题,也可在leetcode官网-贪心算法分类中查找,理解思路后多看多做才能加深印象。