刷题记录-HOT 100(二)

2024-09-02 17:28
文章标签 记录 100 刷题 hot

本文主要是介绍刷题记录-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

这篇关于刷题记录-HOT 100(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130595

相关文章

Servlet中配置和使用过滤器的步骤记录

《Servlet中配置和使用过滤器的步骤记录》:本文主要介绍在Servlet中配置和使用过滤器的方法,包括创建过滤器类、配置过滤器以及在Web应用中使用过滤器等步骤,文中通过代码介绍的非常详细,需... 目录创建过滤器类配置过滤器使用过滤器总结在Servlet中配置和使用过滤器主要包括创建过滤器类、配置过滤

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

python与QT联合的详细步骤记录

《python与QT联合的详细步骤记录》:本文主要介绍python与QT联合的详细步骤,文章还展示了如何在Python中调用QT的.ui文件来实现GUI界面,并介绍了多窗口的应用,文中通过代码介绍... 目录一、文章简介二、安装pyqt5三、GUI页面设计四、python的使用python文件创建pytho

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

记录每次更新到仓库 —— Git 学习笔记 10

记录每次更新到仓库 文章目录 文件的状态三个区域检查当前文件状态跟踪新文件取消跟踪(un-tracking)文件重新跟踪(re-tracking)文件暂存已修改文件忽略某些文件查看已暂存和未暂存的修改提交更新跳过暂存区删除文件移动文件参考资料 咱们接着很多天以前的 取得Git仓库 这篇文章继续说。 文件的状态 不管是通过哪种方法,现在我们已经有了一个仓库,并从这个仓

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

【每日刷题】Day113

【每日刷题】Day113 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 91. 解码方法 - 力扣(LeetCode) 2. LCR 098. 不同路径 - 力扣(LeetCode) 3. 63. 不同路径 II - 力扣(LeetCode) 1. 91. 解码方法 - 力扣(LeetCode) //思路:动态规划。 cl

perl的学习记录——仿真regression

1 记录的背景 之前只知道有这个强大语言的存在,但一直侥幸自己应该不会用到它,所以一直没有开始学习。然而人生这么长,怎就确定自己不会用到呢? 这次要搭建一个可以自动跑完所有case并且打印每个case的pass信息到指定的文件中。从而减轻手动跑仿真,手动查看log信息的重复无效低质量的操作。下面简单记录下自己的思路并贴出自己的代码,方便自己以后使用和修正。 2 思路整理 作为一个IC d

SSM项目使用AOP技术进行日志记录

本步骤只记录完成切面所需的必要代码 本人开发中遇到的问题: 切面一直切不进去,最后发现需要在springMVC的核心配置文件中中开启注解驱动才可以,只在spring的核心配置文件中开启是不会在web项目中生效的。 之后按照下面的代码进行配置,然后前端在访问controller层中的路径时即可观察到日志已经被正常记录到数据库,代码中有部分注释,看不懂的可以参照注释。接下来进入正题 1、导入m

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位 一、背景二、定位问题三、解决方法 一、背景 flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案在博主上面这篇文章的基础上,在机器内存、cpu资源、flume agent资源都足够的情况下,flume agent又出现了tmp文件无法关闭的情况 二、