当前位置: 首页 > news >正文

LeetCode100 -- Day3

1. 回溯:46、78、17

1. 回溯:通过深度优先搜索(DFS)的策略来遍历所有可能的解空间,当遇到不符合条件的情况时,就返回上一步(回溯),尝试其他路径。

2. 实现思路

(1)定义解空间​​:确定问题的解形式(如一个数组或一个字符串序列)。

(2)确定约束条件​​:在解空间中,有些路径可能不符合要求,需要跳过。

(3)深度优先搜索​​:递归或迭代地构建候选解,并在构建过程中检查约束条件。

(4)​回溯​​:当构建到某一步发现不能继续满足条件时,就回退到上一步,选择其他路径。

3. 基本模板:

def backtrack(路径, 选择列表):if 满足结束条件:结果.append(路径.copy())   # 注意:如果直接添加路径,后面修改会影响结果,所以用拷贝returnfor 选择 in 选择列表:做出选择backtrack(路径, 选择列表)  # 递归撤销选择

(1)46 全排列

class Solution(object):def permute(self, nums): def backtrack(cur_result, nums_remain):""" 没有选择,说明已经完成一个排列 """if not nums_remain:## 浅拷贝:创建一个与cur_result内容相同的新列表,避免后续操作影响已保存的结果results.append(cur_result[:])return""" 遍历所有可选择元素 """for i in range(len(nums_remain)):""" 做出选择:将当前元素添加到cur_result """num = nums_remain[i]cur_result.append(num)""" 更新剩余可选择的元素(移除当前元素) """cur_nums_remain = nums_remain[:i]+nums_remain[i+1:] ## 新列表:[0,i)+[i+1,n)""" 递归处理下一层选择 """backtrack(cur_result, cur_nums_remain)""" 撤销选择(回溯) """cur_result.pop()results=[]backtrack([],nums)return results

(2)78 子集

方法一:回溯

class Solution(object):def subsets(self, nums):def backtrack(start,cur_result):results.append(cur_result[:])for i in range(start,len(nums)):cur_result.append(nums[i])backtrack(i+1,cur_result)cur_result.pop()results=[]backtrack(0,[])return results

执行流程示例(nums = [1,2]):

   初始调用: backtrack(0, [])- 添加 [] → results = [[]]- 遍历 i=0: 选择1 → [1]- 递归: backtrack(1, [1])- 添加 [1] → results = [[], [1]]- 遍历 i=1: 选择2 → [1,2]- 递归: backtrack(2, [1,2])- 添加 [1,2] → results = [[], [1], [1,2]]- 回溯: 移除2 → [1]- 回溯: 移除1 → []- 遍历 i=1: 选择2 → [2]- 递归: backtrack(2, [2])- 添加 [2] → results = [[], [1], [1,2], [2]]

方法二:迭代法

class Solution(object):def subsets(self, nums):results=[[]]for num in nums:# 存储当前元素生成的新子集cur_result = [] # 遍历原有子集for result in results:""" 创建新子集 = 原子集 + 当前元素 """cur_result.append(result + [num])# 将新子集添加到结果中results.extend(cur_result)return results

append vs extend:

  • append:将整个对象作为一个元素添加到列表的末尾

  • extend:将可迭代对象中的每个元素逐个添加到列表的末尾

引申:90 子集②(可能存在重复元素)

class Solution(object):def subsetsWithDup(self, nums):"""回溯"""def backtrack(start, cur_result):if cur_result not in results:results.append(cur_result[:])for i in range(start,len(nums)):cur_result.append(nums[i])backtrack(i+1,cur_result)cur_result.pop()results=[]nums=sorted(nums)backtrack(0,[])return results"""迭代"""results=[[]]nums = sorted(nums)for num in nums:cur_result=[]for result in results:cur_set = result+[num]if cur_set not in results:cur_result.append(cur_set)results.extend(cur_result)return results

(3)17 电话号码的字母组合

方法一:回溯

class Solution(object):def letterCombinations(self, digits):mapping = {'2': 'abc','3': 'def','4': 'ghi','5': 'jkl','6': 'mno','7': 'pqrs','8': 'tuv','9': 'wxyz'}if not digits:  return []def backtrack(now,cur_result):""" 已经找到全部digit对应的字母 """if now==len(digits):results.append(cur_result)return """ 当前digit对应的字母们 """cur_letter=mapping[digits[now]]""" 递归处理下一个数字 """for letter in cur_letter:backtrack(now+1,cur_result+letter)results=[]backtrack(0,"")return results

方法二:迭代

        if not digits:  return []results=[""]for digit in digits:cur_result=[]for result in results:for letter in mapping[digit]:cur_result.append(result + letter)results = cur_resultreturn results

2. 滑动窗口:3、438

滑动窗口处理字符问题:

1. 适合处理:

(1)区间统计(无重复字符的最长子串、统计字符串中包含特定字符模式的数量、计算最多包含K个不同字符的子串数量)

(2)模式匹配(字母异位词、最小覆盖子串、字符串排列问题)

(3)字符频率(字符频率满足特定条件的最小子串、特定模式计数问题)

(4)优化问题(满足条件的最小最长子串)

2. 实现模板

(1)变长窗口问题

def variable_length(s):left = 0for right in range(len(s)):update_state(s[right])  # 添加右边字符""" 收缩窗口直到满足条件 """while not condition_satisfied():remove_state(s[left])left += 1update_result()    # 更新结果(可能求最大/最小)

(2)固定窗口问题

def fixed_length(s, k):""" 初始化窗口状态 """for i in range(k):update_state(s[i])""" 检查初始窗口 """if check_state(): record_result(0)""" 滑动窗口 """for i in range(k, len(s)):remove_state(s[i-k])  # 移除最左侧字符update_state(s[i])     # 添加新字符if check_state(): record_result(i-k+1)

