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

刷题记录-HOT 100(二)

一、子串

3、最小覆盖子串问题

使用滑动窗口技术来解决这个问题,通过在 s 中维护一个窗口来包含 t 的所有字符,然后尝试收缩这个窗口以找到最小的子串。

  1. 初始化计数器:首先,我们使用 Counter 来记录 t 中字符的出现次数,并初始化窗口左右边界和一个 less 变量,表示当前窗口中还缺少多少种字符才能完全包含 t 中的所有字符。

  2. 扩展窗口:使用右指针遍历字符串 s,将每个字符添加到窗口中,同时更新窗口内的字符计数器。如果该字符在 t 中的计数达到了要求,less 减少。

  3. 收缩窗口:一旦窗口中包含了 t 中的所有字符(即 less == 0),尝试从左边开始收缩窗口,直到窗口不再包含 t 的所有字符。每次收缩时,检查当前窗口是否比之前记录的最小窗口更小,如果是,更新最小窗口的边界。

  4. 返回结果:最后,如果找到了有效的窗口,返回这个窗口的字符串;如果没有找到,返回空字符串。

class Solution:def minWindow(self, s: str, t: str) -> str:left=0;n=len(s)ans_left,ans_right=-1,ncnt_t=Counter(t)cnt_s=Counter()less=len(cnt_t)for right,ch in enumerate(s):cnt_s[ch]+=1if cnt_s[ch]==cnt_t[ch]:less-=1while less==0:if right-left<ans_right-ans_left:ans_left,ans_right=left,rightx=s[left]if cnt_s[x]==cnt_t[x]:less+=1cnt_s[x]-=1left+=1return "" if ans_left<0 else s[ans_left:ans_right+1]

二、普通数组

1、最大子数组和

用动态规划的思想来解决这个问题。通过遍历数组,并在每一步计算以当前元素为结尾的最大子数组和,同时更新全局的最大子数组和。

  • 初始化变量:定义两个变量 pre 和 ans。其中 pre 表示以当前元素为结尾的子数组的最大和,ans 表示迄今为止找到的最大子数组和。

  • 遍历数组:遍历数组中的每个元素 num,更新 prepre + numnum 之间的较大值。这一步是动态规划的核心,表示当前元素是否应该加入前面的子数组还是单独作为新的子数组开始。

  • 更新最大值:在每一步中,将 ans 更新为 anspre 的较大值,以确保 ans 始终存储的是遍历到当前元素时的最大子数组和。

  • 返回结果:在遍历完数组之后,ans 即为最大子数组和,返回这个值。

class Solution:def maxSubArray(self, nums: List[int]) -> int:pre=0ans=nums[0]for num in nums:pre=max(num,pre+num)ans=max(pre,ans)return ans

2、合并区间

首先对区间进行排序(按照每个区间的起始端点),然后通过遍历的方式来合并重叠的区间,最终得到合并后的区间列表。

  • 排序区间:首先根据每个区间的起始值对所有区间进行排序,这样可以确保在遍历时每个区间的开始时间都是递增的,方便后续的合并操作。

  • 初始化起始和结束变量:在遍历区间之前,先初始化当前区间的起始点 st 和结束点 ed 为第一个区间的起始点和结束点。

  • 遍历区间:从第二个区间开始遍历每个区间:

    • 如果当前区间的起始点小于或等于当前已合并区间的结束点 ed,说明两个区间有重叠,此时更新已合并区间的结束点 ed 为两个区间中结束点的最大值。
    • 如果当前区间的起始点大于 ed,说明没有重叠,将当前已合并的区间加入到结果列表 ans 中,然后更新 sted 为当前区间的起始点和结束点。
  • 处理最后一个区间:在遍历完所有区间后,最后一个区间还未被加入到结果列表中,因此需要将它加入到结果列表。

  • 返回结果:最终返回合并后的区间列表 ans

class Solution:def merge(self, intervals: List[List[int]]) -> List[List[int]]:intervals.sort(key=lambda x:x[0])st,ed=intervals[0][0],intervals[0][1]ans=[]for i in range(1,len(intervals)):if intervals[i][0]<=ed:ed=max(ed,intervals[i][1])else:ans.append([st,ed])st=intervals[i][0]ed=intervals[i][1]ans.append([st,ed])return ans

 3、轮转数组

将一个数组中的元素向右旋转 k 次。可以通过以下步骤来实现这个目标,这种方法的核心思想是通过三次反转来达到旋转数组的效果:

  1. 先将整个数组反转。
  2. 然后反转数组的前 k 个元素。
  3. 最后反转数组的剩余部分。

k 取余是为了处理旋转次数 k 大于数组长度的情况,防止不必要的多次旋转。

