《算法通关之路》-chapter1数学之美

《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。

两数之和

力扣第1题

给定一个整数数组nums和一个整数目标值target,请你在该数组中找出 和为目标值target的那两个整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

'''
解法一:双指针法
时间复杂度:O(nlogn)
'''
class Solution:
    def twoSum(self, nums: list[int], target: int) -> list[int]:
        new_nums = []
        for idx, num in enumerate(nums):
            new_nums.append((num, idx))
        new_nums = sorted(new_nums, key=lambda d: d[0])
        l = 0
        r = len(new_nums) - 1
        while(l < r):
            if (new_nums[l][0] + new_nums[r][0] < target):
                l += 1
            elif (new_nums[l][0] + new_nums[r][0] > target):
                r -= 1
            else:
                return [new_nums[l][1], new_nums[r][1]]
        return []

## 试运行
nums, target= [2,7,11,15], 9
solu = Solution()
solu.twoSum(nums, target)

‘’’
补充:以tuple或list的某个值为key进行排序的方法
‘’’
a_list = [(1, 2), (1, 1)]
sorted(a_list, key=lambda d: d[1], reverse=True) # reverse=True从大到小

'''
解法二:dict空间换时间
时间复杂度:O(n)
'''
class Solution:
    def twoSum(self, nums: list[int], target: int) -> list[int]:
        num_dict = dict()
        for idx, num in enumerate(nums):
            if num_dict.get(target - num) is not None:
                return [idx, num_dict[target - num]]
            else:
                num_dict[num] = idx
        return []

## 试运行
nums, target= [2,7,11,15], 9
solu = Solution()
solu.twoSum(nums, target)

三数之和

力扣第15题
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0。
请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

'''
解法:双指针法
时间复杂度:O(n2)
'''
class Solution:
    def threeSum(self, nums: list[int]) -> list[list[int]]:
        nums.sort()
        n = len(nums)
        res = list()
        for idx in range(n - 2):
            if idx > 0 and nums[idx] == nums[idx-1]: ##去重
                continue
            l , r = idx + 1, n - 1
            while l < r:
                if nums[idx] + nums[l] + nums[r] == 0:
                    res.append([nums[idx], nums[l], nums[r]])
                    while(l < r and nums[l] == nums[l + 1]): ##去重
                        l += 1
                    while(l < r and nums[r] == nums[r - 1]): ##去重
                        r -= 1
                    l += 1
                    r -= 1
                elif nums[idx] + nums[l] + nums[r] < 0:
                    l += 1
                else:
                    r -= 1
        return res

## 试运行
nums = [0,-1,1,-1]
solu = Solution()
solu.threeSum(nums)

‘’’
enumerate or range ?
使用enumerate同时获得index和value比range获得index后取得value的效率高;
range中存在len时,效率会降低;
在只需要value的情况下,直接使用in list的效率更高
‘’’

四数之和

力扣第18题
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
1.0 <= a, b, c, d < n
2.a、b、c 和 d 互不相同
3.nums[a] + nums[b] + nums[c] + nums[d] == target
4.你可以按任意顺序返回答案 。

'''
解法一:双指针法
时间复杂度:O(n3)
'''
class Solution:
    def fourSum(self, nums: list[int], target: int) -> list[list[int]]:
        nums.sort()
        n = len(nums)
        res = list()
        for idx_a in range(n - 3):
            # 去重
            if idx_a > 0 and nums[idx_a] == nums[idx_a - 1]:
                continue
            for idx_b in range(idx_a + 1, n - 2):
                # 去重
                if idx_b > idx_a + 1 and nums[idx_b] == nums[idx_b - 1]:
                    continue
                l, r = idx_b + 1, n - 1
                while l < r:
                    if nums[idx_a] + nums[idx_b] + nums[l] + nums[r] == target:
                        res.append((nums[idx_a], nums[idx_b], nums[l], nums[r]))
                        l += 1
                        r -= 1
                        while l < r and nums[l] == nums[l-1]: # 去重
                            l += 1
                        while l < r  and nums[r] == nums[r+1]: # 去重
                            r -= 1
                    elif nums[idx_a] + nums[idx_b] + nums[l] + nums[r] > target:
                        r -= 1
                    else:
                        l += 1
        return res

# 试运行
nums, target = [1,0,-1,0,-2,2], 0
solu = Solution()
solu.fourSum(nums, target)
'''
解法二:回溯(超时)
时间复杂度:O(n4) T(n)=n(n-1)(n-2)(n-3)
'''
class Solution:
    def fourSum(self, nums: list[int], target: int) -> list[list[int]]:
        res = list()
        temp = list()
        nums_set = set()
        n = len(nums)
        nums.sort()

        def dfs(nums: list[int], remain: int, start: int):
            if len(temp) > 4:
                return
            if remain == 0 and len(temp) == 4:
                if tuple(temp) in nums_set:
                    return
                else:
                    nums_set.add(tuple(temp))
                    return res.append(temp.copy())
            for i in range(start, n):
                temp.append(nums[i])
                dfs(nums, remain-nums[i], i + 1)
                temp.pop()
        
        dfs(nums, target, 0)
        return res

