LeetCode 热题 100之普通数组

news2024/12/22 22:19:47

1.最大子数组和

在这里插入图片描述
思路分析:这个问题可以通过动态规划来解决,我们可以使用Kadane’s Algorithm(卡登算法)来找到具有最大和的连续子数组。

  • Kadane’s Algorithm 的核心思想是利用一个变量存储当前的累加和 currentSum,并通过每次与之前的最大子数组和 maxSum 比较,更新 maxSum。它在遍历过程中不断累加子数组的和,但一旦累加和变为负数时,放弃当前累加,从下一个元素重新开始累加。这样确保了子数组和始终是最大的。

  • 初始化:将 maxSum 和 currentSum 初始化为数组的第一个元素,即 nums[0]

  • 遍历数组

    • 从第二个元素开始,逐个元素检查累加currentSum;
      • currentSum = max(nums[i], currentSum + nums[i]):选择当前元素或当前累加和加上当前元素两者中的较大值。
      • 如果 currentSum 大于 maxSum,则更新 maxSum。
  • 返回结果:遍历完成后,maxSum 即为最大子数组的和。

具体实现代码(详解版):

class Solution {
public:
    int maxSubArray(std::vector<int>& nums) {
        int maxSum = nums[0];       // 初始化 maxSum 为数组第一个元素,用于存储最大子数组的和
        int currentSum = nums[0];    // 初始化 currentSum 为数组第一个元素,用于当前子数组的累加和
        
        // 遍历数组,从第二个元素开始(因为第一个元素已经初始化了)
        for (int i = 1; i < nums.size(); i++) {
            // 更新 currentSum,将当前元素和 currentSum + 当前元素 取较大值
            // 如果 currentSum + nums[i] 比 nums[i] 小,说明重新开始一个新的子数组会更大
            currentSum = max(nums[i], currentSum + nums[i]);

            // 更新 maxSum,存储最大子数组的和
            maxSum = max(maxSum, currentSum);
        }
        
        // 返回最大子数组的和
        return maxSum;
    }
};

2.合并区间

在这里插入图片描述
思路分析1(Acwing板子)
算法基础课-区间合并

具体实现代码(详解版):

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> res;  // 用于存放合并后的区间结果
        sort(intervals.begin(), intervals.end());  // 按区间起点升序排序

        // 初始化st和ed为一个极小的值,用于在第一次遍历时识别有效区间
        int st = -2e9, ed = -2e9;

        // 遍历所有区间
        for (int i = 0; i < intervals.size(); i++) {
            // 如果当前区间的起点大于当前的结束位置,则无重叠
            if (ed < intervals[i][0]) {
                // 如果是有效区间,将前一个区间加入结果中
                if (st != -2e9) res.push_back({st, ed});
                // 更新st和ed为当前区间的起点和终点
                st = intervals[i][0];
                ed = intervals[i][1];
            } else {
                // 当前区间与前一个区间重叠,合并区间
                ed = max(ed, intervals[i][1]);
            }
        }
        // 添加最后一个区间到结果中
        if (st != -2e9) res.push_back({st, ed});

        return res;
    }
};

思路分析2:直接遍历区间数组,判断是否有重叠。

具体实现代码(详解版):

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        // 按区间的起始位置排序
        sort(intervals.begin(), intervals.end());
        
        // 用于存放合并后的结果
        vector<vector<int>> res;
        
        // 遍历区间数组
        for (const auto& interval : intervals) {
            // 如果结果数组为空或当前区间不与最后一个区间重叠,直接加入
            if (res.empty() || res.back()[1] < interval[0]) {
                res.push_back(interval);
            } else {
                // 否则,有重叠,更新最后一个区间的终点
                res.back()[1] = max(res.back()[1], interval[1]);
            }
        }
        
        return res;
    }
};

3.轮转数组

在这里插入图片描述
思路分析1:将后面k个元素和前面n-k个元素添加到res中即可。要注意k = k % n

具体实现代码(详解版):

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;  // 如果 k 大于数组长度,取余数来简化操作
        vector<int> res;

        // 将最后 k 个元素加入结果中
        for (int i = n - k; i < n; ++i) 
            res.push_back(nums[i]);

        // 然后把前 n - k 个元素加在结果后面
        for (int i = 0; i < n - k; ++i) 
            res.push_back(nums[i]);

        // 将旋转后的结果更新到原数组
        nums = res;
    }
};

