笔记——week10

week 10

美好的一周从敲代码开始

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F6oYTEGf-1689496840099)(C:\Users\86152\AppData\Roaming\Typora\typora-user-images\image-20230712104538447.png)]

知识点记录:

位异或运算(XOR)

是一种逻辑运算符,用于比较两个二进制数的对应位,并根据以下规则计算结果:

  • 如果对应位上的两个二进制数相同(都为0或都为1),则结果为0。
  • 如果对应位上的两个二进制数不同(一个为0,一个为1),则结果为1。

位异或运算可以用符号 “^” 表示。

以下是一些位异或运算的示例:

a = 10  # 二进制表示为 1010
b = 6   # 二进制表示为 0110

# 位异或运算
result = a ^ b  # 二进制表示为 1100,十进制为 12
print(result)  # 输出结果为 12

在上述示例中,变量 ab 进行位异或运算,结果为 12。

位异或运算在计算机科学中有多种应用,包括:

  • 交换两个变量的值:a ^= b; b ^= a; a ^= b;
  • 判断两个数是否相等:result = (a ^ b) == 0
  • 加密和解密算法中的数据处理
  • 校验和和错误检测等领域

位异或运算是一种常用的位运算,可以在处理二进制数据和逻辑运算中发挥重要作用。

可哈希计数collections.Counter

collections.Counter 是 Python 标准库中的一个内置类,用于计数可哈希对象的出现次数。它提供了一种方便的方式来统计可迭代对象中各个元素的频率。

使用 collections.Counter 类可以快速统计可迭代对象中元素的出现次数,并以字典的形式返回统计结果,其中元素作为键,出现次数作为值。该类提供了一些实用的方法,可以进行计数的合并、计数器的相减、获取最常见的元素等操作。

以下是使用 collections.Counter 的示例:

from collections import Counter

# 统计字符串中字符的出现次数
s = "hello world"
counter = Counter(s)
print(counter)  # 输出:Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})

# 统计列表中元素的出现次数
nums = [1, 2, 3, 1, 2, 3, 4, 5]
counter = Counter(nums)
print(counter)  # 输出:Counter({1: 2, 2: 2, 3: 2, 4: 1, 5: 1})

# 访问出现次数最多的元素
most_common = counter.most_common(2)
print(most_common)  # 输出:[(1, 2), (2, 2)]

在上述示例中,我们通过创建 Counter 对象并传入可迭代对象,可以快速统计元素的出现次数。通过调用 most_common() 方法,可以获取出现次数最多的元素及其对应的次数。

collections.Counter 是一个非常有用的工具,可以方便地进行频率统计和元素计数的操作,适用于许多数据处理和算法问题。

字母之间进行运算:

在 Python 中,可以使用内置的 ord()chr() 函数将字母与 ASCII 码进行转换。通过将字母转换为 ASCII 码,可以进行一些运算操作。

下面是一些示例操作:

# 字母向后移动 n 个位置
char = 'A'
n = 3
new_char = chr(ord(char) + n)
print(new_char)  # 输出:D

# 字母向前移动 n 个位置
char = 'D'
n = 3
new_char = chr(ord(char) - n)
print(new_char)  # 输出:A

# 字母间的差值
char1 = 'E'
char2 = 'B'
diff = ord(char1) - ord(char2)
print(diff)  # 输出:3

通过对字母的 ASCII 码进行加减操作,可以实现字母的移动或计算字母之间的差值。注意要确保结果仍然是合法的字母。

内置函数bisect_right右二等分

bisect_right是 Python 中模块的函数bisect。它用于在排序列表中查找目标值的插入点。

以下是如何使用的示例bisect_right

from bisect import bisect_right

nums = [1, 3, 5, 7, 9]
target = 4

idx = bisect_right(nums, target)
print(idx)  # Output: 2

在此示例中,bisect_right(nums, target)返回应插入的索引target以维护 list 的排序顺序nums。由于target是 4 并且它应该插入到列表中的值 3 之后,因此返回的索引是 2。

在代码上下文中,您可以使用bisect_right来查找目标字母应插入到字母排序列表中的索引。

回溯算法

回溯算法是一种通过搜索所有可能的解空间来求解问题的算法。它常用于求解组合、排列、子集等问题。回溯算法通过尝试所有可能的选择,并在搜索过程中进行剪枝,从而避免不必要的搜索。

回溯算法的基本思想是递归和回退。它通过递归地尝试所有的选择,每次选择一个元素,并进入下一层进行搜索。如果搜索成功,即找到了满足问题条件的解,就将解保存下来。如果搜索失败,即不能满足问题条件,就进行回退,撤销上一步的选择,继续搜索其他可能的选择。