class Solution:def reverse(self,nums,st,ed):while st<ed:nums[st],nums[ed]=nums[ed],nums[st]st+=1;ed-=1def rotate(self, nums: List[int], k: int) -> None:"""Do not return anything, modify nums in-place instead."""k%=len(nums)self.reverse(nums,0,len(nums)-1)self.reverse(nums,0,k-1)self.reverse(nums,k,len(nums)-1)

4、除自身以外的数组的乘积

通过两次遍历数组(一次从左到右,一次从右到左)来分别计算每个位置的前缀乘积(下三角)和后缀乘积(上三角),最后将两者相乘得到结果。

  • 初始化

    • 首先获取数组 nums 的长度 n
    • 创建一个大小为 n 的结果数组 ans,初始时所有元素都设置为 1,因为乘法的单位元是 1
    • 初始化一个临时变量 tmp1,用于计算从右到左的后缀乘积。
  • 计算下三角乘积(前缀乘积)

    • 从左到右遍历数组 nums,对于每个位置 ians[i] 保存的是 nums 数组中从 0i-1 的所有元素的乘积。即 ans[i] = ans[i-1] * nums[i-1]
  • 计算上三角乘积(后缀乘积)并与前缀乘积相乘

    • 从右到左遍历数组 numstmp 用来保存当前元素右边所有元素的乘积。对于每个位置 i,将 tmpans[i] 相乘,更新 ans[i] 的值为 ans[i] * tmp
    • 更新 tmp 的值为当前元素的后缀乘积。
  • 返回结果

    • 最终的 ans 数组即为所求的结果数组,包含每个位置的元素在不包括自身的情况下的乘积。

class Solution:def productExceptSelf(self, nums: List[int]) -> List[int]:n=len(nums)ans=[1]*n;tmp=1for i in range(1,n):ans[i]=ans[i-1]*nums[i-1]for i in range(n-2,-1,-1):tmp*=nums[i+1]ans[i]*=tmpreturn ans

5、缺失的第一个整数

核心思路是通过将数组中的每个数字放到它应该在的位置上(即数字 i 放在数组的 i-1 位置),然后扫描数组找到第一个不符合条件的位置。这个位置的索引加 1 就是第一个缺失的正整数。

  • 交换数字到正确位置

    • 遍历数组 nums 中的每个元素 nums[i]
    • 如果 nums[i] 在 1 到 nn 是数组长度)之间,并且 nums[i] 还没有在正确的位置上(即 nums[i] 应该放在 nums[nums[i] - 1] 位置上),那么将 nums[i]nums[nums[i] - 1] 交换。
    • 继续检查交换后的 nums[i],直到它不需要再交换。【用while循环实现】
  • 查找第一个缺失的正整数

    • 再次遍历数组,查找第一个位置 i,使得 nums[i] != i + 1
    • 返回 i + 1 作为第一个缺失的正整数。
  • 边界处理

    • 如果所有数字都在正确的位置上,则数组包含从 1 到 n 的所有正整数,因此返回 n + 1
class Solution:def firstMissingPositive(self, nums: List[int]) -> int:n=len(nums)for i in range(n):while 1 <= nums[i] <= n and nums[i] != nums[nums[i] - 1]: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

三、矩阵

1、矩阵置零

具体做法是利用矩阵的第一行和第一列来记录每行和每列是否需要置零,同时使用一个标志位flag_col来记录第一列是否需要置零。

第一列m[i][0]是用来记录i行是否需要置零的,第一行m[0][j]是记录j列是否需要置零的。

思路主要分为两步:

  • 标记:遍历每一行,先检查每行第一列的元素m[i][0],如果为0,则标志位flag_col=True,在后面置零环节需要最后将第一列元素置零;再遍历一行中的每个元素,如果有元素m[i][j]为0,则标记它的行、列m[i][0]=m[0][j]=0。
  • 置零:遍历每一行,如果元素m[i][j]的行列的标志位有一个为0,则置元素为0,最后根据flag_col判断该行第一列元素是否为0。注意遍历的顺序,是从最后一行开始遍历,因为如果从第一行开始遍历,可能会用0覆盖掉原有的标记。
class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:m,n=len(matrix),len(matrix[0])flag_col0=Falsefor i in range(m):if matrix[i][0]==0:flag_col0=Truefor j in range(1,n):if matrix[i][j]==0:matrix[i][0]=matrix[0][j]=0for i in range(m-1,-1,-1):for j in range(1,n):if not matrix[i][0] or not matrix[0][j]:matrix[i][j]=0if flag_col0:matrix[i][0]=0

2、螺旋矩阵

