注:部分思路也被体现在代码注释中!
第1题:
方法(1):直接调用find()内置函数返回索引
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
return haystack.find(needle)
方法(2):从索引0开始匹配,每次在 haystack串中将长度为len(needle)的子串和needle匹配
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# if not needle:
# return 0
n, m = len(haystack), len(needle)
# i代表着起点位置
for i in range(n - m + 1):
# 标识外层循环一次的结果
flag = True
for j in range(m):
# 对应位置字符不匹配,匹配失败
if haystack[i + j] != needle[j]:
flag = False
break
if flag:
return i
# 全部循环结束,没有返回索引代表匹配失败
return -1
第2题:
方法(1):调用split()函数将字符串切分为仅包含字符串的列表,返回列表最后位置的字符串长度
class Solution:
def lengthOfLastWord(self, s: str) -> int:
temp = s.split()
return len(temp[-1])
方法(2):从末尾搜索第一个不为空串的字符即为最后一个字符的末尾,再从此位置搜索到 第一个为空串的索引,相减即长度
class Solution:
def lengthOfLastWord(self, s: str) -> int:
# 求最后一个单词结束字符的下标
end = len(s) - 1
while end >= 0 and s[end] == " ":
end -=1
if end < 0:
return 0
# 求最后一个单词开始字符的下标
start = end
while s[start] != " " and start >= 0:
start -= 1
return end - start
第3题:
回文串的正序和逆序是完全相同的
方法(1):遍历字符串,将字母或数字转小写后扔进列表,判断列表的逆序和正序是否相同
class Solution:
def isPalindrome(self, s: str) -> bool:
if not s:
return True
temp = []
for i in s:
if i.isalnum():
temp.append(i.lower())
return temp == temp[::-1]
方法(2):双指针,从两头判断指针所指位置的字符是否相同
class Solution:
def isPalindrome(self, s: str) -> bool:
# 两端判断
if not s:
return True
l,r = 0,len(s)-1
while l < r:
while l < r and not s[l].isalnum():
l += 1
while l < r and not s[r].isalnum():
r -= 1
if l < r:
if s[l].lower() != s[r].lower():
return False
else:
l,r = l+1,r-1
return True
第4题:
思路:遍历矩阵,判断每个元素是否和左上角相等
class Solution:
def isToeplitzMatrix(self, matrix) -> bool:
for i in range(1,len(matrix)):
for j in range(1,len(matrix[0])):
# 注意边界条件 i-1,j-1;所以i,j都从1开始
if matrix[i][j] != matrix[i-1][j-1]:
return False
return True
第5题:
思路:利用前缀和,要求左侧等于右侧,即2倍的前缀和=数组总和total-nums[i]
class Solution:
def pivotIndex(self, nums) -> int:
total = sum(nums)
temp = 0
for i in range(len(nums)):
if nums[i] + 2*temp == total:
return i
temp += nums[i]
return -1
第6题:
思路:利用位运算,从n的末尾开始,将当前位左移31-i位,求累加,具体见注释
class Solution:
def reverseBits(self, n: int) -> int:
# 位运算
# n & 1 做与运算表示取出n的最后一位
# n >> i 表示n右移i位
res = 0
for i in range(32):
temp = (n >> i) & 1
# 把二进制个位左移 31-i位,相当于翻转操作
res += temp << (31-i)
return res
第7题:
方法(1):将其中一个字符串遍历加入列表,再对另一个串遍历,每次删除列表中相同字符,判断字符是否存在和最后列表是否为空
class Solution:
def isAnagram(self, s: str, t: str):
# 单一匹配
temp = []
for i in s:
temp.append(i)
for j in t:
if j in temp:
temp.remove(j)
else:return False
return len(temp) == 0
方法(2):利用内置函数counter,判断词频字典是否相等
from collections import Counter
class Solution:
def isAnagram(self, s: str, t: str):
return Counter(s) == Counter(t)
第8题:
思路:一个栈做入队,一个栈做出队,具体见代码注释
class MyQueue:
def __init__(self):
# 入队栈
self.temp1 = []
# 出队栈
self.temp2 = []
def push(self, x: int) -> None:
self.temp1.append(x)
def pop(self) -> int:
# 出队时,先从出队栈出,出队栈为空则通过入队栈添值
if self.temp2:
return self.temp2.pop()
while self.temp1:
self.temp2.append(self.temp1.pop())
return self.temp2.pop()
def peek(self) -> int:
# 队首同出队
if self.temp2:
return self.temp2[-1]
while self.temp1:
self.temp2.append(self.temp1.pop())
return self.temp2[-1]
def empty(self) -> bool:
# 只有两个栈全为空,队才空
return len(self.temp1) == 0 and len(self.temp2) == 0
第9题:
思路:双指针,从头匹配,匹配成功两个指针都后移一位,失败则待匹配串指针后移一位
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
# 双指针
i = j = 0
while i < len(s) and j < len(t):
if s[i] == t[j]:
i += 1
j += 1
else:j += 1
return i == len(s)
第10题:
思路:二分查找,把区间一分为2,比中间值大则在后半部分二分,小则在前部分二分
# The guess API is already defined for you.
# @param num, your guess
# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
# def guess(num: int) -> int:
class Solution:
def guessNumber(self, n: int) -> int:
l,r = 1,n
while l < r:
mid = (l+r) // 2
flag = guess(mid)
if flag == 0:
return mid
if flag == -1:
r = mid - 1
else:l = mid + 1
return l
第11题:
思路:dp[i][j]代表着区间[i,j]赢得游戏至少需要的现金,比如dp[1][1] = 0,代表着区间长度为1的[1,1]区间赢得游戏至少需要0元。要想知道某种len=2的情况下所需要的最少现金,必须要知道所有len=1情况下的现金状况,以此类推,填写dp table.具体见注释。
class Solution:
def getMoneyAmount(self, n: int) -> int:
# 1-n
# dp数组: n+1 * n+1
dp = [[0] * (n+1) for i in range(n+2)]
# 区间长度 dp[i][i] 就是len=1的情况 结果为0,所以从2-n
for len in range(2,n+1):
# 区间起点,区间终点 n-len+1
for start in range(1,n-len+2):
# 分割点,第一次猜的数,终点是start + len -1(不包括)
temp = float('inf')
for piv in range(start,start+len-1):
# 每个分割点所获得最大值,局部最大值,再求此区间的最小值
res = piv + max(dp[start][piv-1],dp[piv+1][start+len-1])
temp = min(temp,res)
dp[start][start+len-1] = temp
return dp[1][n]
第12题:
思路:dp[i]代表以索引为i的数字为结尾的最长递增子序列的长度,遍历所有num[i] > num[j],j属于[0,i)区间,得到max(dp[j]),dp[i] = max(dp[j]) + 1,最后的结果就是max(dp)
class Solution:
def lengthOfLIS(self, nums) -> int:
# 动态规划
# dp[i]代表以索引为i的数字为结尾的最长递增子序列的长度
dp = [1] * len(nums)
res = 1
for i in range(1,len(nums)):
for j in range(0,i):
if nums[i] > nums[j]:
dp[i] = max(dp[i],dp[j] + 1)
res = max(res,dp[i])
return res
第13题:
思路:从矩阵右上角搜索,小于矩阵值则舍去这一列,大于矩阵值则舍去这一行
class Solution:
def searchMatrix(self, matrix, target: int) -> bool:
if not matrix:
return matrix
# 从矩阵的右上角开始(中等的数,矩阵的左上角和右下角分别是最大的数和最小的数)
r = len(matrix)
c = len(matrix[0])
x,y = 0,c-1
while x < r and y >= 0:
if matrix[x][y] == target:
return True
# 小于矩阵值,当前列不用搜索了(当列下面的值更大)
if matrix[x][y] > target:
y -= 1
# 大于矩阵值,跳到下一行搜索
else: x += 1
return False
第14题:
思路:利用字典存储字典键(数组的值),值存储一个3个元素的列表[c,i,j]分别代表出现的频数、首次出现的位置、最后一次出现的位置。最后遍历字典的值,得到答案
class Solution:
def findShortestSubArray(self, nums) -> int:
# 字典存储一个3个元素的列表[c,i,j]分别代表出现的频数、首次出现的位置、最后一次出现的位置
map = dict()
for i ,num in enumerate(nums):
if num in map:
map[num][0] += 1
map[num][2] = i
else:
map[num] = [1,i,i]
maxdu = minlen = 0
for c,i,j in map.values():
if maxdu < c:
maxdu = c
minlen = j-i+1
elif maxdu == c:
minlen = min(minlen,j-i+1)
return minlen
第15题:
方法(1):遍历所有城市,对当前城市进行深度优先搜索,看和哪些城市相连通,再对连通城市进行dfs,具体见注释。
class Solution:
def findCircleNum(self, isConnected) -> int:
n = len(isConnected)
# 存储已经访问过的城市
visted = set()
# 统计dfs了几次,也就是省份的数量
count = 0
def dfs(i):
for j in range(n):
# 判断当前城市和哪几个城市连通
if isConnected[i][j] == 1 and j not in visted:
visted.add(j)
# 对连通城市再进行dfs
dfs(j)
for i in range(n):
if i not in visted:
visted.add(i)
count += 1
dfs(i)
return count
方法(2):并查集,把相连的城市合并为一个连通分量,最后查询有几个不同的连通的连通分量。
# 并查集解法
class UnionSet:
def __init__(self,n):
self.parent = [i for i in range(n)]
self.count = [1 for _ in range(n)]
def find(self,x):
# 寻找根节点
root = x
while root != self.parent[root]:
root = self.parent[root]
# 路径压缩
while self.parent[x] != root:
self.parent[self.parent[x]] = root
x = self.parent[x]
return root
def union(self,x,y):
root_x,root_y = self.find(x),self.find(y)
if root_x != root_y:
# 按高度合并
if self.count[root_x] < self.count[root_y]:
self.parent[root_x] = root_y
self.count[root_y] += self.count[root_x]
else:
self.parent[root_y] = root_x
self.count[root_x] += self.count[root_y]
class Solution:
def findCircleNum(self, isConnected):
unionSet = UnionSet(len(isConnected))
for i in range(len(isConnected)):
for j in range(i+1,len(isConnected[0])):
if isConnected[i][j] == 1:
unionSet.union(i,j)
ans = set()
for i in range(len(isConnected)):
ans.add(unionSet.find(i))
return len(ans)
第16题:
方法(1):弗洛伊德算法,dp[i][j]表示节点i到节点j的最短路径,穷举所有的节点k,作为i,j的中间节点,看是否对i,j的最短路径最贡献。在求dp数组中第k行的最大值,就是答案。
class Solution:
def networkDelayTime(self, times, n: int, k: int) -> int:
# 弗洛伊德算法,初始化dp
dp = [[float('inf')]*(n+1) for _ in range(n+2)]
for time in times:
dp[time[0]][time[1]] = time[2]
for i in range(1,n+1):
dp[i][i] = 0
# 算法主体 尝试所有x,以x为分割点,看看k节点对于从i,j的最短路径有没有贡献
for x in range(1,n+1):
for i in range(1,n+1):
for j in range(1,n+1):
dp[i][j] = min(dp[i][j],dp[i][x] + dp[x][j])
res = 0
for i in range(1,n+1):
res = max(dp[k][i],res)
return res if res < float('inf') else -1
方法(2):Dijkstra算法,每次选择一个离初始节点最近的点(权值最小)来更新其它节点的权值。为了方便更快的找出离初始节点最近的点,这里采用了优先级队列。
class Solution:
def networkDelayTime(self, times, n: int, k: int) -> int:
# Dijkstra
g = [[] for _ in range(n+1)]
for time in times:
g[time[0]].append((time[1],time[2]))
dist = [float('inf')] * (n+1)
dist[k] = 0
q = [(k,0)]
while q:
cur,costs = heapq.heappop(q)
if dist[cur] < costs:
continue
for next,next_time in g[cur]:
if dist[cur] + next_time < dist[next]:
dist[next] = dist[cur] + next_time
heapq.heappush(q,(next,dist[next]))
return max(dist[1:]) if max(dist[1:]) < float('inf') else -1
第17题:
思路:利用单调队列存储每个窗口可能的最大值,如果单调队列的队头索引i+len>cur,说明nums[i]在以cur为结尾的窗口中是有效的。
class Solution:
def maxSlidingWindow(self, nums, k: int) :
n = len(nums)
if not nums or k == 1 or n == 1:
return nums
# 单调递减队列,队列存储着窗口可能的最大值
queue = []
for i in range(k):
# 每次和队尾比,如果当前队尾小说明当前队尾不可能是窗口的最大值,删除
while queue and nums[i] >= nums[queue[-1]]:
queue.pop()
queue.append(i)
res = [nums[queue[0]]]
for i in range(k,n):
while queue and nums[i] >= nums[queue[-1]]:
queue.pop()
queue.append(i)
# 超出窗口范围
while queue and queue[0] <= i-k:
queue.pop(0)
res.append(nums[queue[0]])
return res
第18题:
思路:和省份城市那题类似,遍历每一个字符串利用dfs把和该字符串相似的字符串全部找出来,再把相似字符串进行dfs,把访问过的字符串加入set集合中,防止重复访问,dfs了几次就有几个数组。
class Solution:
def numSimilarGroups(self, strs) -> int:
def isVailed(str1,str2):
i = j = 0
while i < len(str1) and j < len(str2):
if str1[i] != str2[j]:
ii,jj = i+1,j+1
while str1[ii] == str2[jj]:
ii += 1
jj += 1
str1 = list(str1)
temp = str1[i]
str1[i] = str1[ii]
str1[ii] = temp
str1 = ''.join(str1)
if str1 != str2:
return False
return True
i += 1
j += 1
def dfs(s):
for t in strs:
if t not in visted and isVailed(s,t):
visted.add(t)
dfs(t)
visted = set()
count = 0
for s in strs:
if s not in visted:
visted.add(s)
dfs(s)
count += 1
return count
第19题:
思路:nums[i][i] 是两个完全平方数之和,如第3行,第3列nums[2][2] = 13 = 2^2 + 3^2.
# 19 蛇形矩阵
# i为你想求的第几行
i = 40
ans = (i-1)*(i-1) + i*i
print(ans)
第20题:
思路:把数字i转化为字符串,遍历字符串寻找字符2
# 20 门牌号
# 计数
count = 0
# n为你想求的门牌号结尾
n = 4040
for i in range(2,n+1):
s =str(i)
for j in s:
if j == '2':
count += 1
print(count)
第21题:
思路:蛮力法,遍历矩阵,以每个元素作为起点,进行尝试,具体见注释
# 21 三升序列
s = str(input())
# 按行切分
s = s.split()
n = len(s)
m = len(s[0])
# print(n,m)
data = [['']*m for _ in range(n+1)]
# print(data)
# 把输入的字符串转化为矩阵
for i in range(n):
for j in range(m):
data[i][j] = s[i][j]
res = 0
# 5个方向
dic = [(-1,1),(0,1),(1,1),(1,0),(1,-1)]
for i in range(n):
for j in range(m):
# 以data[i][j]为起点,从左往右包括右上,右,右下、
# 从上往下包括左下,下,右下一共5中情况,分别遍历
for (dx,dy) in dic:
x1,y1 = i+dx,j+dy
# 单个方向一直搜索
while 0 <= x1 < n and 0 <= y1 < m:
x2,y2 = x1+dx,y1+dy
while 0 <= x2 < n and 0 <= y2 < m:
if data[i][j] < data[x1][y1] < data[x2][y2]:
res += 1
x2 += dx
y2 += dy
x1 += dx
y1 += dy
print(res)
第22题:
思路:先算小时,再算分钟,具体见注释
# 22 时间判定
a = int(input())
b = int(input())
t = int (input())
# 几个小时,向下取整
n = t // 60
a += n
# 判定剩余分钟数加起来是否还够一小时
b += t - n*60
if b > 60:
a += 1
b -= 60
print(a)
print(b)
第23题:
思路:从头开始,两两比较距离
# 23 字母内部距离
# s为你想求的字符串
s = 'WATJKJDXRGZNXYTW'
res = 0
for i in range(len(s)-1):
for j in range(i+1,len(s)):
res += abs(ord(s[i]) - ord(s[j]))
print(res)
第24题:
思路:递归,每一步都有2个台阶或者1个台阶,一直走到39,判断步数是否为偶数
# 24 电影台阶
res = 0
def helper(f,n):
global res
if f > 39:
return
if f == 39 and n % 2 == 0:
res += 1
helper(f+1,n+1)
helper(f+2,n+1)
helper(0,0)
print(res)