代码随想录Day2|209.长度最小的子数组、59.螺旋矩阵II、区间和、开发商购买土地

209.长度最小的子数组

题目链接:Leetcode
文章讲解:代码随想录
视频讲解:bilibili

解题思路

  可以用双重for循环进行暴力求解,遍历每一种可能的子串情况,这题更好的解法是利用滑动窗口思想,因为先前有见过类似的题目,因此尝试从这个方向求解。

初解(这版解法不正确)

  起初的解法是根据leftright双指针进行字串两端的标记,并根据数组中能使得字串总和大于target的元素下标进行双指针的更新,这种写法能通过基本的三个测试样例,但是无法通过评测。想必比较熟悉这类思想的码者已经发现了问题,就是这样的思路无法保证得到的字串是最短的。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int cnt = 0, sum = 0;
        int left = 0, right = 0;
        int length = nums.size();
        for(int i = 0; i < length; i++){
            if(sum + nums[i] < target){
                cnt++;
                sum += nums[i];
                right = i;
            } else{
                if(sum + nums[i] - nums[left] < target){
                    cnt++;
                    sum += nums[i];
                    right = i;
                } else{
                    sum += nums[i];
                    cnt++;
                    right = i;
                    while(left<=right && sum-nums[left]>=target){
                        sum -= nums[left];
                        left++;
                        cnt--;
                    }
                }
            }
        }
        if(sum < target)
            return 0;
        else
            return cnt;
    }
};
再版

  看了资料之后,才知道原先代码的问题是没有将每个符合要求的字串长度进行比较从而得出最短字串的长度,加入比较和存储最小值之后即可通过测试。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int cnt = 0, sum = 0;
        int left = 0, right = 0;
        int length = nums.size();
        int min_length = length;	/*****新增代码****/
        for(int i = 0; i < length; i++){
            if(sum + nums[i] < target){
                cnt++;
                sum += nums[i];
                right = i;
            } else{
                if(sum + nums[i] - nums[left] < target){
                    cnt++;
                    sum += nums[i];
                    right = i;
                } else{
                    sum += nums[i];
                    cnt++;
                    right = i;
                    while(left<=right && sum-nums[left]>=target){
                        sum -= nums[left];
                        left++;
                        cnt--;
                    }
                }
                /*****新增代码****/
                if(sum < target)
                    min_length = min(min_length, ++cnt);
                else
                    min_length = min(min_length, cnt);
                /*****新增代码****/
            }
        }
        if(sum < target)
            return 0;
        else
            return min_length;
    }
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

  经过视频学习后,按照其更加清晰的思路,可以写出更加简洁的版本。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int length = nums.size();
        int left, right, sum = 0;
        int min_length = length;
        for(left = 0, right = 0; right < length; right++){
            sum += nums[right];
            while(sum >= target){
                int sub_length  = right - left + 1;
                min_length  = min(min_length, sub_length);
                sum -= nums[left];
                left++;
            }
        }
        if(left==0 && sum < target)
            return 0;
        else
            return min_length;
    }
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

59.螺旋矩阵II

题目链接:Leetcode
文章讲解:代码随想录
视频讲解:bilibili

解题思路

  这是一道模拟题,编程模拟顺时针遍历数组的过程,拿到这个题目的第一想法就是根据不同的边界条件进行下标的改变。其次,如何处理从外圈到内圈的过渡也是题目的一大难点,这里首次书写使用计数器的方法,当计数器变量恰好等于外圈元素的个数就走到内圈,而走入内圈时修改相应的边界变量。

初版
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n, vector<int>(n, 1));
        if(n>1){
            int cnt = 1;
            int size = n * n;
            int size_now = n;
            int size_sum = 4 * (size_now-1);
            int size_up   = 0;
            int size_low  = n-1;
            int size_right= n-1;
            int size_left = 0;
            int i = 0, j = 0;
            while(cnt < size){
                if(cnt==size_sum){
                    j++;
                    size_up    += 1;
                    size_low   -= 1;
                    size_right -= 1;
                    size_left  += 1;
                    size_now   -= 2;
                    if(size_now > 1)
                        size_sum   += 4 * (size_now-1);
                }
                else if(i==size_up && j < size_right)
                    j++;
                else if(j==size_right && i < size_low)
                    i++;
                else if(i==size_low && j > size_left)
                    j--;
                else if(j==size_left && i > size_up)
                    i--;
                matrix[i][j] = ++cnt;
            }
        } 
        return matrix;
    }
};
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)

❗️ 这里在书写过程中遇到了编译错误的问题,错误信息是数组越界访问,最后询问ai才发现,对于n=1的情况,恰好首次进入while就会进入第一个if分支,由j++引起数组访问越界。因此,需要对n=1的情况做特殊处理,这里令数组全部初始化为1,并在n>1时才进行遍历,功能正确。

