LeetCode 热题 100 题解:普通数组部分

文章目录

  • 题目一:最大子数组和(No. 53)
    • 题解
  • 题目二:合并区间(No. 56)
    • 题解
  • 题目三:轮转数组(No. 189)
    • 题解
  • 题目四:除自身以外数组的乘积(No. 238)
    • 题解
  • 题目五:缺失的第一个正数(No. 41)
    • 题解

题目一:最大子数组和(No. 53)

题目链接:https://leetcode.cn/problems/maximum-subarray/description/?envType=study-plan-v2&envId=top-100-liked

题目难度:中等


给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

**子数组:**是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

提示:

  • 1 <= nums.length <= 105
  • 104 <= nums[i] <= 104

题解

**前缀和:**假设有一个数组 [a1, a2, a3, a4, a5, a6]

  • 前缀和 prefix[1] 为 a1
  • 前缀和 prefix[2] 为 a1 + a2
  • 前缀和 prefix[3] 为 a1 + a2 + a3
  • 前缀和 prefix[4] 为 a1 + a2 + a3 + a4
  • 前缀和 prefix[5] 为 a1 + a2 + a3 + a4 + a5
  • 前缀和 prefix[6] 为 a1 + a2 + a3 + a4 + a5 + a6

通过前缀和,**可以很轻松的去求得一个连续子数组的和:**比如 a3 到 a6 到数组和就可以通过 prefix[6] - prefix[3] 来计算得出。

以 an(1 < n < prefix.length) 结尾的子数组的和最大的时候,就是 prefix[n] 减去 前面出现过的最小的前缀和 或者 就是这个前缀和本身,将这两个结果取一个最大值,就是以这个节点结尾的子数组最大的和。

通过上面的分析, 本题需要这几个变量:一个 prefix 数组、存储结果的 res,存储此时最小的前缀和的 minPre;当遍历到一个节点的时候,比较以下的三个变量:

  • res、prefix[i]、prefix[i] - minPre

当遍历完成后,就能得到最终的结果。

class Solution {
    public int maxSubArray(int[] nums) {
        int temp = 0;
        int[] prefix = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            temp += nums[i];
            prefix[i] = temp;
        }
        int minPre = prefix[0];
        int res = prefix[0];
        for (int i = 1; i < prefix.length; i++) {
            res = Math.max(prefix[i] - minPre, res);
            res = Math.max(prefix[i], res);
            minPre = Math.min(minPre, prefix[i]);
        }
        return res;
    }
}

题目二:合并区间(No. 56)

题目链接:https://leetcode.cn/problems/merge-intervals/?envType=study-plan-v2&envId=top-100-liked

题目难度:中等


以数组 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] 可被视为重叠区间。

提示:

  • 1 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 104

题解

一个区间由左右区间来限定,即 [left, right],要判断两个区间能否合并需要两个条件

  • left1 ≤ left2
  • right1 ≥ left2

但是这样的判定方式显然不适合解题,因为要查找一个区间能和几个区间合并的时候,每次都需要去遍历整个数组。

但如果按照 left 的大小来进行排序,就可以减少一个判断的条件,经过排序后,前后两个区间一定满足 left1 ≤ left 所以只需要去判断右边界即可

  • 当右边界大于下一个区间的左边界的时候,这两个区间就可以合并了。
  • 但如果发现右边界小于下一个区间的左边界,则区间就无法继续合并,就需要开辟一个新的区间来继续执行合并的流程。

通过上面的分析,本题需要这些变量:目前遍历区间的左边界 currentLeft,有边界 currentRight,存储结果的集合 resList。

本题的思路遍历数组,寻找下一个区间能否和本次的区间 [currentLeft, currentRight] 合并,如果可以,就更新 currentRight,否则将当前区间添加到结果数组中,然后继续遍历。

