【刷题(9)】贪心算法

一、贪心算法基础

先来了解一下「贪心算法」的问题需要满足的条件:
最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较大的问题的解只由其中一个规模较小的子问题的解决定;
无后效性:后面阶段的求解不会修改前面阶段已经计算好的结果;
贪心选择性质:从局部最优解可以得到全局最优解。

1 题目

在这里插入图片描述

2 解题思路

从左到右枚举卖出价格 prices[i]\textit{prices}[i]prices[i],那么要想获得最大利润,我们需要知道第 iii 天之前,股票价格的最小值是什么,也就是从 prices[0]\textit{prices}[0]prices[0] 到 prices[i−1]\textit{prices}[i-1]prices[i−1] 的最小值,把它作为买入价格,这可以用一个变量 minPrice\textit{minPrice}minPrice 维护。

3 code

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans=0;
        int min_price=prices[0];

        for(int p:prices){
            ans=max(ans,p-min_price);

            // 维护一个最小值
            min_price=min(min_price,p);
        }
        return ans;
    }
};

三、55.跳跃游戏

1 题目

在这里插入图片描述

2 解题思路

(1)使用动态规划的方法:从后往前,状态枚举,for (int i = n - 2; i >= 0; --i)
(2)状态计算:int furthestJump = min(i + nums[i], n - 1); // 当前位置能够到达的最远位置
for (int j = i + 1; j <= furthestJump; ++j
(3)状态转移:if (dp[j]) { // 如果从 j 位置可以跳到最后一个位置
dp[i] = true; // 则从 i 位置也可以跳到最后一个位置
break; // 不需要再检查后面的位置

3 code

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n =nums.size();

        // 状态定义,非常重要
        vector<bool> dp(n,false);
        // 最后一个位置可以跳到自己
        dp[n-1]=true; 

        for(int i=n-2;i>=0;--i){
            // 当前位置能够到达的最远位置
            int futhestJump=min(i+nums[i],n-1);
           
           // 注意:i+1 <=
           for (int j = i + 1; j <= futhestJump; j++) {
                if (dp[j]) { // 如果从 j 位置可以跳到最后一个位置
                    dp[i] = true; // 则从 i 位置也可以跳到最后一个位置
                    break; // 不需要再检查后面的位置
                }
            }
        }

        return dp[0];
    }
};

四、45.跳跃游戏②

1 题目

在这里插入图片描述

2 解题思路

采用动态规划方法
(1)状态表示
dp[i]表示为到达i位置最短的跳数。怎么理解?即从0到i-1位置,判断是否可以跳到该位置,然后选取其中最短跳数的dp[j]。注意这个j肯定是小于i的。那么如何判断是否可以跳到该位置呢?即 nums[j]+j>i即可。最后比较比较各个dp[i]前的dp[j]的大小关系即可。
(2)状态转移方程,if(nums[j]+j>i) dp[i]=min(dp[i],dp[j]+1);
(3)初始化,dp[0]=0;

3 code

class Solution {
public:
    int jump(vector<int>& nums) {
        
        // 状态定义
        int n =nums.size();
        vector<int> dp(n,INT_MAX);
        // 初始化
        dp[0]=0;
        // 状态转移
        for(int i=1;i<n;i++){
            for(int j=0;j<i;j++){
                if(nums[j]+j>=i){
                    dp[i]=min(dp[i],dp[j]+1);
                }
            }
        }
        return dp[n-1];
    }
};

五、763.划分字母区间

1 题目

在这里插入图片描述

2 解题思路

(1)同一个字母只能出现在同一个片段,显然同一个字母的第一次出现的下标位置和最后一次出现的下标位置必须出现在同一个片段。
(2)因此在遍历字符串过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界{当你出现的下标和自己当前的位置相等时就认为是最远边界分割点},说明这个边界就是分割点了。【隐含的含义就是此时前面出现过所有字母都已经达到了最远的边界了】
图解举例:【引用代码随想录Carl的图画的太好让人理解了,也很好的表达了本题的含义】
在这里插入图片描述
(3)实现步骤:
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
在得到每个字母最后一次出现的下标位置之后,可以使用贪心的方法将字符串划分为尽可能多的片段具体步骤如下:
使用哈希表数组统计每一个字符最后出现的位置 hash[s[i]−′a′]=i;
遍历字符,并更新字符的最远出现下标,如果找到字符出现的最远边界,当最远边界等于当前字符位置时说明可以划分了
返回结果 result。

3 code

class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int>result;
        // 定义哈希表统计字符出现的最远位置
        int hash[26]={0};
        for(int i=0;i<s.size();i++){
            hash[s[i]-'a']=i;
        }
        // 找到字符出现的最远边界,当最远边界等于当前字符位置时,说明可以划分了
        int right=INT_MIN;
        int left=0;
        for(int i=0;i<s.size();i++){
            right=max(right,hash[s[i]-'a']);
            if(right==i){
                result.push_back(right-left+1);
                left=i+1;
            }
        }
        return result;
    }
};
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法与数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法与数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法与数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_51579041

你的奖励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值