力扣面试经典150 —— 16-20题

  • 力扣面试经典150题
  • 在 VScode 中安装 LeetCode 插件即可使用 VScode 刷题,安装 Debug LeetCode 插件可以免费 debug
  • 本文使用 python 语言解题,文中 “数组” 通常指 python 列表;文中 “指针” 通常指 python 列表索引

文章目录

  • 16. [困难] 接雨水
    • 16.1 解法1:按行计算
    • 16.2 解法2:按列计算(动态规划)
    • 16.3 解法3:双指针
    • 16.4 解法4:栈
  • 17. [简单] 罗马数字转整数
    • 17.1 解法1:模拟所有组合情况
    • 17.2 解法2:根据规则模拟
  • 18. [中等] 整数转罗马数字
    • 18.1 解法1:枚举
    • 18.2 解法2:硬编码
  • 19. [简单] 最后一个单词的长度
    • 19.1 解法1:反向遍历
  • 20. [简单] 最长公共前缀
    • 20.1 解法1:纵向扫描

16. [困难] 接雨水

  • 题目链接
  • 标签:栈、数组、双指针、动态规划、单调栈
    在这里插入图片描述

16.1 解法1:按行计算

  • 按列计算水量时,必须要考虑墙壁相对高度,这带来了一些困难。更简单的做法是分层计算水量,这样就不需要考虑墙壁的绝对高度,只要考虑当前位置是否有墙壁就行了。
  • 具体而言,对每一行水从左到右遍历,从找到第一个墙壁开始计数水量,到找到下一个墙壁时把水量累积起来
    class Solution:
        def trap(self, height: List[int]) -> int:
            water_sum = 0
            for h in range(max(height)):        # 按行考察
                water_cnt = 0
                get_left = False
                for i in range(len(height)):    # 考察行内墙壁的情况
                    if height[i] > h:           
                        # 当前位置被墙壁阻挡
                        if not get_left:
                            # 找到左侧墙壁,从此开始要计算水量        
                            get_left = True     
                        elif water_cnt > 0:
                            # 找到墙壁时已经有水,意味着找到右墙壁,累积水量
                            water_sum += water_cnt
                            water_cnt = 0
                    else:
                        # 当前位置无墙壁,若已经找到左墙则加一格水
                        if get_left:
                            water_cnt += 1
            return water_sum
    
  • 时间复杂度 O ( m ∗ n ) O(m*n) O(mn),其中 m m m 是最高墙壁高度;空间复杂度 O ( 1 ) O(1) O(1)。这个解法现在会超时了