思路分析2:直接进行反转,先整体反转,再将将前 k 个元素反转,再将剩余的 n-k 个元素反转
具体实现代码(详解版):

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;  // 计算实际旋转步数,防止 k 超出数组长度
        reverse(nums.begin(), nums.end());  // 1. 反转整个数组
        reverse(nums.begin(), nums.begin() + k);  // 2. 反转前 k 个元素
        reverse(nums.begin() + k, nums.end());  // 3. 反转剩余的 n-k 个元素
    }
};

在这里插入图片描述
很明显第二种方法更优!太快了!

4.除自身以外数组的乘积

在这里插入图片描述
思路分析:使用两个数组:一个数组存储每个元素左侧所有元素的乘积,另一个数组存储每个元素右侧所有元素的乘积。通过将这两个数组的对应元素相乘,得到每个位置的最终结果。

  • 初始化answer 数组初始化为 1,因为乘法的单位是 1
  • 左侧乘积
    • 我们使用一个变量 left_product 来存储当前元素左侧的乘积。
    • 对于每个元素 nums[i],我们将 left_product 的值赋给 answer[i],这表示在位置 i 的左侧乘积
    • 然后,更新 left_product,将 nums[i] 的值乘入,准备计算下一个位置。
  • 右侧乘积:我们继续使用之前的 answer 数组,它现在包含了每个元素左侧的乘积。接下来,我们将计算每个元素右侧的乘积。
    • 使用一个变量 right_product 来存储当前元素右侧的乘积,并将其初始化为 1。
    • 反向遍历
      • 对于每个元素 nums[i],将 right_product 的值乘入 answer[i],这样 answer[i] 就等于左侧乘积乘以右侧乘积
      • 更新 right_product,将 nums[i] 的值乘入,准备计算下一个位置。

具体实现代码(详解版):

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> answer(n, 1); // 初始化结果数组为 1

        // 1. 计算左侧的乘积
        int left_product = 1; // 初始化左侧乘积
        for (int i = 0; i < n; i++) {
            answer[i] = left_product; // 设置 answer[i] 为左侧乘积
            left_product *= nums[i];   // 更新左侧乘积
        }

        // 2. 计算右侧的乘积并更新结果
        int right_product = 1; // 初始化右侧乘积
        for (int i = n - 1; i >= 0; i--) {
            answer[i] *= right_product; // 将右侧乘积乘以之前的结果
            right_product *= nums[i];    // 更新右侧乘积
        }

        return answer; // 返回最终结果
    }
};

另一种写法:

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> answer(n, 1); // 初始化结果数组

        // 计算左侧乘积
        for (int i = 1; i < n; ++i) {
            answer[i] = answer[i - 1] * nums[i - 1];
        }

        // 计算右侧乘积并更新 answer
        int right_product = 1;
        for (int i = n - 1; i >= 0; --i) {
            answer[i] *= right_product; // 将右侧乘积乘入结果
            right_product *= nums[i];   // 更新右侧乘积
        }

        return answer;
    }
};

还有这种双指针的做法,也是大为简洁!值得借鉴!

std::vector<int> productExceptSelf2(std::vector<int>& nums) {
    std::vector<int> answer(nums.size(), 1); // 初始化输出数组
    int left = 0, right = nums.size() - 1; // 指针初始化
    int lp = 1, rp = 1; // 左侧乘积和右侧乘积

    // 双指针法
    while (right >= 0 && left < nums.size()) {
        answer[right] *= rp; // 更新右侧元素的乘积
        answer[left] *= lp; // 更新左侧元素的乘积
        lp *= nums[left++]; // 更新左侧乘积
        rp *= nums[right--]; // 更新右侧乘积
    }
    return answer; // 返回结果
}

5.缺失的第一个正数

在这里插入图片描述
思路分析:要在时间复杂度O(n) 和常数级空间复杂度下找到未排序数组 nums 中最小的缺失正整数,我们可以采用原地哈希(或称为置换排序)的方式。具体思路如下:

  • 目标:我们的目标是将每个正整数 x 放置在下标 x - 1 的位置上(例如,数字 1 应该放在下标 0 的位置,数字 2 应该放在下标 1,依此类推)。这样,如果数组中有数字 1, 2, …, n,它们都会出现在各自对应的位置上。
  • 交换过程
    • 遍历数组,如果nums[i]在[1,n]范围内,并且nums[i] != nums[nums[i] - 1],则将nums[i]放到其正确的位置num[i] -1。通过交换的方式来调整位置。
    • 不断重复交换,直至当前位置的数字被放到了正确的位置为止。此过程会在 O ( n ) O(n) O(n)时间内完成,因为每个元素最多只会被交换一次
  • 查找缺失的正整数
    • 再次遍历数组,找到第一个位置i,使得nums[i] != i + 1,此时i + 1就是我们要找的最小的缺失正整数
    • 如果所有位置都满足nums[i] == i + 1,那么数组中的所有正整数[1,n]都出现过,即最小缺失正整数为n + 1.

