hot100 -- 普通数组

目录

🎂最大子数组和

O(n) 暴力

O(n) 动态规划

🚩合并区间

O(nlogn) 排序

🌼轮转数组

O(n)  辅助数组

O(n)  环状替换

O(n)  数组翻转

🌼除自身以外数组的乘积

O(n)  前缀和

时间O(n)  空间O(1)

🌙缺失的第一个正数

O(n)  哈希

O(n)  哈希 + 空间优化(标记)

O(n)  置换


🎂最大子数组和

53. 最大子数组和 - 力扣(LeetCode)

O(n) 暴力

边加边判断,是否可以更新当前子数组和 && 最大值

class Solution {
public:int maxSubArray(vector<int>& nums) {int n = nums.size();int Max = -1e4, sum = 0;for (int i = 0; i < n; ++i) {sum += nums[i];Max = max(Max, sum);if (sum < nums[i]) { // 子数组和 < 当前值sum = nums[i]; // 更新子数组和Max = max(Max, sum); // 更新最大值}}return Max;}
};

O(n) 动态规划

看注释

代码1

class Solution {
public:int maxSubArray(vector<int>& nums) {const int n = nums.size();int dp[n + 1];dp[0] = nums[0];for (int i = 1; i < n; ++i) dp[i] = max(nums[i], dp[i - 1] + nums[i]);int ans = -1e4;for (int i = 0; i < n; ++i)ans = max(ans, dp[i]);return ans;}
};
/*
1,含义:dp[i] 索引 i 结尾的最大值
2,递推:dp[i] = max(dp[i - 1] + nums[i], nums[i]) 可以连续 或 重新开始
3,初始化:dp[0] = nums[0]
4,遍历顺序:前往后
5,打表检验
*/

代码2

滚动数组的想法,将一维数组优化成变量

class Solution {
public:int maxSubArray(vector<int>& nums) {int sum = 0, ans = -1e4;for (auto x : nums) {sum = max(sum + x, x); // 可以连续 或 重新开始ans = max(ans, sum);}return ans;}
};

🚩合并区间

56. 合并区间 - 力扣(LeetCode)

O(nlogn) 排序

复习下 vector

vector<int>a;
vector<int>a(100); //元素个数100,所有数初值为0
vector<int>a(10, 666); //元素个数100,所有数初值为666
vector<int>b(a); //b是a的复制
vector<int>b(a.begin()+3, a.end()-3); //复制[a.begin()+3, a.end()-3)区间元素到vectorvector<int>a[5]; //创建了5个vector, 每个都是一个数组a.push_back(5); //尾插一个元素5
a.insert(a.begin()+1, 10); //在a.begin()+1指向元素前插入10
a.insert(a.begin()+1, 5, 10); //a.begin()+1前插入5个10
a.insert(a.begin()+1, b.begin(), b.begin()+3); //a.begin()+1前插入b向量区间元素a.pop_back(); //删除向量最后一个元素
a.erase(a.begin()+1); //删除指定位置元素
a.erase(a.begin()+3, a.end()-3); //删除区间[first, last)的元素
a.clear(); //清空向量for(int i = 0; i < a.size(); ++i)cout<<a[i]<<"\t";
for(vector<int>::iterator it = a.begin(); it < a.end; ++it)cout<<*it<<"\t";

容器通用函数

.size()    //元素个数
.empty()   //为空,返回bool值
.front()   //第一个元素
.back()    //最后一个元素
.begin()   //指向第1个的指针
.end()     //指向最后1个的指针
.swap(b)   //交换两个容器内容
::iterator //迭代器

注意,sort(.begin(), .end()),默认第一个元素升序,第一个元素相同,就按第二个元素升序,

我还写了个例子验证👇

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;int main() {vector<vector<int>> intervals = {{1, 3}, {2, 6}, {1, 5}, {1, 2}, {3, 7}, {4, 9}, {1, 8}, {5, 6}, {1, 1}, {7, 8},{1, 4}, {2, 2}, {1, 6}, {3, 4}, {1, 9}, {2, 8}, {1, 7}, {4, 5}, {1, 10}, {3, 3}};// 使用 sort(begin, end) 对二维数组进行排序,先按第一个元素升序,再按第二个元素升序sort(intervals.begin(), intervals.end());// 输出排序后的结果for (const auto& interval : intervals) {cout << "[" << interval[0] << ", " << interval[1] << "]" << endl;}return 0;
}
[1, 1]
[1, 2]
[1, 3]
[1, 4]
[1, 5]
[1, 6]
[1, 7]
[1, 8]
[1, 9]
[1, 10]
[2, 2]
[2, 6]
[2, 8]
[3, 3]
[3, 4]
[3, 7]
[4, 5]
[4, 9]
[5, 6]
[7, 8]

思路

先排序,已知 sort() 根据每个 vector 第一个元素升序,第一个相同就第二个升序(字典序)

然后 for 循环遍历 O(n)

如果 ans[][] 为空 或 ans[][] 最后一个数组 [l, r] 的右边界 r,小于 intervals[][] 当前数组的左边界

说明没有交集,直接插入

反之,如果有交集,就取两个右边界的最大值

class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {vector< vector<int> > ans; // 二维数组存答案int n = intervals.size();sort(intervals.begin(), intervals.end()); // 第一元素升序,第一相同按第二元素for (int i = 0; i < n; ++i) {int l = intervals[i][0], r = intervals[i][1]; // 左右边界if (ans.empty() || ans.back()[1] < l) // 没有交集ans.push_back({l, r}); // 直接插入else // 有交集,答案数组右边界 = 较大值ans.back()[1] = max(ans.back()[1], r);}return ans;}
};

🌼轮转数组

189. 轮转数组 - 力扣(LeetCode)

O(n)  辅助数组

时间复杂度 O(n),空间 O(n)

将末尾的 k 个数,放到开头,会覆盖原来的数,所以需要额外数组 

class Solution {
public:void rotate(vector<int>& nums, int k) { // 没有返回值,需要原数组变化int n = nums.size();vector<int> ans(n); // 要指定大小k %= n;int j = 0;for (int i = n - k; i < n; ++i)ans[j++] = nums[i];for (int i = 0; i < n - k; ++i)ans[j++] = nums[i];for (int i = 0; i < n; ++i)nums[i] = ans[i];}
};

O(n)  环状替换

空间复杂度优化到 O(1),借助 temp 变量

已知位置 0 的元素会放到 x == (0 + k) % n 的位置,然后 temp = x 依次往后推,直到回到出发位置 0

此时仍有部分元素没有替换到新的位置

需要从 0 的下一位置 1 开始,重复上述步骤

那么一共需要遍历多少次呢,每回到一次出发点,算一次

由题解,需要 gcd(n, k) 次,取 数组长度 和 右移距离 的最大公约数

class Solution {
public:void rotate(vector<int>& nums, int k) {int n = nums.size();k %= n;int count = gcd(n, k);// 遍历的趟数for (int i = 0; i < count; ++i) {// 本趟起点 i,temp 保存被更新的元素int now = i, temp = nums[i];do {int Next = (now + k) % n; // 下一位置// 下一元素被替换,temp 保存被替换元素的值swap(temp, nums[Next]); now = Next;} while (now != i); // 未回到起点}}
};

O(n)  数组翻转

思路类似辅助数组的 “将末尾的 k 个数,放到开头”

所以可以先翻转整体,再翻转 [0, k - 1] 和 [k, n - 1]

比如(k = 3) 1 2 3 4 5 6 7  -->  7 6 5 4 3 2 1  -->  5 6 7 1 2 3 4

借助 swap() 创建 reverse() 函数,翻转指定区间的数组

空间优化到 O(1)

class Solution {
public:void reverse(int l, int r, vector<int>& nums) { // 翻转 [l, r] 这个区间for (int i = l, j = r; i < j; ++i, --j)swap(nums[i], nums[j]);}void rotate(vector<int>& nums, int k) {int n = nums.size();k %= n;reverse(0, n - 1, nums);reverse(0, k - 1, nums);reverse(k, n - 1, nums);}
};

🌼除自身以外数组的乘积

238. 除自身以外数组的乘积 - 力扣(LeetCode)

O(n)  前缀和

将 前缀和 的思路换成 前缀积,同样,后缀和 变成 后缀积

前缀积数组 l[i] 表示 i 往左所有元素的乘积

后缀积数组 r[i] 表示 i 往右所有元素的乘积

前缀,后缀数组的计算,先模拟一遍

最后不要用 push_back(),会在 n 个 0 的基础上追加 ans[],要用 ans[i] = l[i] * r[i]

class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int n = nums.size();vector<int> l(n), r(n), ans(n);l[0] = 1, r[n - 1] = 1; // 0左侧 和 n-1右侧 都没有元素// 预处理 前缀 和 后缀for (int i = 1; i < n; ++i)l[i] = l[i - 1] * nums[i - 1]; // l[2] = l[1] * nums[1]for (int i = n - 2; i >= 0; --i)r[i] = r[i + 1] * nums[i + 1]; // r[2] = r[3] * nums[3]for (int i = 0; i < n; ++i)//ans.push_back(l[i] * r[i]);// 不要用push_back,因为已经初始化了 n 个 0// push_back 会在末尾追加 ans[]ans[i] = l[i] * r[i];return ans;}
};

时间O(n)  空间O(1)

在上面前缀和的基础上,只用一个数组 ans[] 代替上面的 3 个数组 l[], r[], ans[]

先用 ans 表示 l[] 数组;在 for 循环中,用变量 r 代替 r[] 数组,同步计算 ans[] 结果

class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int n = nums.size();vector<int> ans(n);ans[0] = 1;for (int i = 1; i < n; ++i) // 计算前缀积ans[i] = ans[i - 1] * nums[i - 1];int r = 1; // 代替 r[] 后缀数组for (int i = n - 1; i >= 0; --i) { // 同步计算后缀和ans[]ans[i] *= r; // 前缀 * 后缀r *= nums[i];}return ans;}
};

🌙缺失的第一个正数

41. 缺失的第一个正数 - 力扣(LeetCode)

O(n)  哈希

先复习下 map👇

size/empty/clear //元素个数,判空,清空
begin / end //指向开始 / 结束位置的指针
insert(x) //将元素x插入集合(x为二元组)
erase(x) //删除所有等于x的元素(x为二元组)
erase(it) //删除it指向的元素(it为指向二元组的迭代器)
find(k) //查找键为k的二元组的位置, 若不存在, 返回尾指针 .end()

复习下 unordered_map👇

std::unordered_map - cppreference.com

std::unordered_map<Key,T,Hash,KeyEqual,Allocator>::insert - cppreference.com

std::unordered_map<Key,T,Hash,KeyEqual,Allocator>::find - cppreference.com

思路

实际就是判断 1~n 是否出现,未出现的就是最小正整数

比较巧妙的思路,用 unordered_map<int, bool>  ans 保存出现过的数字,比如,

ans[3] = 1 表示 3 出现过,ans[5] = 0,表示 5 没有出现过

然后 for 遍历 1~n,判断哈希表中这个键是否出现,未出现的就是最小正整数

都出现了,答案就是 n + 1

时空都是 O(n)

哈希表 增删查 (插入删除查找)都是 O(1) 时间复杂度

class Solution {
public:int firstMissingPositive(vector<int>& nums) {int n = nums.size();unordered_map<int, bool> ans;for (auto x : nums)if (x > 0) // 只考虑正整数ans[x] = 1; // 插入键值对// ans.insert(make_pair(x, 1));// ans.insert({x, 1});int num = 0;for (int i = 1; i <= n; ++i) // 判断 1 ~ nif (!ans[i]) { // i 就是最小正整数num = i;break;}if (num == 0) num = n + 1; // 1~n 都出现了return num;}
};

O(n)  哈希 + 空间优化(标记)

类似将 vis[] 或 book[] 标记数组,转化为,直接在 a[][] 地图上进行标记的思路

这里我们可以不用哈希表,改为直接在原数组上标记

本质都是判断 1~n 是否在数组里

空间优化为 O(1)

class Solution {
public:int firstMissingPositive(vector<int>& nums) {int n = nums.size();// 处理 0 或 负数,不考虑for (auto& x : nums)if (x <= 0)x = n + 1;for (int i = 0; i < n; ++i) {int num = abs(nums[i]); // 出现过的数会标记为负数if (num <= n) // 1 ~ n 出现了nums[num - 1] = -abs(nums[num-1]); // num 出现,num-1 作为索引标记成负数}for (int i = 0; i < n; ++i)if (nums[i] > 0) // 未标记的为最小正整数return i + 1;return n + 1;}
};

O(n)  置换

思路

本质是判断 1~n 是否出现在数组中

那么我们可以将 nums 恢复成 1~n 的形式 [1, 2, 3 ... n]

比如 【3, 4, -1, 1】--> 【1, -1, 3, 4】

int x = nums[i],如果 x ∈ [1, n],那么 nums[i] 原来的位置应该是 nums[x - 1]

比如上面的 3,原来位置应该是下标 2

只需要交换 x 和 i 两个位置的数,此时的 nums[i] 又是新的数,重复上述操作

当 nums[i] == nums[x - 1],说明位置 i 的数据恢复了,就 break 出 while 循环 

解释👇

while (nums[i] >= 1 && nums[i] <= n)

代码中用 while ,不用 if,因为一次 if 无法把所有代码恢复到初始位置 

AC  代码

class Solution {
public:int firstMissingPositive(vector<int>& nums) {int n = nums.size();for (int i = 0; i < n; ++i) {while (nums[i] >= 1 && nums[i] <= n) {int x = nums[i];swap(nums[x - 1], nums[i]); // nums[i] 放到原来的位置 x-1if (nums[x - 1] == nums[i]) break; // 位置已恢复(防止死循环)}}for (int i = 0; i < n; ++i)if (nums[i] != i + 1)return i + 1;return n + 1;}
};

 

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

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

相关文章

【MySQL】数据库的操作(2)

【MySQL】数据库的操作&#xff08;2&#xff09; 目录 【MySQL】数据库的操作&#xff08;2&#xff09;创建表查看表结构修改表删除表 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.3.5 前言&#xff1a;本篇博客将介绍数据库中表的基本操作 创建表 由于使用了不同的…

「Mybatis实战八」:Mybatis的dao层开发使用 - 传统开发方式

一、传统开发方式 1、基础工程代码 数据库环境 CREATE DATABASE mybatis_db; USE mybatis_db; CREATE TABLE user ( id INT(11) NOT NULL AUTO_INCREMENT, username VARCHAR(32) NOT NULL COMMENT 用户名称, birthday DATETIME DEFAULT NULL COMMENT 生日, sex CHAR(1) DEFAUL…

【2024】利用python爬取csdn的博客用于迁移到hexo,hugo,wordpress...

前言 博主根据前两篇博客进行改进和升级 利用python爬取本站的所有博客链接-CSDN博客文章浏览阅读955次&#xff0c;点赞6次&#xff0c;收藏19次。定义一个json配置文件方便管理现在文件只有用户名称,后续可加配置读取用户名称&#xff0c;并且将其拼接成csdn个人博客链接ty…

Gitlab 安装部署

目录 1、Jenkins 结合 Gitlab 构建 CI/CD 环境 CI/CD 介绍 CI/CD 流程 Jenkins 简介 GitLab 简介 项目部署方式 CI系统的工作流程 2、搭建 GitLab 安装 GitLab 配置 GitLab 修改root密码 访问 GitLab 开机自启 3、使用 GitLab 管理 GitLab 关闭 GitLab 注册功能…

Git问题处理汇总

问题1&#xff1a; 出现&#xff1a;Permission denied (publickey).fatal: Could not read from remote repository. 原因&#xff1a;服务器公钥&#xff08;publickey&#xff09;未添加至github, 所以无法识别。因而需要获取本地电脑公钥&#xff0c;然后登录github账号&a…

基于SpringBoot+Apache POI的前后端分离外卖项目-苍穹外卖(十九)

数据导出 1. 工作台1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计1.2.1 Controller层1.2.2 Service层接口1.2.3 Service层实现类1.2.4 Mapper层 1.3 功能测试 2. Apache POI2.1 介绍2.2 入门案例2.2.1 将数据写入Excel文件2.2.2 读取Excel文件中的数据 3. 导出运营数据Excel…

交友盲盒系统PHP开源的盲盒源码

源码介绍&#xff1a; 交友盲盒系统是一款基于PHP开发的开源免费盲盒系统&#xff0c;旨在为用户提供一个充满乐趣和惊喜的社交体验。该系统具有丰富的功能和灵活的扩展性&#xff0c;可以轻松地满足各种线上交友、抽奖活动等场景的需求。 安装说明&#xff1a; PHP版本&…

iptables中的SNAT、DNAT与Firewalld

目录 引言 一、SNAT与DNAT简介 &#xff08;一&#xff09;SNAT 1.SNAT的工作原理 2.SNAT的应用 &#xff08;二&#xff09;DNAT 1.DNAT的工作原理 2.DNAT的应用 二、实现NAT转换 &#xff08;一&#xff09;实现SNAT 1.配置网关服务器 2.修改网关 3.设置SNAT规则…

【leetcode】删除链接的倒数第N个节点

/*** Definition for singly-linked list.* function ListNode(val, next) {* this.val (valundefined ? 0 : val)* this.next (nextundefined ? null : next)* }*/ /*** param {ListNode} head* param {number} n* return {ListNode}*/ var removeNthFromEnd fun…

Java面试题总结8:springboot

Spring Boot自动配置原理 importConfigurationSpring spi 自动配置类由各个starter提供&#xff0c;使用ConfigurationBean定义配置类&#xff0c;放到META-INF/spring.factories下 使用Spring spi扫描META-INF/Spring.factories下的配置类 如何理解Spring Boot中Starter …

04-JNI函数

上一篇&#xff1a;03-JNI 类型和数据结构 本章是 JNI 函数的参考章节。它提供了所有 JNI 函数的完整列表。它还介绍了 JNI 函数表的具体布局。 注意&#xff1a;使用 "必须 "一词来描述对 JNI 程序员的限制。例如&#xff0c;当你看到某个 JNI 函数必须接收一个非 N…

7款炫酷的前端动画特效分享(三)(附效果图及在线演示)

分享7款好玩的前端动画特效 其中有CSS动画、SVG动画、js小游戏等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 CSS3模仿四季交替动画 基于HTML5CSS3实现的卡通风格一年四季交替动画特效 以下效果图只能体现框架的…

超全Chat GPT论文修改指令

文献综述指令润色修改指令论文选题指令论文大指令研究理论指令论文致谢指令参考文献指令论文润色整体逻辑论文整体优化提问指令 1&#xff0e;文献综述指令 请你帮我写一份关于&#xff08;研究主题&#xff09;的文献综述。我的论文选题方向是 XXXX &#xff0c;我已经找到了…

Shell编程——条件测试(五)

在shell编程中&#xff0c;if语句本身不执行任何判断&#xff0c;它实际上接受一个程序作为参数&#xff0c;然后执行这个程序&#xff0c;并依据这个程序的返回值来判断是否执行相应的语句。 程序的返回值是0&#xff0c;则为真&#xff0c;反之则为假。 目录 test命令&…

YOLOv8-Openvino-ByteTrack【CPU】

YOLOv5-Openvino和ONNXRuntime推理【CPU】 YOLOv6-Openvino和ONNXRuntime推理【CPU】 YOLOv8-Openvino和ONNXRuntime推理【CPU】 YOLOv9-Openvino和ONNXRuntime推理【CPU】 注&#xff1a;YOLOv8和YOLOv9代码内容基本一致&#xff01; 全部代码Github&#xff1a;https://gith…

对于网络IO的理解

网络IO理解 首先服务端将本机地址和端口bind在listensock上&#xff0c;再用listen()去将listensock套接字设置为listen状态&#xff0c;然后调用accept&#xff0c;进入阻塞状态。如果此时有客户端请求连接&#xff0c;就是第一次握手的开始。 客户端会先调用connect来申请连…

BUUCTF crypto做题记录(13)新手向

一、[MRCTF2020]vigenere 这是一道维吉尼亚密码&#xff0c;但由于不知道密钥&#xff0c;所以我们需要采用爆破的方式。Vigenere Solver | guballa.de 答案&#xff1a;flag{vigenere_crypto_crack_man} 二、[MRCTF2020]keyboard 之前做过一个类似的题目&#xff0c;用九宫格…

97 spring 中的泛型类型注入

前言 呵呵 同样是 最近同事碰到的一个问题 他不太懂 英语, 看到的说明是 缺少一个 RedisTemplate 的实例, 但是找到了一个 RedisTemplate 的实例 呵呵 和我这里 spring 版本似乎是不太一样, 错误信息 有一些差异 以下环境基于 jdk8 spring-5.0.4-RELEASE 测试用例 BeanCon…

高效备考一级数据分析师考试《CDA Level I 实操训练营》3月30日开课!

曾经报名了考试&#xff0c;买了教程辅导书&#xff0c;却因为各种原因没有坚持学习&#xff0c;这样的经历可能让你感到沮丧和失望。但是&#xff0c;失败并不代表终结&#xff0c;而是迈向成功的必经之路。为了帮助大家能够快速学习考试相关知识&#xff0c;特别为CDA LEVEL …

transformer--解码器

在编码器中实现了编码器的各种组件&#xff0c;其实解码器中使用的也是这些组件&#xff0c;如下图&#xff1a; 解码器组成部分: 由N个解码器层堆叠而成每个解码器层由三个子层连接结构组成第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接第二个子层连…