Leetcode Hot 100 —— 普通数组

53. 最大子数组和

使用动态规划方法,后面再看。

56. 合并区间



思路与解法

本题的核心在于区间合并,首先要对区间进行排序,然后通过遍历并合并相邻区间的方式来消除重叠的部分。

1、排序:将所有区间按左端点升序排列,这样重叠的区间会相邻。

2、遍历合并:遍历排序后的区间,维护一个结果列表 merged,逐个判断当前区间是否与结果中最后一个区间重叠:
若不重叠 ,即已合并的最后一个区间的右端点小于当前区间的左端点,直接加入结果
若重叠 ,则更新最后一个区间的右端点为两者右端点的较大值。

整体思路:建立结果数组merged------sort排序------遍历合并(判断当前区间与前一个区间是否重叠)

核心代码:

cpp 复制代码
class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> merged;
        if (intervals.empty()) return merged;

        // 1. 按照区间的起点排序
        sort(intervals.begin(), intervals.end());

        for (auto& interval : intervals) {
            // 2. 如果 merged 为空或者当前区间与前一个区间不重叠,直接加入
            if (merged.empty() || merged.back()[1] < interval[0]) {
                merged.push_back(interval);
            } else {
                // 3. 否则有重叠,更新 merged 最后一个区间的右边界
                merged.back()[1] = max(merged.back()[1], interval[1]);
            }
        }
        return merged;
    }
};

【注】

1、sort(intervals.begin(), intervals.end())

intervals.begin()指向 intervals 的第一个区间(即第一个 vector<int>)。

intervals.end()指向容器最后一个元素的下一个位置(即尾后迭代器),通常作为结束标记。

sort默认 按每个区间的第一个元素(即左端点)升序排序。

2、merged.back()[1] < interval[0]

merged.back() 返回merged中最后一个元素的引用,即最后一个区间(vector<int>&)。

merged.back()[1]访问该区间的第二个元素,即右端点值。

interval 是当前遍历到的原始区间(来自 intervals),interval[0] 是其左端点。

因此,该语句意思是:已合并的最后一个区间的右端点,是否小于当前区间的左端点。
如果为真 (小于),说明两个区间没有交集(前一个区间完全在当前区间左边),可以将当前区间作为新区间加入 merged。
如果为假(大于或等于),说明有重叠,需要合并,即更新最后一个区间的右端点为两者右端点的较大值。

ACM:

cpp 复制代码
#include <vector>
#include <iostream>
#include<algorithm>
using namespace std;

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> merged;
         sort(intervals.begin(),intervals.end());

         for(auto &interval:intervals){
            if(merged.empty()||merged.back()[1]<interval[0]){
                merged.push_back(interval);
            }
            else{
                merged.back()[1]=max(merged.back()[1],interval[1]);
            }
         }
         return merged;
    }
};

int main(){
    int n;
    cin>>n;
    vector<vector<int>> intervals(n,vector<int>(2));
    for(int i=0;i<n;i++){
        cin>>intervals[i][0]>>intervals[i][1];
    }

    Solution solution;
    vector<vector<int>> results = solution.merge(intervals);
    for(auto& interval:results){
        cout<<interval[0]<<" "<<interval[1]<<endl;
    }
    return 0;
}

【注】

1、intervals(n, vector<int>(2)):这是 vector 的构造函数调用,接受两个参数:

第一个参数 n:指定外部向量的大小,即 intervals 中将包含 n 个元素(每个元素是一个 vector<int>)。

第二个参数 vector<int>(2):指定一个初始值,用于填充外部向量的每一个元素。这里 vector<int>(2) 创建一个匿名临时对象,它是一个包含两个 int 的向量,两个 int 默认初始化为 0。

189. 轮转数组


思路与解法:

假设数组长度为 n,向右旋转 k 位(k 可能大于 n,先取模 k %= n),最终结果相当于将原数组的后 k 个元素移到前面,前 n-k 个元素移到后面,且两部分内部的相对顺序保持不变。即:

原数组: A[0] A[1] ... A[n-k-1] | A[n-k] ... A[n-1]

旋转后: A[n-k] ... A[n-1] | A[0] A[1] ... A[n-k-1]

反转一个数组(或子数组)可以完全颠倒元素的顺序。如果我们能通过反转操作,将上面的"前后交换"效果实现,同时恢复各部分的顺序,就可以得到目标数组。

自然得到三次反转的步骤:

自己举个12345的例子就好理解了

1、反转整个数组:将后 k 个元素移到前面(但顺序颠倒),同时前 n-k 个元素移到后面(顺序也颠倒)。

2、反转前 k 个元素:将前面部分顺序纠正,得到正确的后 k 个元素。

3、反转后 n-k 个元素:将后面部分顺序纠正,得到正确的前 n-k 个元素。

核心代码:

cpp 复制代码
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n=nums.size();
        k=k%n;
        reverse(nums.begin(),nums.end());
        reverse(nums.begin(),nums.begin()+k);
        reverse(nums.begin()+k,nums.end());        
    }
};

【注】

1、k=k%n;防止k过大!!

2、想移动迭代器,直接+k即可。

随机访问迭代器支持算术运算,例如 + 和 -,可以像指针一样进行偏移。因此 nums.begin() + k 表示将迭代器向后移动 k 个位置,得到指向索引为 k 的元素的迭代器。

3、不是reverse(nums.begin()+k+1,nums.end()); !!!

因为reverse是左闭右开的!!!

ACM:

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n=nums.size();
        k=k%n;
        reverse(nums.begin(),nums.end());
        reverse(nums.begin(),nums.begin()+k);
        reverse(nums.begin()+k,nums.end());        
    }
};

