刷题记录-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

相关文章

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1

Spring Boot中定时任务Cron表达式的终极指南最佳实践记录

《SpringBoot中定时任务Cron表达式的终极指南最佳实践记录》本文详细介绍了SpringBoot中定时任务的实现方法,特别是Cron表达式的使用技巧和高级用法,从基础语法到复杂场景,从快速启... 目录一、Cron表达式基础1.1 Cron表达式结构1.2 核心语法规则二、Spring Boot中定

国内环境搭建私有知识问答库踩坑记录(ollama+deepseek+ragflow)

《国内环境搭建私有知识问答库踩坑记录(ollama+deepseek+ragflow)》本文给大家利用deepseek模型搭建私有知识问答库的详细步骤和遇到的问题及解决办法,感兴趣的朋友一起看看吧... 目录1. 第1步大家在安装完ollama后,需要到系统环境变量中添加两个变量2. 第3步 “在cmd中