leetcode303.区域和检索 - 数组不可变、leetcode307.区域和检索 - 数组可修改、lc304.二维区域和检索 - 矩阵不可变、lc308.二维区域和检索 - 可变

题目一:leetcode303.区域和检索 - 数组不可变

1.题目描述

给定一个整数数组  nums,求出数组从索引 i 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点。

示例:

给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()

sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
说明:

你可以假设数组不可变。
会多次调用 sumRange 方法。
在真实的面试中遇到过这道题?

2.解题思路

每次for循环求和会超时。


前缀和

我们用动态规划记录前i的和,那么sum(i, j) = dp[j+1] - dp[i]

为了方便计算加个0

时间复杂度:

初始化:O(n)

求和:O(1)

空间复杂度:O(n)


作者:powcai
链接:https://leetcode-cn.com/problems/range-sum-query-immutable/solution/qian-zhui-he-by-powcai-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.代码实现

class NumArray(object):

    def __init__(self, nums):
        """
        :type nums: List[int]
        """
        self.dp = [0]
        for num in nums:
            self.dp.append(self.dp[-1]+num)
        

    def sumRange(self, i, j):
        """
        :type i: int
        :type j: int
        :rtype: int
        """
        return self.dp[j+1] - self.dp[i]
            
        


# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# param_1 = obj.sumRange(i,j)

题目二:leetcode307.区域和检索 - 数组可修改

1.题目描述

给定一个整数数组  nums,求出数组从索引 i 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点。

update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。

示例:

Given nums = [1, 3, 5]

sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
说明:

数组仅可以在 update 函数下进行修改。
你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。
在真实的面试中遇到过这道题?

2.解题思路

同样是利用分块区域和的思路,下面这种方法使用了一种新的数据结构,叫做 树状数组Binary Indexed Tree,又称 Fenwick Tree,这是一种查询和修改复杂度均为 O(logn) 的数据结构。这个树状数组比较有意思,所有的奇数位置的数字和原数组对应位置的相同,偶数位置是原数组若干位置之和,假如原数组 A(a1, a2, a3, a4 ...),和其对应的树状数组 C(c1, c2, c3, c4 ...)有如下关系:

C1 = A1

C2 = A1 + A2

C3 = A3

C4 = A1 + A2 + A3 + A4

C5 = A5

C6 = A5 + A6

C7 = A7

C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8

...

那么是如何确定某个位置到底是有几个数组成的呢,原来是根据坐标的最低位 Low Bit 来决定的,所谓的最低位,就是二进制数的最右边的一个1开始,加上后面的0(如果有的话)组成的数字,例如1到8的最低位如下面所示:

坐标          二进制       最低位

1               0001          1

2               0010          2

3               001         1

4               0100          4

5               010         1

6               0110          2

7               011         1

8               1000          8

...

最低位的计算方法有两种,一种是 x&(x^(x–1)),另一种是利用补码特性 x&-x。

这道题我们先根据给定输入数组建立一个树状数组 bit,比如,对于 nums = {1, 3, 5, 9, 11, 13, 15, 17},建立出的 bit 数组为:

bit -> 0 1 4 5 18 11 24 15 74

注意到我们给 bit 数组开头 padding 了一个0,这样我们在利用上面的树状数组的性质时就不用进行坐标转换了。可以发现bit数组中奇数位上的数字跟原数组是相同的,参见上面标记蓝色的数字。偶数位则是之前若干位之和,符合上图中的规律。

现在我们要更新某一位数字时,比如将数字5变成2,即 update(2, 2),那么现求出差值 diff = 2 - 5 = -3,然后我们需要更新树状数组 bit,根据最低位的值来更新后面含有这一位数字的地方,一般只需要更新部分偶数位置的值即可。由于我们在开头 padding了个0,所以我们的起始位置要加1我们的j从位置i+1开始,即 j=3,然后现将 bit[3] 更新为2,然后更新下一位,根据图中所示,并不是 bit[3] 后的每一位都需要更新的,下一位需要更新的位置的计算方法为 j += (j&-j),这里我们的j是3,则 (j&-j) = 1,所以下一位需要更新的是 bit[4],更新为15,现在j是4,则 (j&-j) = 4,所以下一位需要更新的是 bit[8],更新为71,具体的变换过程如下所示:

0 1 4 5 18 11 24 15 74

0 1 4 2 18 11 24 15 74

0 1 4 2 15 11 24 15 74

0 1 4 2 15 11 24 15 71

接下来就是求区域和了,直接求有些困难,我们需要稍稍转换下思路。比如若我们能求出前i个数字之和,跟前j+1个数字之和,那么二者的差值就是要求的区间和了。所以我们先实现求前任意i个数字之和,当然还是要利用树状数组的性质,此时正好跟 update 函数反过来,我们的j从位置i开始,每次将 bit[j] 累加到 sum,然后更新j,通过 j -= (j&-j),这样就能快速的求出前i个数字之和了,从而也就能求出任意区间之和了

参考:https://www.cnblogs.com/grandyang/p/4985506.html

3.代码实现

