首页 > 编程语言 >代码随想录算法训练营二天|209. 长度最小的子数组 59.螺旋矩阵II 区间和 开发商购买土地

代码随想录算法训练营二天|209. 长度最小的子数组 59.螺旋矩阵II 区间和 开发商购买土地

时间:2024-09-17 19:23:53浏览次数:10  
标签:59 nums 209 sum 随想录 int range result 数组

209. 长度最小的子数组

太久没做题初始思路只能想到暴力破解,看了一眼提示可能会用到前缀和,能够想到只要建立一个新数组,bi = a0 + a1 + ... + ai 即数组a的前缀,这样子序列i到j就可以表示为bj - bi-1 ,由于数组元素是大于1的,所以b数组必然是递增的,那么在计算子序列的时候,当符合条件的子序列出现后记录下来,接着就可以先向右移动后下标,移动后不满足条件后,再移动左下标,直到满足条件,如此往复。但如何实现没有特别好的思路,复杂度好像也还是很大,自己的思路到此为止。

看完代码随想录,发现自己的思路没有大的问题,但是在细节方面自己没有能够想通,总感觉差了一点细节,在这里做一些补充。

滑动窗口:类似双指针

先对终止位置进行右移,此时的操作是将子序列sum += 终止下标的数;

判断此时sum是否大于等于target:

