剑指offer2 数组

剑指 Offer II 007. 数组中和为 0 的三个数

题目描述

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c ,使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/1fGaJU

关键是怎么做到不重复

题解

这道题是的升级版,其实只要外面套一层for循环就好了。问题是怎么去重,可以用集合(额外增加复杂度)。主要是算法优化

返回的是三元组[i,j,k]

首先排序一下,使第一个元素不充分 nums[i] == nums[i-1]跳过即可

使第二个元素不重复计算  nums[j] == nums[j-1]跳过即可

第三个要不要考虑,当然要,但是如果排序了的话,就解决了

def threeSum(self, nums: List[int]) -> List[List[int]]:
    #找出所有
    #和为 0 且 不重复 的三元组

    # 首先排序
    nums.sort()
    length = len(nums)
    ret = []
    for i in range(length-2):
        if nums[i]>0:break
        # 对第一个元素去重复
        if i>0 and nums[i]==nums[i-1]:continue
        j = i+1
        k = length-1
        while j<k:
            sum = nums[i]+nums[j]+nums[k]
            if sum ==0:
                ret.append([nums[i],nums[j],nums[k]]) 
                #对第二个元素去重
                second = nums[j]
                while j<k and nums[j] == second:
                    j+=1
            elif sum<0:
                j+=1
            else:
                k-=1
    return ret

 

剑指 Offer II 008. 和大于等于 target 的最短子数组

题目描述

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/2VG8Kg

题解

滑动窗口

连续子数组优先想到滑动窗口

def minSubArrayLen(self, target: int, nums: List[int]) -> int:
    # 优化首轮有没有最优解1
    if nums.count(target)>0:return 1
    # 滑动窗口
    start,end = 0,0
    n = len(nums)
    minimun = n+1
    sums = 0
    while end<n:
        sums+=nums[end]
        # 如果窗口和大于等于target,更新最小值,并缩小窗口
        while sums >= target:
            minimun = min(minimun,end-start+1)
            sums -=nums[start]
            start+=1
        # 扩大窗口
        end+=1
    
    return 0 if minimun == n+1 else minimun

前缀和方法

由于数列中的元素都是正整数,所以前缀和函数是单调递增的

def minSubArrayLen(self, target: int, nums: List[int]) -> int:
    # 优化首轮有没有最优解1
    if nums.count(target)>0:return 1
    # 前缀和  单调函数  可以拓展到二分
    left,temp = 0,0
    n = len(nums)
    res = n+1
    for right in range(n):
        temp += nums[right] #前缀和
        while left <= right and temp >= target:
            res = min(res,right - left + 1)
            temp -= nums[left]
            left += 1
    return res if res != n+1 else 0

可以用二分法优化里面的查找

剑指 Offer II 009. 乘积小于 K 的子数组

题目描述

给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。

剑指 Offer II 009. 乘积小于 K 的子数组 - 力扣(LeetCode) (leetcode-cn.com)

题解

滑动窗口

def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
    # 正整数数组 连续的子数组  找...的个数
    
    # 滑动窗口1
    left,ret= 0,0
    n = len(nums)
    prod=1
    for right,num in enumerate(nums):
        prod*=num
        while left<=right and prod>=k:
            prod //= nums[left]
            left+=1
        # 找到了左边界,由于都是正整数的原因,左边界再往左都会成立,只要左边界在右边界的左或等
        if left <=right:
            ret += right-left+1
    return ret
  •  为什么enumerate数组的右边界?保证不会越界

剑指 Offer II 010. 和为 k 的子数组

题目描述

给定一个整数数组和一个整数 k ,请找到该数组中和为 k 的连续子数组的个数。

剑指 Offer II 010. 和为 k 的子数组 - 力扣(LeetCode) (leetcode-cn.com)

题解

整数数组有负数不太能用二分法,考虑前缀和方法

前缀和

  • 遍历数组一次
  • 字典的键key是前缀和,字典的值是前面有几个前缀和为key的位置
def subarraySum(self, nums: List[int], k: int) -> int:
    # 整数数组  连续子数组
    # 可能有负数,滑动窗口不好用了
    left,ret = 0,0
    sums = 0
    # 字典的键key是前缀和,字典的值是前面有几个前缀和为key的位置
    sums_dict = {0:1}
    for num in nums:
        sums +=num #前缀和
        ret += sums_dict.get(sums-k,0)
        sums_dict[sums] = sums_dict.get(sums,0)+1
    return ret

剑指 Offer II 011. 0 和 1 个数相同的子数组

题目描述

给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。

剑指 Offer II 011. 0 和 1 个数相同的子数组 - 力扣(LeetCode) (leetcode-cn.com)

题解

小技巧,把0变-1的话,就有不变的target了,即0,就可以使用前缀和来做,不然的话不好给条件

前缀和

  • 题目求最长的连续子数组,故hash表只要记录第一次遇到的前缀和
  • 如果遍历前缀和list过程中,发现字典中有当前前缀和,就说明添加的这些元素的和刚好等于target(把0变成-1,那么这里的target就是0 )
def findMaxLength(self, nums: List[int]) -> int:
    n = len(nums)
    #将所有0转化为-1,那么如果遇到了相同数量的0和1,累加之后的结果就为0,转化为前缀和,k=0
    pre_dict = {0:-1}
    ret,pre_sum = 0,0
    for index,num in enumerate(nums):
        pre_sum += 1 if num ==1 else -1
        # 如果加了个寂寞
        if pre_sum in pre_dict:
            ret = max(ret, index-pre_dict[pre_sum])
        else:
            # 如果初次见面
            pre_dict[pre_sum] = index
    return ret

