1.两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashmap={}
for i,num in enumerate(nums):
if hashmap.get(target - num) is not None:
return [hashmap.get(target - num), i]
hashmap[num] = i #这句不能放在if语句之前,解决list中有重复值或target-num=num的情况
2.两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = p = ListNode(None) #保存头结点,返回结果
s = 0 #每一步的求和暂存变量
while l1 or l2 or s: #循环条件:l1 或者l2(没有遍历完成),s(进位)不为0
s += (l1.val if l1 else 0) + (l2.val if l2 else 0) #这其实是好多代码,我自己写了好多行,但是作者这样写非常简洁,赞
p.next = ListNode(s % 10) #构建新的list存储结果,其实用较长的加数链表存也可以,%10:求个位
p = p.next
s //= 10 #求进位
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
return dummy.next
3.无重复字符的最长子串
系列:滑动窗口
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
if not s:return 0
left = 0
lookup = set()
n = len(s)
max_len = 0
cur_len = 0
for i in range(n):
cur_len += 1
while s[i] in lookup:
lookup.remove(s[left])
left += 1
cur_len -= 1
if cur_len > max_len:max_len = cur_len
lookup.add(s[i])
return max_len
4.寻找两个正序数组的中位数
系列:归并排序、二分查找
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
nums1.extend(nums2)
nums1.sort()
n = len(nums1)
if n % 2 == 0:
return (nums1[int(n/2) - 1] + nums1[int(n/2)])/2
else:
return float(nums1[int((n + 1) / 2) - 1])
5.最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
- 这个算法巧妙之处是找到了start,max_len和循环i的关系
- even搜索【0:2】,若不是,则继续搜索【1:3】,与此同时odd加入其中,搜索【0:3】,接下来搜索的是【2:4】和【1:4】,以此类推
- even搜索【0:2】,若是,max_len+1,even继续搜索【0:3】,然后搜索【1:4】,与此同时odd加入搜索【0:4】
- 随便假设一个场景,odd在搜索【3:6】时是回文串,则下一循环,even将搜索【3:7】,odd搜索【2:7】.所以,不会漏掉更长回文串
- 总结起来:
若不是回文串:even一直搜索max_len+1,odd一直搜索max_len+2。
若是回文串:even对当前回文串向后扩充一个,odd则向前和后都扩充一个。
寻找最长回文串,故不用搜索更短的串。
def longestPalindrome(self, s: str) -> str:
if not s: return ""
length = len(s)
if length == 1 or s == s[::-1]: return s
max_len,start = 1,0
for i in range(1, length):
# 一直搜索长为max_len+1
even = s[i-max_len:i+1]
# 一直搜索长为max_len+2
odd = s[i-max_len-1:i+1]
if i - max_len - 1 >= 0 and odd == odd[::-1]:
start = i - max_len - 1
max_len += 2
continue
if i - max_len >= 0 and even == even[::-1]:
start = i - max_len
max_len += 1
continue
return s[start:start + max_len]
动态规划(比上面好理解)
def longestPalindrome(self, s: str) -> str:
n = len(s)
dp = [[False] * n for _ in range(n)]
ans = ""
# 枚举子串的长度 l+1
for l in range(n):
# 枚举子串的起始位置 i,这样可以通过 j=i+l 得到子串的结束位置
for i in range(n):
j = i + l
if j >= len(s):
break
if l == 0:
dp[i][j] = True
elif l == 1:
dp[i][j] = (s[i] == s[j])
else:
dp[i][j] = (dp[i + 1][j - 1] and s[i] == s[j])
if dp[i][j] and l + 1 > len(ans):# l+1是回文串长度,因为l从0开始
ans = s[i:j+1]
return ans
6.Z字形变换
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。
def convert(self, s: str, numRows: int) -> str:
if numRows < 2: return s
res = ["" for _ in range(numRows)]
i, flag = 0, -1
for c in s:
res[i] += c
# 两个翻转条件
if i == 0 or i == numRows - 1:
flag = -flag
i += flag
return "".join(res)
7.整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
def reverse(self, x: int) -> int:
if x >= 0:
s = list(str(x))
s.reverse()
l = int(''.join(s))
if l > (2**31 - 1):
return 0
return l
else:
x = abs(x)
s = list(str(x))
s.reverse()
l = int('-'+''.join(s))
if l < -2**31:
return 0
return l
8.字符串转换成整数(atoi)
import re
class Solution:
def myAtoi(self, str: str) -> int:
INT_MAX = 2147483647
INT_MIN = -2147483648
str = str.lstrip() #清除左边多余的空格
num_re = re.compile(r'^[\+\-]?\d+') #设置正则规则
num = num_re.findall(str) #查找匹配的内容
num = int(*num) #由于返回的是个列表,解包并且转换成整数
return max(min(num,INT_MAX),INT_MIN) #返回值
缩成一句,绝了
class Solution:
def myAtoi(self, s: str) -> int:
return max(min(int(*re.findall('^[\+\-]?\d+', s.lstrip())), 2**31 - 1), -2**31)
9.回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
记住这个数学方法:反转一个数
b不断获取个位,a不断移走个位暴露新个位
def isPalindrome(self, x: int) -> bool:
#方法一,转成字符串
'''
return str(x) == str(x)[::-1]
'''
#方法二,不用转成字符串的方式
if x < 0: return False
a, b = x, 0
while a != 0:
b = b * 10 + a % 10
a = a // 10
return b == x
10.正则表达式匹配
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
'’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
def isMatch(self, s: str, p: str) -> bool:
# 装饰符实现记忆化搜索,等价于Top-Down动态规划
@lru_cache(None)
def recur(i,j):
# 结束条件
if j==len(p): return i==len(s)
# 首字母匹配
first_match = (len(s) > i) and (p[j] == s[i] or p[j] == '.')
# 处理 `*`
if len(p) >=j+2 and p[j+1] == '*':
return recur(i, j+2) or (first_match and recur(i+1, j))
# 处理首字母匹配
return first_match and recur(i+1, j+1)
return recur(0,0)
11.盛最多水的容器
系列:双指针
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
def maxArea(self, height: List[int]) -> int:
i, j, res = 0, len(height) - 1, 0
while i < j:
if height[i] < height[j]:
res = max(res, height[i] * (j - i))
i += 1
else:
res = max(res, height[j] * (j - i))
j -= 1
return res
12.整数转罗马数字
字典+减法 VS 元组+求余
def intToRoman(self, num: int) -> str:
d = {1000:'M',900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC', 50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I'}
result = ''
for i in d:
while num >= i:
num -= i
result += d[i]
return result
def intToRoman(self, num: int) -> str:
digits = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"),
(50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")]
roman_digits = []
for value, symbol in digits:
if num == 0: break
count, num = divmod(num, value)
roman_digits.append(symbol * count)
return "".join(roman_digits)
13.罗马数字转整数
def romanToInt(self, s: str) -> int:
d = {1000:'M',900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC', 50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I'}
result = 0
for key, value in d.items():
while value == s[0:1]:
s = s[1:]
result = result + key
while value == s[0:2]:
s = s[2:]
result = result + key
return result
14.最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
第一个全自主效率击败99%的题目
def longestCommonPrefix(self, strs: List[str]) -> str:
if not strs:return ""
x = strs[0]
# 提前找到最短字符串提升效率
for i in range(1, len(strs)):
if len(strs[i]) < len(x):
x = strs[i]
for s in strs:
while not re.match('^' + x, s):
x = x[:-1]
if not x:
return ""
return x
zip+set思路不错
def longestCommonPrefix(self, strs):
ans = ''
for i in zip(*strs):
if len(set(i)) == 1:
ans += i[0]
else:
break
return ans
利用python的max()和min(),在Python里字符串是可以比较的,按照ascII值排,所以只需要比较最大最小的公共前缀就是整个数组的公共前缀
def longestCommonPrefix(self, strs):
if not strs: return ""
s1 = min(strs)
s2 = max(strs)
for i,x in enumerate(s1):
if x != s2[i]:
return s2[:i]
return s1
15.三数之和
双指针
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
def threeSum(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
res=[]
if not nums or n<3:
return []
nums.sort()
res=[]
for i in range(n):
if nums[i]>0:
return res
if i>0 and nums[i]==nums[i-1]:
continue
L=i+1
R=n-1
while L<R:
if nums[i]+nums[L]+nums[R]==0:
res.append([nums[i],nums[L],nums[R]])
while L<R and nums[L]==nums[L+1]:
L=L+1
while L<R and nums[R]==nums[R-1]:
R=R-1
L=L+1
R=R-1
elif nums[i]+nums[L]+nums[R]>0:
R=R-1
else:
L=L+1
return res
16.最接近的三数之和
系列:双指针
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
代码直接改编自上一题
def threeSumClosest(self, nums: List[int], target: int) -> int:
n=len(nums)
nums.sort()
chazhi = 10**4
res = 0
for i in range(n - 2):
if i>0 and nums[i]==nums[i-1]:# 跳过重复
continue
L=i+1
R=n-1
while L<R:
tar = nums[i]+nums[L]+nums[R]
# 此句提高效率
if tar == target:
return target
if abs(tar - target) < chazhi:
res = tar
chazhi = abs(tar - target)
if (tar - target) >= chazhi:
R=R-1
if (target - tar) >= chazhi:
L=L+1
return res
17.电话号码的字母组合
系列:回溯
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
def letterCombinations(self, digits: str) -> List[str]:
d = {'2':'abc', '3':'def', '4':'ghi', '5':'jkl', '6':'mno', '7':'pqrs', '8':'tuv', '9':'wxyz'}
if not digits: return []
tmp = []
for s in digits:
if s in d:
tmp.append(d[s])
result = []
def dfs(s, tmp):
if not tmp:
result.append(s)
return
for i in tmp[0]:
dfs(s + i, tmp[1:])
dfs('', tmp)
return result
18.四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
还是用双指针
left 和 right 是双指针,外层套两个循环 i 和 j。
最外层 i,j 初始值 i+1 , left 初始值 j+1 ,right 初始值最右边。等 left 和 right 移动重合,j 就 +1,又等重合,j+1,到 j 加到头,i 再加 1,等 i 加到头程序结束。
i 和 j 指针不需要安排那么满,以免初始值就重复。
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
result = []
nums.sort()
for i in range(len(nums) - 3):
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, len(nums) - 2):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
left, right = j + 1, len(nums) - 1
while left < right:
currSum = nums[i] + nums[j] + nums[left] + nums[right]
if currSum == target:
result.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
elif currSum < target:
left += 1
else:
right -= 1
return result