16.2 解法2:按列计算(动态规划)

  • 如果最高墙壁高度很高,计算复杂度会到达 O ( n 2 ) O(n^2) O(n2) 导致超时。更好的方式是通过按列计算水量,将时间负责度降低到 O ( n ) O(n) O(n)。考察每一列水量的计算方法,其实只需要该列墙壁高度、该列左侧最高墙高度和该列右侧最高墙高度三个值即可
    在这里插入图片描述
    如上图所示,正在求的列水量为 max ⁡ ( 0 , min ⁡ ( leftMax , rightMax ) − height ) \max\big(0, \min(\text{leftMax},\text{rightMax})-\text{height}\big) max(0,min(leftMax,rightMax)height)

  • 基于以上想法,我们可以先统计每一个位置左右最高墙壁高度,再利用上式来计算水量。注意到计算当前位置左右最高墙壁高度是一个动态规划问题,因为其满足动态规划三要素

    1. 无后效性:当前找到的墙壁不影响之前找到的墙壁
    2. 最优子结构:我们最后要构造长 n n n 的最高墙壁高度列表,其中任意一段都是最高的,即问题最优解的结构包含其子问题的最优解。
    3. 重叠子问题:找第 i i i 个位置左侧的最高墙高度 = 找第 i − 1 i-1 i1 位置左侧最高墙高度,并取其和第 i i i 个位置墙壁高度的最大值。其中出现了自顶向下递归过程中要重复求解的重叠子问题,这提示我们使用递推式自底向上的构造解(动态规划的自底向上形式),或使用带备忘录的递归法(动态规划的自顶向下形式)

    我们通过以下动态规划递推式来高效计算每一个位置左右最高墙壁的高度
    leftMax [ i ] = max ⁡ ( leftMax [ i − 1 ] ,  height [ i ] ) 1 ≤ i ≤ n − 1 rightMax ⁡ [ i ] = max ⁡ ( rightMax ⁡ [ i + 1 ] ,  height [ i ] ) 0 ≤ i ≤ n − 2 \begin{aligned} &\text{leftMax} [i]=\max ( \text{leftMax} [i-1] , \space\text{height}[i]) && 1 \leq i \leq n-1\\ &\operatorname{rightMax}[i]=\max (\operatorname{rightMax}[i+1] , \space\text{height}[i]) &&0 \leq i \leq n-2 \end{aligned} leftMax[i]=max(leftMax[i1], height[i])rightMax[i]=max(rightMax[i+1], height[i])1in10in2

  • 下面给出代码

    def trap(self, height: List[int]) -> int:
    	# 通过动态规划递推式找出每个位置左右最高墙壁高度
    	n = len(height)
    	max_left = [0] * n
    	max_right = [0] * n
    	for i in range(1, n-1):
    	    max_left[i] = max(max_left[i-1], height[i-1])
    	for i in range(n-2, 0, -1):
    	    max_right[i] = max(max_right[i+1], height[i+1])
    	
    	# 基于每个位置左右最高墙壁高度中较低的那个,计算每一列的水量
    	water_sum = 0
    	for i in range(1, n-1):
    	    min_max_height = min(max_left[i], max_right[i])
    	    if min_max_height > height[i]:
    	        water_sum += min_max_height - height[i]
    	
    	return water_sum
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

16.3 解法3:双指针

  • 动态规划通常可以用双指针方法优化空间复杂度。本题中注意到 max_left[i]max_right[i] 都只用过一次,可以用 left, right 两个指针分别向中间移动,实时维护至此为止左右见过的最高墙壁高度 max_left, max_right 并计算列水量
    def trap(self, height: List[int]) -> int:
        # 核心在于找出每个位置左右最高墙壁高度中较低的那个
        left, right = 0, len(height) - 1
        max_left = max_right = 0
        water_sum = 0
        while left < right:
            # 用left,right两个指针分别向中间移动,实时维护至此为止左右见过的最高墙壁高度max_left, max_right
            max_left = max(max_left, height[left])
            max_right = max(max_right, height[right])
            
            # 直接根据左右最大高度判断
            if max_left < max_right:
                # 右边存在最高柱子 max_right 可以挡水,第 left 列存水量由 max_left 决定
                water_sum += max_left - height[left]
                left += 1
            else:
                # 左边存在最高柱子 max_left 可以挡水,第 right 列存水量由 max_right 决定
                water_sum += max_right - height[right]
                right -= 1
    
        return water_sum
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

