227. 基本计算器 II(中等)
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+
, -
,*
,/
四种运算符和空格
。 整数除法仅保留整数部分。
示例 1:
输入: "3+2*2"
输出: 7
示例 2:
输入: " 3/2 "
输出: 1
示例 3:
输入: " 3+5 / 2 "
输出: 5
说明:
- 你可以假设所给定的表达式都是有效的。
- 请不要使用内置的库函数
eval
。
方法:栈
class Solution:
def calculate(self, s: str) -> int:
stack = []
num = 0
sign = '+'
for i in range(len(s)):
if s[i].isdigit():
num = num*10+int(s[i])
if s[i] in '+-*/' or i==len(s)-1:
if sign == '+':
stack.append(num)
elif sign == '-':
stack.append(-num)
elif sign =='*':
stack.append(stack.pop()*num)
else:
stack.append(int(stack.pop()/num))
num = 0
sign = s[i]
return sum(stack)
1002. 查找常用字符(简单)
给定仅有小写字母组成的字符串数组 A
,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。
你可以按任意顺序返回答案。
示例 1:
输入:["bella","label","roller"]
输出:["e","l","l"]
示例 2:
输入:["cool","lock","cook"]
输出:["c","o"]
提示:
1 <= A.length <= 100
1 <= A[i].length <= 100
A[i][j]
是小写字母
方法:计数
class Solution:
def commonChars(self, A: List[str]) -> List[str]:
minfreq = [float("inf")]*26
for word in A:
freq = [0]*26
for ch in word:
freq[ord(ch)-ord("a")] += 1
for i in range(26):
minfreq[i] = min(minfreq[i], freq[i])
ans = list()
for i in range(26):
ans.extend([chr(i+ord("a"))]*minfreq[i])
return ans
43. 字符串相乘(中等)
给定两个以字符串形式表示的非负整数 num1
和 num2
,返回 num1
和 num2
的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = "2", num2 = "3"
输出: "6"
示例 2:
输入: num1 = "123", num2 = "456"
输出: "56088"
说明:
num1
和num2
的长度小于110。num1
和num2
只包含数字0-9
。num1
和num2
均不以零开头,除非是数字 0 本身。- 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
方法1:做加法
如果 num1和num2之一是 0,则直接将 0作为结果返回即可。
如果num1和 num2都不是 0,则可以通过模拟「竖式乘法」的方法计算乘积。从右往左遍历乘数,将乘数的每一位与被乘数相乘得到对应的结果,再将每次得到的结果累加。这道题中,被乘数是 num1,乘数是num2。
需要注意的是,num2除了最低位以外,其余的每一位的运算结果都需要补 0。
复杂度分析
时间复杂度:O(mn+n^2),其中 m 和 n 分别是num1和 num2的长度。需要从右往左遍历 num2,对于 num2的每一位,都需要和 num1的每一位计算乘积,因此计算乘积的总次数是 mn。字符串相加操作共有 n 次,相加的字符串长度最长为 m+n,因此字符串相加的时间复杂度是 O(mn+n^2)。总时间复杂度是 O(mn+n^2)。
空间复杂度:O(m+n),其中 m 和 n 分别是 num1和 num2的长度。空间复杂度取决于存储中间状态的字符串,由于乘积的最大长度为 m+n,因此存储中间状态的字符串的长度不会超过 m+n。
class Solution:
def multiply(self, num1: str, num2: str) -> str:
if num1 == '0' or num2 == '0':
return '0'
m, n = len(num1), len(num2)
res = '0'
for i in range(n-1, -1, -1):
y = int(num2[i])
add = 0
cur = ['0']*(n-i-1)
for j in range(m-1, -1, -1):
product = int(num1[j])*y + add
cur.append(str(product%10))
add = product // 10
if add > 0:
cur.append(str(add))
cur = ''.join(cur[::-1])
res = self.addString(res, cur)
return res
def addString(self, num1: str, num2: str) -> str:
i, j = len(num1)-1, len(num2)-1
add = 0
res = []
while i >= 0 or j >= 0 or add != 0:
x = int(num1[i]) if i >= 0 else 0
y = int(num2[j]) if j >= 0 else 0
result = x + y + add
res.append(str(result%10))
add = result // 10
i -= 1
j -= 1
return ''.join(res[::-1])
方法2:
17. 电话号码的字母组合(中等)
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
方法1:回溯
首先使用哈希表存储每个数字对应的所有可能的字母,然后进行回溯操作。
每次取电话号码的一位数字,从哈希表中获得该数字对应的所有可能的字母,并将其中的一个字母插入到已有的字母排列后面,然后继续处理电话号码的后一位数字,直到处理完电话号码中的所有数字,即得到一个完整的字母排列。然后进行回退操作,遍历其余的字母排列。
回溯算法用于寻找所有的可行解,如果发现一个解不可行,则会舍弃不可行的解。在这道题中,由于每个数字对应的每个字母都可能进入字母组合,因此不存在不可行的解,直接穷举所有的解即可。
……
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits:
return list()
phoneMap = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz"
}
def backtrace(index):
if index == len(digits):
res.append("".join(temp))
else:
digit = digits[index]
for ch in phoneMap[digit]:
temp.append(ch)
backtrace(index+1)
temp.pop()
res = []
temp = []
backtrace(0)
return res
方法2:回溯:itertools.product()
from itertools import product
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits:
return list()
phoneMap = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz'
}
groups = [phoneMap[digit] for digit in digits]
return [''.join(item) for item in product(*groups)]
824. 山羊拉丁文(简单)
给定一个由空格分割单词的句子 S
。每个单词只包含大写或小写字母。
我们要将句子转换为 “Goat Latin”(一种类似于 猪拉丁文 - Pig Latin 的虚构语言)。
山羊拉丁文的规则如下:
- 如果单词以元音开头(a, e, i, o, u),在单词后添加
"ma"
。
例如,单词"apple"
变为"applema"
。 - 如果单词以辅音字母开头(即非元音字母),移除第一个字符并将它放到末尾,之后再添加
"ma"
。
例如,单词"goat"
变为"oatgma"
。 - 根据单词在句子中的索引,在单词最后添加与索引相同数量的字母
'a'
,索引从1开始。
例如,在第一个单词后添加"a"
,在第二个单词后添加"aa"
,以此类推。
返回将 S
转换为山羊拉丁文后的句子。
示例 1:
输入: "I speak Goat Latin"
输出: "Imaa peaksmaaa oatGmaaaa atinLmaaaaa"
示例 2:
输入: "The quick brown fox jumped over the lazy dog"
输出: "heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa"
说明:
S
中仅包含大小写字母和空格。单词间有且仅有一个空格。1 <= S.length <= 150
。
class Solution:
def convert(self, word):
if word[0] not in 'aeiouAEIOU':
word = word[1:] + word[:1]
return word + 'ma'
def toGoatLatin(self, S: str) -> str:
return " ".join(self.convert(word) + 'a' * i for i, word in enumerate(S.split(), 1))
二分查找
贪心法
1231
376
452. 用最少数量的箭引爆气球(中等)
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。
一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
Example:
输入:
[[10,16], [2,8], [1,6], [7,12]]
输出:
2
解释:
对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。
方法:贪心法
跟踪气球的结束坐标:
(1)若下个气球开始坐标在当前气球的结束坐标前,则我们可以用一支箭一起引爆。
(2)若下个气球的开始坐标在当前气球的结束坐标后,则我们必须增加箭的数量,并跟踪下个气球的结束坐标。
算法:
(1)根据 x_end 将气球进行排序。
(2)初始化 first_end 为第一个气球结束的坐标 points[0][1]。
初始化箭的数量 arrows = 1。
(3)遍历所有的气球:
如果气球的开始坐标大于 first_end:则增加箭的数量,
并将 first_end 设置为当前气球的 x_end。
class Solution:
def findMinArrowShots(self, points: List[List[int]]) -> int:
if not points:
return 0
points.sort(key = lambda x: x[1])
res = 1
first_end = points[0][1]
for x_start, x_end in points:
if first_end < x_start:
res += 1
first_end = x_end
return res
392. 判断子序列(简单)
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"
是"abcde"
的一个子序列,而"aec"
不是)。
示例 1:
s = "abc"
, t = "ahbgdc"
返回 true
.
示例 2:
s = "axc"
, t = "ahbgdc"
返回 false
.
解题思路:双指针
本题询问的是,s是否是 t的子序列,因此只要能找到任意一种 s 在 t 中出现的方式,即可认为 s 是 t 的子序列。
而当我们从前往后匹配,可以发现每次贪心地匹配靠前的字符是最优决策。
这样,我们初始化两个指针 i 和 j,分别指向 s 和 t 的初始位置。每次贪心地匹配,匹配成功则 i 和 j 同时右移,匹配 s 的下一个位置,匹配失败则 j 右移,i 不变,尝试用 t 的下一个字符匹配 s。
最终如果 i 移动到 s的末尾,就说明 s 是 t 的子序列。
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
n, m = len(s), len(t)
i = j = 0
while i < n and j < m:
if s[i] == t[j]:
i += 1
j += 1
return i == n
455. 分发饼干(简单)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。
示例 1:
输入: [1,2,3], [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
示例 2:
输入: [1,2], [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
解题思路:贪心
先排序(从小到大,从大到小排序均可),然后利用贪心算法,对饼干进行遍历,如果饼干能满足小孩胃口,满足小孩的数量+1,直到饼干遍历完或者小孩都满足。
从大到小排序:
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g = sorted(g, reverse=True)
s = sorted(s, reverse=True)
n, m = len(g), len(s)
i, j, count = 0, 0, 0
while i < n and j < m:
if g[i] <= s[j]:
j += 1
count += 1
i += 1
return count
********************************************************
从小到大排序:
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g = sorted(g)
s = sorted(s)
n, m = len(g), len(s)
i, j, count = 0, 0, 0
while i < n and j < m:
if g[i] <= s[j]:
i += 1
count += 1
j += 1
return count2021-5