class NumArray(object):

    def __init__(self, nums):
        """
        :type nums: List[int]
        """
        n = len(nums)
        self.data = [0 for _ in range(n)]
        self.bit = [0 for _ in range(n+1)]
        for i in range(n):
            self.update(i, nums[i])
        

    def update(self, i, val):
        """
        :type i: int
        :type val: int
        :rtype: None
        """
        
        diff = val - self.data[i]
        # 这句的顺序很重要,不可以放在前面去
        self.data[i] = val
        j = i+1
        while j < len(self.bit):
            self.bit[j] = self.bit[j] + diff
            j = j + (j&-j)
    def getSum(self,i):
        res = 0
        j = i
        while j > 0:
            res = res + self.bit[j]
            j = j - (j&-j)
        return res

    def sumRange(self, i, j):
        """
        :type i: int
        :type j: int
        :rtype: int
        """
        return self.getSum(j+1) - self.getSum(i)

        


# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# obj.update(i,val)
# param_2 = obj.sumRange(i,j)

题目三:lc304.二维区域和检索 - 矩阵不可变

1.题目描述

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


上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。

示例:

给定 matrix = [
  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12
说明:

你可以假设矩阵不可变。
会多次调用 sumRegion 方法。
你可以假设 row1 ≤ row2 且 col1 ≤ col2。

2.解题思路

动态规划

首先要会求一个矩阵每个元素(i, j)的左上方的元素之和;

比如 :

3 7 1          3  10 11
2 4 0  -->   5  16 17  
9 4 2         14 29 32
得到此矩阵dp,接下来要知道!

sumRegion(row1, col1, row2, col2) = dp[row2][col2] - dp[row2][col1 - 1] - dp[row1 - 1][col2] + dp[row1 - 1][col1 - 1]

为什么呢?我们画图说明,还是以上面例子求sumRegion(2, 2, 1, 1):

1570783297496.png

等于黄色的部分总和 - 两个橙色总和 + 红色部分 ( 因为我们发现当我们减去橙色部分, 红色部分多删除一次)

为了更好求解,我们加了一行和一列0


作者:powcai
链接:https://leetcode-cn.com/problems/range-sum-query-2d-immutable/solution/dong-tai-gui-hua-by-powcai-10/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.代码实现

class NumMatrix(object):

    def __init__(self, matrix):
        """
        :type matrix: List[List[int]]
        """
        if not matrix or not matrix[0]:
            return 
        row = len(matrix)
        col = len(matrix[0])
        self.dp = [[0] * (col + 1) for _ in range(row + 1)]
        # 求行的前缀和
        for i in range(1, row + 1):
            for j in range(1 ,col + 1):
                self.dp[i][j] = self.dp[i][j - 1] + matrix[i - 1][j - 1] 
        # 求列的前缀和
        for j in range(1, col + 1):
            for i in range(1, row + 1):
                self.dp[i][j] += self.dp[i - 1][j]
        

    def sumRegion(self, row1, col1, row2, col2):
        """
        :type row1: int
        :type col1: int
        :type row2: int
        :type col2: int
        :rtype: int
        """
        return self.dp[row2+1][col2+1] - self.dp[row2+1][col1] - self.dp[row1][col2+1] + self.dp[row1][col1]
        


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

题目四:lc308.二维区域和检索 - 可变

1.题目描述

给你一个 2D 矩阵 matrix,请计算出从左上角 (row1, col1) 到右下角 (row2, col2) 组成的矩形中所有元素的和。


上述粉色矩形框内的,该矩形由左上角 (row1, col1) = (2, 1) 和右下角 (row2, col2) = (4, 3) 确定。其中,所包括的元素总和 sum = 8。

示例:

给定 matrix = [
  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
update(3, 2, 2)
sumRegion(2, 1, 4, 3) -> 10
 

注意:

矩阵 matrix 的值只能通过 update 函数来进行修改
你可以默认 update 函数和 sumRegion 函数的调用次数是均匀分布的
你可以默认 row1 ≤ row2,col1 ≤ col2

2.解题思路

那么为了能够通过OJ,我们还是需要用到树状数组Binary Indexed Tree(参见题目二),其查询和修改的复杂度均为O(logn),那么我们还是要建立树状数组,我们根据数组中的每一个位置,建立一个二维的树状数组,然后还需要一个getSum函数,以便求得从(0, 0)到(i, j)的区间的数字和,然后在求某一个区间和时,就利用其四个顶点的区间和关系可以快速求出

3.代码实现

class NumMatrix(object):

    def __init__(self, matrix):
        """
        :type matrix: List[List[int]]
        """
        if not matrix or not matrix[0]:
            return
        m = len(matrix)
        n = len(matrix[0])
        self.mat = [[0 for _ in range(n)] for _ in range(m)]
        self.bit = [[0 for _ in range(n+1)] for _ in range(m+1)]
        for i in range(m):
            for j in range(n):
                self.update(i,j,matrix[i][j])
        

    def update(self, row, col, val):
        """
        :type row: int
        :type col: int
        :type val: int
        :rtype: None
        """

        diff = val - self.mat[row][col]
        self.mat[row][col] = val
        i = row+1
        while i < len(self.bit):
            # 注意j的初始化顺序
            j = col+1
            while j < len(self.bit[0]):
                self.bit[i][j] += diff
                j += j&(-j)
            i += i&(-i)
        #print self.mat
    
            
    def getSum(self,row,col):
        res = 0
        i = row
        while i > 0:
            # 注意j的初始化顺序
            j = col
            while j > 0:
                res += self.bit[i][j]
                j -= j&-j
            i -= i&-i
        return res

    def sumRegion(self, row1, col1, row2, col2):
        """
        :type row1: int
        :type col1: int
        :type row2: int
        :type col2: int
        :rtype: int
        """
        return self.getSum(row2+1, col2+1)-self.getSum(row1, col2+1)-self.getSum(row2+1, col1)+self.getSum(row1, col1)
        


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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值