一个结论:不论数组 nums 是什么内容,数组长度为 N 时,最小缺失的正整数一定落在 [1, N+1]
这个范围内。这就是为什么算法中只需要关注 [1, N+1],而无需遍历其他更大的数字的原因。

具体实现代码(详解版):

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        //将每个数放到对应的位置
        for(int i = 0 ; i < n ; i ++){
            while(nums[i] > 0 && nums[i] <= n
             && nums[i] != nums[nums[i] - 1] ){
                swap(nums[i],nums[nums[i] - 1]);
             }
        }
        //查找第一个位置i,使得nums[i] != i + 1
        for(int i = 0; i < n ; i ++){
            if(nums[i] != i + 1){
                return i + 1;
            }
        }
        //如果所有位置都正确,则返回 n + 1
        return n + 1;
    }
};

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

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

相关文章

Prometheus自定义PostgreSQL监控指标

本文我们将介绍如何在Prometheus中创建自定义PostgreSQL指标。默认情况下由postgres_export运行的查询可能不能满足用户需求&#xff0c;但我们可以创建自定义查询&#xff0c;并要求postgres_exporter公开自定义查询的结果。postgres_exporter最近被移到了Prometheus Communit…

acwing排列数字

排列数字 给定一个整数 n&#xff0c;将数字 1∼n排成一排&#xff0c;将会有很多种排列方法。 现在&#xff0c;请你按照字典序将所有的排列方法输出。 输入格式 共一行&#xff0c;包含一个整数 n。 输出格式 按字典序输出所有排列方案&#xff0c;每个方案占一行。 数…

lvs知识点归纳

LVS&#xff08;Linux Virtual Server&#xff09;是 Linux 内核的一种负载均衡技术&#xff0c;主要用于实现高可用性和高性能的服务器集群。以下是一些关键知识点的归纳&#xff1a; 基本概念 虚拟服务器&#xff1a;将多台物理服务器&#xff08;真实服务器&#xff09;抽象…

论文速读 - Cleaner Pretraining Corpus Curation with Neural Web Scraping

这是论文 Cleaner Pretraining Corpus Curation with Neural Web Scraping 的速读笔记&#xff0c;同时简要分析这篇论文作者的实现代码. 论文的主要工作是提出了基于神经网络的高效crawler. 这里先澄清scraper和crawler的区别&#xff0c;一图胜千言. Abstract The web conta…

openpnp - bug - 散料飞达至少定义2个物料

文章目录 openpnp - bug - 散料飞达至少定义2个物料笔记END openpnp - bug - 散料飞达至少定义2个物料 笔记 散料飞达上定义的物料个数用完了&#xff0c;现在只需要一个料就可以。 用顶部相机去找编带上是否还有一个单独的料&#xff0c;找到了。 定义散料飞达的料为1个&…

springboot使用attachment方式下载文件损坏问题解决

文章目录 场景解决方式全部代码 场景 之前使用springboot下载文件一直正常&#xff0c;今天新对接一个接口出现文件破损&#xff0c;无法下载。 之前的代码: Overridepublic ResponseEntity<ByteArrayResource> resultExcel(ExcelResultDTO excelResultDTO) {log.info(…

CentOS7系统内核升级

1. 安装新内核 采用离线方式升级 去到下面网站中下载rpm安装包 https://mirrors.coreix.net/elrepo-archive-archive/kernel/el7/x86_64/RPMS/下载 wget https://mirrors.coreix.net/elrepo-archive-archive/kernel/el7/x86_64/RPMS/kernel-lt-5.4.278-1.el7.elrepo.x86_64…

探寻闲鱼libsgmain加解密算法(4) ——JNI入口跳转

关注我的人都知道我一直在学习阿里的加密和算法&#xff0c;除了研究逆向问题&#xff0c;还会把学来的阿里技术用在自己的应用上。 为什么&#xff1f;因为学习大厂的应用&#xff0c;是进步最快的方法。而大厂在安全和加密方面的技术&#xff0c;个人觉得阿里做的是最好的。 …