# 试运行
nums, target = [1,0,-1,0,-2,2], 0
solu = Solution()
solu.fourSum(nums, target)
'''
解法三:分治法(比双指针快)
时间复杂度:O(n3)
'''
class Solution:
    def fourSum(self, nums: list[int], target: int) -> list[list[int]]:
        nums.sort()
        res = list()
        temp = list()   

        def findNsum(nums: list[int], target: int, N: int, temp:list[int]):
            if len(nums) < N or N < 2:
                return
            # 两数之和
            if N == 2:
                l, r = 0, len(nums) - 1
                while l < r:
                    if nums[l] + nums[r] == target:
                        res.append(temp + [nums[l], nums[r]])
                        l += 1
                        r -= 1
                        while l < r and nums[l] == nums[l - 1]:
                            l += 1
                        while r > l and nums[r] == nums[r + 1]:
                            r -= 1
                    elif nums[l] + nums[r] < target:
                        l += 1
                    else:
                        r -= 1
            # 缩减问题规模
            else:
                for i in range(len(nums)):
                    if i == 0 or i > 0 and nums[i-1] != nums[i]:
                        findNsum(nums[i+1:], target-nums[i], N-1, temp+[nums[i]])
            return
        
        findNsum(nums, target, 4, temp)
        return res

# 试运行
nums, target = [1,0,-1,0,-2,2], 0
solu = Solution()
solu.fourSum(nums, target)

四数相加II

力扣第454题
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

'''
解法一:dict空间换时间
时间复杂度:O(n2)
'''
class Solution:
    def fourSumCount(self, nums1: list[int], nums2: list[int], nums3: list[int], nums4: list[int]) -> int:
        num_dict = dict()
        res = 0
        for i in nums1:
            for j in nums2:
                num_dict[i + j] = num_dict.get(i + j, 0) + 1
        for i in nums3:
            for j in nums4:
                res += num_dict.get(-(i + j), 0)
        return res

# 试运行
nums1, nums2, nums3, nums4 = [1,2], [-2,-1], [-1,2], [0,2]
solu = Solution()
solu.fourSumCount(nums1, nums2, nums3, nums4)

最接近的三数之和

力扣第16题
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。

'''
解法一:双指针
时间复杂度:O(n2)
'''
class Solution:
    def threeSumClosest(self, nums: list[int], target: int) -> int:
        nums.sort()
        n = len(nums)
        res = nums[0] + nums[1] + nums[2] # res赋初值
        for idx in range(n - 2):
            if idx > 0 and nums[idx] == nums[idx-1]: ##去重
                continue
            l , r = idx + 1, n - 1
            while l < r:
                tmp_sum = nums[idx] + nums[l] + nums[r]
                if tmp_sum == target:
                    return tmp_sum
                if abs(tmp_sum - target) < abs(res - target):
                    res = tmp_sum
                elif tmp_sum < target:
                    l += 1
                    while l < r and nums[l] == nums[l-1]: ##去重
                        l += 1
                else:
                    r -= 1
                    while l < r and nums[r] == nums[r+1]: ##去重
                        r -= 1
        return res

# 试运行
nums, target = [-1,2,1,-4], 1
solu = Solution()
solu.threeSumClosest(nums, target)

最大子序列和

力扣第53题
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。

'''
解法一:暴力法(超时)
时间复杂度:O(n2)
'''
class Solution:
    def maxSubArray(self, nums: list[int]) -> int:
        n = len(nums)
        maxSum = float('-inf') # 负无穷的表示法
        for i in range(n):
            for j in range(i, n):
                tmp_sum = sum(nums[i:j+1])
                if tmp_sum > maxSum:
                    maxSum = tmp_sum
        return maxSum

# 试运行
nums = [-2,1,-3,4,-1,2,1,-5,4]
solu = Solution()
solu.maxSubArray(nums)
'''
解法二:分治法
时间复杂度:O(nlogn)
'''
class Solution:
    def maxSubArray(self, nums: list[int]) -> int:
        def helper(nums: list[int], l: int, r: int):
            if l > r:
                return float('-inf')
            mid = (l + r) // 2
            left = helper(nums, l, mid - 1)
            right = helper(nums, mid + 1, r)

            left_suffix_max_sum = right_prefix_max_sum = 0
            total = 0
            for i in reversed(range(l, mid)):
                total += nums[i]
                left_suffix_max_sum = max(left_suffix_max_sum, total)
            total = 0
            for i in range(mid + 1, r + 1):
                total += nums[i]
                right_prefix_max_sum = max(right_prefix_max_sum, total)
            cross_max_sum = left_suffix_max_sum + right_prefix_max_sum + nums[mid]
            return max(cross_max_sum, left, right)
        
        return helper(nums, 0, len(nums)-1)