回溯算法一般包含以下步骤:

  1. 定义问题的解空间:确定问题的解空间,即所有可能的解组成的空间。
  2. 确定选择列表:对于每一步,确定可以做出的选择列表,即当前可选的路径或分支。
  3. 确定选择条件:确定对于每个选择是否满足问题的限定条件,即剪枝条件。
  4. 定义路径和结果:定义路径,即记录当前的选择路径;定义结果,即保存满足问题条件的解。
  5. 回溯搜索:使用递归回溯的方式搜索解空间,对于每一步,根据选择列表和选择条件进行选择和剪枝。
  6. 处理结果:当搜索到满足问题条件的解时,将其保存到结果中,或进行其他相应的操作。
  7. 回退选择:在回溯过程中,当搜索失败或搜索结束后,需要回退到上一步,撤销当前的选择,继续搜索其他可能的选择。

回溯算法的关键在于定义好选择列表、选择条件、路径和结果,并通过递归回溯的方式遍历解空间。在搜索过程中,可以通过剪枝来减少不必要的搜索,提高算法的效率。

需要注意的是,回溯算法一般用于求解所有可能的解,因此解空间可能很大,可能需要较长的时间进行搜索。在实际应用中,可以根据问题的特点进行优化,避免不必要的搜索。同时,回溯算法通常使用递归实现,需要注意递归的终止条件和边界条件,以避免无限递归或越界访问的问题。

39. 组合总和

题目描述:

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        res = []  # 结果储存
        candidates.sort()  # 对候选数进行排序
        if candidates[0] > target:
            return res

        def backtrack(curr, choices, start):
            if sum(curr) == target:  # 当前结果的和等于目标值
                res.append(curr)  # 将当前结果添加到结果集中
                return
            if sum(curr) > target:  # 当前结果的和大于目标值
                return

            for i in range(start, len(choices)):  # 减少一些计算
                num = choices[i]
                # 递归调用回溯函数,将当前选择的数字添加到当前结果中,并将剩余的候选数作为新的选择列表
                backtrack(curr + [num], choices, i)

        backtrack([], candidates, 0)  # 初始结果为空列表,候选数为给定的数列,从索引0开始选择
        return res  # 返回结果集

解题思路:

回溯法求解:通过不断选择一个候选数,并将其添加到当前结果中,然后递归地处理剩余的候选数,直到当前结果的和等于目标值或大于目标值。如果当前结果的和等于目标值,则将当前结果添加到结果集中。

为了避免重复的组合,每次递归调用时,通过切片操作将当前选择的数字之后的候选数作为新的选择列表。所以要对数组先经行排序,这样可以确保在每一层递归中,选择的数字不会与之前已选择的数字重复。

最终,将得到的结果集作为函数的返回值返回。时间开销较大

40. 组合总和 II

题目描述:

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

**注意:**解集不能包含重复的组合。

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()
        res = []  # 储存结果

        def backtrack(curr, target, choices, start):
            if target == 0:  # 当目标值减到0
                res.append(curr)  # 将当前结果添加到结果集中
                return
            if target < 0:  # 当目标值小于0,废弃该分支
                return 
        
            for i in range(start, len(choices)):
                if i > start and choices[i] == choices[i-1]:  # 避免重复的组合
                    continue
                num = choices[i]
                # 递归调用回溯函数,将当前选择的数字添加到当前结果中,目标值减去选择的数字,选择列表从下一个位置开始
                backtrack(curr + [num], target - num, choices, i + 1)

        backtrack([], target, candidates, 0)
        return res

解题思路:

回溯算法来枚举所有可能的组合,并通过剪枝操作避免了重复的组合。

122. 买卖股票的最佳时机 II

题目描述:

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)  # 股票价格的天数
        res = 0  # 最大利润初始化为0
        for i in range(1, n):  # 遍历股票价格列表
            if prices[i] > prices[i-1]:  # 如果当天的股票价格高于前一天的价格
                res += prices[i] - prices[i-1]  # 将差值加到最大利润上
        return res

解题思路:

贪心算法:在每个阶段选择局部最优解以期望最终获得全局最优解

通过比较相邻两天的股票价格来判断是否进行买卖操作,并将利润累加到最大利润中。由于可以多次买卖股票,我们可以在价格上升的时候买入,价格下降的时候卖出,这样可以获得最大利润。股票总交易可以看作数次小交易的和。

1911. 最大子序列交替和

题目描述:

一个下标从 0 开始的数组的 交替和 定义为 偶数 下标处元素之 减去 奇数 下标处元素之

  • 比方说,数组 [4,2,5,3] 的交替和为 (4 + 5) - (2 + 3) = 4

给你一个数组 nums ,请你返回 nums 中任意子序列的 最大交替和 (子序列的下标 重新 从 0 开始编号)。

一个数组的 子序列 是从原数组中删除一些元素后(也可能一个也不删除)剩余元素不改变顺序组成的数组。比方说,[2,7,4][4,**2**,3,**7**,2,1,**4**] 的一个子序列(加粗元素),但是 [2,4,2] 不是。

class Solution:
    def maxAlternatingSum(self, nums: List[int]) -> int:
        res = 0  # 存储最大交替和
        pre = 0  # 前一个元素的值

        for num in nums:
            if num > pre:
                res += num - pre  # 当前元素大于前一个元素,将差值累加到最大交替和中
            pre = num  # 更新前一个元素的值

        return res