class Solution {
    public int[][] merge(int[][] intervals) {
        if (intervals.length <= 1) return intervals;
        // 收集结果的集合
        List<int[]> resList = new ArrayList<>(); 
        // 按照起点的顺序排序
        Arrays.sort(intervals, (x, y) -> Integer.compare(x[0], y[0]));
        int currentLeft = intervals[0][0];
        int currentRight = intervals[0][1];
        for (int i = 1; i < intervals.length; i++) {
            if (intervals[i][0] > currentRight) {
                // 前一个范围无法覆盖到这个内容
                resList.add(new int[]{currentLeft, currentRight});
                currentLeft = intervals[i][0];
                currentRight = intervals[i][1];
            } else {
                // 可以覆盖的情况
                currentRight = Math.max(intervals[i][1], currentRight) ;
            }
        }
        // 最后一个区间没有添加到结果中,需要额外添加一次
        resList.add(new int[]{currentLeft, currentRight});
        return resList.toArray(new int[resList.size()][]);
    }
}

题目三:轮转数组(No. 189)

题目链接:https://leetcode.cn/problems/rotate-array/description/?envType=study-plan-v2&envId=top-100-liked

题目难度:中等


给定一个整数数组 nums,将数组中的元素向右轮转 k **个位置,其中 k **是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出:[5,6,7,1,2,3,4]解释:
向右轮转 1 步:[7,1,2,3,4,5,6]
向右轮转 2 步:[6,7,1,2,3,4,5]
向右轮转 3 步:[5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

提示:

  • 1 <= nums.length <= 105
  • 231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

题解

设数组的长度为 l,那轮转 l 次,得到的结果和原数组是完全相同的,假如轮转 k 次,其有效的轮转次数其实就是 k 对 l 取余数,也就是 k % l 次。

当轮转 m 次后,最终得到的结果就是数组的 后 m 个 元素到了新数组的开头,而 0 到这个位置的元素向后移动 m 位。

在这里插入图片描述

那第一个思路就很简单了,首先创造一个新的数组,然后对于原数组先从 length - m 遍历到末尾,然后再遍历 0 到 length - m - 1 这部分的内容,将这些内容填充到新的数组中。

再将新数组中的内容转移到旧数组中即可。

class Solution {
    public void rotate(int[] nums, int k) {
        int l = nums.length;
        int realK = k % l; // 移动 l 的整数倍不会产生效果
        int[] res = new int[l];
        int newStart = nums.length - realK;
        int temp = 0;
        for (int i = newStart; i < nums.length; i++) res[temp++] = nums[i];
        for (int i = 0; i < newStart; i++) res[temp++] = nums[i];
        for (int i = 0; i < res.length; i++) nums[i] = res[i];
    }
}

第二种思路就是反转数组,比如数组 1 2 3 4 5 6 7,要轮转 3 位置,那最终需要得到的数字就是这样的:5 6 7 1 2 3 4

要达到这样的效果,只需要先将数组翻转:7 6 5 4 3 2 1

然后翻转前 k 位:5 6 7 4 3 2 1

然后翻转后面的部分:5 6 7 1 2 3 4

只需要经过三次翻转就可以得到最终的结果。

class Solution {
    public void rotate(int[] nums, int k) {
        int realK = k % nums.length;
        reverse(nums, 0, nums.length - 1);
        reverse(nums, 0, realK - 1);
        reverse(nums, realK, nums.length - 1);
    }
    public void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}

题目四:除自身以外数组的乘积(No. 238)

题目链接:https://leetcode.cn/problems/product-of-array-except-self/description/?envType=study-plan-v2&envId=top-100-liked

题目难度:中等


给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

请 **不要使用除法,**且在 O(*n*) 时间复杂度内完成此题。

示例 1:

输入: nums =[1,2,3,4]输出:[24,12,8,6]

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

提示:

  • 2 <= nums.length <= 105
  • 30 <= nums[i] <= 30
  • 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内

题解

如果不考虑 0 的情况下,本题的思路其实是比较容易的,就是先计算出整个数组的乘积,然后遍历到数组中的某个元素的时候,用这个乘积去除以这个元素,得到的结果放到 answer 数组中。

但是当出现 0 的情况就需要进行特殊的处理了,出现 0 的情况也分为两种情况

  • 仅有一个 0 的情况
  • 出现了两个及以上的 0 的情况

仅有一个零的情况也比较容易处理,比如题目中的案例二:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

其实规律也比较明显,为 0 的位置上的存储的是整个数组的乘积(除零以外),其他位置存储的全为 0。

当时当出现两个或者以上 0 的时候,结果数组中就全为 0 了。

所以本题的思路就是:先去遍历数组求出乘积(除零以外),同时统计 0 的个数,对没有 0,有一个 0 和有多个 0 的情况分别进行处理。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int zeroNum = 0;
        int mulNum = 1;
        int zeroIndex = 0; // 为 0 的位置的下标
        int[] res = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0) {
                zeroNum++;
                zeroIndex = i;
            } else mulNum *= nums[i]; // 计算乘积
        }
        if (zeroNum > 1) return res;
        else {
            if (zeroNum == 1) {
                res[zeroIndex] = mulNum;
                return res;
            } else {
                for (int i = 0; i < nums.length; i++) res[i] = mulNum / nums[i];
            }
        }
        return res;
    }
}

