算法题:前缀和


前缀和是一种重要的预处理,可以降低查询的时间复杂度。

面试题 04.12. 求和路径

给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。
典型的前缀和解题。
面试题 04.12. 求和路径

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> int:
        # 统计前缀和的个数
        d = {0:1}
        def helper(t, cur):
            if not t:
                return 0
            cur += t.val
            r = 0
            if cur-sum in d:
                r += d[cur-sum]
            d[cur] = d.get(cur, 0)+1
            r += helper(t.left, cur)
            r += helper(t.right, cur)
            d[cur] -= 1
            return r

        return helper(root, 0)

面试题 17.05. 字母与数字

给定一个放有字母和数字的数组,找到最长的连续子数组,且包含的字母和数字的个数相同。返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。

class Solution:
    def findLongestSubarray(self, array: List[str]) -> List[str]:
        n = len(array)
        cur = 0
        d = {0: -1}
        maxlength = 0
        start = 0
        for i in range(n):
            if array[i].isdigit():
                cur += 1 # 数字用1表示
            else:
                cur -= 1 # 字母用-1表示
            if cur not in d:
                d[cur] = i
            else:
                if maxlength < i-d[cur]:
                    maxlength = i-d[cur]
                    start = d[cur]+1
                    
        return array[start:maxlength+start]

面试题 17.05. 字母与数字

303.区域和检索-数组不可变

303.区域和检索-数组不可变
给定一个整数数组 nums,处理以下类型的多个查询:

计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的 和 ,其中 left <= right
实现 NumArray 类:

NumArray(int[] nums) 使用数组 nums 初始化对象
int sumRange(int i, int j) 返回数组 nums 中索引 left 和 right 之间的元素的 总和 ,包含 left 和 right 两点(也就是 nums[left] + nums[left + 1] + … + nums[right] )

class NumArray:
    def __init__(self, nums: List[int]):
        m = len(nums)
        if m>0:
            self.prefix = [0]*m
            t = 0
            for i in range(m):
                t += nums[i]
                self.prefix[i] = t
    def sumRange(self, left: int, right: int) -> int:
        if left >0:
            ans = self.prefix[right]-self.prefix[left-1]
        else:
            ans = self.prefix[right]
        return ans

304.二维区域和检索-矩阵不可变

304.二维区域和检索-矩阵不可变
给定一个二维矩阵 matrix,以下类型的多个请求:

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

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

class NumMatrix:

    def __init__(self, matrix: List[List[int]]):
        self.m = len(matrix)
        if self.m !=0:
            self.n = len(matrix[0])
            self.prefix = [[0]*self.n for _ in range(self.m)]
            for i in range(self.m):
                for j in range(self.n):
                    if i>0 and j>0:
                        self.prefix[i][j] = matrix[i][j] +self.prefix[i-1][j]+self.prefix[i][j-1]-self.prefix[i-1][j-1]
                    elif i > 0:
                        self.prefix[i][j] = matrix[i][j] +self.prefix[i-1][j]
                    elif j > 0:
                        self.prefix[i][j] = matrix[i][j] +self.prefix[i][j-1]
                    else:
                        self.prefix[i][j] = matrix[i][j]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        if row1>0 and col1>0:
            ans = self.prefix[row2][col2]-self.prefix[row2][col1-1]-self.prefix[row1-1][col2]+self.prefix[row1-1][col1-1]
        elif row1>0:
            ans = self.prefix[row2][col2]-self.prefix[row1-1][col2]
        elif col1 > 0:
            ans = self.prefix[row2][col2]-self.prefix[row2][col1-1]
        else:
            ans = self.prefix[row2][col2]
        return ans

427. 建立四叉树

427. 建立四叉树
给你一个 n * n 矩阵 grid ,矩阵由若干 0 和 1 组成。请你用四叉树表示该矩阵 grid 。

你需要返回能表示矩阵 grid 的 四叉树 的根结点。

四叉树数据结构中,每个内部节点只有四个子节点。此外,每个节点都有两个属性:

