【学习笔记】【Leetcode 分门别类讲解】——数组

本文详细梳理了数组相关问题的解决方案,重点介绍了二分查找、双指针(如对撞指针、快慢指针)及滑动窗口在不同题目中的应用,如移动零、合并有序数组、回文串验证等。同时,通过实例解析了如何利用这些技术高效地解决问题,并强调了边界条件和循环不变量的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组相关总结

0、二分查找问题-左右边界

明确变量的含义、维护好循环不变量,根据循环不变量定义边界问题。
不同的循环不变量会使得代码边界问题发生改变。
小数据集的调试,很重要。

自己整理的看着最香:二分查找

1、双指针大法-对撞指针

【1147. 段式回文】

.substr(指定位置,指定长度)//复制字符串,从指定位置开始具有指定的长度.
如果没有指定长度或超出了源字符串的长度,则子字符串将延续到源字符串的结尾

  • 暴力匹配;哈希思想+重新匹配解决哈希冲突;//这个方法看到再说
class Solution {
public:
    int longestDecomposition(string text) {
        int n = text.size();
        for(int i = n-1;i>0;i--){
            if(text[0]==text[i]&&text.substr(0,n-i)==text.substr(i)){//从后往前找和第一个字符匹配的且整个字符串要匹配
                if(2*i==n) return 2;//正好左边==右边
                else if(2*i<n) return 1;//因为i是从后往前,所以是<,画个图啥都明白了
                else{
                    return 2+longestDecomposition(text.substr(n-i,2*i-n));//把第一个和后面第一个匹配搞定了递归剩下的
                }
            }
        }
        return 1;
    }
};

【283. 移动零】

1.快慢双指针 On,O1
- 定义快慢双指针
- 快指针遍历nums,遇到不为零的数,将快指针对应的数给慢指针对应的数
- 慢指针再右移一位
- 快指针遍历一遍之后,所有不为0的数都已经到了数组的最前面
- 从慢指针开始,往后的数都赋值为0即可

        for(int r = 0;r<n;r++){
            if(nums[r]){//快指针遍历nums,遇到不为零的数
                nums[l++]=nums[r];//将快指针对应的数给慢指针对应的数
            }
        }
        for(int i = l;i<n;i++){
            nums[i]=0;//从慢指针开始,往后的数都赋值为0即可
        }

2.swap;右指针遇到非0一直往左挪; On,O1

        for(int l = 0,r = 0,n=nums.size();r<n;r++){
            if(nums[r]){
                swap(nums[l++],nums[r]);
            }
        }

【88. 合并两个有序数组】

逆向排序,注意P1或者P2排完后(要考虑到P==-1)另一边还要排

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p1=m-1,p2=n-1,i = n+m-1;
        while(p1>=0||p2>=0){
            if(p1==-1){//注意P1或者P2排完后(要考虑到P==-1)另一边还要排
                nums1[i--] = nums2[p2--];
            }
            else if(p2==-1){
                nums1[i--] = nums1[p1--];
            }
            else if(nums1[p1]>nums2[p2]){
                nums1[i--] = nums1[p1--];
            }
            else {
                nums1[i--] = nums2[p2--];
            }
        }
    }
};

【167. 两数之和 II - 输入有序数组】

对撞指针; On,O1

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int l = 0,n = numbers.size(),r = n-1;
        while(l<r){
            if(numbers[l]==target-numbers[r]){return {l+1,r+1};}//+1是题目要求
            else if(numbers[l]>target-numbers[r]){r--;}
            else{l++;}
        }
        return {l+1,r+1};
    }
};

【125. 验证回文串】

对撞指针; isalnum()+tolower() On,O1

class Solution {
public:
    bool isPalindrome(string s) {
        int l = 0,n = s.size(),r=n-1;
        while(l<r){
            while(l<r && !isalnum(s[l])){++l;}
            while(l<r && !isalnum(s[r])){--r;}
            if(tolower(s[l++])!=tolower(s[r--])){return false;}
        }
        return true;
    }
};

【344. 反转字符串】

对撞指针; On,O1

  • 辅助栈用于匹配括号啊匹配回文也可以,但更喜欢双指针
class Solution {
public:
    void reverseString(vector<char>& s) {
        int l = 0,n = s.size(),r = n-1;
        while(l<r) {swap(s[l++],s[r--]);}
    }
};

【345. 反转字符串中的元音字母】

对撞指针; On,O1/On?

class Solution {
public:
    bool isAEIOU(char a){
        return a=='a'||a=='e'||a=='i'||a=='o'||a=='u'||a=='A'||a=='E'||a=='I'||a=='O'||a=='U';
    }
    string reverseVowels(string s) {
        int l = 0,n = s.size(),r = n-1;
        while(l<r){
            while(l<r&&!isAEIOU(s[l])) {l++;}//这里如果没有l<r就不行,有点疑惑,
            								//噢噢因为你是while,如果没有这个的话你下面的l++会多走一次导致错误
            while(l<r&&!isAEIOU(s[r])) {r--;}
            swap(s[l++],s[r--]);
        }
        /*
        while(l<r){
            if(!isAEIOU(s[l])) {l++;}//用if就不用这里再来一个l<r
            if(!isAEIOU(s[r])) {r--;}
            else if(isAEIOU(s[l])&&isAEIOU(s[r])) {swap(s[l++],s[r--]);}//但是用if不像while一样
            											//确定到这里的时候一定是isAEIOU(s[l]),所以要多一个判断
        }*/
        return s;
    }
};

【11. 盛最多水的容器】

对撞指针;考虑好指针移动条件。

class Solution {
public:
    int maxArea(vector<int>& height) {
        int l = 0,n = height.size(),r = n-1;
        int res = 0;
        while(l<r){
            res = max((r-l)*min(height[l],height[r]),res);
            if(height[l]>height[r]){r--;}//考虑好指针移动条件
            else {l++;}
        }
        return res;
    }
};

【189. 旋转数组】

自己整理的看着最香:左/右旋转数组

2、双索引技术-滑动窗口

涉及连续子数组的问题,我们通常有两种思路: 一是滑动窗口、二是前缀和。

自己整理的看着最香:滑动窗口

【209. 长度最小的子数组】

滑动窗口就是要想好怎么滑,什么情况下滑,
怎么创建这个滑动窗口(sum+=nums[r];//第三题也是先由右指针去搞出窗口来),On
其实滑动窗口核心还是双指针,左右边界不就是两个指针决定的嘛

  • 前缀和+二分查找:正整数数组保证前缀和单调性。前缀和之间的差,就是每个连续子数组。O(nlogn)

【3. 无重复字符的最长子串】

队列也好数组也好,其实就是一个控制左右的东东,右边满足条件就扩一下,左边不满足了就踢出去,然后注意边界条件!
**A1.unordered_set //res要放外面,不能放while里,不然如果没有重复字符res就一直是0了
A2.fre[256]**注意r=-1开始确保不漏掉第一个数

【219. 存在重复元素 II】

固定窗口大小,关键实现方法:
if(st.size()>k) {st.erase(nums[r-k]);}

【567. 字符串的排列】

//搞两个窗口出来,小的那个不动
//滑大的那个

【76. 最小覆盖子串】

  • 滑动窗口+检查匹配;检查匹配可以优化;//还未做此题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值