系列文章目录
leetcode刷题:第一周
leetcode刷题:第二周
leetcode刷题:二分查找
leetcode刷题:双指针
leetcode刷题:广度/深度优先搜索
leetcode刷题:递归 / 回溯
leetcode刷题:动态规划
leetcode刷题:最后一次
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
本来是准备将leetcode刷题做成一个系列的,但这个时间安排实在有点麻烦,每日一篇有点太浪费时间精力,这个系列先停掉吧,题目自己做就不分享。
一、leetcode例题
考试的最大困扰度
"""
一位老师正在出一场由 n道判断题构成的考试,每道题的答案为 true (用 'T' 表示)或者 false (用 'F'表示)。老师想增加学生对自己做出答案的不确定性,方法是最大化有 连续相同结果的题数。(也就是连续出现 true 或者连续出现 false)。
给你一个字符串answerKey,其中answerKey[i]是第 i个问题的正确结果。除此以外,还给你一个整数 k,表示你能进行以下操作的最多次数:
每次操作中,将问题的正确答案改为'T' 或者'F'(也就是将 answerKey[i] 改为'T'或者'F')。
请你返回在不超过 k次操作的情况下,最大连续 'T'或者 'F'的数目。
示例 1:
输入:answerKey = "TTFF", k = 2
输出:4
解释:我们可以将两个 'F' 都变为 'T' ,得到 answerKey = "TTTT" 。
总共有四个连续的 'T' 。
示例 2:
输入:answerKey = "TFFT", k = 1
输出:3
解释:我们可以将最前面的 'T' 换成 'F' ,得到 answerKey = "FFFT" 。
或者,我们可以将第二个 'T' 换成 'F' ,得到 answerKey = "TFFF" 。
两种情况下,都有三个连续的 'F' 。
示例 3:
输入:answerKey = "TTFTTFTT", k = 1
输出:5
解释:我们可以将第一个 'F' 换成 'T' ,得到 answerKey = "TTTTTFTT" 。
或者我们可以将第二个 'F' 换成 'T' ,得到 answerKey = "TTFTTTTT" 。
两种情况下,都有五个连续的 'T' 。
"""
class Solution:
def __init__(self):
self.answerKey = "TTFTTFTT"
self.k = 1
def maxConsecutiveAnswers(self):
answerKey = self.answerKey
k = self.k
# 算法部分
def maxConsecutiveChar(ch):
ans = 0
left = 0
sum = 0
for right in range(len(answerKey)):
sum += answerKey[right] != ch
while sum > k:
sum -= answerKey[left] != ch
left += 1
ans = max(ans, right - left + 1)
return ans
return max(maxConsecutiveChar('T'), maxConsecutiveChar('F'))
找到处理最多请求的服务器
"""
你有 k个服务器,编号为 0到 k-1,它们可以同时处理多个请求组。每个服务器有无穷的计算能力但是 不能同时处理超过一个请求。请求分配到服务器的规则如下:
第i(序号从 0 开始)个请求到达。
如果所有服务器都已被占据,那么该请求被舍弃(完全不处理)。
如果第(i % k)个服务器空闲,那么对应服务器会处理该请求。
否则,将请求安排给下一个空闲的服务器(服务器构成一个环,必要的话可能从第 0 个服务器开始继续找下一个空闲的服务器)。比方说,如果第 i个服务器在忙,那么会查看第 (i+1)个服务器,第 (i+2)个服务器等等。
给你一个 严格递增的正整数数组arrival,表示第i个任务的到达时间,和另一个数组load,其中load[i]表示第i个请求的工作量(也就是服务器完成它所需要的时间)。你的任务是找到 最繁忙的服务器。最繁忙定义为一个服务器处理的请求数是所有服务器里最多的。
请你返回包含所有最繁忙服务器序号的列表,你可以以任意顺序返回这个列表。
示例 1:
输入:k = 3, arrival = [1,2,3,4,5], load = [5,2,3,3,3]
输出:[1]
解释:
所有服务器一开始都是空闲的。
前 3 个请求分别由前 3 台服务器依次处理。
请求 3 进来的时候,服务器 0 被占据,所以它被安排到下一台空闲的服务器,也就是服务器 1 。
请求 4 进来的时候,由于所有服务器都被占据,该请求被舍弃。
服务器 0 和 2 分别都处理了一个请求,服务器 1 处理了两个请求。所以服务器 1 是最忙的服务器。
示例 2:
输入:k = 3, arrival = [1,2,3,4], load = [1,2,1,2]
输出:[0]
解释:
前 3 个请求分别被前 3 个服务器处理。
请求 3 进来,由于服务器 0 空闲,它被服务器 0 处理。
服务器 0 处理了两个请求,服务器 1 和 2 分别处理了一个请求。所以服务器 0 是最忙的服务器。
示例 3:
输入:k = 3, arrival = [1,2,3], load = [10,12,11]
输出:[0,1,2]
解释:每个服务器分别处理了一个请求,所以它们都是最忙的服务器。
示例 4:
输入:k = 3, arrival = [1,2,3,4,8,9,10], load = [5,2,10,3,1,2,2]
输出:[1]
示例 5:
输入:k = 1, arrival = [1], load = [1]
输出:[0]
"""
from heapq import heappop, heappush
from sortedcontainers import SortedList
class Solution:
def __init__(self):
self.k = 3
self.arrival = [1, 2, 3, 4, 8, 9, 10]
self.load = [5, 2, 10, 3, 1, 2, 2]
def busiestServers(self):
k = self.k
arrival = self.arrival
load = self.load
# 算法部分
available = SortedList(range(k))
busy = []
requests = [0] * k
for i, (start, t) in enumerate(zip(arrival, load)):
while busy and busy[0][0] <= start:
available.add(busy[0][1])
heappop(busy)
if len(available) == 0:
continue
j = available.bisect_left(i % k)
if j == len(available):
j = 0
id = available[j]
requests[id] += 1
heappush(busy, (start + t, id))
available.remove(id)
maxRequest = max(requests)
return [i for i, req in enumerate(requests) if req == maxRequest]
if __name__ == '__main__':
s=Solution()
t=s.busiestServers()
print(t)
自除数
"""
自除数 是指可以被它包含的每一位数整除的数。
例如,128 是一个 自除数 ,因为 128 % 1 == 0,128 % 2 == 0,128 % 8 == 0。
自除数 不允许包含 0 。
给定两个整数 left 和 right ,返回一个列表,列表的元素是范围 [left, right] 内所有的 自除数 。
示例 1:
输入:left = 1, right = 22
输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]
示例 2:
输入:left = 47, right = 85
输出:[48,55,66,77]
"""
class Solution:
def __init__(self):
self.left = 47
self.right = 85
def selfDividingNumbers(self):
left = self.left
right = self.right
# 算法分析
def chu(num):
if num < 10:
return True
x = num
while x:
x, d = divmod(x, 10)
if d == 0 or num % d:
return False
return True
return [i for i in range(left, right + 1) if chu(i)]
二倍数对数组
"""
给定一个长度为偶数的整数数组 arr,只有对 arr 进行重组后可以满足 “对于每个 0 <=i < len(arr) / 2,都有 arr[2 * i + 1] = 2 * arr[2 * i]”时,返回 true;否则,返回 false。
示例 1:
输入:arr = [3,1,3,6]
输出:false
示例 2:
输入:arr = [2,1,2,6]
输出:false
示例 3:
输入:arr = [4,-2,2,-4]
输出:true
解释:可以用 [-2,-4] 和 [2,4] 这两组组成 [-2,-4,2,4] 或是 [2,4,-2,-4]
"""
from collections import Counter
class Solution:
def __init__(self):
self.arr = [3, 1, 3, 6]
def canReorderDoubled(self):
arr = self.arr
# 算法部分
cnt = Counter(arr)
if cnt[0] % 2:
return False
for x in sorted(cnt, key=abs):
if cnt[2 * x] < cnt[x]:
return False
cnt[2 * x] -= cnt[x]
return True
强密码检验器
"""
如果一个密码满足下述所有条件,则认为这个密码是强密码:
由至少 6 个,至多 20 个字符组成。
至少包含 一个小写 字母,一个大写 字母,和 一个数字 。
同一字符 不能 连续出现三次 (比如 "...aaa..." 是不允许的, 但是"...aa...a..." 如果满足其他条件也可以算是强密码)。
给你一个字符串 password ,返回将 password 修改到满足强密码条件需要的最少修改步数。如果 password 已经是强密码,则返回 0 。
在一步修改操作中,你可以:
插入一个字符到 password ,
从 password 中删除一个字符,或
用另一个字符来替换 password 中的某个字符。
示例 1:
输入:password = "a"
输出:5
示例 2:
输入:password = "aA1"
输出:3
示例 3:
输入:password = "1337C0d3"
输出:0
"""
class Solution:
def __init__(self):
self.password = "aA1"
def strongPasswordChecker(self):
password = self.password
# 算法部分
n = len(password)
has_lower = has_upper = has_digit = False
for ch in password:
if ch.islower():
has_lower = True
elif ch.isupper():
has_upper = True
elif ch.isdigit():
has_digit = True
categories = has_lower + has_upper + has_digit
if n < 6:
return max(6 - n, 3 - categories)
elif n <= 20:
replace = cnt = 0
cur = "#"
for ch in password:
if ch == cur:
cnt += 1
else:
replace += cnt // 3
cnt = 1
cur = ch
replace += cnt // 3
return max(replace, 3 - categories)
else:
replace, remove = 0, n - 20
rm2 = cnt = 0
cur = "#"
for ch in password:
if ch == cur:
cnt += 1
else:
if remove > 0 and cnt >= 3:
if cnt % 3 == 0:
remove -= 1
replace -= 1
elif cnt % 3 == 1:
rm2 += 1
replace += cnt // 3
cnt = 1
cur = ch
if remove > 0 and cnt >= 3:
if cnt % 3 == 0:
remove -= 1
replace -= 1
elif cnt % 3 == 1:
rm2 += 1
replace += cnt // 3
use2 = min(replace, rm2, remove // 2)
replace -= use2
remove -= use2 * 2
use3 = min(replace, remove // 3)
replace -= use3
remove -= use3 * 3
return (n - 20) + max(replace, 3 - categories)
寻找比目标字母大的最小字母
class Solution:
def nextGreatestLetter(self, letters: List[str], target: str) -> str:
return letters[bisect_right(letters, target)] if target < letters[-1] else letters[0]
区域和检索 - 数组可修改
"""
给你一个数组 nums ,请你完成两类查询。
其中一类查询要求 更新 数组 nums 下标对应的值
另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 ,其中 left <= right
实现 NumArray 类:
NumArray(int[] nums) 用整数数组 nums 初始化对象
void update(int index, int val) 将 nums[index] 的值 更新 为 val
int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], ..., nums[right])
示例 1:
输入:
["NumArray", "sumRange", "update", "sumRange"]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
输出:
[null, 9, null, 8]
解释:
NumArray numArray = new NumArray([1, 3, 5]);
numArray.sumRange(0, 2); // 返回 1 + 3 + 5 = 9
numArray.update(1, 2); // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8
"""
class NumArray:
def __init__(self, nums):
n = len(nums)
size = int(n ** 0.5)
sums = [0] * ((n + size - 1) // size)
for i, num in enumerate(nums):
sums[i // size] += num
self.nums = nums
self.sums = sums
self.size = size
def update(self, index, val) -> None:
self.sums[index // self.size] += val - self.nums[index]
self.nums[index] = val
def sumRange(self, left, right):
m = self.size
b1, b2 = left // m, right // m
if b1 == b2:
return sum(self.nums[left:right + 1])
return sum(self.nums[left:(b1 + 1) * m]) + sum(self.sums[b1 + 1:b2]) + sum(self.nums[b2 * m:right + 1])
# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# obj.update(index,val)
# param_2 = obj.sumRange(left,right)
二进制表示中质数个计算置位
class Solution:
def countPrimeSetBits(self, left: int, right: int) -> int:
return sum(((1 << x.bit_count()) & 665772) != 0 for x in range(left, right + 1))
最小高度树
"""
树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。
给你一棵包含n个节点的树,标记为0到n - 1 。给定数字n和一个有 n - 1 条无向边的 edges列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。
可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树 。
请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。
树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。
示例 1:
输入:n = 4, edges = [[1,0],[1,2],[1,3]]
输出:[1]
解释:如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。
示例 2:
输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出:[3,4]
"""
from collections import deque
class Solution:
def __init__(self):
self.n = 6
self.edges = [[3, 0], [3, 1], [3, 2], [3, 4], [5, 4]]
def findMinHeightTrees(self):
n = self.n
edges = self.edges
#算法部分
if n == 1:
return [0]
g = [[] for _ in range(n)]
for x, y in edges:
g[x].append(y)
g[y].append(x)
parents = [0] * n
def bfs(start: int):
vis = [False] * n
vis[start] = True
q = deque([start])
while q:
x = q.popleft()
for y in g[x]:
if not vis[y]:
vis[y] = True
parents[y] = x
q.append(y)
return x
x = bfs(0) # 找到与节点 0 最远的节点 x
y = bfs(x) # 找到与节点 x 最远的节点 y
path = []
parents[x] = -1
while y != -1:
path.append(y)
y = parents[y]
m = len(path)
return [path[m // 2]] if m % 2 else [path[m // 2 - 1], path[m // 2]]
旋转字符串
"""
给定两个字符串, s和goal。如果在若干次旋转操作之后,s能变成goal,那么返回true。
s的 旋转操作 就是将s 最左边的字符移动到最右边。
例如, 若s = 'abcde',在旋转一次之后结果就是'bcdea'。
示例 1:
输入: s = "abcde", goal = "cdeab"
输出: true
示例 2:
输入: s = "abcde", goal = "abced"
输出: false
"""
class Solution:
def __init__(self):
self.s = "abcde"
self.goal = "abced"
def rotateString(self):
s = self.s
goal = self.goal
# 算法部分
if len(s) != len(goal):
return False
for i in range(len(goal)):
if goal[i] == s[0]:
if (goal[i:]+goal[0:i])==s:
return True
return False
if __name__ == '__main__':
s = Solution()
print(s.rotateString())
总结
这个系列暂时先到这里吧,看看以后会不会重启。