【面试】每日力扣 Day4

42、接雨水

链接:42、接雨水

题目描述:

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
在这里插入图片描述

方法 1

# 方法1:前后缀分解
class Solution:
    def trap(self, height: List[int]) -> int:
        n = len(height)
        pre_max = [0] * n  # pre_max[i] 表示从 height[0] 到 height[i] 的最大值
        pre_max[0] = height[0]
        for i in range(1, n):
            pre_max[i] = max(pre_max[i - 1], height[i])

        suf_max = [0] * n  # suf_max[i] 表示从 height[i] 到 height[n-1] 的最大值
        suf_max[-1] = height[-1]
        for i in range(n - 2, -1, -1):
            suf_max[i] = max(suf_max[i + 1], height[i])

        ans = 0
        for h, pre, suf in zip(height, pre_max, suf_max):
            ans += min(pre, suf) - h  # 累加每个水桶能接多少水
        return ans

代码逻辑

  1. 初始化变量:
    • n:柱子的数量。
    • pre_max:一个数组,pre_max[i] 表示从 height[0] 到 height[i] 的最大值。
    • suf_max:一个数组,suf_max[i] 表示从 height[i] 到 height[n-1] 的最大值。
    • ans:用于存储最终结果,即总共能接住的雨水量。
  2. 计算 pre_max 数组:
    • pre_max[0] 初始化为 height[0]。
    • 从左到右遍历数组,对于每个位置 i,pre_max[i] 等于 pre_max[i-1] 和 height[i] 中的较大值。
  3. 计算 suf_max 数组:
    • suf_max[-1] 初始化为 height[-1]。
    • 从右到左遍历数组,对于每个位置 i,suf_max[i] 等于 suf_max[i+1] 和 height[i] 中的较大值。
  4. 计算总共能接住的雨水量:
    • 遍历 height 数组,对于每个位置 i,能接住的雨水量等于 min(pre_max[i], suf_max[i]) - height[i]。
    • 将每个位置能接住的雨水量累加到 ans 中。
  5. 返回结果:
    • 返回 ans,即总共能接住的雨水量。

复杂度分析

  • 时间复杂度:O(n),因为我们遍历了数组三次。
  • 空间复杂度:O(n),因为我们使用了两个额外的数组 pre_max 和 suf_max。

方法 2

# 方法 2:相向双指针
class Solution:
    def trap(self, height: List[int]) -> int:
        ans = left = pre_max = suf_max = 0
        right = len(height) - 1
        while left < right:
            pre_max = max(pre_max, height[left])
            suf_max = max(suf_max, height[right])
            if pre_max < suf_max:
                ans += pre_max - height[left]
                left += 1
            else:
                ans += suf_max - height[right]
                right -= 1
        return ans

代码逻辑

  1. 初始化变量:
    • ans:用于存储最终结果,即总共能接住的雨水量。
    • left:左指针,初始值为 0。
    • right:右指针,初始值为 len(height) - 1。
    • pre_max:左侧的最大高度,初始值为 0。
    • suf_max:右侧的最大高度,初始值为 0。
  2. 双指针遍历:
    • 使用 while left < right 循环,直到左指针和右指针相遇。
    • 在每次循环中,更新 pre_max 和 suf_max:
      • pre_max:左侧的最大高度,等于 pre_max 和 height[left] 中的较大值。
      • suf_max:右侧的最大高度,等于 suf_max 和 height[right] 中的较大值。
    • 比较 pre_max 和 suf_max:
      • 如果 pre_max 小于 suf_max,说明左侧的最大高度决定了当前能接住的雨水量:
        • ans 增加 pre_max - height[left]。
        • 左指针右移 (left += 1)。
      • 否则,说明右侧的最大高度决定了当前能接住的雨水量:
        • ans 增加 suf_max - height[right]。
        • 右指针左移 (right -= 1)。
  3. 返回结果:
    • 返回 ans,即总共能接住的雨水量。

示例

  • 假设输入 height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
  • 双指针遍历过程:
    • 初始状态:left = 0, right = 11, pre_max = 0, suf_max = 0, ans = 0
    • 第一次循环:pre_max = 0, suf_max = 1, ans = 0, right = 10
    • 第二次循环:pre_max = 0, suf_max = 2, ans = 0, left = 1
    • 第三次循环:pre_max = 1, suf_max = 2, ans = 0, left = 2
    • 第四次循环:pre_max = 1, suf_max = 2, ans = 1, left = 3
    • 第五次循环:pre_max = 2, suf_max = 2, ans = 1, right = 9
    • 第六次循环:pre_max = 2, suf_max = 2, ans = 2, right = 8
    • 第七次循环:pre_max = 2, suf_max = 2, ans = 2, right = 7
    • 第八次循环:pre_max = 2, suf_max = 3, ans = 2, left = 4
    • 第九次循环:pre_max = 2, suf_max = 3, ans = 3, left = 5
    • 第十次循环:pre_max = 2, suf_max = 3, ans = 5, left = 6
    • 第十一循环:pre_max = 2, suf_max = 3, ans = 6, left = 7

