代码随想录算法训练营 Day02 LeetCode209. 长度最小的子数组 LeetCode59. 螺旋矩阵II

LeetCode209 长度最小的子数组

题目链接:长度最小的子数组

暴力求解

        本题的暴力求解思路很简单,由于是在一个完整的数组中寻找能够满足满足>=target的最小子数组,那么很容易就能想到使用两层for循环来遍历。外层for循环来遍历子数组的开始位置,内层for循环来遍历子数组的结束位置,当子数组之和满足条件(即>= target)时,即跳出内层循环。

        通过这种两层for循环的方式,我们可以遍历所有的搜索空间。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        sLen = len(nums) + 1
        # 暴力求解: 双重循环,遍历所有搜索空间   外层循环遍历区间的起始值,内层循环遍历区间的终止值  会超时
        for i  in range(len(nums)):
            temp = 0
            for j in range(i, len(nums)):
                temp += nums[j]
                if temp >= target: # 满足条件,即跳出内部循环,前移子数组的起始位置,即 i + 1
                    if j - i + 1 < sLen:
                        sLen = j - i + 1
                    continue
        if sLen > len(nums):
            return 0
        else:
            return sLen

        但是使用暴力求解O(n^{2})的方式,会导致程序运行超时,需要对程序进行优化,也就是引入下面的滑动窗口

滑动窗口求解

        首先需要思考的问题是:在使用滑动窗口时,需要对滑动窗口的开始位置进行遍历还是对结束位置进行遍历?我们思考一下,如果对开始位置进行遍历,那么此时和暴力求解好像没有什么区别,优化在哪里? 因此,我们需要对滑动窗口的结束位置进行遍历

        此外,当滑动窗口的右端前移之后,满足了大于等于target的条件,此时需要尝试迁移滑动窗口的左端,即滑动窗口的起始点。由于此时可能左端前移的此时大于1,因此此时需要使用while,而非if。

        一个简单的例子:

        若输入数组为:1,1,1,1,1,1,10,目标值targrt为11

        那么此时在循环移动滑动窗口的结束点(右端)时,会直接移动到数组的末尾,此时尝试前移滑动窗口的左端,那么显然此时仅前移1次是不够的。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        total = len(nums)
        sLen = total + 1
        # 滑动窗口
        i = 0
        temp = 0
        for j in range(total): # 遍历滑动窗口的结束位置
            temp += nums[j] # 计算滑动窗口之和
            while temp >= target: # 滑动窗口满足,尝试前滑动窗口起始点
                if j - i + 1 < sLen:
                    sLen = j - i + 1
                temp -= nums[i]
                i += 1
        
        if sLen > total:
            return 0
        else:
            return sLen

LeetCode59 螺旋矩阵II

题目链接:螺旋矩阵II

        本题并没有考验太多的算法,而是直接的控制数组。但是在实际写代码时,容易遇到的问题是:每次遍历边时,所采取的策略不一致,那么此时就会存在边界问题,导致一如循环深似海。

        本题与Day1中的二分法一致,需要明确循环不变量,即在多次循环时需要保持一致的策略。这里在遍历每条边时,我们采用左闭右开,在处理每条边时,只处理该边起始点与结束点前的一个点,具体的示意图如下所示,十分清晰:

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        startx = 0
        starty = 0
        offset = 1
        num = 1
        result = [[n*n]*n for _ in range(n)] # 构建默认值为n*n的二维数组

        times = 1
        i = 0
        j = 0
        while times <= n//2:
            # 遍历一圈
            i = startx
            j = starty
            # 遍历上层    
            while(j < n - offset):
                result[i][j]  = num
                num += 1
                j += 1
            # 遍历右层
            while(i < n - offset):
                result[i][j] = num
                num += 1
                i += 1
            # 遍历下层
            while(j > starty):
                result[i][j] = num
                num += 1
                j -= 1
            # 遍历左层
            while(i > startx):
                result[i][j] = num
                num += 1
                i -= 1
            # 一圈遍历完,移动到内圈
            startx += 1
            starty += 1
            offset += 1
            # 进行下一次循环
            times += 1 
        
        return result
            

代码随想录 58.区间和

题目链接:区间和

        本题需要确定一个数组内部指定两个索引的子区间内所有元素之和。

我们非常容易想到暴力遍历求解,但是会超时。

前缀和: 前缀和是数组问题中比较常用的解决策略,通过维护一个与输入数组等长的额外数组来存储前缀和,那么对于区间a,b之间所有元素的和就位对应前缀和相减。

#include <iostream>
#include <vector>
using namespace std;
int main(){
    int n, a, b;
    cin >> n;
    vector<int> vec(n);
    vector<int> p(n);
    int presum = 0;
    for(int i = 0; i < n; i++){
        cin >> vec[i];
        presum += vec[i];
        p[i] = presum;
    }

    while(cin >> a >> b){
        int sum;
        if(a == 0) sum = p[b];
        else sum = p[b] - p[a - 1];
        cout << sum << endl;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值