【贪心算法指针】C++ 解决子数组 / 子序列的相关问题(最大数、数组和减半的最小操作数、连续/递增序列)

news2024/9/20 0:11:02

文章目录

  • 1. 前言
    • 1.1 贪心算法介绍
  • 2. 算法题
    • 2.1_将数组和减半的最少操作次数
    • 2.2_最大数
    • 2.3_最长递增子序列
    • 2.4_递增的三元子序列
    • 2.5_最长连续递增序列
    • 2.6_数组中的最长连续子序列
    • 2.7_在字符串中找出连续最长的数字串

1. 前言

1.1 贪心算法介绍

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法。贪心算法通常用来解决最优化问题,其核心思想是通过局部最优解逐步推导出全局最优解。

在贪心算法中,我们并不总是考虑到未来可能发生的情况,而是只关注当前的最优选择。这种贪心选择性质使得贪心算法特别适合解决那些具有最优子结构性质的问题,即局部最优解能够推导出全局最优解的问题。

贪心算法的基本思路可以总结为以下几步:

  1. 确定问题的最优子结构:问题的最优解可以通过子问题的最优解逐步推导得到。
  2. 构造贪心选择:在每一步都做出当前状态下的最优选择,即局部最优解。
  3. 证明贪心选择性质:证明每一步的贪心选择都是最优的,能够推导出全局最优解。

需要注意的是,贪心算法并不适用于所有的问题,因为并非所有问题都具有最优子结构性质。在某些情况下,贪心算法得到的结果可能并不是全局最优解,而只是一个较好的解。因此,在应用贪心算法时,需要仔细分析问题的特性,以确定贪心算法是否适用于该问题。

下面我们会解决一些 数组相关的算法题(子数组、子序列类问题)


2. 算法题

2.1_将数组和减半的最少操作次数

在这里插入图片描述

  • 题意分析:题目要求将数组nums的数组和至少减少一半的最少操作数
  • 思路分析:要求使数组和减少一半的最少操作,自然我们可以每次对最大的数进行减半操作(贪心),如何进行?—— 利用堆
    • 此时就有了思路:将数组元素加入到堆heap中,统计数组和的一半sum后,通过一个循环每次提取堆顶元素进行减半,直到sum<=0

代码:

class Solution {
public:
    int halveArray(vector<int>& nums) {
        priority_queue<double> heap;
        // 统计sum
        double sum = 0.0;
        for(auto x : nums)
        {
            heap.push(x);
            sum += x; 
        }
        sum /= 2.0;

        // 统计最少操作次数
        int count = 0;
        while(sum > 0)
        {
            double tmp = heap.top() / 2;
            heap.pop();
            sum -= tmp;
            ++count;

            heap.push(tmp);
        }

        return count;
    }
};

2.2_最大数

在这里插入图片描述

  • 题意分析:题目要求将给定的一组整数重排使其最大
  • 思路分析:要得到最大的重排结果,自然会想到将更大的数放到前面(贪心),这里如何排列顺序就是重点
    • 思路:
      • 将所给的数转为字符串,便于设定规则比较
      • 由于字符串是根据字典序比较的,“23” > “221”,排到前面,是和我们想要的规则一致的,则直接对字符串数组进行排序(根据两个元素的先后顺序大小)
      • 最后提取结果即可(注意全0的情况)

代码:

class Solution {
public:
    string largestNumber(vector<int>& nums) {
        // 将数字转换为字符串
        vector<string> strs;
        for(auto x : nums) strs.push_back(to_string(x));
        // 根据字符串排序
        // s1s2 > s2s1 则 s1s2在前,s2s1在后
        sort(strs.begin(), strs.end(), [&](const string& s1, const string& s2){
            return s1 + s2 > s2 + s1;
        });

        // 提取结果
        string ret;
        for(auto s : strs)
            ret += s;
        
        // 判断特殊情况:000000...->0
        if(ret[0] == '0') return "0";
        return ret;
    }
};

2.3_最长递增子序列

在这里插入图片描述

  • 题意分析:题目要找到数组的最长递增子序列的长度
  • 思路分析:首先对于这道题,是可以利用动态规划解题的;而对于贪心,我们可以用一个数组存放长度为n时的最后一位元素是什么,具体规则在下图:
    • 思路:
      在这里插入图片描述
    • 找ret中大于nums[i]的最小值的过程可以用二分查找进行优化

