Hot100——普通数组
53最大子数字
思路
核心思想:
- 遍历数组时,持续维护一个当前子数组的和(
now
) - 同时记录遍历过程中遇到的最大子数组和(
ans
)
- 遍历数组时,持续维护一个当前子数组的和(
具体逻辑:
- 初始化
now
为 0(当前子数组和),ans
为一个极小值(-1e9,确保能覆盖所有可能的负值情况) - 遍历数组中的每个元素:
- 将当前元素加入
now
(扩展当前子数组) - 用
now
更新ans
(保留最大值) - 如果
now
小于 0,说明当前子数组的和为负,继续保留会拖累后续结果,因此重置now
为 0(相当于重新开始一个新的子数组)
- 将当前元素加入
- 初始化
适用场景:
- 解决 "最大子序和" 问题,即从整数数组中找出一个具有最大和的连续子数组
- 能高效处理包含负数的数组,例如
[-2,1,-3,4,-1,2,1,-5,4]
这样的输入,会正确返回 6(对应子数组[4,-1,2,1]
)
这个算法的优势在于只需要一次遍历就能得到结果,是解决此类问题的最优方案之一。
代码
class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {sort(intervals.begin(),intervals.end(),[](const vector<int> &a,const vector<int> &b){if(a[0]!=b[0]){return a[0]<b[0];}return a[1]<b[1];});vector<vector<int>> ans;int st=intervals[0][0],end=intervals[0][1];for(int i=1;i<intervals.size();i++){if(intervals[i][0]<=end){end=max(end,intervals[i][1]);}else {ans.push_back({st,end});st=intervals[i][0];end=intervals[i][1];}}ans.push_back({st,end});return ans;}
};
56合并区间
思路
区间排序:
- 首先对所有区间进行排序,排序规则是先按区间的起始位置(
a[0]
)升序排列 - 若起始位置相同,则按区间的结束位置(
a[1]
)升序排列 - 排序的目的是让后续的合并过程可以按顺序进行,确保相邻的区间在物理位置上也相邻
- 首先对所有区间进行排序,排序规则是先按区间的起始位置(
区间合并逻辑:
- 初始化
st
和end
为第一个区间的起始和结束位置 - 从第二个区间开始遍历:
- 若当前区间的起始位置
intervals[i][0]
小于等于end
,说明两个区间重叠或相邻,将end
更新为两个区间结束位置的最大值(合并区间) - 若当前区间的起始位置大于
end
,说明两个区间不重叠,将之前合并好的区间[st, end]
加入结果集,然后更新st
和end
为当前区间的起始和结束位置
- 若当前区间的起始位置
- 遍历结束后,将最后一个合并的区间加入结果集
- 初始化
示例说明:
- 对于输入
[[1,3],[2,6],[8,10],[15,18]]
,排序后不变 - 合并过程:
[1,3]
与[2,6]
合并为[1,6]
,然后[8,10]
和[15,18]
保持不变 - 最终结果为
[[1,6],[8,10],[15,18]]
- 对于输入
该算法的时间复杂度主要由排序步骤决定,为O(n log n)
,空间复杂度为O(1)
(不考虑存储结果所需的空间),是区间合并问题的经典高效解法。
代码
class Solution {
public:vector<vector<int>> merge(vector<vector<int>>& intervals) {sort(intervals.begin(),intervals.end(),[](const vector<int> &a,const vector<int> &b){if(a[0]!=b[0]){return a[0]<b[0];}return a[1]<b[1];});vector<vector<int>> ans;int st=intervals[0][0],end=intervals[0][1];for(int i=1;i<intervals.size();i++){if(intervals[i][0]<=end){end=max(end,intervals[i][1]);}else {ans.push_back({st,end});st=intervals[i][0];end=intervals[i][1];}}ans.push_back({st,end});return ans;}
};
189轮转数组
思路
这段代码实现了数组的向右旋转操作,采用了基于最大公约数(GCD)的环状替换算法,是一种高效的原地旋转方法。具体解析如下:
核心思路
利用数组长度 n
和旋转步数 k
的最大公约数(GCD)来确定旋转的 "环" 数量,每个环内的元素通过环状替换完成旋转,从而实现整体数组的旋转效果。
步骤分解
特殊情况处理:当
k=0
时,无需旋转,直接返回。计算最大公约数:
- 通过自定义的
mygcd
函数计算数组长度nums.size()
与旋转步数k
的最大公约数ma
。 - 这个
ma
决定了需要处理的环的数量(即需要进行ma
次独立的环状替换)。
- 通过自定义的
环状替换过程:
- 对每个环(从
pos=0
到pos < ma
)进行处理:- 保存当前位置
pos
的元素值last
。 - 通过
now = (now + k) % n
计算下一个要替换的位置。 - 依次将
last
的值放入下一个位置,同时更新last
为被替换的元素值。 - 当
now
回到初始位置pos
时,完成一个环的替换,将最后保存的last
放入pos
位置。
- 保存当前位置
- 递增
pos
,处理下一个环,直到所有环都处理完毕。
- 对每个环(从
优势分析
- 空间效率:原地旋转,仅使用常数级额外空间(
O(1)
)。 - 时间效率:每个元素仅被移动一次,总时间复杂度为
O(n)
。 - 适用性:适用于任意长度的数组和任意旋转步数,包括
k
大于数组长度的情况(通过取模自动处理)。
例如,对于数组 [1,2,3,4,5,6]
和 k=2
:
- 数组长度为 6,
GCD(6,2)=2
,需要处理 2 个环。 - 第一个环(
pos=0
):0→2→4→0
,元素依次替换。 - 第二个环(
pos=1
):1→3→5→1
,元素依次替换。 - 最终结果为
[5,6,1,2,3,4]
,实现了向右旋转 2 步的效果。
这种算法巧妙利用数学性质减少了操作次数,是数组旋转问题的最优解法之一。
代码
class Solution {
public:int mygcd(int a,int b){if(a<b)swap(a,b);if(b==0)return a;return mygcd(b,a%b);}void rotate(vector<int>& nums, int k) {int pos=0;if(k==0)return;int ma=mygcd(nums.size(),k);while (pos < nums.size() && pos < ma){int last = nums[pos];int tmp;int now = pos;now += k;while (now != pos){//cout<<now<<endl;now %= nums.size();tmp = nums[now];nums[now] = last;last = tmp;now += k;now %= nums.size();}nums[pos] = last;pos++;}}
};
238除自身以外数组的乘积
思路
算法思路解析:
核心思想:
- 利用两次遍历,分别计算每个元素左侧所有元素的乘积和右侧所有元素的乘积
- 将这两个乘积相乘,即可得到每个位置除除自身外所有元素的乘积
具体步骤:
第一次遍历(左到右):
- 初始化
answer[0] = 1
(第一个元素左侧没有元素,乘积为 1) - 对于
i > 0
,answer[i]
存储nums[0]
到nums[i-1]
的乘积(即当前元素左侧所有元素的乘积)
- 初始化
第二次遍历(右到左):
- 初始化
R = 1
(右侧乘积的初始值) - 对于每个
i
,answer[i]
乘以R
(即当前元素右侧所有元素的乘积) - 更新
R
为R * nums[i]
(将当前元素加入右侧乘积,供左侧元素使用)
- 初始化
优势分析:
- 避免了使用除法(处理了数组中可能包含 0 的情况)
- 仅使用常数额外空间,效率极高
- 两次线性遍历,时间复杂度为 O (n)
代码
class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int length = nums.size();vector<int> answer(length);answer[0] = 1;for (int i = 1; i < length; i++) {answer[i] = nums[i - 1] * answer[i - 1];}int R = 1;for (int i = length - 1; i >= 0; i--) {answer[i] = answer[i] * R;R *= nums[i];}return answer;}
};
41缺失的第一个正数
思路
代码
class Solution {
public:int firstMissingPositive(vector<int>& nums) {for(int i=0;i<nums.size();i++){while(nums[i]>0&&nums[i]<nums.size()){swap(nums[i],nums[nums[i]-1]);if(nums[i]<=0||nums[i]>=nums.size()||nums[i]==i+1||nums[i]==nums[nums[i]-1])break;}}for(int i=0;i<nums.size();i++){if(nums[i]!=i+1)return i+1;}return nums.size()+1;}
};