Maven项目管理工具-初始+环境配置

1. Maven的概念 1.1. 什么是Maven Maven是跨平台的项目管理工具。主要服务于基于Java平台的项目构建&#xff0c;依赖管理和项目信息管理。 理想的项目构建&#xff1a;高度自动化&#xff0c;跨平台&#xff0c;可重用的组件&#xff0c;标准化的流程 maven能够自动下载依…

Maven 不同环境灵活构建

需求: 使用 Maven根据不同的构建环境&#xff08;如开发、测试、生产&#xff09;来定义不同的配置&#xff0c;实现灵活的构建管理。 需要Demo项目的可以参考&#xff1a;我的demo项目 一、项目分层 一般的初创项目不会有特别多的配置文件&#xff0c;所以使用 spring.profile…

Android调用系统相机录像并设置参数

最近要做一个 Android上的录像功能&#xff0c;由于之前做拍照功能完全是用自定义方式&#xff0c;太麻烦。故这次决定直接调用系统相机来录像。 一、添加权限 首先&#xff0c;添加必要的权限 <!-- 授予该程序使用摄像头的权限 --><uses-permission android:name&q…

K8s中TSL证书如何续期

TSL是什么 K8s中的作用是什么&#xff1f; 在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;TSL 指的是 Transport Layer Security&#xff0c;也就是传输层安全协议。它是用来保护在网络上传输的数据的安全性和隐私性。 TSL 在 Kubernetes 中的作用包括&#xff1a;…

B+树(B树的改进)

目录 一、什么是B树&#xff1f; 二、B树的性质 1.B树被广泛作为数据库索引的索引结构 2.m个分支的结点有m个元素 3.每个元素对应子结点最大值 4.多级索引结构 5.叶子结点层包含所有元素 三、B树和B树的区别 四、B树的查找 1.顺序查找 2.随机查找 3.范围查找 一、什…

JVM机制

文章目录 JVM 简介JVM内存划分堆&#xff08;线程共享&#xff09;Java虚拟机栈&#xff08;线程私有&#xff09;本地方法栈&#xff08;线程私有&#xff09;程序计数器&#xff08;线程私有&#xff09;方法区&#xff08;线程共享&#xff09; JVM类加载机制类加载过程双亲…

校园表白墙源码修复版

此校园表白墙源码基于thinkphp&#xff0c;因为时代久远有不少bug&#xff0c;经本人修复已去除大部分bug&#xff0c;添加了美化元素。 https://pan.quark.cn/s/1f9b3564c84b https://pan.baidu.com/s/1bb9vu9VV2jJoo9-GF6W3xw?pwd7293 https://caiyun.139.com/m/i?2hoTc…

群控系统服务端开发模式-应用开发-业务架构逻辑开发准备工作

安装与仓库已经调整完毕&#xff0c;现在开发业务架构逻辑&#xff0c;其次再开发功能逻辑。业务架构逻辑开发与功能逻辑开发不是一回事&#xff0c;一定要明白。业务架构指的是做某一件事或是某一种类型的事的逻辑&#xff0c;在互联网web应用中通常指一套系统的外在逻辑&…

js 的宏任务和微任务

宏任务 (macro-task) 与微任务 (micro-task) 在 JavaScript 中&#xff0c;宏任务&#xff08;macro-task&#xff09;和微任务&#xff08;micro-task&#xff09;是任务队列&#xff08;task queue&#xff09;中两个不同的任务类型&#xff0c;它们是 JavaScript 异步编程机…

DBeaver查看已保存连接的密码

打开Dbeaver窗口菜单-首选项-工作空间&#xff0c;找到工作空间路径 在文件管理器中打开工作空间路径\General.dbeaver&#xff0c;找到credentials-config.json。 在Linux下&#xff0c;使用如下命令对credentials-config.json文件进行解密 openssl aes-128-cbc -d -K babb4…

13 实战:使用Python和Pygame实现视频运动估计播放器

首先看运行效果: 在多媒体处理领域,视频的运动估计是一个重要的课题。在本文中,我们将详细介绍如何使用Python结合Pygame、OpenCV等库,实现一个支持运动估计的视频播放器。本项目旨在展示如何在Python中处理视频帧,实现块匹配算法进行运动估计,并将结果以可视化的方式呈现…

kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的顺序消费?】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的…