最终结果为 6,即总共能接住 6 个单位的雨水。

复杂度分析

  • 时间复杂度:O(n),因为我们只遍历了数组一次。
  • 空间复杂度:O(1),因为我们只使用了常数空间的变量。

方法 3

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        st = []
        for i, h in enumerate(height):
            while st and h >= height[st[-1]]:
                bottom_h = height[st.pop()]
                if not st:  # len(st) == 0
                    break
                left = st[-1]
                dh = min(height[left], h) - bottom_h  # 面积的高
                ans += dh * (i - left - 1)
            st.append(i)
        return ans

代码逻辑

  1. 初始化变量:
    • ans:用于存储最终结果,即总共能接住的雨水量。
    • st:一个栈,用于存储柱子的索引。
  2. 遍历数组:
    • 使用 for i, h in enumerate(height) 遍历数组,i 是当前柱子的索引,h 是当前柱子的高度。
    • 在每次循环中,检查栈顶元素对应的柱子高度是否小于等于当前柱子的高度:
      • 如果是,则弹出栈顶元素,计算能接住的雨水量。
      • bottom_h:弹出栈顶元素对应的柱子高度。
      • 如果栈为空,说明没有左边界,跳出循环。
      • left:栈顶元素的索引,表示左边界。
      • dh:当前能接住的雨水的高度,等于 min(height[left], h) - bottom_h。
      • ans 增加 dh * (i - left - 1),即当前能接住的雨水量。
    • 将当前柱子的索引压入栈中。
  3. 返回结果:
    • 返回 ans,即总共能接住的雨水量。

示例

  1. 假设输入 height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
  2. 单调栈遍历过程:
    • 初始状态:ans = 0, st = []
    • 第一次循环:i = 0, h = 0, st = [0]
    • 第二次循环:i = 1, h = 1, st = [0, 1]
    • 第三次循环:i = 2, h = 0, st = [0, 1, 2]
    • 第四次循环:i = 3, h = 2
      • 弹出 2,bottom_h = 0
      • 弹出 1,bottom_h = 1
      • left = 0, dh = min(0, 2) - 1 = 1
      • ans += 1 * (3 - 0 - 1) = 2
      • st = [0, 3]
    • 第五次循环:i = 4, h = 1, st = [0, 3, 4]
    • 第六次循环:i = 5, h = 0, st = [0, 3, 4, 5]
    • 第七次循环:i = 6, h = 1
      • 弹出 5,bottom_h = 0
      • 弹出 4,bottom_h = 1
      • left = 3, dh = min(2, 1) - 1 = 1
      • ans += 1 * (6 - 3 - 1) = 1
      • st = [0, 3, 6]
    • 第八次循环:i = 7, h = 3
      • 弹出 6,bottom_h = 1
      • 弹出 3,bottom_h = 2
      • left = 0, dh = min(0, 3) - 2 = 2
      • ans += 2 * (7 - 0 - 1) = 12
      • st = [0, 7]
    • 第九次循环:i = 8, h = 2, st = [0, 7, 8]
    • 第十次循环:i = 9, h = 1, st = [0, 7, 8, 9]
    • 第十一循环:i = 10, h = 2
      • 弹出 9,bottom_h = 1
      • 弹出 8,bottom_h = 2
      • left = 7, dh = min(3, 2) - 2 = 1
      • ans += 1 * (10 - 7 - 1) = 2
      • st = [0, 7, 10]
    • 第十二循环:i = 11, h = 1, st = [0, 7, 10, 11]

最终结果为 6,即总共能接住 6 个单位的雨水。

复杂度分析

  • 时间复杂度:O(n),因为我们只遍历了数组一次,并且每个元素最多只会被压入和弹出栈一次。
  • 空间复杂度:O(n),因为在最坏情况下,栈中可能会存储所有的柱子索引。

13、罗马数字转整数

链接:13、罗马数字转整数

题目描述:

罗马数字包含以下七种字符: IVXLCDM

字符      数值  
  I       1 
  V       5
  X       10
  L       50  
  C       100
  D       500 
  M       1000

例如, 罗马数字 2 写做 II ,即为两个并列的 112 写做 XⅡ ,即为 X + II27 写做 XⅤⅠ, 即为 XX + + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

示例 1:
输入: s = “III
输出: 3

示例 2:
输入: s = “IV
输出: 4

示例 3:
输入: s = “IX
输出: 9

示例 4:
输入: s = “LVIII
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:
输入: s = “MCMXCIV
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

方法 1

