代码随想录算法训练营第二天|数组|Leetcode209、904、76、59、54、卡码网44

209.长度最小的子数组

文章链接:代码随想录

题目链接:力扣题目链接

思路

题目要求

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

分析

        题干要求寻找一个满足条件的长度最小的子数组,可以考虑使用双指针法。

        初始化左、右指针指向数组的首部,随后右指针遍历数组,一边遍历,一遍将数组值加入到一个变量sum中(该变量用于计算从左指针到右指针的数组数值大小)。

        当sum>=target时,进入while循环,计算此时子数组的长度,并将左指针向右移动。左指针移动的同时,sum要减去左指针指向的数值,直到不满足条件:sum>=target,退出while循环,右指针继续向右移动。

        当遍历完整个数组时,便能找到答案。代码如下:

代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = INT_MAX;/*用于记录数组长度,因要求最小长度,所以初始化为最大值*/
        int sum = 0;
        int left = 0;
        for(int right = 0;right<nums.size();++right){
            sum += nums[right];
            while(sum>=target){
                n = min(n,right-left+1);
                sum -= nums[left];
                left++;
            }
        }
        return n == INT_MAX?0:n;
    }
};

904.水果成篮

题目链接:力扣题目链接

思路

题目要求

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

分析

        刚读完题目时感觉题目有点抽象,甚至不明白题目是什么含义。其实仔细分析,会发现,题目的意思其实是找出整数数组fruits中的最长子数组,要求子数组中只能含有两种数(注意!是两种数,不是两个数)。比如1、2或2、3,不能是1、2、3,因为此时含有三种类型的数。

        明白题目要求后,现在开始解题。

        初始化左右指针指向数组的首部,同时使用哈希表来记录子数组中数的种数,当种数大于2时,进入while循环,移动从哈希表中减去左指针指向的数,如果此时哈希表统计的左指针指向的数据的个数为0,则将该数从哈希表中移除,然后移动左指针(注意!是先减再移,不能先移再减)。直到种数小于等于2时,退出循环。

        还需初始化一个变量len,用于记录子数组的长度,此时len在while循环之外。因为len只记录种数小于等于2的数组长度,当进入while循环时,说明此时种数已经大于2了,无需记录数组长度。

代码
class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int len = 0;
        map<int,int> cnt;
        int left = 0;
        for(int right = 0;right<fruits.size();++right){
            cnt[fruits[right]] += 1;
            while(cnt.size() > 2){
                cnt[fruits[left]] -= 1;
                if(cnt[fruits[left]] == 0){
                    cnt.erase(fruits[left]);
                }
                left++;
            }
            len = max(len,right-left+1);
        }
        return len;

    }
};

76.最小覆盖字串

题目链接:76. 最小覆盖子串 - 力扣(LeetCode)

思路

题目要求

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

分析

        初始化两个哈希表s_cnt,t_cnt用于统计s、t中的字符个数。首先遍历t,统计t中的字母个数。

        初始化变量min_left(记录最小字串的左端)、min_right(记录最小字串的右段)、left、right

        接着初始化左右指针指向s的首部,右指针遍历字符串s,当出现左右指针的子串完全包含t的情况时,判断right-left是否小于min_right-min_left,即该子串是否为最小子串,若是,则记录,反之,则跳过。当遍历完s后,判断min_left是否为-1,如果是,证明s中不存在涵盖t所有字符的子串,此时返回空字符串,反之返回子串。

代码
class Solution {
public:
    bool is_covered(unordered_map<char,int>& s_cnt,unordered_map<char,int>& t_cnt){
        for(char i = 'A';i<='Z';++i){
            if(s_cnt[i]<t_cnt[i]) return false;
        }
        for(char i = 'a';i<='z';++i){
            if(s_cnt[i]<t_cnt[i]) return false;
        }
        return true;
    }
    string minWindow(string s, string t) {
        unordered_map<char,int> s_cnt;
        unordered_map<char,int> t_cnt;
        for(char x:t){
            t_cnt[x]++;
        }
        int min_left = -1,min_right = s.size();
        int left = 0;
        for(int right = 0;right<s.size();++right){
            s_cnt[s[right]]++;
            while(is_covered(s_cnt,t_cnt)){
                if(right-left<min_right-min_left){
                    min_left = left;
                    min_right = right;
                }
                s_cnt[s[left]]--;
                left++;
            }
        }
        return min_left == -1?"":s.substr(min_left,min_right-min_left+1);
    }
};

59.螺旋矩阵Ⅱ

文章链接:代码随想录

题目链接:59. 螺旋矩阵 II - 力扣(LeetCode)

思路

题目描述

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

分析

一、使用vector新建一个n*n的矩阵。

二、初始化startx,starty为0,表示转圈的行起点和列起点。

三、计算圈数loop为n/2,若n为偶数,则转完时,矩阵刚好填充完;若n为奇数,则转弯时,最中间的值需要手动进行赋值,此时最中间的坐标为[startx,starty]。初始化变量offset为1,因为遍历时要遵循左闭右开的原则,需要用offset来控制边的尾部。