解题思路:

贪心算法:将其看成买卖股票的最佳时机,每次都选择能使结果最大化的操作。通过比较当前元素与前一个元素的大小关系,我们可以确定是否将当前元素的差值添加到最大交替和中。因为是偶数位上的数减去奇数位上的数

示例 3:

输入:nums = [6,2,1,2,4,5]
输出:10
解释:最优子序列为 [6,1,5] ,交替和为 (6 + 5) - 1 = 10 。

本例子中就是[0, 6, 1, 5] 0 买, 6 出售, 1买, 5出售。

46. 全排列

题目描述:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        res = []  # 储存结果集

        def backtrack(curr, choices):  # 定义回溯函数,参数为当前结果和待选择数集
            if not choices:  # 如果待选择数集为空,说明已经选择完毕,将当前结果添加到结果集中
                res.append(curr)
                return
            
            for i in range(len(choices)):  # 遍历每一个数
                num = choices[i]  # 选择的数字
                backtrack(curr + [num], choices[:i] + choices[i+1:])  # 递归调用回溯函数,将选择的数字添加到当前结果中,并更新待选择数集
        
        backtrack([], nums)  # 调用回溯函数,初始结果为空列表,待选择数集为给定的数字列表
        return res  # 返回结果集

解题思路:

回溯法求解:在回溯函数中,通过不断选择一个数字,并将其添加到当前结果中,然后递归地处理剩余的待选择数集,直到待选择数集为空。每次递归结束后,将当前结果添加到结果集中。选择所有的开始数字并重复回溯过程,就可以得到所有可能的全排列。

每日一题:

2544. 交替数字和 ——难度简单

题目描述:

给你一个正整数 nn 中的每一位数字都会按下述规则分配一个符号:

  • 最高有效位 上的数字分配到 号。
  • 剩余每位上数字的符号都与其相邻数字相反。

返回所有数字及其对应符号的和。

class Solution:
    def alternateDigitSum(self, n: int) -> int:
        res = 0
        sign = 1
        while n:
            res += (n % 10) * sign
            n //= 10
            sign = -sign
        return res * -sign
解题思路:

示例 3:

输入:n = 886996
输出:0
解释:(+8) + (-8) + (+6) + (-9) + (+9) + (-6) = 0

从事例可以得出,从最后一位开始遍历,sign = 1,循环结束时,

  • len(n)如果是偶数,sign = -1, 结果res * -sign = res
  • len(n)如果是奇数,sign = 1, 结果res * -sign = -res,就是说起始位置是n的末尾应该是sign = -1

931. 下降路径最小和——难度中等

题目描述:

给你一个 n x n方形 整数数组 matrix ,请你找出并返回通过 matrix下降路径最小和

下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)(row + 1, col) 或者 (row + 1, col + 1)

示例 1:

img

输入:matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出:13
解释:如图所示,为和最小的两条下降路径

示例 2:

img

输入:matrix = [[-19,57],[-40,-5]]
输出:-59
解释:如图所示,为和最小的下降路径
class Solution:
    def minFallingPathSum(self, matrix: List[List[int]]) -> int:
        n = len(matrix)  # 矩阵的大小
        dp = [[0] * n for _ in range(n)]  # dp数组
        dp[0] = matrix[0]  # 初始化dp数组
        
        for i in range(1, n):
            for j in range(n):
                # 计算当前位置的最小路径和
                if j == 0:  # 左边界
                    dp[i][j] = matrix[i][j] + min(dp[i-1][j], dp[i-1][j+1])
                elif j == n - 1:  # 右边界
                    dp[i][j] = matrix[i][j] + min(dp[i-1][j], dp[i-1][j-1])
                else:  # 正常情况
                    dp[i][j] = matrix[i][j] + min(dp[i-1][j], dp[i-1][j-1], dp[i-1][j+1])
        
        return min(dp[n-1])
解题思路:

最开始想的是贪心算法,第一行的最小的位置 (row, col) 的,加上第二行的 (row + 1, col - 1)(row + 1, col) 或者 (row + 1, col + 1) 最小的一个,只通过一般的测试点,然后改用动规的方法,每一行都需要找到最小数的合并结果,输出dp数组最后一行的的最小值就是我们要的答案。注意:python数组对边界溢出较为敏感,要考虑到边界也就是特殊情况的处理。
p[i-1][j-1])
else: # 正常情况
dp[i][j] = matrix[i][j] + min(dp[i-1][j], dp[i-1][j-1], dp[i-1][j+1])

    return min(dp[n-1])



#### 解题思路:

最开始想的是贪心算法,第一行的最小的位置 `(row, col)` 的,加上第二行的 `(row + 1, col - 1)`、`(row + 1, col)` 或者 `(row + 1, col + 1)` 最小的一个,只通过一般的测试点,然后改用`动规`的方法,每一行都需要找到最小数的合并结果,输出`dp数组`最后一行的的最小值就是我们要的答案。注意:`python数组`对边界溢出较为敏感,要考虑到边界也就是特殊情况的处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值