二、数组类题目相关内容

学习来源:
代码随香炉:https://www.programmercarl.com/
labuladong算法:https://labuladong.github.io/algo/

数组

前缀和数组

区域和检索 - 数组不可变 (一维)

size设置大于n,为了统一起来,主要有一个元素的情况 比如 sum[1]-sums[0], sums[0] = 0
实现一个类,实现o1的复杂度,计算区域和

在这里插入图片描述

二维区域和检索 - 矩阵不可变 (二维矩阵)

在这里插入图片描述

和为K的子数组个数

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
在这里插入图片描述

暴力解法 超出时间限制
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int count = 0;
        int len = nums.size();
        for (int left = 0; left < len; left++) {
            int sum = 0;
            // 区间里可能会有一些互相抵销的元素
            for (int right = left; right < len; right++) {
                sum += nums[right];
                if (sum == k) {
                    count++;
                }
            }
        }
        return count;
    }
};

前缀和 + 哈希表优化

在这里插入图片描述
在这里插入图片描述

差分数组

前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和。
差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。

在这里插入图片描述

在这里插入图片描述

区间加法

在这里插入图片描述

航班预定统计

在这里插入图片描述

class Solution {
public:
    vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
        vector<int> nums(n);
        for (auto& booking : bookings) {
            nums[booking[0] - 1] += booking[2];
            if (booking[1] < n) {
                nums[booking[1]] -= booking[2];
            }
        }
        for (int i = 1; i < n; i++) {
            nums[i] += nums[i - 1];
        }
        return nums;
    }
};

在这里插入图片描述

拼车

在这里插入图片描述

trips = [[2,1,5],[3,3,7]], capacity = 4

class Solution {
public:
    bool carPooling(vector<vector<int>>& trips, int capacity) {
        vector<int> d(1001);
        for(auto &e: trips) {
            d[e[1]] += e[0];
            d[e[2]] -= e[0];
        }
        for(int i = 0; i <= 1000; i ++ ) {
            if(i) d[i] += d[i - 1];
            if(d[i] > capacity) return false;
        }
        return true;
    }
};

二分查找

二分查找

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

搜索插入位置(返回target index 或者插入的index)

在这里插入图片描述

// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1

在这里插入图片描述

搜索插入位置(上下界)

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size()==0){
            return {-1,-1};
        }
        int l =lb(nums,target);
        int r =rb(nums,target);
        return {l,r};
    }
    int lb(vector<int>& nums,int target){
        int l=0,r=nums.size()-1;
        
        while(l<=r){
            int mid = l+(r-l)/2;
            if(nums[mid]<target){
                l=mid+1;
            }else if(nums[mid]>target){
                r = mid-1;
            }else if(nums[mid]==target){
                r = r-1;
            }
        }
        if(l>=nums.size()||nums[l]!=target){
            return -1;
        }
        return l;
    }
    int rb(vector<int>& nums,int target){
        int l=0,r=nums.size()-1;
        while(l<=r){
            int mid = l+(r-l)/2;
            if(nums[mid]<target){
                l=mid+1;
            }else if(nums[mid]>target){
                r = mid-1;
            }else if(nums[mid]==target){
                l = l+1;
            }
        }
        if(r<0||nums[r]!=target){
            return -1;
        }
        return r;
    }

};

x的平方根

class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x, ans = -1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if ((long long)mid * mid <= x) {
                ans = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return ans;
    }
};

有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
输入:num = 16
输出:true

在这里插入图片描述

双指针法

双指针技巧总纲

1. 快慢指针 删除有序数组 和 有序链表的重复项

在这里插入图片描述
在这里插入图片描述

2. 左右指针 二分查找+有序数组的两数之和

在这里插入图片描述

3. 反转数组

在这里插入图片描述

4. 回文串判断
boolean isPalindrome(String s) {
    // 一左一右两个指针相向而行
    int left = 0, right = s.length() - 1;
    while (left < right) {
        if (s.charAt(left) != s.charAt(right)) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

5. 最长回文子串

找回文串的难点在于,回文串的的长度可能是奇数也可能是偶数,解决该问题的核心是从中心向两端扩散的双指针技巧。
如果回文串的长度为奇数,则它有一个中心字符;如果回文串的长度为偶数,则可以认为它有两个中心字符。所以我们可以先实现这样一个函数:

// 在 s 中寻找以 s[l] 和 s[r] 为中心的最长回文串
String palindrome(String s, int l, int r) {
    // 防止索引越界
    while (l >= 0 && r < s.length()
            && s.charAt(l) == s.charAt(r)) {
        // 双指针,向两边展开
        l--; r++;
    }
    // 返回以 s[l] 和 s[r] 为中心的最长回文串
    return s.substring(l + 1, r);
}

这样,如果输入相同的 l 和 r,就相当于寻找长度为奇数的回文串,如果输入相邻的 l 和 r,则相当于寻找长度为偶数的回文串。

在这里插入图片描述

移除元素

双指针方法

在这里插入图片描述

删除有序数组中的重复项

在这里插入图片描述

移动零(将0移动到末尾,保持相对顺序)

在这里插入图片描述

比较含退格的字符串

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
输入:s = “ab#c”, t = “ad#c”
输出:true
解释:s 和 t 都会变成 “ac”。

法一:使用栈,重构字符串比价
在这里插入图片描述
法二:双指针法
M+N 1

在这里插入图片描述

有序数组的平方(有正负数)

方法一:直接排序
在这里插入图片描述
方法二:找分界线,归并排序

方法三:双指针

在这里插入图片描述

滑动窗口

长度最小的子数组 连续

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
2*n 1

在这里插入图片描述

水果成篮

在这里插入图片描述
哈希map 套滑动窗口的模板
在这里插入图片描述

最小覆盖子串

本问题要求我们返回字符串 s 中包含字符串 t的全部字符的最小窗口。我们称包含 tt 的全部字母的窗口为「可行」窗口。
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”

在这里插入图片描述

找所有字母的异位词(同找排列问题)

相当于,输入一个串 S,一个串 T,找到 S 中所有 T 的排列,返回它们的起始索引
在这里插入图片描述

字符串排列( t 的某个排列 在 s中出现)

在这里插入图片描述

最长无重复子串

在这里插入图片描述

剑指offerII的题目

在这里插入图片描述

模拟行为

翻转图像

请你将图像顺时针旋转 90 度。
主对角线镜像对称,然后每一行翻转。

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        // 先沿对角线反转二维矩阵
        for(int i=0;i<=m-1;i++){
            for(int j =i;j<=n-1;j++){
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        // 然后反转二维矩阵的每一行
        for(int i = 0;i<m;i++){
            int left = 0,right = matrix[0].size()-1;
            // 反转一维数组
            while(left<right){
                int temp = matrix[i][left];
                matrix[i][left] = matrix[i][right];
                matrix[i][right] = temp;
                left++;
                right--;
            }
        }
    }
};

螺旋矩阵(顺时针遍历->vector中)

在这里插入图片描述

螺旋矩阵II(顺时针生成矩阵)

你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
在这里插入图片描述

顺时针打印矩阵

同螺旋矩阵(顺时针遍历->vector中)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ray Song

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值