(1)3 无重复字符的最长子串→变长窗口问题

class Solution(object):def lengthOfLongestSubstring(self, s):charset = set()left=0max_len=0for right in range(len(s)):while s[right] in charset:charset.remove(s[left])left+=1charset.add(s[right])max_len = max(max_len, right-left+1)return max_len

(2)438 找到字符串中所有字母异位词→固定窗口问题

from collections import defaultdict
class Solution(object):def findAnagrams(self, s, p):p_len,s_len = len(p),len(s)if s_len<p_len: return []## 字符计数器p_count = defaultdict(int)for char in p:p_count[char] += 1window = defaultdict(int)results=[]  """ 处理第一个窗口并检查 """for right in range(p_len):window[s[right]] += 1if window==p_count:results.append(0)""" 开始滑动窗口 """for right in range(p_len,s_len):left_char = s[right-p_len]window[left_char] -= 1## 避免数量为0的字符干扰判断if window[left_char]==0:del window[left_char]right_char = s[right]window[right_char] += 1if window==p_count:results.append(right-p_len+1)return results

3. 子串:560

(1)560 和为K的子数组

定义 prefix[i] 表示数组从开头到索引 i 的元素和。

对于每个结束位置 j,找到起始位置 i 使得 prefix[j] - prefix[i] = k

→ 只需要统计每个结束位置前,有多少个起始位置满足 prefix[i] = prefix[j] - k

class Solution(object):def subarraySum(self, nums, k):prefix_count={0:1}    ## 空数组和为0count,cur_sum = 0,0for num in nums:""" 更新当前位置的前缀和 """cur_sum += num""" 查找需要的前缀和值 """count += prefix_count.get(cur_sum-k,0)""" 更新当前前缀和的计数 """prefix_count[cur_sum] = prefix_count.get(cur_sum,0) + 1return count 

引申:前缀和

通过对原始数组进行预处理,构建新的"前缀和数组",能够快速计算任意区间的元素和。

(1)构建前缀和数组,将原始数组转化为累加和数组

(2)将原始问题转化为前缀和数组上的计算,并使用辅助数据结构优化查询

  • 哈希表(用于计数、快速查找)

  • 双指针(处理非负数组)

  • 二分搜索(处理有序前缀和)

4. 数组:53、56

(1)53 最大子数组和

方法一:贪心

cur_sum:当前连续子数组的和。如果 cur_sum+nums[i]<nums[i] → 从当前元素开始重新加和(否则再怎么加也是比从当前开始要小)

class Solution(object):def maxSubArray(self, nums):n=len(nums)cur_sum=0max_sum=float('-inf')if n==1:    return nums[0]for i in range(n):if cur_sum+nums[i] < nums[i]:cur_sum = 0cur_sum += nums[i]max_sum = max(max_sum, cur_sum)return max_sum

方法二:动规

dp[i]:以第 i 个元素结尾的连续子数组的最大和

状态转移方程:dp[i] = max( dp[i-1]+nums[i], dp[i] )

class Solution(object):def maxSubArray(self, nums):n=len(nums)if n==1:    return nums[0]dp=[float('-inf')]*ndp[0]=nums[0]for i in range(1,n):dp[i] = max( dp[i-1]+nums[i], nums[i] )return max(dp)

(2)56 合并区间

class Solution(object):def merge(self, intervals):results=[]""" 对区间按起始位置排序 """intervals.sort(key=lambda x: x[0])for interval in intervals:""" 如果当前区间与已合并区间不重叠(当前区间的start大于已合并区间的end) """if not results or interval[0]>results[-1][1]:results.append(interval)""" 如果重合→更新已合并区间的end值 """else:results[-1][1]=max(interval[1],results[-1][1])return results

http://www.xdnf.cn/news/1339651.html

相关文章:

  • C++---滑动窗口平滑数据
  • 深度学习之NLP基础
  • KB5063878补丁故障解决方案:从蓝屏幕到系统修复的全面指南
  • 短波红外科研相机:开启科研新视野的利器​
  • 【矩池云】实现Pycharm远程连接,上传数据并解压缩
  • C++入门自学Day16-- STL容器类型总结
  • 全文 part1 - DGEMM Using Tensor Cores, and Its Accurate and Reproducible Versions
  • 阿里云对象存储OSS之间进行数据转移教程
  • 打工人项目日报计划
  • 数据安全管理——解读银行保险机构数据安全管理办法【附全文阅读】
  • Elasticsearch Ruby 客户端elasticsearch / elasticsearch-api
  • DBLens 业界首创AI表结构变更审查,智能评估影响,助力开发效率跃升。
  • 数据库原理及应用_数据库基础_第2章关系数据库标准语言SQL_数据查询(2)分组查询
  • 第三方软件测试报告的行业价值
  • 两台电脑之间如何传输大文件?
  • C++设计模式--策略模式与观察者模式
  • 安卓app、微信小程序等访问多个api时等待提示调用与关闭问题
  • QT QProcess, WinExec, ShellExecute中文路径带空格程序或者脚本执行并带参数
  • 灵活使用UE5 Modeling中的UV编辑功能
  • QT-初识
  • 日志收集(ELK)
  • javaweb开发笔记——微头条项目开发
  • 【笔记】Facefusion3.3.2 之 NSFW 检测屏蔽测试
  • Windows 系统中,添加打印机主要有以下几种方式
  • macos使用FFmpeg与SDL解码并播放H.265视频
  • Git常用操作大全(附git操作命令)
  • 【LeetCode】18. 四数之和
  • 微服务的编程测评系统13-我的竞赛列表-elasticSearch
  • javaweb开发笔记—— 前端工程化
  • Spring Boot 集成 Redis 发布订阅实现消息通信