【Leetcode】随笔

🌈你好呀!我是 山顶风景独好
🎈欢迎踏入我的博客世界,能与您在此邂逅,真是缘分使然!😊
🌸愿您在此停留的每一刻,都沐浴在轻松愉悦的氛围中。
📖这里不仅有丰富的知识和趣味横生的内容等您来探索,更是一个自由交流的平台,期待您留下独特的思考与见解。🌟
🚀让我们一起踏上这段探索与成长的旅程,携手挖掘更多可能,共同进步!💪✨

题目一:全排列(LeetCode 46)

题目分析:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。例如:输入nums = [1,2,3],输出[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]。

解题思路:

回溯法:通过递归探索所有可能的排列组合,利用标记数组记录已使用的元素,避免重复选择。
递归逻辑:

  • 定义递归函数,参数包括当前排列路径和记录元素使用状态的数组。
  • 若当前路径长度等于数组长度,说明已生成一个完整排列,加入结果集。
  • 遍历数组中的每个元素,若未被使用,则将其加入路径,标记为已使用,递归进入下一层。
  • 回溯:递归返回后,将元素从路径中移除,标记为未使用,继续尝试其他元素。

示例代码:

def permute(nums):
    result = []
    n = len(nums)
    used = [False] * n  # 标记元素是否已使用
    
    def backtrack(path):
        # 路径长度等于数组长度,加入结果
        if len(path) == n:
            result.append(path.copy())
            return
        # 遍历所有元素
        for i in range(n):
            if not used[i]:
                # 选择当前元素
                used[i] = True
                path.append(nums[i])
                # 递归探索
                backtrack(path)
                # 回溯
                path.pop()
                used[i] = False
    
    backtrack([])
    return result

# 测试示例
if __name__ == "__main__":
    nums = [1,2,3]
    print("所有全排列:", permute(nums))
    # 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

代码解析:

backtrack函数通过递归构建排列路径,used数组用于记录哪些元素已被选入当前路径,避免重复选择。
当路径长度等于数组长度时,将当前路径的副本加入结果集(避免后续修改影响已存入的路径)。
遍历元素时,只选择未使用的元素,加入路径后标记为已使用,递归返回后进行回溯操作,恢复状态以尝试其他组合。
时间复杂度为O(n×n!)(n为数组长度,共n!个排列,每个排列需O(n)时间生成),空间复杂度为O(n)(递归栈深度和路径存储)。

题目二:子集(LeetCode 78)

题目分析:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。例如:输入nums = [1,2,3],输出[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]。

解题思路:

回溯法:通过控制遍历起点,递归生成所有可能的子集,确保子集元素的顺序与原数组一致,避免重复。
递归逻辑:

  • 定义递归函数,参数包括当前子集和遍历的起始索引。
  • 将当前子集加入结果集(所有子集都需记录,包括空集)。
  • 从起始索引开始遍历数组元素,将元素加入当前子集,递归进入下一层(起始索引+1,确保元素不重复)。
  • 回溯:递归返回后,将元素从当前子集中移除,继续尝试其他元素。

示例代码:

def subsets(nums):
    result = []
    n = len(nums)
    
    def backtrack(path, start):
        # 将当前子集加入结果
        result.append(path.copy())
        # 从start开始遍历,避免重复子集
        for i in range(start, n):
            # 选择当前元素
            path.append(nums[i])
            # 递归,下一次从i+1开始
            backtrack(path, i + 1)
            # 回溯
            path.pop()
    
    backtrack([], 0)
    return result

# 测试示例
if __name__ == "__main__":
    nums = [1,2,3]
    print("所有子集:", subsets(nums))
    # 输出:[[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]](顺序可能不同)

代码解析:

backtrack函数在每次递归时都将当前路径(子集)加入结果集,包括初始的空集。
通过start参数控制遍历起点,确保每个元素只在其后续元素中选择,避免生成重复子集(如[1,2]和[2,1])。
遍历元素时,将元素加入路径后递归,递归返回后移除元素,继续遍历下一个元素,生成所有可能的子集。
时间复杂度为O(n×2ⁿ)(共2ⁿ个子集,每个子集需O(n)时间生成),空间复杂度为O(n)(递归栈深度和路径存储)。

题目三:打家劫舍(LeetCode 198)

题目分析:

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。例如:输入nums = [1,2,3,1],输出4(偷窃1号和3号房屋,金额1+3=4);输入nums = [2,7,9,3,1],输出12(偷窃2号、4号和5号房屋,金额2+9+1=12)。

解题思路:

动态规划:定义dp[i]为前i间房屋能偷窃到的最高金额。
状态转移:

  • 对于第i间房屋,有两种选择:不偷窃则dp[i] = dp[i-1];偷窃则dp[i] = dp[i-2] + nums[i-1](注意数组索引与房屋编号的对应)。
  • 因此dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])。
    边界处理:
  • dp[0] = 0(0间房屋,金额0)。
  • dp[1] = nums[0](1间房屋,偷该房屋)。
    空间优化:由于dp[i]只与dp[i-1]和dp[i-2]有关,可使用两个变量代替数组,节省空间。

示例代码:

def rob(nums):
    n = len(nums)
    if n == 0:
        return 0
    if n == 1:
        return nums[0]
    
    # 初始化前两间房屋的最大金额
    prev_prev = nums[0]  # 前两间(i=1)
    prev = max(nums[0], nums[1])  # 前两间(i=2)
    
    # 从第三间房屋开始计算
    for i in range(2, n):
        current = max(prev, prev_prev + nums[i])
        prev_prev, prev = prev, current
    
    return prev

# 测试示例
if __name__ == "__main__":
    nums1 = [1,2,3,1]
    print("最高偷窃金额1:", rob(nums1))  # 输出:4
    
    nums2 = [2,7,9,3,1]
    print("最高偷窃金额2:", rob(nums2))  # 输出:12

代码解析:

使用prev_prev和prev两个变量分别记录dp[i-2]和dp[i-1]的值,避免使用数组存储所有状态,空间复杂度优化为O(1)。
对于每间房屋i(从2开始,对应数组索引2),当前最高金额为“不偷i(即prev)”和“偷i(即prev_prev + nums[i])”中的最大值。
遍历结束后,prev即为前n间房屋的最高偷窃金额。
时间复杂度为O(n)(遍历一次数组),空间复杂度为O(1)(使用常数变量)。


✨ 这就是今天要分享给大家的全部内容了,我们下期再见!😊
🏠 我在CSDN等你哦!我的主页😍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

山顶风景独好

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

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

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

打赏作者

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

抵扣说明:

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

余额充值