题目五:缺失的第一个正数(No. 41)

题目链接:https://leetcode.cn/problems/first-missing-positive/description/?envType=study-plan-v2&envId=top-100-liked

题目难度:困难


给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为

O(n)

并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。

示例 2:

输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。

示例 3:

输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

提示:

  • 1 <= nums.length <= 105
  • 231 <= nums[i] <= 231 - 1

题解

本题的基础解法还是比较好想的,先将数组进行排列;定义一个 temp 变量,从 1 开始自增,遍历数组到正数部分的时候,就去判断数字和 temp 是否相等,不想等就直接返回 temp。

其中会有一个极端的情况,假如数组的长度为 N,而数组中的元素恰好就是 1 ~ N 中的所有元素,那此时最小的正数就是 N + 1;代码中 temp 是一直自增到结束的,所以当遍历结束后,如果还没有返回的话,此时 temp 存储的就是 N + 1 的值。

class Solution {
    public int firstMissingPositive(int[] nums) {
        Arrays.sort(nums); // 排序
        int temp = 1;
        // 对下标为 0 的情况进行特殊处理
        if (nums[0] != temp && nums[0] > 0) return temp;
        else if (nums[0] == temp) temp++; 
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > 0) {
                if (nums[i] == nums[i - 1]) continue; // 避免重复元素的干扰
                if (temp != nums[i]) return temp; // 得到结果
                else temp++;
            }
        }
        return temp;
    }
}

对于一个长度为 N 的数组,其所未包含的最小的正数,最大就是 N + 1,也就是上面提到的特殊情况,结果的范围其实就是 1 ~ N + 1;第二种方法就是对原数组进行操作,将每个元素在结果范围内的元素放到其对应位置上,然后再次遍历数组,找到位置不对的元素。

比如对案例 2 中的元素来说:[3, 4, -1, 1]

按照对应的位置排序完成是这样的:[1, -1, 3, 4]

处于 nums[i] 上的元素就是 i + 1,再次遍历发现不符合这种情况的就是最小的那个正数。

这个思路非常简单,就是对范围内的元素放到对应的位置上,然后再次遍历,但实现起来还是有很多地方需要注意的。

来梳理一下交换的条件:

  1. 首先 i 要在范围内,也就是 1 ≤ i ≤ nums.length
  2. 这个位置的元素要交换到 nums[nums[i] - 1] 的位置上去,要保证这个位置的元素不与 nums[i] 相等,如果相等就无需交换(其实还有避免死循环的因素,这个后面再说)。

写出来条件就是这样的:

nums[i] >= 1 && nums[i] <= nums.length && nums[i] != nums[nums[i] - 1]

那这个条件前面跟的是什么语句,if 还是 while 呢?

如果我们使用 if,交换了一个新的元素过来,此时去继续遍历下一个节点,那这个交换过来的新的元素是有效的元素的话,那这个元素就有可能未被处理到

比如案例 2,模拟一下使用 if 的情况:

原数组:[3, 4, -1, 1]

  • 第一次交换:[-1, 4, 3, 1]
  • 第二次交换:[-1, 1, 3, 4]

遍历结束,这里的问题就出现在第二次交换上,这里的 1 未被处理就直接放在了错误的位置上。

为了避免这样的情况,上面应该使用 while 来限制。