四、进行while循环进行转圈遍历,循环条件为(loop--),遵循左闭右开的原则,即每次处理一条边时,处理边的首部,不处理边的尾部。每循环完一次,startx、starty、offset都要进行加1。

五、处理n为奇数的情况,进行单独赋值。

六、返回矩阵即可。

代码
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n,vector<int>(n,0));
        int startx = 0,starty = 0;
        int quanshu = n/2;
        int offset = 1;
        int cnt = 1;
        while(quanshu--){
            int i = startx,j = starty;
            for(;j<n-offset;j++){
                matrix[i][j] = cnt++;
            }
            for(;i<n-offset;i++){
                matrix[i][j] = cnt++;
            }
            for(;j>starty;j--){
                matrix[i][j] = cnt++;
            }
            for(;i>startx;i--){
                matrix[i][j] = cnt++;
            }
            startx++;
            starty++;
            offset++;
        }
        if(n % 2 == 1){
            matrix[startx][starty] = cnt;
        }
        return matrix;
    }
};

54.螺旋矩阵

题目链接:54. 螺旋矩阵 - 力扣(LeetCode)

思路

题目描述

        给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

分析

        该题与59的区别在于,给定的矩阵的行和列不一定是相等的,所以需要一些额外的操作。

一、使用变量minimum来记录行和列的最小值,即minimum = min(m,n);

二、遍历的圈数loop由minimum来决定,即loop = minimum/2;

三、while循环遍历时,遍历列用n,遍历行用m(不要都顺手写成了n,不然会报错)。

四、while循环结束后,需判断minimum是奇数还是偶数,若为偶数,则说明矩阵已全部遍历完毕,若为奇数,则需要判断minimum是行还是列,若为行,则说明列大,需单独取剩余元素,若为列,则说明行大,需单独取剩余元素。

代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> ans;
        int m = matrix.size();
        int n = matrix[0].size();
        int minimum = min(m,n);
        int quanshu = minimum/2;
        int startx = 0,starty = 0;
        int offset = 1;
        while(quanshu--){
            int i = startx,j = starty;
            for(;j<n-offset;++j){
                ans.push_back(matrix[i][j]);
            }
            for(;i<m-offset;++i){
                ans.push_back(matrix[i][j]);
            }
            for(;j>starty;--j){
                ans.push_back(matrix[i][j]);
            }
            for(;i>startx;--i){
                ans.push_back(matrix[i][j]);
            }
            startx++;
            starty++;
            offset++;
        }
        if(minimum % 2 == 1){
            if(minimum == m){
              for(int j = starty;j<=n-offset;++j){
                ans.push_back(matrix[startx][j]);
                }  
            }
            else if(minimum == n){
                for(int i = startx;i<=m-offset;++i){
                ans.push_back(matrix[i][starty]);
                }
            }
        }
        return ans;
    }
};

卡码网44.开发商购买土地

文章链接:代码随想录

题目链接:44. 开发商购买土地(第五期模拟笔试)

思路

题目描述

在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。 

现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。 为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。 

注意:区块不可再分。

输入描述

第一行输入两个正整数,代表 n 和 m。 

接下来的 n 行,每行输出 m 个正整数。

输出描述

请输出一个整数,代表两个子区域内土地总价值之间的最小差距。

分析

        该题目含义是将一个n*m矩阵通过横切或纵切的方式分为两个矩阵,且要求这两个矩阵的差值最小。

一、建立一个n*m的矩阵,初始化变量sum为0(sum用于记录矩阵的总值),边填充矩阵,边用sum记录总值。

二、初始化一个长度为n的数组,用于记录矩阵的行区间和,初始化一个长度为m的数组,用于记录矩阵的列区间和。

三、遍历行区间和,记录区间相减的最小值,遍历列区间和,记录区间相减的最小值。

四、比较并返回最小值。

代码
#include <iostream>
#include <vector>
#include <climits>/*若要使用INT_MAX,需引入该库*/
using namespace std;

int main(){
    int n,m;
    cin>>n>>m;
    int sum = 0;
    vector<vector<int>> matrix(n,vector<int>(m,0));
    for(int i=0;i<n;++i){
        for(int j=0;j<m;++j){
            cin>>matrix[i][j];
            sum += matrix[i][j];
        }
    }
    vector<int> hang(n,0);
    vector<int> lie(m,0);
    for(int i = 0;i<n;++i){
        for(int j = 0;j<m;++j){
            hang[i] += matrix[i][j];
        }
    }
    for(int j = 0;j<m;++j){
        for(int i = 0;i<n;++i){
            lie[j] += matrix[i][j];
        }
    }
    int res_hang = INT_MAX,res_lie = INT_MAX;
    int res_sum_hang = 0;
    int res_sum_lie = 0;
    for(int i=0;i<n;++i){
        res_sum_hang += hang[i];
        res_hang = min(res_hang,abs(sum-res_sum_hang-res_sum_hang));
    }
    for(int j = 0;j<m;++j){
        res_sum_lie += lie[j];
        res_lie = min(res_lie,abs(sum-res_sum_lie-res_sum_lie));
    }
    cout<<min(res_hang,res_lie);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值