val:储存叶子结点所代表的区域的值。1 对应 True,0 对应 False。注意,当 isLeaf 为 False 时,你可以把 True 或者 False 赋值给节点,两种值都会被判题机制 接受 。
isLeaf: 当这个节点是一个叶子结点时为 True,如果它有 4 个子节点则为 False 。

class Node {
    public boolean val;
    public boolean isLeaf;
    public Node topLeft;
    public Node topRight;
    public Node bottomLeft;
    public Node bottomRight;
}
class Solution:
    def construct(self, grid: List[List[int]]) -> 'Node':
        n = len(grid)
        prefix = [[0]*n for _ in range(n)]
        for i in range(n):
            for j in range(n):
                if i>0 and j>0:
                    prefix[i][j] = grid[i][j] +prefix[i-1][j]+prefix[i][j-1]-prefix[i-1][j-1]
                elif i > 0:
                    prefix[i][j] = grid[i][j] +prefix[i-1][j]
                elif j > 0:
                    prefix[i][j] = grid[i][j] +prefix[i][j-1]
                else:
                    prefix[i][j] = grid[i][j]
        def helper(x1,y1,x2,y2):
            if x2-x1==0:
                return Node(grid[x1][y1], True, None, None, None, None)
            
            a = x2-x1
            # 计算和
            if x1>0 and y1>0:
                ans = prefix[x2][y2]-prefix[x2][y1-1]-prefix[x1-1][y2]+prefix[x1-1][y1-1]
            elif x1>0:
                ans = prefix[x2][y2]-prefix[x1-1][y2]
            elif y1>0:
                ans = prefix[x2][y2]-prefix[x2][y1-1]
            else:
                ans = prefix[x2][y2]
            
            if ans==0 or  ans == (a+1)*(a+1)*1:
                return Node(grid[x1][y1], True, None, None, None, None)
            else:
                a = a//2
                return Node(0, False, helper(x1,y1,x1+a,y1+a), helper(x1,y1+a+1,x1+a,y2), helper(x1+a+1,y1,x2,y1+a), helper(x1+a+1,y1+a+1,x2,y2))
        return helper(0,0, n-1, n-1)

918. 环形子数组的最大和

918. 环形子数组的最大和
这道题目是很好的一道题目。

给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。

环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。

子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], …, nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。

class Solution:
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        n = len(nums)
        # 1.连续的
        # 2.两部分,计算前缀和和后缀和
        pres = [nums[0]]
        pre,ans,pp,mpp = nums[0],nums[0],nums[0],nums[0]
        for i in range(1,n):
            pre = max(pre+nums[i], nums[i])
            ans = max(pre, ans)
            pp += nums[i]
            mpp = max(pp, mpp)
            pres.append(mpp)
        t = 0
        for i in range(n-1, 0, -1):
            t += nums[i]
            ans = max(ans, t+pres[i-1])
        return ans

class Solution:
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        n = len(nums)
        # 1.连续的
        # 2.两部分,计算前缀和和后缀和 == sum - 最小连续子数组和
        pre,ans,ssum,mpre,mmin = nums[0],nums[0],nums[0],nums[0],nums[0]
        for i in range(1,n):
            pre = max(pre+nums[i], nums[i])
            ans = max(pre, ans)

            mpre = min(mpre+nums[i], nums[i])
            mmin = min(mpre, mmin)
            ssum += nums[i]
        ans = max(ans, ssum-mmin) if ssum != mmin else ans
        return ans

class Solution:
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        n = len(nums)
        # nums + nums 转化为长度小于等于n的最大连续子数组
        # 使用前缀数组 +
        prefix = []
        t = 0
        for i in range(n+n):
            t += nums[i%n]
            prefix.append(t)
        ans = nums[0]
        dq = deque()
        for i in range(n*2):
            if not dq:
                dq.append(i)
            else:
                while dq and i-dq[0] > n:
                    dq.popleft()
                ans = max(ans, prefix[i]-prefix[dq[0]])
                while dq and prefix[i] <= prefix[dq[-1]]:
                    dq.pop()
                dq.append(i)

        return ans

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值