class Solution {
    public int firstMissingPositive(int[] nums) {
       for (int i = 0; i < nums.length; i++) {
       // 排列数组
        while (nums[i] >= 1 && nums[i] <= nums.length && nums[i] != nums[nums[i] - 1]) {
            swap (nums, i, nums[i] - 1);
        }
       } 
       for (int i = 0; i < nums.length; i++) {
       // 检测哪个位置是错误的
        if (nums[i] != i + 1) return i + 1;
       }
       return nums.length + 1; // 如果都没问题,说明是特殊情况
    }
    // 交换节点的方法
    void swap(int[] nums, int index1, int index2) {
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springSecurity用户认证和授权

一&#xff0c;框架介绍 Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架&#xff0c;提供了一套 Web 应用安全性的完整解决方案。一般来说&#xff0c;Web 应用的安全性包括用户认证&#xff08;Authentication&#xff09;和用户授权&am…

zig v0.12.0 发布 — x-cmd 提供 zig 快捷安装方法和 x zig 模块

文章目录 简介功能特点v0.12.0 新特性[重新设计 Autodoc 的工作原理](https://ziglang.org/download/0.12.0/release-notes.html#Redesign-How-Autodoc-Works)语法变更各类标准库变更构建系统变更 常见用法**使用案例**:竞品和相关项目进一步阅读 简介 Zig 是一种通用编程语言…

模电期末复习(五)集成运算放大电路

集成运算放大电路 5.1 集成放大电路的特点5.2 集成运放的主要技术指标5.3 集成运放的基本组成部分5.3.1 偏置电路5.3.2 差分放大输入级5.3.3 中间级5.3.4 输出级 5.4 集成运放的典型电路5.4.1 双极型集成运放LM741 5.5 各类集成运放的性能特点5.6 集成运放使用中的几个具体问题…

JAVAEE——IP协议

文章目录 IP协议IP协议报头格式IP协议报头的各个区段四位版本四位首部长度八位服务类型16位总长度16位标识&#xff0c;3位标志&#xff0c;13位片偏移八位生存时间八位协议 地址管理IP地址解决提议1&#xff1a;动态分配Ip地址解决提议2&#xff1a;NAT机制 IP协议 IP协议报头…

基于SpringBoot+Vue钢材销售管理系统的设计与实现

系统介绍 为了更好地发挥本系统的技术优势&#xff0c;根据钢材销售管理系统的需求&#xff0c;本文尝试以B/S经典设计模式中的Spring Boot框架&#xff0c;JAVA语言为基础&#xff0c;通过必要的编码处理、钢材销售管理系统整体框架、功能服务多样化和有效性的高级经验和技术…

分类神经网络3:DenseNet模型复现

目录 DenseNet网络架构 DenseNet部分实现代码 DenseNet网络架构 论文原址&#xff1a;https://arxiv.org/pdf/1608.06993.pdf 稠密连接神经网络&#xff08;DenseNet&#xff09;实质上是ResNet的进阶模型&#xff08;了解ResNet模型请点击&#xff09;&#xff0c;二者均是…

葡萄书--关系图卷积神经网络

异质图和知识图谱 同质图与异质图 同质图指的是图中的节点类型和关系类型都仅有一种 异质图是指图中的节点类型或关系类型多于一种 知识图谱 知识图谱包含实体和实体之间的关系&#xff0c;并以三元组的形式存储&#xff08;<头实体, 关系, 尾实体>&#xff0c;即异…

vlan的学习笔记1

vlan&#xff1a; 1.一般情况下:以下概念意思等同: 一个vlan一个广播域 一个网段 一个子网 2.一般情况下: &#xff08;1&#xff09;相同vlan之间可以直接通信&#xff0c;不同vlan之间不能直接通信! &#xff08;2&#xff09;vlan技术属于二层技术&…

三、Flask模型基础

ORM 创建模型 # exts.py:插件管理 # 扩展的第三方插件 # 1.导入第三方插件 from flask_sqlalchemy import SQLAlchemy # ORM插件 from flask_migrate import Migrate # 2. 初始化 db = SQLAlchemy() # ORM migrate = Migrate() # 数据迁移 # 3. 和app对象绑定 def init_ex…

基于Bootstrap 4的企业项目体验服务网站模板

目录 一.前言 二.展示 三.下载链接 一.前言 网页包含以下内容&#xff1a; 页面基本信息&#xff1a;设置页面的字符集、兼容性、视口等元数据。 网站标题和描述&#xff1a;定义了网站的标题为"Benten"&#xff0c;同时也设置了关键词和描述。 CSS样式表链接&a…

基于SpringBoot+Vue七匹狼商城系统的设计与实现

系统介绍 近年来随着社会科技的不断发展&#xff0c;人们的生活方方面面进入了信息化时代。计算机的普及&#xff0c;使得我们的生活更加丰富多彩&#xff0c;越来越多的人使用通过网络来购买各类的商品。早期商品的销售和购买都是通过实体店&#xff0c;这种购买方式需要耗费…

《苍穹外卖》Day03部分知识点记录

一、公共字段自动填充 业务表中的公共字段&#xff1a; 序号字段名含义数据类型操作类型1create_time创建时间datetimeinsert2create_user创建人idbigint3update_time修改时间datetimeinsert、update4update_user修改人idbigint 问题&#xff1a;代码冗余&#xff0c;不便于…

const成员函数 以及 取地址及const取地址操作符重载

目录 const成员函数 结论&#xff1a; 取地址及const取地址操作符重载 const成员函数 将const 修饰的 “ 成员函数 ” 称之为 const成员函数 &#xff0c; const 修饰类成员函数&#xff0c;实际修饰该成员函数的&#xff08;*this&#xff09; &#xff0c;表明在该成员函数…

ROS2 王牌升级:Fast-DDS 性能直接碾压 zeroMQ 「下」

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/aU1l3HV3a9YnwNtC1mTiOA 性能比较 下面就以官网的测试数据为准&#xff0c;让我们一起来看看它们的性能差别到底怎样。 本次比较仅针对 Fast RT…

AI安全之问:我们的智能助手真的安全吗?

在我们日益依赖人工智能来撰写文档、编写程序代码、甚至创作艺术作品的今天&#xff0c;我们是否曾经想过这些智能系统可能面临的被恶意操纵的风险&#xff1f; 分享几个网站 GPT-3.5研究测试&#xff1a; https://hujiaoai.cn GPT-4研究测试&#xff1a; https://higpt4.cn…

2024数学建模时间汇总与竞赛攻略

目录 2024数学建模汇总&#xff08;时间、报名费、获奖率、竞赛级别、是否可跨校&#xff09; 中国高校大数据挑战赛 “华数杯”国际大学生数学建模竞赛 美国大学生数学建模竞赛&#xff08;美赛&#xff09; 数学中国&#xff08;认证杯&#xff09;数学建模网络挑战赛 …

Python基础04-操作系统中的文件与目录操作

在与操作系统交互时&#xff0c;我们经常需要执行文件和目录的操作。Python提供了丰富的库来帮助我们完成这些任务。以下是一些常见的操作&#xff0c;以及如何使用Python代码来实现它们。 1. 导航文件路径 在不同的操作系统中&#xff0c;文件路径的格式可能不同。Python的o…

OpenUI在windows下部署使用

OpenUI OpenUI是一个基于Python的AI对话平台&#xff0c;支持接入多种AI模型。 通过聊天的方式来进行UI设计&#xff0c;你可以通过文字来描述你想要的UI界面&#xff0c;OpenUI可以帮你实时进行渲染出效果 安装OpenUI 这里预设你的电脑上已安装git、Python和pip&#xff0…

2023年美国西岸行 - Day 1

本来旅行回来就像记录的&#xff0c;结果一拖就是快1年。当然中间原先记录的博客平台关闭也有部分原因&#xff0c;但不是主要原因。趁着今天复活20年前的CSDN博客&#xff0c;赶紧记录一下。 疫情之后第一次全家长途旅行。这次美国之行&#xff0c;在3-4月份订机票的时候跟临…

Pages by User Role for WordPress:强化内容访问控制的必备插件

在数字化时代&#xff0c;WordPress已成为众多网站开发者和设计师的首选平台。然而&#xff0c;如何根据用户角色精确控制内容的访问权限&#xff0c;一直是困扰他们的难题。Pages by User Role for WordPress插件应运而生&#xff0c;为这一难题提供了完美的解决方案。 Pages …