前言
刷了好久了,但是八月的现在才总结,因为今天往回看,刷了一些题,但是有些题早已经忘记,有些算法下意识还是使用了不好的思路,或者思路不够清晰,所以,直面自己早就想但是总是逃避的总结,通过总结来回顾,引申。
8.1
老规矩,直接上题
这个题可以使用动态规划,或者滑动窗口,如果使用动态规划,就要找到状态转移方程,首先分析题,这个就是在多个数组中,找到一个范围,然后这个范围大小最小,最大最小问题用动态规划。可以把每个数组的头放到一个数组中,每次最小的头出去 ,这个数组中的下一个元素进来,然后有个数组的元素全用过,因为动的是最小的,所以如果全动完,这个数组的最后一个元素肯定是头,头就不能动了,然后后边的元素如果动,一定是变大,最后一个范围只能更大,所以这个时候肯定是最小。
滑动窗口也是这个道理。
class Solution:#动态规划
def smallestRange(self, nums):
def find_min(num):
return num.index(min(num))
lenth=[]
k1=0
k=len(nums)
for j in range(k):
lenth.append(0)
k1+=len(nums[j])
dp = [[0 for i in range(k)] for i in range(k1)] # 建立DP数组
for j in range(k):
dp[0][j]=nums[j][0]
min_temp=find_min(dp[0])
a=lenth[min_temp]+1
if a>len(nums[min_temp])-1:
return [min(dp[0]),max(dp[0])]
lenth.pop(min_temp)
lenth.insert(min_temp,a)
tem = []
for j in range(k):
tem.append(nums[j][lenth[j]])
for i in range(1,k1):
b=max(tem)-min(tem)
c=max(dp[i-1])-min(dp[i-1])
if b>=c:
dp[i]=dp[i-1].copy()
else:
dp[i]=tem.copy()
min_temp = find_min(tem)
a = lenth[min_temp] + 1
if a > len(nums[min_temp])-1:
return [min(dp[i]),max(dp[i])]
lenth[min_temp]+=1
tem[min_temp]=nums[min_temp][lenth[min_temp]]
class Solution1:#滑动窗口
def smallestRange(self, nums) :
k = len(nums)
if k == 1: return [nums[0][0], nums[0][0]]
indexs = [0] * k
lists = []
largest = -10 ** 5
import heapq
# 生成大小为k的堆 是小顶堆,每次都将最小值取出去,然后换成这个值所在数组的下一个元素
for i in range(k):
num = nums[i][0]
heapq.heappush(lists, (num, i))
largest = max(largest, num)
left, right = lists[0][0], largest
while True:
num, i = heapq.heappop(lists)
# 取出最小的数,以及对应的列表的下标
indexs[i] += 1
# 下标+1
if indexs[i] >= len(nums[i]): # 退出循环
break
# 通过下标i找到列表,再通过indexs数组找到下一个元素的下标
newnum = nums[i][indexs[i]]
heapq.heappush(lists, (newnum, i)) # 插入新的数
largest = max(largest, newnum) # 更新最大值
if largest - lists[0][0] < right - left: # 更新区间
left, right = lists[0][0], largest
return [left, right]
8.3
这个是老题了,三种方式,
从头哈希表遍历,出现次数最多的就是
排序,中间的那个值肯定是
摩尔投票:众数和非众数
众数是超过长度一半的数,
票数正负抵消: 设数组 nums 中的众数为 x ,数组长度为 n 。若 nums 的前 a 个数字的 票数和 = 0,则 数组后 (n−a) 个数字的 票数和一定仍 >0 (即后 (n-a) 个数字的 众数仍为 x )
为0的时候重新设定众数,
class Solution:
def majorityElement(self, nums: List[int]) -> int:
votes = 0
for num in nums:
if votes == 0: x = num
votes += 1 if num == x else -1
return x
三个要素:加数,被加数,进位
返回值是字符型
class Solution:
def addStrings(self, num1: str, num2: str) -> str:
## 主要是用于大数相加,对于python来说没问题
## 首先将长度归一化
m = len(num1)
n = len(num2)
while m > n:
num2 = '0' + num2
n += 1
while m < n:
num1 = '0' + num1
m += 1
res = ''
carry = 0
for i in range(m - 1, -1, -1):
a = int(num1[i])
b = int(num2[i])
sum = a + b + carry
res = str(sum % 10) + res
carry = sum // 10
if carry:
res = '1' + res
return res
这个看题目,除了C之外,其他的都可以看做是数,需要一个栈,每次的字母都对栈的最后元素操作,最后计算栈内数字的总和即可
class Solution:
def calPoints(self, ops) -> int:
a=[]
for i in range(len(ops)):
if ops[i]=='C':
a.pop()
elif ops[i]=='D':
a.append(int(a[-1])*2)
elif ops[i]=='+':
a.append(int(a[-1])+int(a[-2]))
else:
a.append(int(ops[i]))
return sum(a)
思路:
从头到尾,如果没有遇到这个指定字符,就给前边元素+1,具体实现可以用数组下标减去前边的指定字符位置
然后再从后往前,来这么一遍,然后最后输出每个位置的最小值的数组,这种方法的时间复杂度和空间复杂度都为O(N)
还有一种思路,先找出指定字符位置,然后做一个表来标记,然后遍历,用当前的数组下标减去表中位置的绝对值最小即是
class Solution(object):
def shortestToChar(self, S, C):
prev = float('-inf')
ans = []
for i, x in enumerate(S):
if x == C: prev = i
ans.append(i - prev)
prev = float('inf')
for i in range(len(S) - 1, -1, -1):
if S[i] == C: prev = i
ans[i] = min(ans[i], prev - i)
return ans
class Solution1:
def shortestToChar(self, S: str, C: str) :
c_idx = [i for i in range(len(S)) if S[i]==C ]
return [min(abs(i- j) for j in c_idx) for i in range(len(S))]
经典岛屿问题,之后碰到了好多,这种围成圈圈或者咋滴咋滴的。可以使用数组来简化代码,思路就正常思路
class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:
res=0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j]==1:
res+=4
if i-1>=0 and grid[i-1][j]==1:
res-=2
if j-1>=0 and grid[i][j-1]==1:
res-=2
return res
其中中间的元素并不对周长产生影响,如果这个元素周围连着元素,只能多两个边,而且是从上往下数,所以只要考虑元素的前边和上边是否连着即可
8.7
可以用深度遍历和广度遍历来进行判断:
class Solution:#BFS
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
root1=[p]
root2=[q]
while root1 and root2:
w=root1.pop()
e=root2.pop()
if isinstance(w,TreeNode) and isinstance(e,TreeNode):
if w.val!=e.val:
return False
root1.extend([w.left,w.right])
root2.extend([e.left,e.right])
elif isinstance(w,TreeNode) or isinstance(e,TreeNode):
return False
#if root1 or root2:
#return False
# else:
#return True 这个本身是判断两个栈是否为空,但实际不用判断,因为最后的元素如果是null对整个树没有影响,前边如果有位置不一样的话在上边就已经判断完毕了
return True
class Solution1:#DFS
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not q and not p:
return True
elif not p or not q:
return False
elif p.val != q.val:
return False
else:
return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)
实际测试,这两个算法的复杂度一样
先把数组转化为集合,然后看集合内元素种类,如果大于长度二分之一就是长度二分之一,否则就是集合大小
class Solution:
def distributeCandies(self, candies: List[int]) -> int:
a=collections.Counter(candies)#集合a=set(candies)
if len(a)<=len(candies)//2:
return len(a)
else:
return len(candies)//2
每个数都是由数位上的数字乘以权重,比如abcdef这个数,其实是a * 105 + b * 104 + c * 10 3 + d * 102 + e * 101+f
例如 a * 105 可以化成a * (99999+1),所以,模除9即可变成a+b+c+d+e+f,直到结果小于等于9
class Solution:
def addDigits(self, num: int) -> int:
if num == 0:
return 0
if num % 9 == 0:
return 9
else:
return num % 9
这个题就是看两个字符串中元素是否相同,内容大小。使用collections类即可,通过哈希表
class Solution:
def CheckPermutation(self, s1: str, s2: str) -> bool:
if collections.Counter(s1)==collections.Counter(s2):
return True
else:
return False
杨辉三角就是从第三行开始,每个元素都是上边两个元素的和,最边上的元素一直是1后边的是[i-1][j-1]+[i-1][j]
class Solution:
def generate(self, numRows: int) :
a = [[1], [1, 1]]
if numRows==0:
return []
elif numRows==1:
return [[1]]
elif numRows==2:
return a
else:
tem=0
for i in range(2,numRows):
a.append([1,1])
for j in range(1,i):
tem=a[i-1][j-1]+a[i-1][j]
a[i].insert(j,tem)
return a
sorted函数本身自带很多种排序方式,所以只要设置sorted排序方式即可
class Solution:
def relativeSortArray(self, arr1, arr2) :
return sorted(arr1, key=lambda x: (0, arr2.index(x)) if x in arr2 else (1, x))
Python sort函数:
https://blog.csdn.net/lyy14011305/article/details/76148512
sort sorted
a.sort函数定义:sort(cmp=None, key=None, reverse=False)
sorted函数定义:sorted(iterable, cmp=None, key=None, reverse=False)
1、函数sorted()不改变原来的list,而是返回一个新的排好序的list。
2、cmp与key均可以采用lambda表达式
3、采用cmp是确定排序方式(如:从大到小还是从小到大),排序的key是函数自己选择;采用key是确定排序的key,排序方式是函数自己选择。
8.10
先数第一个出现的元素个数,如果变了就计数第二个元素个数,输出最小值,然后再变再数,直到结尾
class Solution:
def countBinarySubstrings(self, s: str) -> int:
tem = s[0]
a = 0
c = []
d=0
for i in s:
if i == tem:
a += 1
else:
c.append(a)
tem = i
a = 1
c.append(a)
for i in range(1,len(c)):
d+=min(c[i-1],c[i])
return d
class Solution1:
def countBinarySubstrings(self, s: str) -> int:
pre, cur, res, prec = 0, 1, 0, s[0]
for c in s[1:]:
if c != prec: pre, cur = cur, 1
else: cur += 1
if cur <= pre: res += 1
prec = c
return res
简单方法:变成字符串,双指针,前后指针同时移动,直到后大于等于前。
如果是整数操作,则先判断长度,然后每次原数除以10,新数乘10+原数模10,最后判断是否相等
class Solution:
def isPalindrome(self, x: int) -> bool:
n=0
while x>n or (x%10==0 and x!=0):
n = n * 10 + x % 10
x = x // 10
if n == x or n//10==x:
return True
return False
弄俩头,然后每次接上一个最小的,如果有一个空了,直接全续后边
class Solution:
def mergeTwoLists(self, l1, l2):
prehead = ListNode(-1)
prev = prehead
while l1 and l2:
if l1.val <= l2.val:
prev.next = l1
l1 = l1.next
else:
prev.next = l2
l2 = l2.next
prev = prev.next
# 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next = l1 if l1 is not None else l2
return prehead.next
8.11
两种方法:动态规划,或者中心扩展,每次都找一个中心,然后两边判断,直到不一样或者到了边界停止
class Solution:#中心扩展法1
def expandAroundCenter(self, s, left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return left + 1, right - 1
def longestPalindrome(self, s: str) -> str:
start, end = 0, 0
for i in range(len(s)):
left1, right1 = self.expandAroundCenter(s, i, i)
left2, right2 = self.expandAroundCenter(s, i, i + 1)
if right1 - left1 > end - start:
start, end = left1, right1
if right2 - left2 > end - start:
start, end = left2, right2
return s[start: end + 1]
class Solution:#中心扩展法2
def longestPalindrome(self, s: str) -> str:
n=len(s)
start=0
end=0
def find(left,right):
while left>=0 and right<n and s[left]==s[right]:
left-=1
right+=1
return left+1,right-1
for i in range(n-1):
a1,b1=find(i,i)
a2,b2=find(i,i+1)
if b1-a1>end-start:
start,end=a1,b1
if b2-a2>end-start:
start,end=a2,b2
return s[start:end+1]
函数如果设定在函数里边,调用会省时间,如果两个函数处于同一等级,调用可能更费时间 在此题中,2比1快200ms左右
class Solution:#动态规划
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):
ans = s[i:j+1]
return ans
此题可以看做是找到所有未被围绕的区域,可以从边上出发,将所有连起来的‘0’改为‘T’,等到最后将所有0改为’X’,再将‘T’改为‘0’,如果中间部分小当然从头遍历,找内部‘0’好,但是中间如果特别大,则这样寻找费时费力,不如找到边界方便,边界的话复杂度为O(N),中间为O((N-1)2).
class Solution:
def solve(self, board):
if board == []:
return []
n1 = len(board)
n2 = len(board[0])
def find(x,y):
board[x][y]='T'
q=[-1,1]
for i in q:
if 0<=x+i<n1:
if board[x+i][y]=='O':
find(x+i,y)
if 0<=y+i<n2:
if board[x][y+i]=='O':
find(x,y+i)
for i in range(n2):
if board[0][i]=='O':
find(0,i)
if board[n1-1][i]=='O':
find(n1-1,i)
for i in range(n1):
if board[i][0]=='O':
find(i,0)
if board[i][n2-1]=='O':
find(i,n2-1)
for i in range(n1):
for j in range(n2):
if board[i][j]=='O':
board[i][j]='X'
if board[i][j]=='T':
board[i][j]='O'
return board
8.12
这个题是图的遍历,图的遍历分为深度优先和广度优先,其中要注意的是要保存遍历过的节点,不再进行遍历,防止死锁,然后拷贝
class Solution(object):
def __init__(self):
self.visited = {}
def cloneGraph(self, node):
"""
:type node: Node
:rtype: Node
"""
if not node:
return node
# 如果该节点已经被访问过了,则直接从哈希表中取出对应的克隆节点返回
if node in self.visited:
return self.visited[node]
# 克隆节点,注意到为了深拷贝我们不会克隆它的邻居的列表
clone_node = Node(node.val, [])
# 哈希表存储
self.visited[node] = clone_node
# 遍历该节点的邻居并更新克隆节点的邻居列表
if node.neighbors:
clone_node.neighbors = [self.cloneGraph(n) for n in node.neighbors]
return clone_node
深度优先:每次都进去一个节点都要把邻居都遍历完
from collections import deque
class Solution(object):
def cloneGraph(self, node):
"""
:type node: Node
:rtype: Node
"""
if not node:
return node
visited = {}
# 将题目给定的节点添加到队列
queue = deque([node])
# 克隆第一个节点并存储到哈希表中
visited[node] = Node(node.val, [])
# 广度优先搜索
while queue:
# 取出队列的头节点
n = queue.popleft()
# 遍历该节点的邻居
for neighbor in n.neighbors:
if neighbor not in visited:
# 如果没有被访问过,就克隆并存储在哈希表中
visited[neighbor] = Node(neighbor.val, [])
# 将邻居节点加入队列中
queue.append(neighbor)
# 更新当前节点的邻居列表
visited[n].neighbors.append(visited[neighbor])
return visited[node]
广度优先:
一个一个节点接着复制
从后向前进行删除操作,就不会越界了
class Solution:
def removeElement(self, nums, val: int) -> int:
n=len(nums)
for i in range(n-1,-1,-1):
if nums[i]==val:
nums.pop(i)
return len(nums)
class Solution:
def countAndSay(self, n: int) -> str:
def find(s):
a = []
b = s[0]
c = 0
for i in range(len(s)):
if s[i] == b:
c += 1
else:
a.append(str(c))
a.append(b)
c=1
b=s[i]
a.append(str(c))
a.append(b)
d = "".join(a)
return d
fin = find("1")
if n==1:
return "1"
for i in range(n-2):
fin = find(fin)
return fin
简单加法计算,和大数加法一样,关键是进位,先补全长度,然后再进行加减,最后看进位
class Solution:
def addBinary(self, a: str, b: str) -> str:
a=list(a)
b=list(b)
a.reverse()
b.reverse()
n1 = len(a)
n2 = len(b)
c = []
d = 0
if n2 > n1:
a, b, n1, n2 = b, a, n2, n1
for i in range(n2):
tem = (int(a[i]) + int(b[i]) + d) % 2
d = (int(a[i]) + int(b[i]) + d) // 2
c.append(str(tem))
for i in range(n2,n1,1):
tem = (int(a[i]) + d) % 2
d = (int(a[i]) + d) // 2
c.append(str(tem))
if d == 1:
c.append(str(d))
c.reverse()
return "".join(c)
a = "100"
b="110010"
print(Solution().addBinary(a,b))
或者通过移位(上课讲的方法)
class Solution:
def addBinary(self, a, b) -> str:
x, y = int(a, 2), int(b, 2)
while y:
answer = x ^ y
carry = (x & y) << 1
x, y = answer, carry
return bin(x)[2:]
这个题想了好久,用了一个巧妙的方法。
这种不固定范围,需要消耗一些东西来进行改变(横坐标变小),需要遍历找到最大的题一般都要变自己的最差的一边,把两个运动拆解为一个不动一个运动,这个题木桶理论,最差的一边决定自己的上限,而且有消耗的移动
动差的对于动好的一边来说,收益更好 ,
class Solution:
def maxArea(self, height: List[int]) -> int:
l, r = 0, len(height) - 1
ans = 0
while l < r:
area = min(height[l], height[r]) * (r - l)
ans = max(ans, area)
if height[l] <= height[r]:
l += 1
else:
r -= 1
return ans
8.13
大数的乘法,先计算每个位的乘积,然后最后再统一加减进位
class Solution:
def multiply(self, num1: str, num2: str) -> str:
if num1=="0"or num2=="0":
return "0"
n1=len(num1)
n2=len(num2)
d=0
a=[0]*(n1+n2-1)
for i in range(n1-1,-1,-1):
for j in range(n2-1,-1,-1):
a[i+j]+=int(num1[i])*int(num2[j])
for i in range(n1+n2-2,-1,-1):
tem=(a[i]+d)
a[i]=str(tem%10)
d=tem//10
if d>0:
d=str(d)
return d+"".join(a)
return "".join(a)
可以使用双指针,因为结果固定,所以只要把a+b+c=0转化为a+b=-c
然后再使用双指针来判断
class Solution:
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
还可以尝试使用记忆化搜索,也可以叫做回溯
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if not nums: return []
# 先排序,关键!
nums.sort()
ans = set()
N, target = 3, 0
self._find_sum(nums, 0, N, target, [], ans)
return list(ans)
def _find_sum(self, nums, start, N, target, path, ans):
# terminator
if len(nums) < N or N < 2: return
# process
if N == 2:
# 两数求和
d = set()
for j in range(start, len(nums)):
if target - nums[j] in d:
ans.add(tuple(path + [target - nums[j], nums[j]]))
else:
d.add(nums[j])
else:
for i in range(start, len(nums)):
# 剪枝1: target比剩余数字能组成的最小值还要小 或 比能组成的最大值还要大,就可以停止循环了
if target < nums[i] * N or target > nums[-1] * N: break
# 剪枝2: 去重
if i > start and nums[i] == nums[i - 1]: continue
# drill down
self._find_sum(nums, i + 1, N - 1, target - nums[i], path + [nums[i]], ans)
return
class Solution:#普通记忆化搜索,会超时,并未剪枝
def threeSum(self, nums) :
nums=sorted(nums)
res=[]
n=len(nums)
def find(nums,use):
if sum(use) == 0 and len(use)==3:
res.append(use)
return
if sum(use) > 0 or len(use)>2: return
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i - 1]:
continue
find(nums[i + 1:], use + [nums[i]])
return res
return find(nums,[])
记忆化搜索题都是这一种套路
套路总结2
nums1安排一个指针,如果比二大就换,nums2也安排一个指针, 每次上下交换之后对二重新排序定位,直到m次之后,将2附在后边即可
修改:
可以从后往前来加,这样就不用对2进行排序,因为后边的大
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
a=n+m-1
n-=1
m-=1
while n>=0:
if m>=0 and nums1[m]>nums2[n]:
nums1[a]=nums1[m]
m-=1
else:
nums1[a]=nums2[n]
n-=1
a-=1
class Solution:
def mySqrt(self, x: int) -> int:
if x == 0:
return 0
ans = int(math.exp(0.5 * math.log(x)))
return ans + 1 if (ans + 1) ** 2 <= x else ans
剩下的有二分法,高中课本学过,设定上界下界,然后每次减小一半
class Solution:
def mySqrt(self, x: int) -> int:
l, r, ans = 0, x, -1
while l <= r:
mid = (l + r) // 2
if mid * mid <= x:
ans = mid
l = mid + 1
else:
r = mid - 1
return ans
一看最大序列啥的,肯定dp没跑了,刚总结了这类的题怎么做,正好试试手
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n=len(nums)
dp=[0 for _ in range(n)]
dp[0]=nums[0]
for i in range(1,n):
if nums[i]>nums[i]+dp[i-1]:
dp[i]=nums[i]
else:
dp[i]=nums[i]+dp[i-1]
return max(dp)
8.19
这个也是动态规划,没跑了写呗
class Solution:
def countSubstrings(self, s: str) -> int:
# dp[i][j] 代表 子串[i, j] 是否是一个 回文串
n = len(s)
dp = [[False] * n for _ in range(n)]
count = 0
# 枚举所有可能 因为代表子串 所以 i <= j
for j in range(n):
for i in range(0, j + 1):
# 子串长度
length = j - i + 1
# 只有一个字符 直接就是一个回文串
if length == 1:
dp[i][j] = True
count += 1
# 两个字符 只有相等才是回文串
if length == 2 and s[i] == s[j]:
dp[i][j] = True
count += 1
# 超过两个字符 首位相同 且除去首尾的子串是回文串 才是回文串
if length > 2 and s[i] == s[j] and dp[i+1][j-1] is True:
dp[i][j] = True
count += 1
return count
class Solution:#中心扩展法
def countSubstrings(self, s: str) -> int:
def check(s,l,r):
num=0
while l>=0 and r<len(s) and s[l]==s[r]:
l-=1
r+=1
num+=1
return num
num=0
for i in range(len(s)):
num+=check(s,i,i)#以本节点为中心
if i==len(s)-1:
continue
num+=check(s,i,i+1)#以本节点和下一个节点为中心
return num
class Solution:
def getRow(self, rowIndex: int) :
dp=[[0]*i for i in range(3,rowIndex+4)]
dp[0][1]=1
if rowIndex>0:
dp[1][1],dp[1][2]=1,1
for i in range(2,rowIndex+1):
for j in range(1,i+2):
dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
return dp[-1][1:-1]
print(Solution().getRow(0))
链表的环操作一般都用快慢指针,为空弹出则没环,如果快慢指针碰到了,就是有环
class Solution:
def hasCycle(self, head: ListNode) -> bool:
slow = fast = head
# if not head: # 没必要这样写可以加入while循环判断更简洁
# return False
while fast and fast.next: # 防止head为空和出现空指针的next的情况
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
8.20
扫雷老游戏了,这个题是说其中的一步会造成什么样的后果,而不是扫完,扫完的话,有些地方不能判断,不多BB,
看题意,第一个选项,挖到雷,直接炸就完事了
第二个选项和第三个选项意味着挖到空需要递归,如果旁边有雷,则显示数字,如果没有则变为‘B’
class Solution:
def updateBoard(self, board, click):
n1=len(board)
n2=len(board[0])
a = click[0]
b = click[1]
if board[a][b] == "M":
board[a][b] = "X"
return board
def confirm(i,j):
res = 0
for x in [1, -1, 0]:
for y in [1, -1, 0]:
if x == 0 and y == 0: continue
if 0 <= i + x < n1 and 0 <= j + y < n2 and board[i + x][j + y] == "M": res += 1
return res
def find(i,j):
num = confirm(i, j)
if num > 0:
board[i][j] = str(num)
return
board[i][j] = "B"
for x in [1, -1, 0]:
for y in [1, -1, 0]:
if x == 0 and y == 0: continue
nxt_i, nxt_j = i + x, j + y
if 0 <= nxt_i < n1 and 0 <= nxt_j < n2 and board[nxt_i][nxt_j] == "E": find(nxt_i, nxt_j)
find(a, b)
return board
简单方式:集合和的二倍减去数组和
或者二进制异或,遍历开始异或,最后输出的就是一个的
class Solution:
def singleNumber(self, nums: List[int]) -> int:
t=0
for i in nums:
t=t^i
return t
记录遍历到的最低值,如果下个值大,就计算差值,下个值比最小的小,就重新标记最小值
class Solution:
def maxProfit(self, prices: List[int]) -> int:
inf = int(1e9)
minprice = inf
maxprofit = 0
for price in prices:
maxprofit = max(price - minprice, maxprofit)
minprice = min(price, minprice)
return maxprofit
可以这么看,如果下一个要降价,那么铁卖了。如果下一个涨价,那么今天的就买。
所以需要两个指针,然后判断
class Solution:
def maxProfit(self, prices) -> int:
n = len(prices)
sum = 0
for i in range(1, n):
if prices[i]>prices[i-1]:
sum+=prices[i]-prices[i-1]
return sum
8.21
先深度遍历到最底,每次返回值,最后取最小即可
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root:
return 0
if not root.left and not root.right:
return 1
min_depth = 10**9
if root.left:
min_depth = min(self.minDepth(root.left), min_depth)
if root.right:
min_depth = min(self.minDepth(root.right), min_depth)
return min_depth + 1
先取中间的值,然后再建立二叉树,每次都建立左孩子节点,如果想要让高度最小的话,可以递归调用,每次都建立左右节点(用数组中间的值当做根节点)
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
def helper(left, right):
if left > right:
return None
# 总是选择中间位置左边的数字作为根节点
mid = (left + right) // 2
root = TreeNode(nums[mid])
root.left = helper(left, mid - 1)
root.right = helper(mid + 1, right)
return root
return helper(0, len(nums) - 1)
8.30
先用空格分块,最后再组合
class Solution:
def reverseWords(self, s: str) -> str:
a=s.split( )
t=""
for i in a:
for j in range(len(i)-1,-1,-1):
t=t+i[j]
t=t+" "
return t[:-1]
将两个字符串连在一起,去掉头尾,如果还在里边,则肯定是重复的
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
return (s + s).find(s, 1) != len(s)
或者使用KPM算法(带有固定表格的判断字符串相同)
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
def kmp(pattern: str) -> bool:
n = len(pattern)
fail = [-1] * n
for i in range(1, n):
j = fail[i - 1]
while j != -1 and pattern[j + 1] != pattern[i]:
j = fail[j]
if pattern[j + 1] == pattern[i]:
fail[i] = j + 1
return fail[n - 1] != -1 and n % (n - fail[n - 1] - 1) == 0
return kmp(s)
8.31
这个就是图的遍历,类似一笔画完啥啥啥
思路:使用两个集合,一个作为访问集合,一个作为访问过集合,然后每次先将访问到的数据加入访问集合,访问过的加入访问过集合,访问新的要在访问集合,但不能在访问过集合,访问完集合所有内容后,判断访问集合元素个数是否与数组长度(有0这个元素)相同,相同则true,不同则False
class Solution:
def canVisitAllRooms(self, rooms) -> bool:
n=len(rooms)
past=set()
now=set()
now.add(0)
t=0
while t>=0:
tem=set(rooms[t])
now.update(tem.symmetric_difference(past))
past.add(t)
tem1=now.symmetric_difference(past)
if len(tem1)==0:
if len(now)==n:
return True
else:
return False
for i in tem1:
t=i
break
先删去其他不是字母和数字的字符,然后全变成小写,最后再判断是否是回文串
class Solution:
def isPalindrome(self, s: str) -> bool:
a=""
if s.isspace():
return True
s=s.lower()
for i in s:
if 'a'<=i<='z' or '0'<=i<='9':
a=a+i
n=len(a)
if n % 2==1:
b=a[0:n//2]
c=a[n//2+1:n]
else:
b=a[0:n//2]
c=a[n//2:n]
d=b[::-1]
if d==c:
return True
else:
return False
使用字符串的isalnum函数,可以直接判断选中的字符是否是数字或字符,然后遍历判断
class Solution:
def isPalindrome(self, s: str) -> bool:
sgood = "".join(ch.lower() for ch in s if ch.isalnum())
return sgood == sgood[::-1]
还可以使用双指针
class Solution:
def isPalindrome(self, s: str) -> bool:
n = len(s)
left, right = 0, n - 1
while left < right:
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if left < right:
if s[left].lower() != s[right].lower():
return False
left, right = left + 1, right - 1
return True
这种带循环的题都可以使用快慢指针,追击问题,慢指针运行一遍之前都可以追到(步进的问题)步进一定要互质
这个题有特殊解法,如果循环的话,只有一个序列
{4, 16, 37, 58, 89, 145, 42, 20}
所以数能不能到这个序列,决定这个数之后是1还是循环
class Solution:
def isHappy(self, n: int) -> bool:
a=[]
while n!=1:
s=str(n)
n=0
for i in s:
n+=int(i)*int(i)
if s in a:
return False
a.append(s)
return True
总结
八月的力扣就这些,现在反回去看还有好多不是很熟,之前刷题为了尽快熟悉代码,熟悉思路,有些东西并没有总结,而是采用原始思路或者不规范,不合理的思路,现在看,有好多题都是固定套路,所以,等到再刷多一点,就要总结一下题的类型和套路,然后再用这些总结的结果来做题。