算法(十八)数组之贪心

leetcode

[hot] 45. 跳跃游戏 II

题目

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

题解

遍历过程中记录从当前位置能跳的最远位置,并记录最少的跳跃次数,示意图如下所示:
在这里插入图片描述
示例代码如下所示:

class Solution {
public:
    int jump(vector<int>& nums) {
        int max_pos = 0;
        int end = 0; // 记录上一次能跳到的最远距离
        int steps = 0; // 步数记录
        // 题目为能否跳到最后一个位置,因而不能遍历到最后一个位置,否则会多跳一次
        for (int i = 0; i < nums.size() - 1; ++i) {
            max_pos = max(max_pos, i + nums[i]);
            if (i == end) { // i到达了上次的最远距离,则更新end和step
                end = max_pos;
                ++steps;
            }
        }

        return steps;
    }
};

复杂度

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

[hot] 55. 跳跃游戏

题目

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。

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

示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

题解

贪心算法解决问题,记录在当前位置能够跳跃的最远距离rightmost,到最后看是否能够跳到末尾。注意题意是"能否到达最后一个下标",那么需要遍历的其实是[0, length - 1),这时length = 1时需要特殊处理,直接返回true,示例代码如下所示:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int length = nums.size();
        if (length == 1) {
            return true;
        }

        int rightmost = 0;
        for (int i = 0; i < length - 1; ++i) {
        	// i之前的最远跳不到i,出现断层,直接返回false
            if (i > rightmost) {
                return false;
            }
            
            rightmost = max(rightmost, i + nums[i]);
            if (rightmost >= length - 1) {
                return true;
            }
        }
        
        return false;
    }
};

复杂度

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

关键点

到达数组最后一个位置,但是不是跳完,这个一定要注意

[hot] 56. 合并区间

题目

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

题解

常规思路合并,边遍历边合并,示例代码如下所示:

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end());

        vector<vector<int>> res;
        for (auto& elem: intervals) {
            auto& L = elem[0];
            auto& R = elem[1];
            if (res.empty() || res.back()[1] < L) {
                res.emplace_back(elem);
            } else {
                res.back()[1] = max(res.back()[1], R);
            }
        }

        return res;
    }
};

复杂度

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),排序的时间复杂度
空间复杂度: O ( 1 ) O(1) O(1)

关键点

待插入区间和数组中最后一个区间有重合时,需要取两者区间右侧元素的最大值

57. 插入区间

题目

给你一个 无重叠的 ,按照区间起始端点排序的区间列表。在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

示例 1:
输入:intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]

示例 2:
输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出:[[1,2],[3,10],[12,16]]
解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。

示例 3:
输入:intervals = [], newInterval = [5,7]
输出:[[5,7]]

示例 4:
输入:intervals = [[1,5]], newInterval = [2,3]
输出:[[1,5]]

示例 5:
输入:intervals = [[1,5]], newInterval = [2,7]
输出:[[1,7]]

题解

一图抵千言,示意图如下所示,S像一个漏斗一样,来区分区间列表中的每一个区间,具体讲解见leetcode官方讲解
在这里插入图片描述
示例代码如下所示:

class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        int left = newInterval[0];
        int right = newInterval[1];
        bool placed = false;
        vector<vector<int>> ans;
        for (const auto& interval: intervals) {
            if (interval[0] > right) {
                // 在插入区间的右侧且无交集
                if (!placed) {
                    ans.push_back({left, right});
                    placed = true;                    
                }
                ans.push_back(interval);
            }
            else if (interval[1] < left) {
                // 在插入区间的左侧且无交集
                ans.push_back(interval);
            }
            else {
                // 与插入区间有交集,计算它们的并集
                left = min(left, interval[0]);
                right = max(right, interval[1]);
            }
        }
        if (!placed) {
            ans.push_back({left, right});
        }
        return ans;
    }
};

复杂度

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

关键点

需要想到一种拿到区间的第一个元素后,就无处可插的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值