从矩阵的左上角开始,依次向右、向下、向左、向上重复遍历,逐层向内,直到遍历完整个矩阵。

  • 初始化边界和结果容器

    • 首先,获取矩阵的行数 m 和列数 n,并初始化四个边界变量 topbottomleftright,分别表示当前层的上边界、下边界、左边界和右边界。
    • 初始化一个空列表 ans,用于存储遍历结果。
  • 螺旋遍历矩阵

    • 使用一个 while 循环,条件是 top <= bottomleft <= right,表示当前还有未遍历的矩阵层。
    • 在每次循环中,依次按照从左到右、从上到下、从右到左、从下到上的顺序遍历当前边界的元素,将它们添加到 ans 列表中。
  • 更新边界

    • 每遍历一条边后,更新对应的边界:
      • 从左到右遍历完上边界后,top 加 1。
      • 从上到下遍历完右边界后,right 减 1。
      • 从右到左遍历完下边界后,bottom 减 1。
      • 从下到上遍历完左边界后,left 加 1。
    • 在更新 topbottomleftright 后,需要检查是否还存在未遍历的区域,如果已经没有可遍历的区域,则退出循环。
  • 返回结果

    • 当所有的层都被遍历完成后,ans 列表即为所求的螺旋顺序遍历结果,返回该列表。
class Solution:def spiralOrder(self, matrix: List[List[int]]) -> List[int]:m,n=len(matrix),len(matrix[0])top,bottom,left,right=0,m-1,0,n-1ans=[]while top<=bottom and left<=right:for i in range(left,right+1):ans.append(matrix[top][i])top+=1for i in range(top,bottom+1):ans.append(matrix[i][right])right-=1if top<=bottom and left<=right:for i in range(right,left-1,-1):ans.append(matrix[bottom][i])bottom-=1for i in range(bottom,top-1,-1):ans.append(matrix[i][left])left+=1return ans

3、旋转图像

通过两步操作:水平翻转主对角线翻转,可以高效地实现矩阵的顺时针旋转。这种方法基于矩阵的对称性质,可以在 O(n^2) 的时间复杂度和 O(1) 的空间复杂度内完成任务。

  • 水平翻转

    • 将矩阵的每一行上下对调,即第 i 行与第 n-i-1 行互换。这一步骤相当于将矩阵沿着水平方向翻转。
  • 主对角线翻转

    • 将矩阵沿主对角线(从左上到右下的对角线)进行翻转。具体来说,交换 matrix[i][j]matrix[j][i] 的值,这会将矩阵的行列进行互换。