剑指 Offer II 012. 左右两边子数组的和相等

题目描述

给你一个整数数组 nums ,请计算数组的 中心下标 。

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/tvdfij

题解

 

 

前缀和

  • 由于最左边也可以视为中心下标且左端值视为0,那么前缀和最开始的时候便插入一个0
  • 如果最右端可以是中心下标,那么最左端也是中心下标,返回最左端
def pivotIndex(self, nums: List[int]) -> int:
    pre_sum = [0]
    tmp = 0
    for num in nums:
        tmp+=num
        pre_sum.append(tmp)

    # 剩下的问题就只和前缀和数组有关了
    
    for index,pre in enumerate(pre_sum[:-1]):
        if pre == pre_sum[-1] - pre_sum[index+1]:
            return index
    return -1
    '''
    # 这个方法为什么不行,因为没有考虑边界
    pre_sum = []
    tmp = 0
    for num in nums:
        tmp+=num
        pre_sum.append(tmp)
    for index,pre in enumerate(pre_sum[:-1]):
        if pre_sum[index-1] == pre_sum[-1] - pre:
            return index
    return -1
    '''

剑指 Offer II 013. 二维子矩阵的和

题目描述

给定一个二维矩阵 matrix,以下类型的多个请求:

  •   计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2) 。

实现 NumMatrix 类:

  • NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
  • int sumRegion(int row1, int col1, int row2, int col2) 返回左上角 (row1, col1) 、右下角 (row2, col2) 的子矩阵的元素总和。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/O4NDxx题解

还是用前缀和的方法就可以做,有点像动态规划

  • i,j位置记录了i,j 到左下角所有位置的前缀和
  • 注意最下面一行和最左边一列的初始化
  • 注意返回时的数组越界问题
0,0       0,4
1,0        
      i,j  
         
4,0       4,4

前缀和

def __init__(self, matrix: List[List[int]]):
    
    rows,columns = len(matrix),len(matrix[0])
    # 初始化
    self.presum_table = [[0 for c in range(columns)] for r in range(rows)]
    # 求前缀和,从左开始一层一层往上往右求
    # 最左边和最下面一行就等于matrix的值
    
    for j in range(columns):
        self.presum_table[rows-1][j] = matrix[rows-1][j] + self.presum_table[rows-1][j-1]
    
    for i in list(range(rows))[:-1][::-1]:
        self.presum_table[i][0] = matrix[i][0] +self.presum_table[i+1][0]
    # 从(1,1)开始往上求和
    for i in list(range(rows))[::-1][1:]:
        for j in list(range(columns))[1:]:
            self.presum_table[i][j] = self.presum_table[i][j-1] + self.presum_table[i+1][j] - self.presum_table[i+1][j-1] + matrix[i][j]


def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
    rows,columns = len(self.presum_table),len(self.presum_table[0])
    if row2==rows-1 and col1 == 0:
        return self.presum_table[row1][col2]
    if row2==rows-1:
        return self.presum_table[row1][col2]-self.presum_table[row1][col1-1]
    if col1 == 0:
        return self.presum_table[row1][col2]-self.presum_table[row2+1][col2]
    if row2<rows-1 and col1>=1:
        ret = self.presum_table[row1][col2]-self.presum_table[row1][col1-1]-self.presum_table[row2+1][col2]+self.presum_table[row2+1][col1-1]
        return ret

 现在问题来了,为什么大佬的代码那么短啊!!!!?

def __init__(self, matrix: List[List[int]]):
    m, n = len(matrix), (len(matrix[0]) if matrix else 0)
    self.sums = [[0] * (n + 1) for _ in range(m + 1)]
    _sums = self.sums

    for i in range(m):
        for j in range(n):
            _sums[i + 1][j + 1] = _sums[i][j + 1] + _sums[i + 1][j] - _sums[i][j] + matrix[i][j]

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]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 3, 3, 4, 8, 10]
[0, 8, 14, 18, 24, 27]
[0, 9, 17, 21, 28, 36]
[0, 13, 22, 26, 34, 49]
[0, 14, 23, 30, 38, 58]

可以看到大佬的前缀和矩阵是6*6的,但是数据只有5*5

主要是边界不好取,只要多一个格子存边界前,或者边界下,anyway, 全0 ,那么最后就可以省很多代码,上上面的方法用于原地修改比较好

  • 前缀和改进

 row不变,因为全0加在底下,但是col却要变,因为加了一列全0在左边,固col标号+1

def __init__(self, matrix: List[List[int]]):

    rows,columns = len(matrix),len(matrix[0])
    # 初始化
    self.presum_table = [[0 for c in range(columns+1)] for r in range(rows+1)]
    # 求前缀和,从左开始一层一层往上往右求
    # 最左边和最下面一行就等于matrix的值
    for i in list(range(rows+1))[::-1][1:]:
        for j in list(range(columns+1))[1:]:
            self.presum_table[i][j] = self.presum_table[i][j-1] + self.presum_table[i+1][j] - self.presum_table[i+1][j-1] + matrix[i][j-1]


def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:

    ret = self.presum_table[row1][col2+1]-self.presum_table[row1][col1-1+1]-self.presum_table[row2+1][col2+1]+self.presum_table[row2+1][col1-1+1]
    return ret

欸现在我的代码也很短了🤗

posted @ 2021-12-23 21:04  PiaYie  阅读(53)  评论(0编辑  收藏  举报