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;
}
};