Leetcode——普通数组

53.最大子数组和

给你一个整数数组nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
题解:使用dpi表示以索引为i的最大子数组和

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 0:
            return 0
        dp = [0] * n
        dp[0] = nums[0]
        for i in range(1, n):
            dp[i] = max(nums[i], nums[i] + dp[i - 1])
        res = float("-inf")
        for i in range(n):
            res = max(res, dp[i])
        return res

dpi只与dpi-1有关,利用空间压缩实现优化

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 0:
            return 0
        # base case
        dp_0 = nums[0]
        dp_1 = 0
        res = dp_0

        for i in range(1, n):
            # dp[i] = max(nums[i], nums[i] + dp[i-1])
            dp_1 = max(nums[i], nums[i] + dp_0)
            dp_0 = dp_1
            # 顺便计算最大的结果
            res = max(res, dp_1)
        
        return res

也可以使用前缀和,nums[i]为结尾的最大子数组之和是多少?其实就是preSum[i+1]-min(preSum[0..i])

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        preSum = [0] * (n + 1)
        preSum[0] = 0
        # 构造 nums 的前缀和数组
        for i in range(1, n + 1):
            preSum[i] = preSum[i - 1] + nums[i - 1]
        
        res = float('-inf')
        minVal = float('inf')
        for i in range(n):
            # 维护 minVal 是 preSum[0..i] 的最小值
            minVal = min(minVal, preSum[i])
            # 以 nums[i] 结尾的最大子数组和就是 preSum[i+1] - min(preSum[0..i])
            res = max(res, preSum[i + 1] - minVal)
        return res

另外也可以使用滑动窗口,在窗口内元素之和大于等于0时扩大窗口,在窗口内元素之和小于0时缩小窗口,在每次移动窗口时更新答案

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        left, right = 0, 0
        windowSum, maxSum = 0, float('-inf')
           
        while right < len(nums):
            # 扩大窗口并更新窗口内的元素和
            windowSum += nums[right]
            right += 1
            
            # 更新答案
            maxSum = max(windowSum, maxSum)
            
            # 判断窗口是否要收缩
            while windowSum < 0:
                # 缩小窗口并更新窗口内的元素和
                windowSum -= nums[left]
                left += 1
                
        return maxSum

区间问题

1288.删除被覆盖区间

给你一个区间列表,请你删除列表中被其他区间所覆盖的区间。只有当c <= ab <= d时,我们才认为区间[a,b)被区间[c,d)覆盖。在完成所有删除操作后,请你返回列表中剩余区间的数目。
题解:将所有区间按起点排序,则无非有以下3种情况
在这里插入图片描述
1.找到了覆盖区间
2.两个区间有交集,可以合并成一个大区间
3.两个区间完全不相交。

class Solution:
    def removeCoveredIntervals(self, intervals: List[List[int]]) -> int:
        intervals.sort(key=lambda x: (x[0], -x[1]))  #按照起点升序排列,起点相同时降序排列
        left = intervals[0][0]
        right = intervals[0][1]
        res = 0
        for i in range(1, len(intervals)):
            intv = intervals[i]
            if left <= intv[0] and right >= intv[1]: #找到覆盖区间
                res += 1
            if right >= intv[0] and right <= intv[1]: #找到相交区间,合并
                right = intv[1]
            if right < intv[0]: #完全不相交,更新起点和终点
                left = intv[0]
                right = intv[1]
        return len(intervals) - res

56.合并区间

以数组intervals表示若干个区间的集合,其中单个区间为intervals[i] = [starti, endi]。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key=lambda x: x[0])  # 按区间的 start 升序排列
        left = intervals[0][0]
        right = intervals[0][1]
        res = []
        res.append([left, right])

        for i in range(1, len(intervals)):
            intv = intervals[i]
            if intv[0] <= res[-1][1]:
                res[-1][1] = max(res[-1][1], intv[1])  # 找到最大的 end
            else:
                res.append(intv)
        return res

986.区间列表的交集

给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序 。
返回这 两个区间列表的交集 。形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b 。
两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3] 。
题解:
两个区间可分为两种情况:相交和不相交
在这里插入图片描述

