【算法】剑指 Offer 专项突击版 Day4数组部分

本文详细解析了《剑指Offer》中关于数组的四道经典题目,包括和为k的子数组、0和1个数相同的子数组、左右两边子数组的和相等以及二维子矩阵的和。通过前缀和与哈希表的巧妙应用,阐述了解题思路和优化技巧,帮助读者深入理解数组操作和动态规划问题。
摘要由CSDN通过智能技术生成

【算法】剑指 Offer 专项突击版 Day4数组部分

题目地址https://leetcode-cn.com/study-plan/lcof/?progress=wgzvtig

目标:要点总结,分享思路
注:在合理的条件下仅采用理解起来最简单,用起来实用的代码实现



I 【中等】010. 和为 k 的子数组

在这里插入图片描述
难度:普通,要理解一下就好,笔者最开始对于题解给出的前缀和有点懵

要点

  • 思路:先按照暴力求解方法去理解题目,这道题可以构造前缀和数组presum[],这样presum[j]-presum[i]==k的双重循环就可以求解了,时间复杂度 O ( n 2 ) O(n^2) O(n2)
  • 优化思路1:连续子数组,可不可以用滑动窗口解决,尝试后不行,因为有负数。这样在滑动窗口中无法确定到底是改滑动左指针还是右指针使其满足窗口之和
  • 优化思路2:前缀和数组用哈希表存储,边构造边寻找
    在这里插入图片描述

引用于刷穿剑指offer-Day07-数组III-010.和为k的子数组 前缀和的思路讲解

  • 个人解释(通俗解释,看懂上面的请跳过):在众多题解中这篇最简洁清晰,但个人还是不理解这里的哈希表是怎么存储到前缀和数组i和j的差值的。详细解释如下
         我们在nums循环中,每次
class Solution:
    def subarraySum(self, nums: List[int], target: int) -> int:
        presum = 0 # 前缀和
        dic = {0:1}   # dic[key]:value,代表前缀和key出现的次数
        ans = 0    
        for num in nums:
            presum += num
            # presum - target 表示以起始点到当前num为终点的前缀和比target多出的距离
            # 如果在前面点中找到刚好等于这段距离的前缀,那么就找到了路径解
            ans += dic.get(presum-target,0) 
            # 
            dic[presum] = dic.get(presum,0) + 1 
        return ans 

II 【中等】011. 0 和 1 个数相同的子数组

在这里插入图片描述
难度:有上一题作为基础,该题转换思路后简单

要点

  • 思路:数组中仅有0和1,不做任何更改滑动窗口也无法使用,因为右指针右移多个0和左指针右移少个0的效果是相同的,无法确定指针移动方向即滑窗。那为什么不直接使用前缀和算法?(笔者当时自问自答的时候懵了) 同样若直接使用前缀和会出现该怎么确定前缀和等于多少时条件成立
  • 重点:将0换成-1就可以使用前缀和了,这样只要前缀和为0就满足条件了,题目转换为前缀和为0的最长子数组。
  • 那更改后能用滑动窗口了吗,不能,即使全部改为正数,满足滑窗滑动要求,但是滑窗内的数字之和为多少才能满足条件呢?
class Solution:
    def findMaxLength(self, nums: List[int]) -> int:
        ans = 0
        dic = {0:-1} # 当前缀和等于0时,说明已经满足要求,直接i+1即可
        presum = 0
        for i,num in enumerate(nums):
            if not num:num=-1 # 0转-1
            presum += num     
            # 这里用else才对哈希表赋值可以保证仅更新最早的点,这样得到的长度最大
            # 同时这道题如果变种为求子数组长度最小在这里调整无条件字典赋值即可
            if presum in dic: 
                ans = max(ans,i-dic.get(presum))
            else:
                dic[presum] = i
        return ans

II 【简单】012. 左右两边子数组的和相等

在这里插入图片描述
难度:简单,前缀和问题

要点

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        sums   = sum(nums)
        presum = 0
        for i,num in enumerate(nums):
            leftSum  = presum
            rightSum = sums-leftSum-num
            if leftSum == rightSum:
                return i
            presum += num
        return -1

II 【中等】013. 二维子矩阵的和

在这里插入图片描述
难度

要点

# Your NumMatrix object will be instantiated and called as such:
# obj = NumMatrix(matrix)
# param_1 = obj.sumRegion(row1,col1,row2,col2)

class NumMatrix:

    def __init__(self, matrix: List[List[int]]):
        rows,cols = len(matrix),len(matrix[0]) if matrix else 0
        sums = [[0] * (cols+1) for _ in range(rows+1)]
        for i in range(rows):     # y轴向下
            for j in range(cols): # x轴向右 
            	# 求二维矩阵的前缀和
                sums[i+1][j+1] = sums[i][j+1] + sums[i+1][j] - sums[i][j] + matrix[i][j]
        self.sums = sums

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        _sums = self.sums

        return _sums[row2 + 1][col2 + 1] - _sums[row1][col2 + 1] - _sums[row2 + 1][col1] + _sums[row1][col1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿芒Aris

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

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

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

打赏作者

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

抵扣说明:

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

余额充值