Hot100——普通数组

53最大子数字

思路

  1. 核心思想

    • 遍历数组时,持续维护一个当前子数组的和(now
    • 同时记录遍历过程中遇到的最大子数组和(ans
  2. 具体逻辑

    • 初始化 now 为 0(当前子数组和),ans 为一个极小值(-1e9,确保能覆盖所有可能的负值情况)
    • 遍历数组中的每个元素:
      • 将当前元素加入 now(扩展当前子数组)
      • now 更新 ans(保留最大值)
      • 如果 now 小于 0,说明当前子数组的和为负,继续保留会拖累后续结果,因此重置 now 为 0(相当于重新开始一个新的子数组)
  3. 适用场景

    • 解决 "最大子序和" 问题,即从整数数组中找出一个具有最大和的连续子数组
    • 能高效处理包含负数的数组,例如 [-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合并区间

思路

  1. 区间排序

    • 首先对所有区间进行排序,排序规则是先按区间的起始位置(a[0])升序排列
    • 若起始位置相同,则按区间的结束位置(a[1])升序排列
    • 排序的目的是让后续的合并过程可以按顺序进行,确保相邻的区间在物理位置上也相邻
  2. 区间合并逻辑

    • 初始化stend为第一个区间的起始和结束位置
    • 从第二个区间开始遍历:
      • 若当前区间的起始位置intervals[i][0]小于等于end,说明两个区间重叠或相邻,将end更新为两个区间结束位置的最大值(合并区间)
      • 若当前区间的起始位置大于end,说明两个区间不重叠,将之前合并好的区间[st, end]加入结果集,然后更新stend为当前区间的起始和结束位置
    • 遍历结束后,将最后一个合并的区间加入结果集
  3. 示例说明

    • 对于输入[[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)来确定旋转的 "环" 数量,每个环内的元素通过环状替换完成旋转,从而实现整体数组的旋转效果。

步骤分解

  1. 特殊情况处理 :当 k=0 时,无需旋转,直接返回。

  2. 计算最大公约数

    • 通过自定义的 mygcd 函数计算数组长度 nums.size() 与旋转步数 k 的最大公约数 ma
    • 这个 ma 决定了需要处理的环的数量(即需要进行 ma 次独立的环状替换)。
  3. 环状替换过程

    • 对每个环(从 pos=0pos < 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除自身以外数组的乘积

思路

算法思路解析:

  1. 核心思想

    • 利用两次遍历,分别计算每个元素左侧所有元素的乘积和右侧所有元素的乘积
    • 将这两个乘积相乘,即可得到每个位置除除自身外所有元素的乘积
  2. 具体步骤

    • 第一次遍历(左到右)

      • 初始化 answer[0] = 1(第一个元素左侧没有元素,乘积为 1)
      • 对于 i > 0answer[i] 存储 nums[0]nums[i-1] 的乘积(即当前元素左侧所有元素的乘积)
    • 第二次遍历(右到左)

      • 初始化 R = 1(右侧乘积的初始值)
      • 对于每个 ianswer[i] 乘以 R(即当前元素右侧所有元素的乘积)
      • 更新 RR * nums[i](将当前元素加入右侧乘积,供左侧元素使用)
  3. 优势分析

    • 避免了使用除法(处理了数组中可能包含 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;
    }
};
相关推荐
啊我不会诶2 小时前
【数据结构】字典树
数据结构·算法
信奥卷王2 小时前
[GESP202403 五级] 成绩排序
数据结构·算法
小欣加油3 小时前
leetcode 494 目标和
c++·算法·leetcode·职场和发展·深度优先
Jiezcode4 小时前
LeetCode 55.跳跃游戏
c++·算法·leetcode·游戏
wheeldown5 小时前
【Leetcode高效算法】用双指针策略打破有效三角形的个数
python·算法·leetcode
蒙奇D索大5 小时前
【数据结构】考研重点掌握:顺序查找算法实现与ASL计算详解
数据结构·笔记·学习·考研·算法·改行学it
TTGGGFF5 小时前
MATLAB仿真:编程基础实验全解析——从入门到实战
数据结构·算法·matlab
Ivanqhz5 小时前
LR算法中反向最右推导(Reverse RightMost Derivation)
人工智能·算法
zl_dfq5 小时前
数据结构 之 【图的最短路径】(Dijstra、BellmanFord、FloydWarShall算法实现)
数据结构·算法