class Solution:def rotate(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""n=len(matrix)for i in range(n//2):for j in range(n):matrix[i][j],matrix[n-i-1][j]=matrix[n-i-1][j],matrix[i][j]for i in range(n):for j in range(i):matrix[i][j],matrix[j][i]=matrix[j][i],matrix[i][j]

4、搜索二维矩阵 II

从矩阵的右上角开始遍历,根据当前值与目标值的比较结果,决定向左移动一列(减小当前值)或向下移动一行(增大当前值)。这种方法能在 O(m + n) 的时间复杂度内找到目标值。

  • 初始化指针

    • 设定初始位置为矩阵的右上角,即 i = 0, j = n - 1,其中 i 为行索引,j 为列索引。
  • 比较当前值与目标值

    • 如果 matrix[i][j] == target,则找到了目标值,返回 True
    • 如果 matrix[i][j] > target,则当前值大于目标值,应该向左移动一列(j -= 1),因为左边的值较小。
    • 如果 matrix[i][j] < target,则当前值小于目标值,应该向下移动一行(i += 1),因为下面的值较大。
  • 终止条件

    • 如果 i 超过矩阵的行数或 j 小于 0,表示已经遍历完可能的路径,未找到目标值,返回 False
class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:m,n=len(matrix),len(matrix[0])i,j=0,n-1while i<m and j>=0:if matrix[i][j]==target:return Trueelif matrix[i][j]>target:j-=1else:i+=1return False

四、链表

1、相交链表

利用两个指针 pApB 分别遍历链表 headAheadB。当一个指针到达链表末尾时,将其重定向到另一个链表的头部。这种方式使得两个指针在第二次遍历时可以同步到达相交点(如果存在)。如果链表不相交,两个指针最终会同时到达 null

逻辑:

首先,A+B和B+A序列长度是一样的,两个指针两次遍历走过的距离是相等的。

假如A是1-2-3-4-5,B是6-4-5。两个指针pa,pb初始化分别指向A和B的链表头结点。

pa第一遍遍历链表A,遍历到结尾的时候重定向到链表B的头结点,也就是pa要遍历的序列变成了1-2-3-4-5-6-4-5。

同理,pb第一遍遍历链表B,遍历到结尾的时候重定向到链表A的头结点,也就是pb要遍历的序列变成了6-4-5-1-2-3-4-5。

其次,如果存在相交节点的话,A链和B链从相交节点开始到序列结尾的长度是相同的(也就是从相交结点4开始,4-5是相交序列。相交部分一定在A和B的尾巴,如下面序列加粗部分所示)。

1-2-3-4-5-6-4-5

6-4-5-1-2-3-4-5

因此在第二次遍历的时候,pa和pb一定能够同时遍历到4。【A+B=B+A,且相交部分到结束的长度也相同】

class Solution:def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:if not headA or not headB:return Nonepa=headA;pb=headBwhile pa!=pb:pa=pa.next if pa else headBpb=pb.next if pb else headAreturn pa

2、反转链表

使用递归的方式实现,逐层深入链表直至末尾节点,然后在递归回溯时逐层反转节点指向,最终返回新的头节点。

  • 基准条件判断

    • 处理空链表或已经到达最后一个节点的情况。如果链表为空或只有一个节点,则返回该节点(作为新链表的头节点)。
  • 递归深入到链表末尾

    • 如果链表有多个节点,递归调用 reverseList 以反转 head.next 及后续部分链表。每次递归调用会将当前链表缩短,直至到达最后一个节点。
  • 反转当前节点指向

    • 在递归回溯阶段,逐层返回新链表的头节点,并逐步将当前节点的 nextnext 指向当前节点,实现局部的链表反转。然后将当前节点的 next 置为 None,断开当前节点与后续节点的连接,避免形成环。
  • 返回新头节点

    • 最终返回 new_head,即递归过程中最早返回的节点(原链表的最后一个节点),作为反转后链表的头节点。
class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:if not head or not head.next:return headnew_head=self.reverseList(head.next)head.next.next=headhead.next=Nonereturn new_head

3、回文链表

主要分三步走:

第一步,使用快慢指针法找到链表的中间位置,将链表分为前后两部分。

第二步,反转链表后半部分。

第三步,比较链表前半部分和后半部分。

class Solution:def reverseList(self,head):if not head or not head.next:return headnew_head=self.reverseList(head.next)head.next.next=headhead.next=Nonereturn new_headdef isPalindrome(self, head: Optional[ListNode]) -> bool:if not head:return Truefast,slow=head,headwhile fast and fast.next:fast=fast.next.nextslow=slow.nextrev_second=self.reverseList(slow)ph=head;ps=rev_secondwhile ph!=slow:if ph.val!=ps.val:return Falseph=ph.nextps=ps.nextreturn True

4、环形链表

快慢指针法来判断链表中是否存在环。通过两个指针(slowfast),它们以不同的速度遍历链表。如果链表中存在环,那么这两个指针最终会相遇;如果链表无环,则 fast 指针会遍历到链表末尾,终止循环。

class Solution:def hasCycle(self, head: Optional[ListNode]) -> bool:slow=fast=headwhile fast and fast.next:slow=slow.nextfast=fast.next.nextif fast is slow:return Truereturn False

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【C++11及其特性】智能指针——unique_ptr
  • 用 BigQuery ML 和 Google Sheets 数据预测电商网站访客趋势
  • Linux驱动开发MODULE_DEVICE_TABLE的作用
  • 【Spring Boot-IDEA创建spring boot项目方法】
  • EXCEL文件如何批量加密,有什么方法
  • 零基础学习Redis(7) -- hash类型命令使用
  • TCP的流量控制深入理解
  • 92. UE5 GAS RPG 使用C++创建GE实现灼烧的负面效果
  • 【操作系统】同步互斥与Golang互斥锁实现
  • 【TomCat】安装部署
  • 实训day41(9.2)
  • Python读取Excel数据教程 - 详细版
  • HTTPS 通信时是对称加密还是非对称加密?
  • 2024年互联网公司时薪排行榜大曝光!看完我酸了,第一竟是他…
  • 云计算实训40——部署nmt、部署project_exam_system项目
  • 网络传输文件的问题
  • [PHP内核探索]PHP中的哈希表
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 2019年如何成为全栈工程师?
  • Brief introduction of how to 'Call, Apply and Bind'
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • ES6 学习笔记(一)let,const和解构赋值
  • Fastjson的基本使用方法大全
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • Laravel 菜鸟晋级之路
  • miaov-React 最佳入门
  • MySQL的数据类型
  • Python学习之路13-记分
  • 阿里云前端周刊 - 第 26 期
  • 高性能JavaScript阅读简记(三)
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 检测对象或数组
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 最简单的无缝轮播
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • ​2020 年大前端技术趋势解读
  • #define、const、typedef的差别
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (007)XHTML文档之标题——h1~h6
  • (1)Jupyter Notebook 下载及安装
  • (arch)linux 转换文件编码格式
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (pojstep1.3.1)1017(构造法模拟)
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (二)fiber的基本认识
  • (翻译)terry crowley: 写给程序员
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (转)nsfocus-绿盟科技笔试题目
  • (转)Oracle存储过程编写经验和优化措施
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .jks文件(JAVA KeyStore)
  • .net下的富文本编辑器FCKeditor的配置方法