16.4 解法4:栈

  • 另一种解此题的思路是:计算水量 -> 找每一行水的左右墙壁 -> 括号匹配,这样就可以用栈解决了
    在这里插入图片描述
  • 具体而言,我们使用基于栈符号匹配的思想找出每一对墙壁,同时计算水量
    1. 栈元素:位置索引
    2. 入栈时机:当前高度小于栈顶位置高度,说明找到了积水位置,入栈成为栈顶
    3. 出栈时机:当前高度大于栈顶位置高度,说明找到了栈顶位置的那水的右侧墙,不断出栈计算水量,直到当前高度不大于栈顶或栈空,再把当前位置入栈
    4. 栈底元素:还没有计算水量的部分的左侧最高墙壁位置
  • 下面给出代码
    def trap(self, height: List[int]) -> int:
    	water_sum = 0
    	stack = []  # 用列表充当栈,stack[0] 是栈底, stack[-1] 是栈顶
    	
    	for i, h in enumerate(height):
    	    # 栈非空,且当前位置高度 > 栈顶位置高度,一直出栈计算水量
    	    while len(stack) != 0 and h > height[stack[-1]]:
    	        # 弹出栈顶,计算此位置水所在的整行水量
    	        top = stack.pop()   
    	        if len(stack) == 0: # 弹出之后栈空了,说明弹出的是最左侧的墙,不计算水量直接跳出
    	            break
    	        
    	        # 目标行水左边最高墙位置和高度
    	        left = stack[-1]
    	        max_left = height[left]
    	
    	        # 目标行水宽度
    	        water_width = i - left - 1
    	
    	        # 计算目标行水量,求和
    	        water_height = min(max_left, height[i]) - height[top]
    	        water_sum += water_width * water_height
    	    
    	    stack.append(i)
    	return water_sum
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

17. [简单] 罗马数字转整数

  • 题目链接
  • 标签:哈希表、数学、字符串
    在这里插入图片描述

17.1 解法1:模拟所有组合情况

  • 注意到罗马数字规则是左结合的,一种简单的想法是列出题目范围内所有可能的罗马数字组合情况,然后从左到右遍历,识别各个罗马数字相加
    def romanToInt(self, s: str) -> int:
    	char2value = {
    	     'I': 1,
    	     'V': 5,
    	     'X': 10,
    	     'L': 50,
    	     'C': 100,
    	     'D': 500,
    	     'M': 1000,
    	     'IV': 4,
    	     'IX': 9,
    	     'XL': 40,
    	     'XC': 90,
    	     'CD': 400,
    	     'CM': 900
    	 }
    	
    	 i, value = 0, 0
    	 while True:
    	     if s[i:i+2] in char2value:
    	         value += char2value[s[i:i+2]]
    	         i += 2
    	     else:
    	         value += char2value[s[i:i+1]]
    	         i += 1
    	
    	     if i >= len(s):
    	         break
    	 
    	 return value
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

17.2 解法2:根据规则模拟

  • 仔细分析罗马数字的构造规则,注意到
    1. 通常情况下小的数字在大的数字的右边。若输入的字符串满足该情况,那么可以将每个字符视作一个单独的值,累加每个字符对应的数值即可
    2. 若存在小的数字在大的数字的左边的情况,根据规则需要减去小的数字。对于这种情况,我们也可以将每个字符视作一个单独的值,若一个数字右侧的数字比它大,则将该数字的符号取反
  • 给出代码如下
    def romanToInt(self, s: str) -> int:
    	char2value = {
    	    'I': 1,
    	    'V': 5,
    	    'X': 10,
    	    'L': 50,
    	    'C': 100,
    	    'D': 500,
    	    'M': 1000,
    	}
    	
    	l = len(s)
    	value = 0
    	for i in range(l):
    	    if i < l - 1 and char2value[s[i]] < char2value[s[i+1]]:
    	        value -= char2value[s[i]]
    	    else:
    	        value += char2value[s[i]]
    	
    	return value
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

18. [中等] 整数转罗马数字

  • 题目链接
  • 标签:哈希表、数学、字符串
    在这里插入图片描述