再版

  看了资料之后,才知道这道题可以结合二分中的循环不变量的思路去做,就是把每个矩形框拆分成等长的互不重叠的四段进行处理,并利用矩阵规模得出需要进行遍历的圈数,每一步都有清晰的目的和解决思路,代码也很简洁,相比之下我的做法十分暴力,能debug对也是万幸了hhh。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n, vector<int>(n, 1));
        int start_x=0, start_y=0, offset=1;
        int i, j, cnt = 1;
        int size = n / 2;
        while(size--){
            for(j = start_y; j < n-offset; j++)
                matrix[start_x][j] = cnt++;
            for(i = start_x; i < n-offset; i++)
                matrix[i][j] = cnt++;
            for(; j > start_y; j--)
                matrix[i][j] = cnt++;
            for(; i > start_x; i--)
                matrix[i][j] = cnt++;
            start_x++;
            start_y++;
            offset++;
        }
        if(n%2)
            matrix[start_x][start_y] = cnt;
        return matrix;
    }
};
  • 时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

区间和

题目链接:Leetcode
文章讲解:代码随想录

解题思路

  这道题乍一看要求十分简单,只要暴力遍历求解就好了,这里输入输出用的是scanfprintf,之前听说比较省时。其实想用cincout,但是太久没用忘记了,看到原来这题暴力求解会超时,但是因为输入输出函数的原因误打误撞过了。(试了一下改成cincout就会超时)

初版
# include <iostream>
# include <vector>
using namespace std;
 
int main(){
    int n;
    scanf("%d", &n);
    vector<int> nums(n);
    for(int i=0; i<n; i++)
        scanf("%d", &nums[i]);
    int x, y;
    while(~scanf("%d %d", &x, &y)){
        int sum = 0;
        for(int i=x; i<=y; i++)
            sum += nums[i];
        printf("%d\n", sum);
    }
}
  • 时间复杂度: O ( m n ) O(mn) O(mn)
  • 空间复杂度: O ( 1 ) O(1) O(1)

💡 值得一提的是代码中while的循环结束条件,对于scanf函数,有三种返回值:

  1. 匹配成功:返回元素个数
  2. 一个输入都不匹配:返回0
  3. EOF或读入错误:返回-1

  这里在结束读入时接收到的是EOF,而返回值-1的二进制补码进行按位取反是0,此外其余数取反均不为0,可以继续循环。

再版

  这题更好的解法用到了前缀和的思想,先将顺序字串和计算好,再根据目标字串下标进行相减即可得到结果。有点类似动态规划的思想,用空间换取时间。

# include <iostream>
# include <vector>
using namespace std;

int main(){
    int n;
    scanf("%d", &n);
    vector<int> nums(n);
    vector<int> p(n);
    scanf("%d", &nums[0]);
    p[0] = nums[0];
    for(int i=1; i<n; i++){
        scanf("%d", &nums[i]);
        p[i] = p[i-1] + nums[i];
    }

    int x, y;
    while(~scanf("%d %d", &x, &y)){
        int sum;
        if(x==0)
            sum = p[y];
        else
            sum = p[y] - p[x-1];
        printf("%d\n", sum);
    }
}
  • 时间复杂度: O ( m ) O(m) O(m)
  • 空间复杂度: O ( n ) O(n) O(n)

开发商购买土地

题目链接:Leetcode
文章讲解:代码随想录

解题思路

  也是一道可以暴力求解的题目,但根据上一题也猜得到纯粹暴力求解过不了,因此先借鉴前缀和的思路进行求解,虽然还是 n 2 n^2 n2,但比起 n 3 n^3 n3来说还是有很大进步的。

解法
# include <iostream>
# include <vector>
# include <algorithm>
# include <climits>
# include <cmath>

using namespace std;

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    vector<vector<int>> matrix(n, vector<int>(m));
    int sum = 0;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++){
            scanf("%d", &matrix[i][j]);
            sum += matrix[i][j];
        }
    vector<int> x_sum(n, 0);
    vector<int> y_sum(m, 0);
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            x_sum[i] += matrix[i][j];
    for(int j=0; j<m; j++)
        for(int i=0; i<n; i++)
            y_sum[j] += matrix[i][j];
    int res = INT_MAX;
    int sum_x=0, sum_y=0;
    for(int i=0; i<n-1; i++){
        sum_x += x_sum[i];
        res = min(res, abs(sum-2*sum_x));
    }
    for(int j=0; j<m-1; j++){
        sum_y += y_sum[j];
        res = min(res, abs(sum-2*sum_y));
    }
    printf("%d", res);
}
  • 时间复杂度: O ( m n ) O(mn) O(mn)
  • 空间复杂度: O ( n + m ) O(n+m) O(n+m)

今日总结

  时间没安排好,现在已经是第二天的一点多了hhh。好在今天工具用起来熟练很多,也找到点刷题的感觉了。数组总结暂且搁置(不知道会鸽多久)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值