代码:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        // 贪心
        vector<int> ret;
        ret.push_back(nums[0]);

        for(int i = 0; i < nums.size(); ++i)
        {
            if(nums[i] > ret.back()) {
                ret.push_back(nums[i]);
            } else { // 二分查找
                int left = 0, right = ret.size() - 1;
                while(left < right)
                {
                    int mid = (left + right) >> 1;
                    if(ret[mid] < nums[i])
                        left = mid + 1;
                    else
                        right = mid;
                }
                ret[left] = nums[i]; // 插入
            }
        }

        return ret.size();
    }
};

2.4_递增的三元子序列

在这里插入图片描述

  • 题意分析:题目要求找是否存在长度为3的递增子序列,实际上算是前面递增子序列的变体。
  • 思路分析:同理于上一题,首先可以使用动态规划解决,每次计算判断是否dp[i]为3即可,对于贪心,我们依然采取一样的策略,但可以进行优化
    • 思路:
    • 用变量a表示长度为1时的元素,变量b表示长度为2 的最后一位元素,遍历数组,只要存在一个nums[i] > b,就证明有长度为3的递增子序列

代码:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int a = nums[0], b = INT_MAX;

        for(auto x : nums)
        {
            if(x > b) return true;
            else if (x > a) b = x;
            else a = x;
        }

        return false;
    }
};

2.5_最长连续递增序列

在这里插入图片描述

  • 题意分析:读题后发现,该题实际上就是找最长的递增子数组(子数组和子序列的区别就在于是否连续)
  • 思路分析:对于连续子数组,可以直接利用双指针进行解题:
    • 思路:
    • 创建两指针left、right
    • 遍历数组,如果出现递增则right++,否则就更新结果并将left移动到right位置,继续寻找;

代码:

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        vector<int> tmp(nums.begin(), nums.end());
        tmp.push_back(INT_MIN); // 用于简化特殊处理

        int ret = 0;
        int i = 0, j = 1; // 即left 于 right
        while (i < tmp.size() && j < tmp.size()) {
            if (tmp[j - 1] < tmp[j]) {
                ++j;
            } else {
                // 更新结果 + 移动 i 指针
                ret = max(ret, j - i);
                i = j; // 将 i 指针移到 j 的位置继续寻找连续递增子序列
                ++j; // 同时移动 j 指针
            }
        }

        return ret;
    }
};

2.6_数组中的最长连续子序列

在这里插入图片描述