如果符合,需要判断符合的子序列长度是否小于上一次的sum(这里我自己有个纠结的思考,其实画图会清楚很多,而且这一步思考的意义可能不大,记录一下:会以为终止下标右移到符合条件之后,一定会导致【之后的子序列长度】都大于【上一次最后符合条件的长度】,因此导致【之后能得出的结果】都是大于【上一次最后符合条件的长度】的,这个想法走进了误区,是错误的,因为虽然第一次右移到符合条件的位置时,会大于等于【上一次最后符合条件的长度】,但在之后会进行起始下标的右移,使得子序列的长度不断减小,直到小于【上一次最后符合条件的长度】,也就是从上一次符合到本次符合的过程中,起始下标可能右移了多次,并且【起始下标右移次数】>【终止下标右移次数】,使得符合的子序列长度变小,这个过程自己记录的尽可能详细了,但文字表述难免混乱模糊,只作为自己理解,看到这的朋友不理解请见代码随想录讲解视频:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili

如果不符合,则继续将终止下标右移。

思路过程文字表述如上

另外解答了我初始思路中时间复杂度的疑问,感觉进行了两层循环,时间复杂度好像还是很大的问题:

这里强调一下,不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数

了解上述思路后,开始用python实现代码(再次感叹上面C++精髓代码的精简):

补充:在Python中,float('inf') 表示正无穷大。这是一个特殊的浮点数值,用于表示超出浮点数表示范围的数值。当你尝试进行一个结果超出浮点数最大表示范围的计算时,Python会返回 float('inf')。

class Solution:

    def minSubArrayLen(self, target: int, nums: List[int]) -> int:

        length = len(nums)

        i, result, sum = 0, float('inf'), 0         #i为起始下标, result为【当前“得到过的”最小子序列“长度”】, sum为【当前子序列】之“和”

        for j in range(length):

            sum += nums[j]

            while sum >= target:    # 当子序列之和大于target的时候进入循环,开始尝试缩小滑动窗口以获得最小值

                result = min(result, j-i+1)

                sum -= nums[i]

                i += 1

        # 退出循环后,有符合条件的返回result,否则返回0

        if result < float('inf'):

            return result

        else:

            return 0

规范代码比自己的可读性稍强点,放作对比:

class Solution:

    def minSubArrayLen(self, s: int, nums: List[int]) -> int:

        l = len(nums)

        left = 0

        right = 0

        min_len = float('inf')

        cur_sum = 0 #当前的累加值

        

        while right < l:

            cur_sum += nums[right]

            

            while cur_sum >= s: # 当前累加值大于目标值

                min_len = min(min_len, right - left + 1)

                cur_sum -= nums[left]

                left += 1

            

            right += 1

        

        return min_len if min_len != float('inf') else 0

59.螺旋矩阵II

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

示例 1:

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

输入:n = 1
输出:[[1]]

提示:

  • 1 <= n <= 20

四个运动状态,且变化都是固定的,暂时没想到什么好办法,只能枚举硬解。

整理如下:

运动方向: 向右   下个状态变化: 向下/停止

运动方向: 向下   下个状态变化: 向左

运动方向: 向左   下个状态变化: 向上/停止

运动方向: 向上   下个状态变化: 向下

Right:向右时,首先为自己赋值,然后判断右边是否为数组内且为空,是的话向右执行right,如果右边不为空或者超出数组范围,则向下执行down,如果下边也不为空或者超出数组范围,则返回。

Down:向下时,首先为自己赋值,然后判断下边是否为数组内且为空,是的话向下执行down,如果下边不为空或者超出数组范围,则向左执行left,如果左边也不为空或者超出数组范围,则返回。

Left:向左时,首先为自己赋值,然后判断左边是否为数组内且为空,是的话向左执行left,如果左边不为空或者超出数组范围,则向上执行up,如果上边也不为空或者超出数组范围,则返回。

Up:向上时,首先为自己赋值,然后判断上边是否为数组内且为空,是的话向上执行up,如果上边不为空或者超出数组范围,则向右执行down,如果右边也不为空或者超出数组范围,则返回。

(由于要考虑n=1的情况,所以把每个方向的两种情况都考虑了,实际情况可能不需要,所以存在优化空间,自己画图模拟了一下,发现好像只有向右和向左的情况下会停下来成为终点。而且完成最外围一圈之后,剩下的部分起始是新的完整的一圈。原本考虑想用递归,发现不是很好用甚至有点忘了咋怎么用。。决定换个写法,但过程不变,写完发现实在过于暴力,可维护性非常地差,并且debug了很多次,虽然过了,但非常不推荐

class Solution:
   def generateMatrix(self, n: int) -> List[List[int]]:
       direction = 'r'
       count = 1
       x, y = 0, 0
       nums = [[0 for _ in range(n)] for _ in range(n)]
       while count <= n ** 2:
           nums[x][y] = count
           if direction == 'r': # 向右
               if (y + 1) < n and nums[x][y + 1] == 0:  # 未超出范围且数组为空
                   y += 1
                   direction = 'r'
               elif (y + 1 < n and nums[x][y + 1] != 0) or y + 1 >= n:  # 未超出但数组不为空(已填)或者超出范围,这类情况均转向
                   x += 1
                   direction = 'd'
               else: # 向右和向左存在终止的情况,可能返回
                   return nums
           elif direction == 'd': # 向下的情况,除了不会终止过程,其他过程与上述类似。
               if x + 1 < n and nums[x + 1][y] == 0:
                   x += 1
                   direction = 'd'
               elif (x + 1 < n and nums[x + 1][y] != 0) or x + 1 >= n:
                   y -= 1
                   direction = 'l'
           elif direction == 'l':
               if y - 1 >= 0 and nums[x][y - 1] == 0:  
                   y -= 1
                   direction = 'l'
               elif (y - 1 >= 0 and nums[x][y - 1] != 0) or y - 1 < 0:  
                   x -= 1
                   direction = 'u'
               else:
                   return nums
           else:
               if x - 1 >= 0 and nums[x - 1][y] == 0:
                   x -= 1
                   direction = 'u'
               elif (x - 1 >= 0 and nums[x - 1][y] != 0) or x - 1 < 0:
                   y += 1
                   direction = 'r'
           count += 1
       return nums

虽然结果过了,但这过程确实很痛苦,要足够细心和耐心,尤其代码可维护性相当差,非常不推荐。下面附上学习卡哥的题解,感觉简单很多,就是需要认真看看视频理解一下。

核心是:循环不变量原则左闭右开

class Solution:

    def generateMatrix(self, n: int) -> List[List[int]]:

        nums = [[0] * n for _ in range(n)]

        startx, starty = 0, 0               # 起始点

        loop, mid = n // 2, n // 2          # 迭代次数、n为奇数时,矩阵的中心点

        count = 1                           # 计数



        for offset in range(1, loop + 1) :      # 每循环一层偏移量加1,偏移量从1开始

            for i in range(starty, n - offset) :    # 从左至右,左闭右开

                nums[startx][i] = count

                count += 1

            for i in range(startx, n - offset) :    # 从上至下

                nums[i][n - offset] = count

                count += 1

            for i in range(n - offset, starty, -1) : # 从右至左

                nums[n - offset][i] = count

                count += 1

            for i in range(n - offset, startx, -1) : # 从下至上

                nums[i][starty] = count

                count += 1                

            startx += 1         # 更新起始点

            starty += 1



        if n % 2 != 0 : # n为奇数时,填充中心点

            nums[mid][mid] = count

        return nums

这里不多说什么了。。只要耐心总有人能A,但是理解了核心方法才有可能在短时间内做出来。

区间和

题目描述

给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。

输入描述

第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。

输出描述

输出每个指定区间内元素的总和。

输入示例

5
1
2
3
4
5
0 1
1 3

1
2
3
4
5
6
7
8

输出示例

3
9

1
2

数据范围:

0 < n <= 100000

def main():
   n = int(input())
   nums = [None] * n
   sum = [None] *n
   for i in range(n):
       val = int(input())
       nums[i] = val
   sum [0] = nums[0]
   for i in range(n-1):
       sum[i+1] = sum[i] + nums[i+1]
   while True:
       try:
           ab = input().split()
           start, end = int(ab[0]), int(ab[1])
           print(int(sum[end]) - int(sum[start]) + int(nums[start]))
'''
            #这个位置今天debug了很久,一开始的写法是 

            # print(sum[ab[1]] - sum[ab[0]] + nums[ab[0]])

            #后面发现一直不过,原因可能如下:

            #在 print(int(sum[ab[1]]) - int(sum[ab[0]]) + int(nums[ab[0]])) 这一行,如果 ab[0] 和 ab[1] 是数组的索引,那么 sum[ab[1]] 可能会超出数组的索引范围,因为 ab[1] 应该是一个数字,而不是直接用作索引。输入的 ab 应该是两个数字,代表查询的起始和结束位置。但是,程序没有对这些输入进行有效的错误处理或验证。         

'''
       except:
           break


if __name__ == "__main__":
   main()

今日另外一个纠错:

#ab=int(input().split())  这个是错误的

这样的代码是不正确的,因为它试图将一个列表转换为整数,这会导致错误。input().split() 会返回一个字符串列表,即使你只输入了一个数字。

如果你想要读取一个数字,并且确保它是一个整数,你应该这样做:

python

ab = int(input())

如果你想要读取两个数字,并将它们存储在两个变量中,你可以这样做:

python

a, b = map(int, input().split())

这里,input().split() 会读取一行输入并将其分割成多个字符串,map(int, ...) 会将这些字符串转换为整数。

今天使用的ACM模式,以前掌握了C++的写法,头回使用python,还是非常难受的,不过有收获,下一次使用应该不会再浪费那么多时间了。

提供规范代码,写的比自己好很多,需要学习

import sys
input = sys.stdin.read

def main():
    data = input().split()
    index = 0
    n = int(data[index])
    index += 1
    vec = []
    for i in range(n):
        vec.append(int(data[index + i]))
    index += n

    p = [0] * n
    presum = 0
    for i in range(n):
        presum += vec[i]
        p[i] = presum

    results = []
    while index < len(data):
        a = int(data[index])
        b = int(data[index + 1])
        index += 2

        if a == 0:
            sum_value = p[b]
        else:
            sum_value = p[b] - p[a - 1]

        results.append(sum_value)

    for result in results:
        print(result)

if __name__ == "__main__":
    main()

44. 开发商购买土地

【题目描述】

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

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

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。

为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。

注意:区块不可再分。

【输入描述】

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

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

输出描述

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

【输入示例】

3 3 1 2 3 2 1 3 1 2 3

【输出示例】

0

【提示信息】

如果将区域按照如下方式划分:

1 2 | 3 2 1 | 3 1 2 | 3

两个子区域内土地总价值之间的最小差距可以达到 0。

【数据范围】:

  • 1 <= n, m <= 100;
  • n 和 m 不同时为 1。

初始思路没有什么特别的,实现思路很快完成,但是这个ACM模式真的又是卡了我好久。。希望明天不要又是这个情况了。

思路:先将需要的数组数值填满,由于需要切一刀将总土地分成两份,所以在遍历数组的时候,分为两种情况,我用两个循环实现,复杂度不意外的是O(n2)。

  1. 从左向右遍历,同时记录遍历到的加数总和total_x,每到达一次右边界的时候计算一次差值,相当于到达右边界的时候进行了横切一刀的操作。表达式为|total_上-total_下| = |total - 2*total_上|
  2. 从上往下遍历,同理到达下边界的时候计算一次差值,相当于到达下边界的时候进行了竖切一刀。

def main():

    data = input().split()

    n = int(data[0])

    m = int(data[1])



    space = [[0 for _ in range(m)] for _ in range(n)]

    total = 0

    total_x, total_y = 0, 0

    result = float('inf')



    for i in range(n):

        row = input().split()

        for j in range(m):

            space[i][j] = int(row[j])

            total += space[i][j]



    for i in range(n):

        for j in range(m):

            total_x += space[i][j]

            if j == m-1:

                result = min(result, abs(total-2*total_x))



    for j in range(m):

        for i in range(n):

            total_y += space[i][j]

            if i == n-1:

                result = min(result, abs(total-2*total_y))



    print(result)









if __name__ == "__main__":

    main()

和规范代码的实现差不多,思路基本没问题。还是得吐槽,又在acm的输入上浪费了太多的时间。。。

以下是规范代码:

def main():

    import sys

    input = sys.stdin.read

    data = input().split()

    

    idx = 0

    n = int(data[idx])

    idx += 1

    m = int(data[idx])

    idx += 1

    sum = 0

    vec = []

    for i in range(n):

        row = []

        for j in range(m):

            num = int(data[idx])

            idx += 1

            row.append(num)

            sum += num

        vec.append(row)



    result = float('inf')

    

    count = 0

    # 行切分

    for i in range(n):

        

        for j in range(m):

            count += vec[i][j]

            # 遍历到行末尾时候开始统计

            if j == m - 1:

                result = min(result, abs(sum - 2 * count))



    count = 0

    # 列切分

    for j in range(m):

        

        for i in range(n):

            count += vec[i][j]

            # 遍历到列末尾时候开始统计

            if i == n - 1:

                result = min(result, abs(sum - 2 * count))



    print(result)

if __name__ == "__main__":

    main()

标签:59,nums,209,sum,随想录,int,range,result,数组
From: https://blog.csdn.net/weixin_47681529/article/details/142186124

相关文章

  • 代码随想录算法训练营第三天|203移除链表元素 707设计链表 206反转链表
    203.移除链表元素题意:删除链表中等于给定值val的所有节点。示例1:输入:head=[1,2,6,3,4,5,6],val=6输出:[1,2,3,4,5]示例2:输入:head=[],val=1输出:[]示例3:输入:head=[7,7,7,7],val=7输出:[]链表一直是处理的不太好的数据结构,太久没处理过,第一次做......
  • 代码随想录算法训练营第四天|24两两交换链表中的节点 19删除链表的倒数第N个节点 02.0
    24.两两交换链表中的节点给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。由于今天赶时间,第一眼看完题目没思路就直接看文字解析了,但还是复述一下看完之后自己的理解/想法/思路。这一题感觉对......
  • 代码随想录Day2 | LeetCode 209. 长度最小的子数组、LeetCode 59. 螺旋矩阵 II、KamaC
    LeetCode209.长度最小的子数组子数组是一个连续的,很容易想到滑动窗口classSolution:defminSubArrayLen(self,target:int,nums:List[int])->int:windowSum=0left,right=0,0res=float('inf')whileright<len(nums......
  • 代码随想录算法训练营,9月17日 | 669. 修剪二叉搜索树,108.将有序数组转换为二叉搜索树,5
    669.修剪二叉搜索树题目链接:669.修剪二叉搜索树文档讲解︰代码随想录(programmercarl.com)视频讲解︰修剪二叉搜索树日期:2024-09-17想法:节点为空返回空,值在中间时,继续递归左右两边,小于时递归右子树,大于时递归左子树Java代码如下:classSolution{publicTreeNodetrimBST......
  • ICM20948 DMP代码详解(25)
    接前一篇文章:ICM20948DMP代码详解(24) 上一回讲到了inv_icm20948_load_firmware函数,对于大体功能进行了介绍,本回深入其具体实现代码细节。为了便于理解和回顾,再次贴出相关代码: //SetupIvoryDMP. result|=inv_icm20948_load_firmware(s,dmp3_image,dmp3_image_size);......
  • 代码随想录Day15 动态规划--2
    01背包理论基础01背包是有一个背包有M的容量给我们N个物品每个物品有自己的价值我们需要装进背包里尽可能获得最大的价值分析题目把背包想象成一个二维数组先遍历每个物品放入背包里面dp数组的含义表示的就是背包所含有的价值当我们遍历物品1的时候我们要开始更新背包......
  • 代码随想录算法 - 二叉树7
    题目1669.修剪二叉搜索树给你二叉搜索树的根节点root,同时给定最小边界low和最大边界high。通过修剪二叉搜索树,使得所有节点的值在[low,high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。可以证明,存在唯一的答案......
  • 59. 螺旋矩阵 II
    不知道一年后会成长成什么样,只感觉好难好难。有好多东西要学,源码也看不懂,项目也不会做。classSolution{public:vector<vector<int>>generateMatrix(intn){vector<vector<int>>vec(n,vector<int>(n,0));intnum=1;for(inti=0;i......
  • 代码随想录算法 - 二叉树6
    题目1235.二叉搜索树的最近公共祖先给定一个二叉搜索树,找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树T的两个结点p、q,最近公共祖先表示为一个结点x,满足x是p、q的祖先且x的深度尽可能大(一个节点也可以是它自己的祖先)。”例如......
  • 代码随想录训练营第34天|dp前置转移
    62.不同路径classSolution{public:intuniquePaths(intm,intn){vector<vector<int>>dp(m,vector<int>(n,1));for(inti=1;i<m;i++){for(intj=1;j<n;j++){dp[i][j]=dp[i-1][j]+dp[i][......