①不相交:b2<a1 or a2<b1
②相交:b2<a1 and a2<b1
我们只需考虑相交情况,又可分为以下情况:
在这里插入图片描述交集区间[c1,c2]具有一定的规律:c1 = max(a1,b1)c2 = min(a2,b2)

class Solution:
    def intervalIntersection(
        self, firstList: List[List[int]], secondList: List[List[int]]) -> List[List[int]]:
        res = []
        i, j = 0, 0
        while i < len(firstList) and j < len(secondList):
            a1 = firstList[i][0]
            a2 = firstList[i][1]
            b1 = secondList[j][0]
            b2 = secondList[j][1]
            if b2 >= a1 and a2 >= b1:
                res.append([max(a1, b1), min(a2, b2)])
            if b2 < a2:
                j += 1
            else:
                i += 1
        return res

189.轮转数组

给定一个整数数组nums,将数组中的元素向右轮转k个位置,其中k是非负数。
方法1:使用切片赋值(需注意nums[:],必须带[:],否则原地址的数据并未发生改变)

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
     k = k % len(nums) #确保k在数组长度内
     nums[:] = nums[-k:] + nums[:-k]  #

方法2:翻转字符,将数组分成两部分进行翻转,然后再翻转整个数组来达到目标。

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
    k = k % len(nums)
    def reverse(nums, start, end):  #翻转函数
        while start < end:
            nums[start], nums[end] = nums[end], nums[start]
            start += 1
            end -= 1
    reverse(nums, 0, len(nums) - 1) #翻转整个数组
    reverse(nums, 0, k - 1)# 翻转前 k 个元素
    reverse(nums, k, len(nums) - 1) # 翻转后 len(nums) - k 个元素

方法3:将末尾删除后插入到数组开头

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        for i in range(k):
            nums.insert(0,nums.pop())  
            # nums.pop() 从数组末尾删除最后一个元素,并返回它。
            #nums.insert(0, element) 在数组的起始位置插入这个元素。

238.除自身以外数组的乘积

给你一个整数数组nums,返回 数组answer,其中answer[i]等于nums中除nums[i]之外其余各元素的乘积 。
题目数据保证数组nums之中任意元素的全部前缀元素和后缀的乘积都在32位整数范围内。
请 不要使用除法,且在O(n)时间复杂度内完成此题。

题解:构造一个 prefix 数组记录「前缀积」,再用一个 suffix 记录「后缀积」,根据前缀和后缀积就能计算除了当前元素之外其他元素的积。

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)

        prefix = [0] * n # 构造前缀积
        prefix[0] = nums[0]
        for i in range(1, n):
            prefix[i] = prefix[i - 1] * nums[i]

        suffix = [0] * n  #构造后缀积
        suffix[n - 1] = nums[n - 1]
        for i in range(n - 2, -1, -1):
            suffix[i] = suffix[i + 1] * nums[i]

        res = [0] * n
        res[0] = suffix[1] # 开头结果
        res[n - 1] = prefix[n - 2] # 末尾结果
        for i in range(1, n - 1):
            res[i] = prefix[i - 1] * suffix[i + 1]
        return res

进阶版:对前缀积和后缀积的数据进行优化

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        res = [1] * n
        prefix = 1
        for i in range(n):
            res[i] = prefix
            prefix *= nums[i] #前缀积
        suffix = 1
        for i in range(n - 1, -1, -1):
            res[i] *= suffix
            suffix *= nums[i]
        return res

41.缺失的第一个正数

给你一个未排序的整数数组nums,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

题解:将所有数字都放在它应该在的位置,若出现不应该在的位置则说明缺失
1.遍历数组,将数字nums[i]放到索引nums[i] - 1上。例如:[1,2,3,4,...,n]
2.遍历数组,找到第一个不在正确位置上的数字
3.若都满足,则缺失的最小整数为n+1

class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            while 0 < nums[i] <= n and nums[nums[i]-1] !=nums[i]:
                nums[nums[i]-1],nums[i]=nums[i],nums[nums[i]-1]
        for i in range(n):
            if nums[i] != i+1:
                return i+1
        return n+1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值