class Solution:
    def romanToInt(self, s: str) -> int:
        # 创建一个字典,将罗马数字字符映射到对应的整数值
        Roman2Int = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
        
        # 初始化结果变量Int为0
        Int = 0
        
        # 获取字符串s的长度
        n = len(s)
        
        # 遍历字符串的每个字符,除了最后一个字符
        for i in range(n - 1):
            # 如果当前字符的值小于下一个字符的值
            if Roman2Int[s[i]] < Roman2Int[s[i + 1]]:
                # 减去当前字符的值
                Int -= Roman2Int[s[i]]
            else:
                # 否则,加上当前字符的值
                Int += Roman2Int[s[i]]
        
        # 最后加上字符串的最后一个字符的值
        return Int + Roman2Int[s[-1]]

12、整数转罗马数字

链接:

题目描述:

七个不同的符号代表罗马数字,其值如下:

 字符     数值  
  I       1 
  V       5
  X       10
  L       50  
  C       100
  D       500 
  M       1000

罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:

  • 如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
  • 如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (V) 减 1 (I): IV ,9 是 10 (X) 减 1 (I):IX。仅使用以下减法形式:4 (IV),9 (IX),40 (XL),90 (XC),400 (CD) 和 900 (CM)。
  • 只有 10 的次方(I, X, C, M)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V),50 (L) 或 500 (D)。如果需要将符号附加4次,请使用 减法形式。

给定一个整数,将其转换为罗马数字。

示例 1:
输入:num = 3749
输出: “MMMDCCXLIX”
解释:
3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M)
700 = DCC 由于 500 (D) + 100 © + 100 ©
40 = XL 由于 50 (L) 减 10 (X)
9 = IX 由于 10 (X) 减 1 (I)
注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位

示例 2:
输入:num = 58
输出:“LVIII”
解释:
50 = L
8 = VIII

示例 3:
输入:num = 1994
输出:“MCMXCIV”
解释:
1000 = M
900 = CM
90 = XC
4 = IV

方法

R = (
    ("", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"),  # 个位
    ("", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"),  # 十位
    ("", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"),  # 百位
    ("", "M", "MM", "MMM"),  # 千位
)
        
class Solution:
    def intToRoman(self, num: int) -> str:
        return R[3][num // 1000] + R[2][num // 100 % 10] + R[1][num // 10 % 10] + R[0][num % 10]

详细步骤

  1. 定义元组 R:R 是一个包含四个子元组的元组,每个子元组分别对应个位、十位、百位和千位的罗马数字表示。
    • 第一个子元组表示个位的罗马数字。
    • 第二个子元组表示十位的罗马数字。
    • 第三个子元组表示百位的罗马数字。
    • 第四个子元组表示千位的罗马数字。
  2. 转换逻辑:
    • num // 1000 获取千位的数字,并从 R[3] 中获取对应的罗马数字。
    • num // 100 % 10 获取百位的数字,并从 R[2] 中获取对应的罗马数字。
    • num // 10 % 10 获取十位的数字,并从 R[1] 中获取对应的罗马数字。
    • num % 10 获取个位的数字,并从 R[0] 中获取对应的罗马数字。
  3. 返回结果:将各个位的罗马数字拼接起来,形成最终的罗马数字表示。

复杂度分析

  • 时间复杂度:O(1)。
  • 空间复杂度:O(1)。

58、最后一个单词的长度

给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串

示例 1:

输入:s = “Hello World”
输出:5
解释:最后一个单词是“World”,长度为 5。

方法 1

class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        # 初始化长度变量为0
        length = 0
        
        # 清除字符串首尾的空格
        s = s.strip()
        
        # 从字符串的末尾向前遍历
        for i in range(len(s) - 1, -1, -1):
            # 如果遇到空格,返回当前长度
            if s[i] == ' ':
                return length
            else:
                # 否则,长度加1
                length += 1
        
        # 如果遍历完字符串都没有遇到空格,返回当前长度
        return length

详细步骤

  1. 初始化长度变量:
    • length = 0,初始化一个变量length为0,用于存储最后一个单词的长度。
  2. 清除字符串首尾的空格:
    • s = s.strip(),使用strip()方法去除字符串首尾的空格,以确保我们只处理实际的单词部分。
  3. 从字符串的末尾向前遍历:
    • for i in range(len(s) - 1, -1, -1): 使用一个循环从字符串的末尾向前遍历。range(len(s) - 1, -1, -1)表示从len(s) - 1(字符串的最后一个字符)到0(字符串的第一个字符)进行遍历。
    • 判断当前字符是否为空格:
      • 如果当前字符是空格,说明我们已经找到了最后一个单词的结束位置,返回当前的length。
      • 否则,将length加1,继续向前遍历。
  4. 返回最后一个单词的长度

复杂度分析:

  • 时间复杂度:O(m + n)
  • 空间复杂度:O(m), word_dict 的复杂度是 O(m)。

方法 2

class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        return len(s.rstrip().split(" ")[-1])

思路

  • 先去掉字符串的最后空格
  • 将字符串按照空格分组
  • 取分组后的最后一项
  • 计算它的长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值