int main() {
    int n, k;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    cin >> k;
    
   Solution solution;
   solution.rotate(nums, k);  // 调用类的成员函数
    
    for (int i = 0; i < n; i++) {
        if(i>0)  cout<< " ";
        cout << nums[i];
    }
    cout << endl;

    return 0;
}

238. 除了自身以外数组的乘积


思路与解法:

核心代码:

cpp 复制代码
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        //初始化结果数组,所有元素先设为1,因为要乘,不能设为0!!
        vector<int> answer(n,1);

        int prefix=1;
        for(int i =0;i<n;i++){
            answer[i]=prefix; //将当前左边乘积存入answer[i]
            prefix*=nums[i];  //更新前缀积,为下一个位置做准备
        }

        int suffix=1;
        for(int i=n-1;i>=0;i--){
            answer[i]*=suffix; //将右边乘积乘到answer[i]上
            suffix*=nums[i];   //更新后缀积,为前一个位置做准备       
        }

        return answer;
        
    }
};

【注】

1、vector<int> answer(n,1); int prefix=1; int suffix=1;

注意都初始化为1,因为要做乘法!!

2、for(int i=n-1;i>=0;i--){ 注意是i>=0

3、 answer[i]=prefix; 计算前缀积不是*=!!
answer[i]*=suffix; 计算后缀积才是*=,因为answer[i] = prefix[i] * suffix[i],在原来计算好的前缀积的基础上再乘以后缀积。

ACM:

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> answer(n,1);

        int prefix=1;
        for(int i =0;i<n;i++){
            answer[i]=prefix; 
            prefix*=nums[i];  
        }

        int suffix=1;
        for(int i=n-1;i>=0;i--){
            answer[i]*=suffix;
            suffix*=nums[i];  
        }

        return answer;
        
    }
};

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    Solution solution;
    vector<int> answer = solution.productExceptSelf(nums);
    
    for (int i = 0; i < n; i++) {
        if(i>0) cout <<" ";
        cout << answer[i];
    }
    cout << endl;

    return 0;
}

41.缺失的第一个整数


思路与解法

本题要求时间复杂度O(n)且常数级 额外空间。常见方法包括:

1、哈希表:O(n) 时间,但空间 O(n),不符合要求。

2、排序:O(n log n),不符合要求。

3、"原地置换" 方法:符合要求,利用数组本身作为哈希表。

对于一个长度为 n 的数组,最小的缺失正数一定在 [1, n+1] 范围内。因为如果 1到n 都出现了,那么答案就是 n+1。因此,我们可以利用数组本身来标记 1~n 这些数是否出现。

具体做法:将每个正数 x(1 ≤ x ≤ n)放到数组下标为 x-1 的位置上(即索引 0 放 1,索引 1 放 2,以此类推)。这样,经过一次遍历调整后,所有在范围内的正数都回到了它们"应该"在的位置。然后第二次遍历,第一个不满足 nums[i] == i+1 的位置 i 就对应缺失的正数 i+1。

如何处理重复的数字?

通过条件 nums[i] != nums[nums[i] - 1] (nums[i]不在正确的位置)避免了无限交换。每个在范围内的数最多被交换一次,因为一旦它到达正确位置,就不会再被移动。重复的数会被"挤"到后面,不会影响正确位置的判断。

核心代码:

cpp 复制代码
class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        //第一次遍历:将每个在 [1, n] 范围内的数放到正确的位置
        for(int i = 0;i<n;i++){
        //当 nums[i] 是正数且 ≤ n,并且它不在正确的位置上时,循环交换
            while(nums[i]>0&&nums[i]<=n&&nums[i]!=nums[nums[i]-1]){
                swap(nums[i],nums[nums[i]-1]);
            }
        }
        //第二次遍历:找出第一个位置 i 使得 nums[i] != i+1
        for(int i = 0;i<n;i++){
            if(nums[i]!=i+1){
                return i+1;
            }
        }
        // 如果所有位置都正确,则缺失的是 n+1
        return n+1;        
    }
};

ACM:

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        for(int i = 0;i<n;i++){
        //当 nums[i] 是正数且 ≤ n,并且它不在正确的位置上时,循环交换
            while(nums[i]>0&&nums[i]<=n&&nums[i]!=nums[nums[i]-1]){
                swap(nums[i],nums[nums[i]-1]);
            }
        }
        //第二次遍历:找出第一个位置 i 使得 nums[i] != i+1
        for(int i = 0;i<n;i++){
            if(nums[i]!=i+1){
                return i+1;
            }
        }
        // 如果所有位置都正确,则缺失的是 n+1
        return n+1;        
    }
};

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }
    Solution solution;

    cout << solution.firstMissingPositive(nums) << endl;
    return 0;
}
相关推荐
@Mike@2 小时前
【算法】高精度
算法
leo__5202 小时前
MHT多假设跟踪算法(Multiple Hypothesis Tracking)MATLAB实现
开发语言·算法·matlab
ShineWinsu2 小时前
对于C++中unordered_set的详细介绍
数据结构·c++·算法·面试·stl·哈希表·unordered_set
吃着火锅x唱着歌2 小时前
LeetCode 456.132模式
数据结构·算法·leetcode
二木九森2 小时前
LeetCode-寻找环形链表的入口
算法·leetcode·链表
飞Link2 小时前
耳机连接电脑时调节耳机音量电脑音量也会随着改变
算法·电脑
此方ls2 小时前
机器学习聚类算法一——K均值
算法·机器学习·聚类
再难也得平2 小时前
力扣73. 矩阵置零(Java解法)
算法·leetcode·矩阵
进击切图仔2 小时前
生成 .so 和使用 .so
java·javascript·算法