18.1 解法1:枚举

  • 分析罗马数字的构造规则,发现其在个十百千位的构成规则是一样的,取值可以分成五种情况
    1. 取值 “1,2,3”:直接重复对应次取值 “1” 的符号(I/X/C
    2. 取值 “4”:由取值 “5” 的符号加-1前缀构成(IV/XL/CD
    3. 取值 “5”:有特殊符号(V/L/D
    4. 取值 “6,7,8”:由取值 “5” 的符号加重复对应次取值 “1” 的符号构成
    5. 取值 “9”:由更高一位取值 “1” 的符号和-1前缀构成(IX/XC/CM
  • 可以先拆分个十百千位取值,然后通过以上枚举关系转换
    def intToRoman(self, num: int) -> str:
    	s = ''
    	
    	# 千位
    	thousands = num // 1000
    	s += 'M' * thousands
    	
    	# 百位
    	num -= thousands * 1000
    	hundreds = num // 100
    	if hundreds == 9:
    	    s += 'CM'
    	elif hundreds > 5:
    	    s += 'D' + 'C' * (hundreds-5)
    	elif hundreds == 5:
    	    s += 'D'
    	elif hundreds == 4:
    	    s += 'CD'
    	else:
    	    s += 'C' * hundreds
    	
    	# 十位
    	num -= hundreds * 100
    	tens = num // 10
    	if tens == 9:
    	    s += 'XC'
    	elif tens > 5:
    	    s += 'L' + 'X' * (tens-5)
    	elif tens == 5:
    	    s += 'L'
    	elif tens == 4:
    	    s += 'XL'
    	else:
    	    s += 'X' * tens
    	
    	# 个位
    	num -= tens * 10
    	ones = num
    	if ones == 9:
    	    s += 'IX'
    	elif ones > 5:
    	    s += 'V' + 'I' * (ones-5)
    	elif ones == 5:
    	    s += 'V'
    	elif ones == 4:
    	    s += 'IV'
    	else:
    	    s += 'I' * ones
    	
    	return s
    
  • 时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( 1 ) O(1) O(1)

18.2 解法2:硬编码

  • 以上枚举过程写起来比较麻烦,可以将其全部整理成如下硬编码表,然后直接根据各位数据取值组合出结果
    在这里插入图片描述
  • 代码如下
    class Solution:
    
        THOUSANDS = ["", "M", "MM", "MMM"]
        HUNDREDS = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]
        TENS = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]
        ONES = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]
    
        def intToRoman(self, num: int) -> str:
            return Solution.THOUSANDS[num // 1000] + \
                Solution.HUNDREDS[num % 1000 // 100] + \
                Solution.TENS[num % 100 // 10] + \
                Solution.ONES[num % 10]
    
  • 时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( 1 ) O(1) O(1)

19. [简单] 最后一个单词的长度

  • 题目链接
  • 标签:字符串
    在这里插入图片描述

19.1 解法1:反向遍历

  • 虽然这个可以直接用python字符串的 split 语法切分,然后反向遍历找到最后一个单词,但作为算法题还是不用这些语法了。首先我们去除字符串最右边的空格,然后反向遍历计数至第一个空格即可
    def lengthOfLastWord(self, s: str) -> int:
        # 去掉最右边的空格
        right = len(s) - 1
        while s[right] == ' ':
            right -= 1
        s = s[:right+1]
        
        # 从后往前遍历直到出现空格或遍历完成,同时计数
        l = 0
        for i in range(len(s)-1,-1,-1):
            if s[i] != ' ':
                l += 1
            else:
                break
        
        return l    
    
  • 时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

20. [简单] 最长公共前缀

  • 题目链接
  • 标签:字典树、字符串
    在这里插入图片描述

20.1 解法1:纵向扫描

  • 遍历第一个字符串,同时检查其他每一个字符串前缀是否和第一个的相同,遇到不同时即返回
    在这里插入图片描述
  • 代码如下
    def longestCommonPrefix(self, strs: List[str]) -> str:
    	prefix = ''
    	for i in range(len(strs[0])):
    	    char = strs[0][i]
    	    for str in strs[1:]:
    	        if i >= len(str) or str[i] != char:
    	            return prefix
    	    prefix += char
    	    i += 1
    	return prefix
    
  • 时间复杂度 O ( m n ) O(mn) O(mn),其中 m m m 是字符串数组中的字符串的平均长度, n n n 是字符串的数量;空间复杂度 O ( 1 ) O(1) O(1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/452298.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nginx有几种启动方式

Nginx 通常可以以两种主要的方式启动&#xff1a;作为前台进程运行或作为守护进程&#xff08;后台&#xff09;运行。 前台运行&#xff1a; 当Nginx以前台模式运行时&#xff0c;它会在命令行保持活动状态&#xff0c;所有的日志输出都会直接显示在命令行上。这种模式通常用于…

execl/python读取数据库( Access、MySQL)

目录 一 、读取access数据库 &#xff08;一&#xff09;execl读取数据库 1.搜索ODBC&#xff08;注意自己的execl是64位还是32位&#xff09; 2.安装数据源的驱动程序 3.打开execl 4. 补充&#xff1a;选择数据源时&#xff0c;也可以直接在execl中选择数据源 &#xff…

丘一丘正则表达式

正则表达式(regular expression,regex,RE) 正则表达式是一种用来简洁表达一组字符串的表达式正则表达式是一种通用的字符串表达框架正则表达式是一种针对字符串表达“简洁”和“特征”思想的工具正则表达式可以用来判断某字符串的特征归属 正则表达式常用操作符 操作符说明实…

linux 模拟shell

&#x1f493;博主CSDN主页:麻辣韭菜-CSDN博客&#x1f493;   ⏩专栏分类&#xff1a;http://t.csdnimg.cn/G90eI⏪   &#x1f69a;代码仓库:Linux: Linux日常代码练习&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Linux知识   &#x1f51d;&#x1f5…

前端JavaScript篇之常见事件

目录 JavaScript常见事件click&#xff08;点击&#xff09;mouseover&#xff08;鼠标悬停&#xff09;keydown&#xff08;按键按下&#xff09;load&#xff08;加载&#xff09;submit&#xff08;提交&#xff09; JavaScript常见事件 JavaScript中的事件是指用户与网页元…

剑指offer C ++双栈实现队列

1. 基础 队列&#xff1a;先进先出&#xff0c;即插入数据在队尾进行&#xff0c;删除数据在队头进行&#xff1b; 栈&#xff1a;后进先出&#xff0c;即插入与删除数据均在栈顶进行。 2. 思路 两个栈实现一个队列的思想&#xff1a;用pushStack栈作为push数据的栈&#xff…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑碳捕集机组与氢储能系统协调运行的源荷储低碳经济调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

ansible-playbook的角色(role)

1前言 角色目录如下&#xff08;分别为httpd角色和nginx角色&#xff09; handlers/ &#xff1a;至少应该包含一个名为 main.yml 的文件&#xff1b; 其它的文件需要在此文件中通过include 进行包含 vars/ &#xff1a;定义变量&#xff0c;至少应该包含一个名为 main.yml 的…

React Hooks 那些事儿

翻了波之前写的文章还有笔记&#xff0c;发现关于前端的文章并不多&#xff08;好歹也划水做过点前端开发&#xff09;。巧了&#xff0c;最近没什么好话题可写&#xff0c;做下 React Hooks 学习笔记吧。 Effect Hook 不得不说 Hook 的出现降低了我们在 React 中处理副作用&…

极简云商业版 开源源码

简化版的云商业源码已经以开源形式发布了&#xff0c;现在可以解绑卡密和查询卡密。总体而言&#xff0c;这个版本已经相当完善了。在对接示例网盘中有一个用户注册的例子&#xff0c;需要配置一个邮箱。您可以在网页上启用QQ邮箱的标准版SMTP&#xff0c;并生成一个授权码。 …

【Spring】学习Spring框架那点小事儿

Spring作者&#xff1a;Rod Johnson Rod Johnson 是一位软件开发人员和作家&#xff0c;他在软件开发领域有着广泛的影响力。他出生于澳大利亚&#xff0c;拥有计算机科学和音乐双学位&#xff08;能写出有优雅的代码一定有艺术细胞&#xff09;。 Rod Johnson 在 2002 年出版…

保研复习数据结构记(7)--散列查找(哈希表)

哈希表有什么特点&#xff1f;数据元素的关键字与其存储地址直接相关&#xff08;通过哈希函数相关&#xff09;&#xff0c;典型的用空间换时间的算法处理冲突的方法&#xff1f;拉链法&#xff08;链地址法&#xff09;&#xff0c;开放定址法&#xff0c;再散列法什么是查找…

2024年G3锅炉水处理证模拟考试题库及G3锅炉水处理理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年G3锅炉水处理证模拟考试题库及G3锅炉水处理理论考试试题是由安全生产模拟考试一点通提供&#xff0c;G3锅炉水处理证模拟考试题库是根据G3锅炉水处理最新版教材&#xff0c;G3锅炉水处理大纲整理而成&#xff0…

【YOLOv9】训练模型权重 YOLOv9.pt 重新参数化轻量转为 YOLOv9-converted.pt

【YOLOv9】训练模型权重 YOLOv9.pt 重新参数化轻量转为 YOLOv9-converted.pt 1. 模型权重准备2. 模型重新参数化2.1 文件准备2.2 参数修改2.3 重新参数化过程 3. 重新参数化后模型推理3.1 推理超参数配置3.2 模型推理及对比 4. onnx 模型导出&#xff08;补充内容&#xff09;4…

在WSL2中安装多个Ubuntu教程

文章目录 前言一、前期准备1、WSL安装2、Docker安装 二、安装第二个Ubuntu系统1.切换为WSL22.获取Ubuntu16.04的tar文件从容器中导出tar 3. 将tar文件导入WSL4. 设置默认用户 总结 前言 适用于 Linux 的 Windows 子系统 (WSL) 是 Windows 的一项功能&#xff0c;可用于在 Wind…

指针篇章-(5)+最终思维导图

sizeof和strlen的对比 sizeof不是函数 侧面证明sizeof不是函数 如果是函数 应该需要有括号 不能落下来 strlen 只针对字符串 包含头文件 string.h 并且这个是个函数 随机数值 sizeof里面有表达式的话 表达式里面是不参与计算的 下面的s求出的是4 就是因为是不参与计算的 不…

03_渲染进程调用node

我们先创建一个文件夹及文件&#xff0c;并且在 html 引入 JS 文件。 在 render.js 里面输入以下内容&#xff1a; let fs require(fs) // let是在当前代码块有效console.log(fs) // 将fs对象的内容打印到控制台供调试和查看 fs 模块&#xff1a;对文件系统进行操作&#xf…

七月论文审稿GPT第3.1版和第3.2版:通过paper-review数据集分别微调Mistral、gemma

前言 我司第二项目组一直在迭代论文审稿GPT(对应的第二项目组成员除我之外&#xff0c;包括&#xff1a;阿荀、阿李、鸿飞、文弱等人)&#xff0c;比如 七月论文审稿GPT第1版&#xff1a;通过3万多篇paper和10多万的review数据微调RWKV七月论文审稿GPT第2版&#xff1a;用一万…

使用Flask快速搭建轻量级Web应用【第127篇—Flask】

使用Flask快速搭建轻量级Web应用 在Web开发领域&#xff0c;选择适合项目需求的框架至关重要。Flask&#xff0c;一个轻量级的Python Web框架&#xff0c;以其简洁、灵活和易扩展的特性而备受开发者青睐。本文将介绍如何使用Flask迅速搭建一个轻量级的Web应用&#xff0c;并通过…

FreeRTOS学习笔记-基于stm32(5)列表和列表项

一、列表与列表项简介 列表是FreeRTOS中的一种数据结构&#xff0c;类似双向循环链表。用来跟踪FreeRTOS中的任务。列表项就是存放在列表中的项目。 二、列表 列表结构体&#xff1a; typedef struct xLIST {listFIRST_LIST_INTEGRITY_CHECK_VALUE //校验值c…