在这里插入图片描述

  • 题意分析:题目要求找到连续子序列,注意这里的连续要求的是值连续,位置不用连续(所以可以先进行排序、排序后本质就变成了子数组问题)
  • 思路分析:这道题可以使用哈希表;这里我们先排序、随后遍历数组,利用两个变量(curLen、maxLen)解题。
    • 思路:
    • 创建变量curLen(当前连续子序列的长度)、maxLen(连续子序列的最大长度)
    • 遍历数组,i指针用于遍历数组,当出现连续,则cur++,不连续就更新结果值max,并重置cur
    • (本质是和双指针一样的在这里插入图片描述

代码:

int MLS(vector<int>& arr) {
    sort(arr.begin(), arr.end());
    int curLen = 1, maxLen = 1;
    for (int i = 1; i < arr.size(); ++i)
    {
        if (arr[i - 1] != arr[i]) // 不等于前一元素
        {
            if (arr[i - 1] + 1 == arr[i]) { // 连续
                curLen++;
            }
            else { // 不连续
                maxLen = max(maxLen, curLen);
                curLen = 1;
            }
        }
    }

    return max(curLen, maxLen);
}

2.7_在字符串中找出连续最长的数字串

在这里插入图片描述

  • 题意分析:题目要求找到字符串中的最长连续数字串,即最长的数字子数组
  • 思路分析:自然我们可以使用双指针来解题:
    • 思路:
    • i指针负责遍历数组,一直++,直到遇到数字串时,将j数组移动到i的位置用来遍历该数字串,走出数字串后更新结果
    • 需要注意的是该题会存在多个长度一样的数字串,所以需要用一个string数组来存储结果。

代码:

int findLongestNumSub() {
    string s;
    while (cin >> s) {
        int maxLen = 0; // 最长数字串的长度
        vector<string> substrings; // 存储最长数字串
        int i = 0, j = 0;
        while (i < s.size()) {
            while (i < s.size() && !(s[i] >= '0' && s[i] <= '9')) {
                ++i;
            }

            j = i;
            while (j < s.size() && s[j] >= '0' && s[j] <= '9') {
                ++j;
            }
            if (j - i > maxLen) {
                maxLen = j - i;
                substrings.clear();
                substrings.push_back(s.substr(i, maxLen));
            }
            else if (j - i == maxLen) {
                substrings.push_back(s.substr(i, maxLen));
            }

            i = j; // 移出数字串
        }

        for (const auto& sub : substrings) {
            cout << sub;
        }
        cout << "," << maxLen;
        cout << endl;
    } // end while

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1702788.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

民国漫画杂志《时代漫画》第27期.PDF

时代漫画27.PDF: https://url03.ctfile.com/f/1779803-1248635258-b6a842?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

Thingsboard规则链:Message Type Filter节点详解

一、Message Type Filter节点概述 二、具体作用 三、使用教程 四、源码浅析 五、应用场景与案例 智能家居自动化 工业设备监控 智慧城市基础设施管理 六、结语 在物联网&#xff08;IoT&#xff09;领域&#xff0c;数据处理与自动化流程的实现是构建智能系统的关键。作…

我的创作纪念日——我与CSDN一起走过的128天

目录 一、机缘&#xff1a;旅程的开始 二、收获&#xff1a;沿路的花朵 三、日常&#xff1a;不断前行中 四、成就&#xff1a;一点小确幸 五、憧憬&#xff1a;梦中的重点 一、机缘&#xff1a;旅程的开始 最开始开始写博客是在今年一二月份的时候&#xff0c;也就是寒假…

(2024,DDDM,ODE,少量步生成,迭代生成)直接去噪扩散模型

Directly Denoising Diffusion Model 公众号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 3. 直接去噪扩散模型 3.1. 迭代求解 4. Psuedo-LPIPS 指标 5. 实验 7. 讨论和局限性 0. 摘…

HTTP方法、状态码和请求过程

一、HTTP方法概念&#xff1a; HTTP客户端发出请求&#xff0c;告知服务端需要执行不同类型的请求命令&#xff0c;这些命令被称为HTTP方法。 简说:HTTP方法是告诉服务器要做什么。 1、GET方法&#xff1a;获取资源 作用&#xff1a; ①通常用于请求服务器发送某个资源&am…

微服务项目收获和总结---第5天(定时发布)

延迟任务 目录 延迟任务技术对比&#xff1a; Redis实现定时任务&#xff1a;​编辑新增任务&#xff1a;取消任务&#xff1a;拉取任务&#xff1a;Zset定时刷新数据到List中&#xff1a;分布式锁实现定时任务只刷新一次&#xff1a; 技术对比&#xff1a; Redis实现定时任…

[Windows] GIF动画、动图制作神器 ScreenToGif(免费)

ScreenToGif 是开源免费的 Gif 动画录制工具&#xff0c;小巧原生单文件&#xff0c;功能很实用。它有录制屏幕、录制摄像头、录制画板、图像编辑器等功能&#xff0c;可以将屏幕任何区域及操作过程录制成 GIF 格式的动态图像。保存前还可对 GIF 图像编辑优化&#xff0c;支持自…

七年之痒!一个 PHP 程序员职业生涯的自述

大家好&#xff0c;我是码农先森。 今年刚好是我毕业的第七个年头&#xff0c;在婚姻感情当中都有一种「七年之痒」的说法&#xff0c;这次我把这个词「七年之痒」用一次在我的职业生涯复盘上。七年前我从告别校园&#xff0c;踏入互联网编程行业&#xff0c;七年后我依旧在编…

多线程笔记

1. run() VS start() run()方法&#xff1a; run()方法是java.lang.Runnable接口中定义的一个方法。当一个类实现了Runnable接口&#xff0c;并创建了一个线程对象时&#xff0c;你需要覆盖run()方法来定义线程要执行的任务。run()方法定义了线程的主体逻辑&#xff0c;当线程…

变异系数法

前言 变异系数法是一种根据统计学方法计算系统各指标变化程度的客观赋权法&#xff0c; 变异系数法在金融行业主要应用于风险评估、资产配置和绩效评价。 变异系数法是通过计算数据中包含的信息来确定各指标的权重。该方法认为&#xff0c;变化差异较大的指标应该被赋予较大的…

消息回复及时,客户不流失!这个微信自动回复设置快快码住!

你是不是也遇到过由于回复不及时&#xff0c;导致客户流失的情况发生&#xff1f;或是好友申请太多&#xff0c;来不及通过&#xff1f; 别担心&#xff0c;试试个微管理系统&#xff0c;让你实现自动回复&#xff0c;提高回复效率&#xff01; 1、自动通过好友 当有新的好友…

Qt QScript 之 C++/JavaScript相互调用

文章目录 Qt Script什么是ECMAScriptQt 中JavaScriptclass 详解Basic UsageQObject对脚本引擎可用使用信号槽connect 三种模式访问属性, 子对象使c++对象可用于用Qt Script编写的脚本C++ 类成员函数可用于脚本C++ 类属性可用于脚本对脚本中的c++对象信号的反应函数对象和本机函…

Day37 代码随想录打卡|二叉树篇---对称二叉树

题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 方法&#xff1a;本体可以用递归和迭代两种方法&#xff0c;但我更喜欢迭代的方式&#xff0c;因此使用迭代的方式做一下。首先我们分析一下不对称的情况。因为对称的情况很简单&#xff0c;即两…

新购入的读码器该如何测试呢?

物联网技术的飞速发展&#xff0c;条码二维码作为一种高效、便捷的数据传输方式&#xff0c;已经广泛应用于仓储、物流配送、零售与结算、MES系统等生活和工业领域。新购的条码二维码读码器&#xff0c;在使用前要了解它的使用方法和性能&#xff0c;以确保其性能稳定、读取准确…

【练手项目】基于STM32的智能空调系统

项目设计说明&#xff1a; 所用到的知识点&#xff1a; GPIO、串口通信、 定时器、ADC采样、 LCD显示屏、 DHT11的通信协议。 功能概述&#xff1a; LCD显示屏&#xff1a;开机显示开启界面&#xff0c;设备自检成功后显示温湿度&#xff0c; 风机开关情况 &#xff0c;制冷片…

python数据处理与分析入门-Pandas数据可视化例子

相关内容 Matplotlib可视化练习 Pandas 数据可视化总结 柱状图 reviews[points].value_counts().sort_index().plot.bar()散点图 reviews[reviews[price] < 100].sample(100).plot.scatter(xprice, ypoints)蜂窝图 reviews[reviews[price] < 100].plot.hexbin(xprice…

Day08:CSS 高级

目标&#xff1a;掌握定位的作用及特点&#xff1b;掌握 CSS 高级技巧 一、定位 作用&#xff1a;灵活的改变盒子在网页中的位置 实现&#xff1a; 1.定位模式&#xff1a;position 2.边偏移&#xff1a;设置盒子的位置 leftrighttopbottom 水平方向偏移&#xff1a;left、…

图论(四)—最短路问题(Dijkstra)

一、最短路 概念&#xff1a;从某个点 A 到另一个点B的最短距离&#xff08;或路径&#xff09;。从点 A 到 B 可能有多条路线&#xff0c;多种距离&#xff0c;求其中最短的距离和相应路径。 最短路径分类&#xff1a; 单源最短路&#xff1a;图中的一个点到其余各点的最短路径…

成功案例(IF=7.4)| 代谢组+16s联合分析助力房颤代谢重构的潜在机制研究

研究背景 心房颤动&#xff08;AF&#xff09;是临床上最常见的持续性心律失常&#xff0c;具有显著的发病率和死亡率。高龄是房颤发病率、患病率和进展最显著的危险因素。与年龄在50-59岁之间的参与者相比&#xff0c;80-89岁之间的参与者患房颤的风险增加了9.33倍。目前尚不…

【第4章】SpringBoot整合Lombok

文章目录 前言一、准备1. 安装插件2. 引入库 二、使用1.实体类2.测试类3. 输出 总结 前言 Project Lombok是一个java库&#xff0c;它可以自动插入编辑器和构建工具&#xff0c;为您的java程序锦上添花。 再也不要写另一个getter或equals方法了&#xff0c;只要有一个注释&…