算法题(贪心算法)

一、题目

1、跳跃游戏(LC 55)

2、跳跃游戏2 (LC 45)

3、划分字母区间(LC 763)

二、题解

1、跳跃游戏(LC 55)

(1)分析

这道题判断能否从数组的第一个位置跳到最后一个位置,就是看每一步能覆盖到的最远范围。

可以维护一个变量表示当前能到达的最远下标,从头开始遍历每一个位置。在遍历过程中,如果当前位置已经超出了能到达的最远下标,那就说明根本跳不到这里,直接返回 false 就可以。如果能到达当前位置,就用当前位置加上能跳的长度,更新能到达的最远下标。只要一直遍历到数组结束,都没有出现无法到达的情况,就说明一定能跳到最后一位,返回 true 即可。过程中不纠结具体怎么跳,只关注最远覆盖范围。

时间复杂度是 O (n),只需要一次遍历数组。空间复杂度是 O (1),只用了几个变量记录状态,没有额外空间开销。

(2)题解
java 复制代码
class Solution {
    public boolean canJump(int[] nums) {
        int maxIndex = 0;         //可以到达的最大下标
        for(int i = 0; i < nums.length; i++){
            if(maxIndex < i){
                return false;   //到达不了当前位置,返回false
            }
            maxIndex = Math.max(maxIndex, i+nums[i]);  //更新最大下标
        }
        return true;  //循环结束,说明可以到达最大下标
    } 
}

2、跳跃游戏2 (LC 45)

(1)分析

这道题在跳跃游戏的基础上,要求算出跳到终点的最少步数,同样适用贪心算法,在当前能到达的范围内,尽可能跳到最远的地方,以此减少跳跃次数。

使用三个变量来完成整个逻辑,分别记录跳跃次数、当前这一步能到达的最远位置、下一步能到达的最远位置。遍历数组的时候,不断更新下一步能到达的最远位置,当遍历到当前这一步的边界时,就必须再跳一次,这时候把跳跃次数加一,并且把当前边界更新为下一步的最远位置。因为题目保证一定可以到达终点,所以只需要遍历到倒数第二个位置就够了。

时间复杂度是 O (n),一次遍历完成计算。空间复杂度是 O (1),仅使用常数变量。

(2)题解
java 复制代码
class Solution {
    public int jump(int[] nums) {
        int ans = 0;        //步数
        int curEnd = 0;     //当前的位置
        int nextEnd = 0;    //当前可以到达的最大位置
        for(int i = 0; i < nums.length - 1; i++){
            nextEnd = Math.max(nextEnd, i + nums[i]);
            if(i == curEnd){   //此时需要再走一步,到达nextEnd
                curEnd = nextEnd;
                ans++;
            }
        }
        return ans;
    }
}

3、划分字母区间(LC 763)

(1)分析

这道题要把字符串划分成最多的片段,保证同一个字母只出现在一个片段里,最关键的思路就是先确定每个字母最后出现的位置,再用贪心的方式确定分割点。

先遍历一遍字符串,用数组记录下每一个字母最后一次出现的下标。然后再次遍历字符串,不断更新当前片段的结束位置,这个结束位置取当前字母最后出现位置的最大值。当遍历到当前片段的结束位置时,就说明这个片段里所有字母都不会再出现在后面了,此时可以完成一次分割,记录片段长度,再把起点设为下一个位置继续遍历。遍历结束后返回 ans 结果。

时间复杂度是 O (n),只需要两次线性遍历字符串。空间复杂度是 O (1),记录最后位置的数组大小固定,属于常数级空间。

(2)题解
java 复制代码
class Solution {
    public List<Integer> partitionLabels(String s) {
        List<Integer> ans = new ArrayList<>(); //结果集合
        char[] chs = s.toCharArray();      
        int n = chs.length;
        int[] last = new int[26];  //每个字符最后出现的位置
        for(int i = 0; i < n; i++){
            last[chs[i] - 'a'] = i;  //记录每个字符的最后下标
        }

        int start = 0;      //闭区间
        int end = 0;
        for(int i =0; i <n; i++){
            end = Math.max(last[chs[i] -'a'], end); //更新最大下标
            if(i == end){              //此时可以分割
                ans.add(end - start + 1);
                start = end + 1;
            }
        }
        return ans;
    }
}
相关推荐
黄敬峰1 小时前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术2 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六5 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术6 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize7 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考20 小时前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC2 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
BadBadBad__AK2 天前
线段树维护区间 k 次方和
c++·数学·算法·stl