# 试运行
nums = [-2,1,-3,4,-1,2,1,-5,4]
solu = Solution()
solu.maxSubArray(nums)
'''
解法三:动态规划
时间复杂度:O(n)
'''
class Solution:
    def maxSubArray(self, nums: list[int]) -> int:
        n = len(nums)
        dp = [0] * (n + 1)
        dp[0] = float('-inf')
        for i in range(1, n + 1):
            dp[i] = max(nums[i-1], nums[i-1] + dp[i-1])
        return max(dp)
    
# 优化空间复杂度
class Solution:
    def maxSubArray(self, nums: list[int]) -> int:
        n = len(nums)
        max_sum = max_cur_sum = float('-inf')
        for i in range(1, n + 1):
            max_cur_sum = max(nums[i-1], nums[i-1] + max_cur_sum)
            if max_cur_sum > max_sum:
                max_sum = max_cur_sum
        return max_sum

# 试运行
nums = [-2,1,-3,4,-1,2,1,-5,4]
solu = Solution()
solu.maxSubArray(nums)
'''
解法四:前缀和
时间复杂度:O(n)
'''
class Solution:
    def maxSubArray(self, nums: list[int]) -> int:
        n = len(nums)
        max_sum = nums[0]
        # min_sum 最小前缀和表示序列和不再继续扩展的最后一个位置
        min_sum = tmp_sum = 0
        for i in range(n):
            tmp_sum += nums[i]
            max_sum = max(max_sum, tmp_sum - min_sum)
            min_sum = min(min_sum, tmp_sum)
        return max_sum

# 试运行
nums = [-2,1,-3,4,-1,2,1,-5,4]
solu = Solution()
solu.maxSubArray(nums)

最大数

力扣第179题
给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

'''
解法一:排序
时间复杂度:O(nlogn)
'''
class Solution:
    def largestNumber(self, nums: list[int]) -> str:
        nums_str = list(map(str, nums))
        import functools
        def comp(a, b):
            if a + b > b + a:
                return 1
            elif a + b < b + a:
                return -1
            else:
                return 0
        nums_str.sort(reverse=True, key=functools.cmp_to_key(comp))
        return ''.join(nums_str) if nums_str[0] != '0' else '0'

# 试运行
nums = [10,2]
solu = Solution()
solu.largestNumber(nums)

分数到小数

力扣第166题
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。
如果小数部分为循环小数,则将循环的部分括在括号内。
如果存在多个答案,只需返回 任意一个。
对于所有给定的输入,保证 答案字符串的长度小于 104 。

'''
解法一:短除法
时间复杂度:O(l)
'''
class Solution:
    def fractionToDecimal(self, numerator: int, denominator: int) -> str:
        # divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)
        n, remainder = divmod(abs(numerator), abs(denominator)) # 去除符号
        sign = ''
        if(numerator // denominator < 0):
            sign = '-'
        res = [str(n), '.']
        seen = []
        # 短除法
        while remainder not in seen:
            seen.append(remainder)
            n, remainder = divmod(remainder * 10, abs(denominator))
            res.append(str(n))
        # 处理循环节
        index = seen.index(remainder)
        res.insert(index + 2, '(')
        res.append(')')
        # 去除不循环时末尾的(0)还有整除时末尾的小数点
        return sign + ''.join(res).replace('(0)', '').rstrip('.')

# 试运行
numerator, denominator  = 4, 333
solu = Solution()
solu.fractionToDecimal(numerator, denominator)

最大整除子集

力扣第368题
给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:
answer[i] % answer[j] == 0 ,或 answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。

'''
解法一:动态规划
时间复杂度:O(n2)
'''
class Solution:
    def largestDivisibleSubset(self, nums: list[int]) -> list[int]:
        S = {-1: list()}
        for x in sorted(nums):
            S[x] = max((S[d] for d in S if x % d == 0), key=len) + [x]
        return max(S.values(), key=len) # 取list中长度最大的那一个

# 试运行
nums = [1,2,4,8]
solu = Solution()
solu.largestDivisibleSubset(nums)

质数排列

力扣第1175题
请你帮忙给从 1 到 n 的数设计排列方案,使得所有的「质数」都应该被放在「质数索引」(索引从 1 开始)上;你需要返回可能的方案总数。
让我们一起来回顾一下「质数」:质数一定是大于 1 的,并且不能用两个小于它的正整数的乘积来表示。
由于答案可能会很大,所以请你返回答案 模 mod 10^9 + 7 之后的结果即可。

'''
解法一:求质数+阶乘
时间复杂度:O(n^3/2
'''
import math
class Solution:
    def numPrimeArrangements(self, n: int) -> int:
        count = 1
        for num in range(3, n+1):
            flag = True
            for div in range(2, int(math.sqrt(num)) + 1):
                if num % div == 0:
                    flag = False
                    break
            if flag == True:
                count += 1
        res = 1
        for i in range(1, count + 1):
            res = (i * res) % (1e9 + 7)
        for i in range(1, n - count + 1):
            res = (i * res) % (1e9 + 7)
        return int(res) # 其中一个或两个数是浮点数,则求余的结果将是浮点数

# 试运行
n = 100
solu = Solution()
solu.numPrimeArrangements(n)

github笔记本

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jiawen9

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

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

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

打赏作者

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

抵扣说明:

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

余额充值