coding/coding1.md
2025-06-26 12:44:31 +00:00

2108 lines
76 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 两指针
```python
27. 移除元素
快慢指针法
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 快慢指针
fast = 0 # 快指针
slow = 0 # 慢指针
size = len(nums)
while fast < size: # 不加等于是因为a = size 时nums[a] 会越界
# slow 用来收集不等于 val 的值,如果 fast 对应值不等于 val则把它与 slow 替换
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
```
```python
# 15. 三数之和
# 判断 nums 中是否存在三个元素 abc ,使得 a + b + c = 0 请你找出所有满足条件且不重复的三元组。两指针
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort()
for i in range(len(nums)):
# 如果第一个元素已经大于0不需要进一步检查
if nums[i] > 0:
return result
# 跳过相同的元素以避免重复
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = len(nums) - 1
while right > left:
sum_ = nums[i] + nums[left] + nums[right]
if sum_ < 0:
left += 1
elif sum_ > 0:
right -= 1
else:
result.append([nums[i], nums[left], nums[right]])
# 跳过相同的元素以避免重复
while right > left and nums[right] == nums[right - 1]:
right -= 1
while right > left and nums[left] == nums[left + 1]:
left += 1
right -= 1
left += 1
return result
```
```python
# 1 4Sum/四数之和 (Medium)
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
n = len(nums)
res = []
for i in range(n):
if i > 0 and nums[i] == nums[i - 1]: continue
for k in range(i+1, n):
if k > i + 1 and nums[k] == nums[k-1]: continue
p = k + 1
q = n - 1
while p < q:
if nums[i] + nums[k] + nums[p] + nums[q] > target: q -= 1
elif nums[i] + nums[k] + nums[p] + nums[q] < target: p += 1
else:
res.append([nums[i], nums[k], nums[p], nums[q]])
while p < q and nums[p] == nums[p + 1]: p += 1
while p < q and nums[q] == nums[q - 1]: q -= 1
p += 1
q -= 1
return res
```
```python
# 26 Remove Duplicates from Sorted Array
def removeDuplicates(nums):
if not nums:
return 0
# 使用双指针法
slow = 0 # 慢指针,指向不重复的元素
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1 # 返回新数组的长度
```
```python
#R 75 Sort Colors/颜色分类 (Medium)
# p0指向 0 应该放置的位置p0左边全是0, p0本身并不包括0
# p2指向 2 应该放置的位置p2右边全是0, p2本身并不包括2
# i当前遍历位置
class Solution:
def sortColors(self, nums: List[int]) -> None:
p0, i , p2 = 0,0,len(nums) - 1
while i <= p2:
if nums[i] == 0:
nums[i], nums[p0] = nums[p0], nums[i]
p0 += 1
i += 1
elif nums[i] == 2:
nums[i], nums[p2] = nums[p2], nums[i]
p2 -= 1
# 注意i 不增加,因为换过来的 nums[i] 还要检查
else:
i += 1
```
```python
# 88 Merge Sorted Array/合并两个有序数组 (Easy)
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
tail = len(nums1) - 1
i1 = m - 1
i2 = n - 1
# 个人经常用 while true然后在循环内部列出所有情况
while True:
# 都左移到头,退出
if i1 == -1 and i2 == -1:
break
# 一方左移到头,选取另一方赋值,然后左移
elif i1 == -1:
nums1[tail] = nums2[i2]
i2 -= 1
elif i2 == -1:
nums1[tail] = nums1[i1] # 也可以省去,直接 i1 -= 1;
i1 -= 1
# 选取大的元素赋值,然后左移
elif nums1[i1] > nums2[i2]:
nums1[tail] = nums1[i1]
i1 -= 1
else:
nums1[tail] = nums2[i2]
i2 -= 1
tail -= 1
```
```python
# 167 Two Sum II - Input array is sorted $/两数之和 II - 输入有序数组 (Medium)
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
i, j = 0, len(numbers) - 1
while i < j:
s = numbers[i] + numbers[j]
if s > target: j -= 1
elif s < target: i += 1
else: return i + 1, j + 1
return []
```
```python
# 283 Move Zeroes/移动零 (Easy) 将所有 0 移动到数组的末尾
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
i0 = 0
for i in range(len(nums)):
if nums[i]:
nums[i], nums[i0] = nums[i0], nums[i]
i0 += 1
```
```python
# 345 "Reverse Vowels of a String"
def reverseVowels(s):
vowels = set("aeiouAEIOU")
s = list(s)
left, right = 0, len(s) - 1
while left < right:
while left < right and s[left].lower() not in vowels:
left += 1
while left < right and s[right].lower() not in vowels:
right -= 1
# 交换左右两侧的元音字母
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
return ''.join(s)
```
```python
# 415 Add Strings/字符串相加 (Easy)
class Solution:
def addStrings(self, num1: str, num2: str) -> str:
res = ""
i, j, carry = len(num1) - 1, len(num2) - 1, 0
while i >= 0 or j >= 0:
n1 = int(num1[i]) if i >= 0 else 0
n2 = int(num2[j]) if j >= 0 else 0
tmp = n1 + n2 + carry
carry = tmp // 10
res = str(tmp % 10) + res
i, j = i - 1, j - 1
return "1" + res if carry else res
```
```python
# 443 String Compression/压缩字符串 (Easy)
# ["a","2","b","2","c","3"]
# 三指针指针1当前字符指针2找这个字符有连续多少个指针3当前在数组中的读写位置。
class Solution:
def compress(self, chars: List[str]) -> int:
n = len(chars)
i = 0
write = 0
while i < n:
j = i
while j < n and chars[j] == chars[i]:
j += 1
chars[write] = chars[i]
write += 1
if j - i > 1:
for c in str(j-i):
chars[write] = c
write += 1
i = j
return write
```
```python
# 0 Split Array with Equal Sum $/将数组分割成和相等的子数组 (Medium)
# 子数组 (0, i - 1) (i + 1, j - 1) (j + 1, k - 1) (k + 1, n - 1) 的相等,左闭右闭
class Solution:
def splitArray(self, nums: List[int]) -> bool:
n = len(nums)
presum = [0 for _ in range(n + 1)] #用的虚指从1开始计数
for i in range(n):
presum[i + 1] = presum[i] + nums[i]
#(1) L (2) M (3) R (4) 3个指针4个区域
for M in range(3, n - 3):
memo = set()
for L in range(1, M - 1):
zoom1 = presum[L]
zoom2 = presum[M] - presum[L + 1]
if zoom1 == zoom2:
memo.add(zoom1)
for R in range(M + 2, n - 1):
zoom3 = presum[R] - presum[M + 1]
zoom4 = presum[n] - presum[R + 1]
if zoom3 == zoom4 and zoom3 in memo:
return True
return False
```
```python
# 633 Sum of Square Numbers/平方数之和 (Easy)
class Solution:
def judgeSquareSum(self, c: int) -> bool:
low, high = 0, int(c**0.5)
while low<=high:
sumOf = low*low+high*high
if sumOf==c: return True
elif sumOf<c: low += 1
else: high -= 1
return False
```
```python
#R 259 3Sum Smaller $/较小的三数之和 (Medium)
# satisfy the condition nums[i] + nums[j] + nums[k] < target
class Solution:
def threeSumSmaller(self, nums, target):
if len(nums) < 3:
return 0
res = 0
nums.sort()
n = len(nums)
for i in range(n - 2):
left, right = i + 1, n - 1
while left < right:
if nums[i] + nums[left] + nums[right] < target:
res += right - left # 所有 (nums[i], nums[left], nums[left+1]...nums[right]) 都满足条件
left += 1
else:
right -= 1
return res
```
```python
# 977.有序数组的平方
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
l, r, i = 0, len(nums)-1, len(nums)-1
res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果
while l <= r:
if nums[l] ** 2 < nums[r] ** 2: # 左右边界进行对比,找出最大值
res[i] = nums[r] ** 2
r -= 1 # 右指针往左移动
else:
res[i] = nums[l] ** 2
l += 1 # 左指针往右移动
i -= 1 # 存放结果的指针需要往前平移一位
return res
```
```python
# 845 "Longest Mountain in Array" 给定一个整数数组 A找出数组中的最长山脉的长度
# 遍历数组中的每个可能的山顶,然后从山顶向左右两侧扩展,找到山脉的左侧和右侧
def longestMountain(A):
n = len(A)
if n < 3:
return 0
max_length = 0
for peak in range(1, n - 1):
left, right = peak, peak
# 找到山脉的左侧
while left > 0 and A[left - 1] < A[left]:
left -= 1
# 找到山脉的右侧
while right < n - 1 and A[right + 1] < A[right]:
right += 1
# 确保是山脉
if left < peak < right:
max_length = max(max_length, right - left + 1)
return max_length
```
```python
# 985.9 Interval List Intersections/区间列表的交集 (Medium)
class Solution:
def intervalIntersection(self, A: List[List[int]], B: List[List[int]]) -> List[List[int]]:
ans = []
i = j = 0
while i < len(A) and j < len(B):
lo = max(A[i][0], B[j][0])
hi = min(A[i][1], B[j][1])
if lo <= hi:
ans.append([lo, hi])
if A[i][1] < B[j][1]:
i += 1
else:
j += 1
return ans
```
```python
# 1155.9 Swap For Longest Repeated Character Substring/单字符重复子串的最大长度 (Medium)
# 你只能交换其中两个字符一次或者什么都不做,返回单字符最长的子串的长度。
# j 指向 i并不断地向右移动 j直到 j 指向的字符与 i 指向的字符不同,跳过指针 j 指向的字符,用指针 k 继续向右移动,直到 k 指向的字符与 i 指向的字符不同
class Solution:
def maxRepOpt1(self, text: str) -> int:
cnt = Counter(text)
n = len(text)
ans = i = 0
while i < n:
j = i
while j < n and text[j] == text[i]:
j += 1
l = j - i
k = j + 1
while k < n and text[k] == text[i]:
k += 1
r = k - j - 1
ans = max(ans, min(l + r + 1, cnt[text[i]]))
i = j
return ans
```
```python
#H 1163 Last Substring in Lexicographical Order/按字典序排在最后的子串 (Hard)
# 找出它的所有子串并按字典序排列,返回排在最后的那个子串。
# i指向的是当前找到字典序最大的字符j指向的是当前要进行比较的字符。使用一个位移指针k来比较i和j构成的子串[i,..,i + k]和[j,...,j + k]的顺序。i始终指向当前找到字典序最大的字符
class Solution:
def lastSubstring(self, s: str) -> str:
n = len(s)
i = 0
j = 1
k = 0
while j + k < n:
if s[i + k] == s[j + k]:
k += 1
elif s[i + k] < s[j + k]:
i += k + 1
k = 0
if i >= j:
j = i + 1
else:
j += k + 1
k = 0
return s[i:]
```
# 滑动窗口
```python
209.长度最小的子数组
class Solution:
def minSubArrayLen(self, s: int, nums: List[int]) -> int:
l = len(nums)
left = 0
right = 0
min_len = float('inf')
cur_sum = 0 #当前的累加值
while right < l:
cur_sum += nums[right]
while cur_sum >= s: # 当前累加值大于目标值
min_len = min(min_len, right - left + 1)
cur_sum -= nums[left]
left += 1
right += 1
return min_len if min_len != float('inf') else 0
```
```python
# Max Consecutive Ones II 你可以改变其中最多 k 个 0 的位求该数组中最大连续1的个数滑动窗口
def findMaxConsecutiveOnes(nums, k):
left, right = 0, 0
max_length = 0
zero_count = 0
while right < len(nums):
if nums[right] == 0:
zero_count += 1
while zero_count > k:
if nums[left] == 0:
zero_count -= 1
left += 1
max_length = max(max_length, right - left + 1)
right += 1
return max_length
```
```python
# 340 "Longest Substring with At Most K Distinct Characters" 给定一个字符串 s找出至多包含 k 个不同字符的最长子串的长度,滑动窗口
def lengthOfLongestSubstringKDistinct(s, k):
if k == 0:
return 0
left, right = 0, 0
char_count = {}
max_length = 0
while right < len(s):
char_count[s[right]] = char_count.get(s[right], 0) + 1
while len(char_count) > k:
char_count[s[left]] -= 1
if char_count[s[left]] == 0:
del char_count[s[left]]
left += 1
max_length = max(max_length, right - left + 1)
right += 1
return max_length
```
```python
#R 1358 Number of Substrings Containing All Three Characters/包含所有三种字符的子字符串数目 (Medium)
# 请你返回 ab 和 c 都 至少 出现过一次的子字符串数目。
# 滑动窗口的内层循环结束时,右端点固定在 right左端点在 0,1,2,…,left1 的所有子串都是合法的,这一共有 left 个,加入答案。
class Solution:
def numberOfSubstrings(self, s: str) -> int:
ans = left = 0
cnt = defaultdict(int)
for c in s:
cnt[c] += 1
while len(cnt) == 3:
out = s[left] # 离开窗口的字母
cnt[out] -= 1
if cnt[out] == 0:
del cnt[out]
left += 1
ans += left
return ans
```
```python
#R 395 Longest Substring with At Least K Repeating Characters/至少有 K 个重复字符的最长子串 (Medium) s = "ababbc", k = 2 最长子串为 "ababb"
class Solution:
def longestSubstring(self, s: str, k: int) -> int:
ans = 0
for i in range(1,27): #分治遍历可能的字符种类数量1~26
cnt = Counter() #计数
unique = 0 #字符种类
num_k = 0 #满足出现次数大于等于k的字符串数量
left = 0
for right,char in enumerate(s):
if cnt[char] == 0:
unique += 1
cnt[char] += 1
if cnt[char] == k:
num_k += 1
#当字符串种类超过i时移动左窗口
while unique > i:
left_char = s[left]
if cnt[left_char] == k:
num_k -= 1 #因为要一直移动左窗口,所以计数会一直减少,当进入循环时刚好==k时再减一后num_k就要减一了
cnt[left_char] -= 1
if cnt[left_char] == 0: #如果减完了,种类就少一个
unique -= 1
left += 1
if unique == i and num_k == i: #当种类满足要求,且子串里的数量都满足>=k时就更新ans
ans = max(ans ,right - left +1)
return ans
```
```python
#R 424 Longest Repeating Character Replacement/替换后的最长重复字符 (Medium) 可替换字符k次
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
count = [0 for _ in range(26)] #记录当前窗口的字母出现次数
left = 0 #滑动窗口左边界
right = 0 #滑动窗口右边界
retval = 0 #最长窗口长度
while right < len(s):
count[ord(s[right])-ord('A')] += 1
benchmark = max(count) #选择出现次数最多的字母为基准
others = sum(count) - benchmark #则其他字母需要通过替换操作来变为基准
if others <= k: #通过与K进行比较来判断窗口是进行扩张
right += 1
retval = max(retval, right-left)#记录当前有效窗口长度
else: #通过与K进行比较来判断窗口还是进行位移
count[ord(s[left])-ord('A')] -= 1
left += 1
right += 1 #这里注意位移操作需要整个向右移不仅仅只是left向右
return retval #返回最长窗口长度
```
```python
# 486.99 Max Consecutive Ones II $/最大连续1的个数 II (Medium)
# 最多可以翻转一个 0 ,则返回数组中连续 1 的最大个数
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
res=l=count_zero = 0
for r, num in enumerate(nums):
if not num :
count_zero +=1
while count_zero >1 :
if not nums[l]:
count_zero-=1
l+=1
res = max(res, r-l+1)
return res
```
```python
# 1100 Find K-Length Substrings With No Repeated Characters/长度为 K 的无重复字符子串 (Medium)
# 找出所有长度为 K 且不含重复字符的子串,返回子串数目。
class Solution:
def numKLenSubstrNoRepeats(self, s: str, k: int) -> int:
ans = 0
cnt = Counter(s[: k - 1])
for i, (in_, out) in enumerate(zip(s[k - 1 :], s)):
cnt[in_] += 1
if len(cnt) == k:
ans += 1
cnt[out] -= 1
if cnt[out] == 0:
del cnt[out]
return ans
```
```python
# 验证子列不带顺序 + 连续 permutation-in-string定长滑动窗口
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
m, n = len(s1), len(s2)
if m > n:
return False
cnt = collections.Counter(s1) # 哈希表:记录需要匹配到的各个字符的数目
need = m # 记录需要匹配到的字符总数【need=0表示匹配到了】
for right in range(n):
# 窗口右边界
ch = s2[right] # 窗口中新加入的字符
if ch in cnt: # 新加入的字符ch位于s1中
if cnt[ch] > 0: # 此时新加入窗口中的字符ch对need有影响
need -= 1
cnt[ch] -= 1
# 窗口左边界
left = right - m
if left >= 0:
ch = s2[left]
if ch in cnt: # 刚滑出的字符位于s1中
if cnt[ch] >= 0: # 此时滑出窗口的字符ch对need有影响
need += 1
cnt[ch] += 1
if need == 0: # 找到了一个满足题意的窗口
return True
return False
```
```python
# 1.最长连续公共子列2.最长不连续公共子列3.S 中最长重复的子列==718
# 4.允许swap一次的最长重复子列5.两个字交错和
# 3. Longest Substring Without Repeating Characters 子串必须连续,不同于子序列
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic, res, i = {}, 0, -1
for j in range(len(s)):
if s[j] in dic:
i = max(dic[s[j]], i) # 更新左指针 i
dic[s[j]] = j # 哈希表记录
res = max(res, j - i) # 更新结果
return res
```
# 二分法
```python
# 0. 二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1 # 定义target在左闭右闭的区间里[left, right]
while left <= right:
middle = left + (right - left) // 2
if nums[middle] > target:
right = middle - 1 # target在左区间所以[left, middle - 1]
elif nums[middle] < target:
left = middle + 1 # target在右区间所以[middle + 1, right]
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
```
```python
# 34 Search for a Range/在排序数组中查找元素的第一个和最后一个位置 (Medium) 二分法
# 实现 upper_bound (bisect.bisect_right)和 lower_bound(bisect.bisect_left)
def upper_bound(vec, target):
l, r = 0, len(vec) - 1
while l <= r:
m = (l + r) // 2
if vec[m] > target:
r = m - 1
else:
l = m + 1
return l
def lower_bound(vec, target):
l, r = 0, len(vec) - 1
while l <= r:
m = (l + r) // 2
if vec[m] >= target:
r = m - 1
else:
l = m + 1
return l
vec,tgt=[0,2,2,2,2,3,4,5,6],2
# print(upper_bound(vec,tgt),lower_bound(vec,tgt)) # ans=5,1
```
```python
# 56.9 Insert Interval/插入区间 (Hard)
class Solution:
def insert(
self, intervals: List[List[int]], newInterval: List[int]
) -> List[List[int]]:
new_start, new_end = newInterval
n = len(intervals)
left = bisect.bisect_left(
range(n),
True,
key=lambda i: intervals[i][1] >= new_start,
)
right = bisect.bisect_left(
range(n),
True,
key=lambda i: intervals[i][0] > new_end,
)
if left < n:
new_start = min(new_start, intervals[left][0])
if right > 0:
new_end = max(new_end, intervals[right - 1][1])
return intervals[:left] + [[new_start, new_end]] + intervals[right:]
```
```python
#H LeetCode 33 "Search in Rotated Sorted Array"
def search(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
# 判断哪一部分是有序的
if nums[left] <= nums[mid]: # 左半段有序
if nums[left] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else: # 右半段有序
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1 # 没有找到目标值
```
```python
#H 81 Search in Rotated Sorted Array II/搜索旋转排序数组 II (Medium)
# 数组中的值不必互不相同,基于 33 题的简洁写法,只需增加一个 if
class Solution:
def search(self, nums: List[int], target: int) -> bool:
if not nums:
return False
n = len(nums)
if n == 1:
return nums[0] == target
l, r = 0, n - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return True
if nums[l] == nums[mid] and nums[mid] == nums[r]:
l += 1
r -= 1
elif nums[l] <= nums[mid]:
if nums[l] <= target and target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target and target <= nums[n - 1]:
l = mid + 1
else:
r = mid - 1
return False
```
```python
# Search a 2D Matrix
class Solution(object):
def searchMatrix(self, matrix, target):
m = len(matrix)
if m == 0:
return False
n = len(matrix[0])
l,r = 0, m*n-1
while l <= r:
mid = (l+r)//2
i = mid//n
j = mid%n
pivot = matrix[i][j]
if pivot == target:
return True
if pivot < target:
l = mid + 1
else:
r = mid -1
return False
```
```python
# LeetCode 240 "Search a 2D Matrix II"
def searchMatrix(matrix, target):
if not matrix or not matrix[0]:
return False
rows, cols = len(matrix), len(matrix[0])
row, col = 0, cols - 1 # 从矩阵的右上角开始搜索
while row < rows and col >= 0:
if matrix[row][col] == target:
return True
elif matrix[row][col] < target:
row += 1 # 如果当前元素小于目标值,说明当前行不可能存在目标值,向下移动一行
else:
col -= 1 # 如果当前元素大于目标值,说明当前列不可能存在目标值,向左移动一列
return False
```
```python
# 1237 枚举 xxx然后在二分查找或下面的相向双指针方法
class Solution:
def findSolution(self, customfunction: 'CustomFunction', z: int) -> List[List[int]]:
ans = []
x, y = 1, 1000
while x <= 1000 and y:
res = customfunction.f(x, y)
if res < z:
x += 1
elif res > z:
y -= 1
else:
ans.append([x, y])
x += 1
y -= 1
return ans
```
```python
#R 378 Kth Smallest Element in a Sorted Matrix/有序矩阵中第 K 小的元素 (Medium)
class Solution:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
n = len(matrix)
def check(mid):
"""遍历获取较小元素部分元素总数并与k值比较"""
i, j = n-1, 0
num = 0
while i >= 0 and j < n:
if matrix[i][j] <= mid:
# 当前元素小于mid则此元素及上方元素均小于mid
num += i + 1
# 向右移动
j += 1
else:
# 当前元素大于mid则向上移动直到找到比mid小的值或者出矩阵
i -= 1
return num >= k
left, right = matrix[0][0], matrix[-1][-1]
while left < right:
mid = (left + right) >> 1
if check(mid):
# 满足 num >= k范围太大移动right至mid 范围收缩
right = mid
else:
left = mid + 1
return left
```
```python
#R 410 Split Array Largest Sum/分割数组的最大值 (Hard)
# 分成 k 个非空的连续子数组,使得这 k 个子数组各自和的最大值 最小。
class Solution:
def splitArray(self, nums: List[int], k: int) -> int:
def check(mx):
s, cnt = inf, 0
for x in nums:
s += x
if s > mx:
s = x
cnt += 1
return cnt <= k
left, right = max(nums), sum(nums)
return left + bisect_left(range(left, right + 1), True, key=check)
```
```python
#R 中位数可以奇偶统一表示
def get_median_universal(nums):
sorted_nums = sorted(nums)
n = len(sorted_nums)
median = (sorted_nums[(n - 1) // 2] + sorted_nums[n // 2]) / 2.0
return median
# Median of Two Sorted Arrays
import bisect
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
n = len(nums1) + len(nums2)
n1, n2 = len(nums1), len(nums2)
if n1 == 0: return (nums2[n2//2] + nums2[(n2-1)//2]) / 2
if n2 == 0: return (nums1[n1//2] + nums1[(n1-1)//2]) / 2
minN, maxN = min(nums1[0], nums2[0]), max(nums1[-1], nums2[-1])
nums = list(range(minN, maxN+1))
def func(x):
# nums1和nums2中<=x的数的个数记为y和x单调
k1 = bisect.bisect_right(nums1, x)
k2 = bisect.bisect_right(nums2, x)
return k1 + k2
k1 = bisect_right(nums, n//2, key=func)
if n % 2: k2 = k1
else: k2 = bisect_right(nums, (n-1)//2, key=func)
return (nums[k1] + nums[k2]) / 2
```
```python
# 611 Valid Triangle Number/有效三角形的个数 (Medium)
# ni<=nj 找出最大的满足 nums[k]<nums[i]+nums[j] 的下标 k这样一来在 [j+1,k] 范围内的下标都可
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
n = len(nums)
nums.sort()
ans = 0
for i in range(n):
for j in range(i + 1, n):
left, right, k = j + 1, n - 1, j
while left <= right:
mid = (left + right) // 2
if nums[mid] < nums[i] + nums[j]:
k = mid
left = mid + 1
else:
right = mid - 1
ans += k - j
return ans
```
```python
# 981 Time Based Key-Value Store/基于时间的键值存储 (Medium)
# void set( key, value, timestamp) 存储给定时间戳 timestamp 时的键 key 和值 value。
# String get( key, timestamp) 返回一个值,该值在之前调用了 set其中 timestamp_prev <= timestamp 。如果有多个这样的值,它将返回与最大 timestamp_prev 的值。如果没有值,则返回空字符串("")。
class TimeMap:
def __init__(self):
self.dct = defaultdict(list)
def set(self, key: str, value: str, timestamp: int) -> None:
self.dct[key].append([timestamp, value])
def get(self, key: str, timestamp: int) -> str:
a = bisect_right(self.dct[key], [timestamp, "z"*101])
if a == 0:
return ""
return (self.dct[key])[a-1][1]
```
```python
# 1901 Find a Peak Element II/寻找峰值 II (中等)
# 任意两个相邻格子的值都 不相同 。我们考虑第 i 行的最大值,不妨将其下标记为 j。如果 mat[i][j]>mat[i+1][j],那么第 [0,..i] 行中必然存在一个峰值,我们只需要在第 [0,..i] 行中找到最大值即可。
class Solution:
def findPeakGrid(self, mat: List[List[int]]) -> List[int]:
l, r = 0, len(mat) - 1
while l < r:
mid = (l + r) >> 1
j = mat[mid].index(max(mat[mid]))
if mat[mid][j] > mat[mid + 1][j]:
r = mid
else:
l = mid + 1
return [l, mat[l].index(max(mat[l]))]
```
# 字符串&哈希表&数组
```python
# 验证子列,顺序+不连续ab, acbd392如果考虑有多少个这样的子列要用动态规划(115. Distinct Subsequences)
# 验证子列不带顺序 + 连续 permutation-in-string参考滑动窗口部分
# 392. Is Subsequence 动态规划看mycodes 双指针解法如下
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
if not s: return True
i = 0
for c in t:
if s[i] == c:
i += 1
# 若已经遍历完 s ,则提前返回 true
if i == len(s):
return True
return False
```
```python
# 验证s, t是不是最多一个字母不同即|s.size-t.size|<=1不同字母出现时只需要验证后半段是否一样
def isOneEditDistance(s, t):
# 遍历短的字符串
for i in range(min(len(s), len(t))):
# 比较字符
if s[i] != t[i]:
# 验证后半段是否一样
if len(s) == len(t):
return s[i + 1:] == t[i + 1:]
elif len(s) < len(t):
return s[i:] == t[i + 1:]
else:
return s[i + 1:] == t[i:]
# 前段完全一样,则最后必然多一个,这里假设 s, t 一定不同
return abs(len(s) - len(t)) == 1
```
```python
# 205. Isomorphic String 如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。
def isIsomorphic(self, s, t):
d1, d2 = {}, {}
for c1, c2 in zip(s, t):
if c1 in d1 and d1[c1] != c2: return False
else: d1[c1] = c2
if c2 in d2 and d2[c2] != c1: return False
else: d2[c2] = c1
return True
```
```python
# string中获得多位数字
def extract_number_loop(num):
ct = 0
for char in num:
if '0' <= char <= '9':
ct = ct * 10 + int(char)
return ct
```
```python
# 242.有效的字母异位词
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
record = [0] * 26
for i in s:
#并不需要记住字符a的ASCII只要求出一个相对数值就可以了
record[ord(i) - ord("a")] += 1
for i in t:
record[ord(i) - ord("a")] -= 1
for i in range(26):
if record[i] != 0:
#record数组如果有的元素不为零0说明字符串s和t 一定是谁多了字符或者谁少了字符。
return False
return True
```
```python
# 1002. 查找常用字符
# 给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。
# 示例输入words = ["bella","label","roller"] 输出:["e","l","l"]
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
if not words: return []
result = []
hash = [0] * 26 # 用来统计所有字符串里字符出现的最小频率
for i, c in enumerate(words[0]): # 用第一个字符串给hash初始化
hash[ord(c) - ord('a')] += 1
# 统计除第一个字符串外字符的出现频率
for i in range(1, len(words)):
hashOtherStr = [0] * 26
for j in range(len(words[i])):
hashOtherStr[ord(words[i][j]) - ord('a')] += 1
# 更新hash保证hash里统计26个字符在所有字符串里出现的最小次数
for k in range(26):
hash[k] = min(hash[k], hashOtherStr[k])
# 将hash统计的字符次数转成输出形式
for i in range(26):
while hash[i] != 0: # 注意这里是while多个重复的字符
result.extend(chr(i + ord('a')))
hash[i] -= 1
return result
```
```python
# 349. 两个数组的交集
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
# 使用哈希表存储一个数组中的所有元素
table = {}
for num in nums1:
table[num] = table.get(num, 0) + 1
# 使用集合存储结果
res = set()
for num in nums2:
if num in table:
res.add(num)
del table[num]
return list(res)
```
```python
# 1. 快乐数
# 快乐数:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1。如果 可以变为 1那么这个数就是快乐数。
class Solution:
def isHappy(self, n: int) -> bool:
record = set()
while True:
n = self.get_sum(n)
if n == 1:
return True
# 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
if n in record:
return False
else:
record.add(n)
def get_sum(self,n: int) -> int:
new_num = 0
while n:
n, r = divmod(n, 10)
new_num += r ** 2
return new_num
```
```python
# 1. 两数之和,使用字典
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
records = dict()
for index, value in enumerate(nums):
if target - value in records: # 遍历当前元素并在map中寻找是否有匹配的key
return [records[target- value], index]
records[value] = index # 如果没找到匹配对就把访问过的元素和下标加入到map中
return []
```
```python
# 453.99 四数相加II
# 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
class Solution(object):
def fourSumCount(self, nums1, nums2, nums3, nums4):
# 使用字典存储nums1和nums2中的元素及其和
hashmap = dict()
for n1 in nums1:
for n2 in nums2:
if n1 + n2 in hashmap:
hashmap[n1+n2] += 1
else:
hashmap[n1+n2] = 1
# 如果 -(n1+n2) 存在于nums3和nums4, 存入结果
count = 0
for n3 in nums3:
for n4 in nums4:
key = - n3 - n4
if key in hashmap:
count += hashmap[key]
return count
```
```python
# 1198.给定一个二维整数数组 mat其中每一行都是升序排列的找到一个最小的正整数使得每一行中都存在这个数。
def smallestCommonElement(mat):
# 哈希表,记录每个元素在各行中的出现次数
element_count = {}
# 遍历每一行,统计每个元素的出现次数
for row in mat:
for num in row:
if num not in element_count:
element_count[num] = 1
else:
element_count[num] += 1
# 遍历哈希表,找到所有行中都存在的最小正整数
for num, count in sorted(element_count.items()):
if count == len(mat):
return num
return -1 # 如果没有找到符合条件的元素,返回 -1
```
```python
#R 9 回文数
class Solution:
def isPalindrome(self, x: int) -> bool:
# 同样地,如果数字的最后一位是 0为了使该数字为回文则其第一位数字也应该是 0
if x < 0 or (x % 10 == 0 and x != 0):
return False
reverted_number = 0
while x > reverted_number:
reverted_number = reverted_number * 10 + x % 10
x //= 10
# 当数字长度为奇数时,我们可以通过 reverted_number // 10 去除处于中位的数字。
# 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12reverted_number = 123
# 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == reverted_number or x == reverted_number // 10
```
```python
# 12 整数转罗马数字
class Solution:
def intToRoman(self, num: int) -> str:
symbol = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']
value = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
result = ""
temp = num
for i in range(len(value)):
d = temp // value[i]
if d > 0:
result += symbol[i]*d
temp -= d*value[i]
return result
```
```python
# 0 Longest Common Prefix/最长公共前缀 (Easy)
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
s0 = strs[0]
for j, c in enumerate(s0):
for s in strs:
if j == len(s) or s[j] != c:
return s0[:j]
return s0
```
```python
# 36 Valid Sudoku/有效的数独 (Easy)
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# 初始化行、列、宫的集合
row = [set() for _ in range(9)]
col = [set() for _ in range(9)]
palace = [[set() for _ in range(3)] for _ in range(3)]
# 双重循环遍历数独
for i in range(9):
for j in range(9):
# 获取当前位置的数字
num = board[i][j]
# 跳过空格
if num == ".":
continue
# 重复数字检查
if(num in row[i] or num in col[j] or num in palace[i//3][j//3]):
return False
# 更新数字集合
row[i].add(num)
col[j].add(num)
palace[i//3][j//3].add(num)
return True
```
```python
# 43 Multiply Strings/字符串相乘 (Medium)
class Solution:
def multiply(self, num1: str, num2: str) -> str:
if num1 == "0" or num2 == "0":
return "0"
m, n = len(num1), len(num2)
l = m+n
ans = [0]*l
for i in range(m-1,-1,-1):
a = int(num1[i])
for j in range(n-1,-1,-1):
b = int(num2[j])
c = a*b + ans[i+j+1]
ans[i+j+1] = c%10
ans[i+j] += c//10
if ans[0]==0:
ans.remove(0)
return ''.join(str(i) for i in ans)
```
```python
# 49 Anagrams/字母异位词分组 (Medium)
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
d = defaultdict(list)
for s in strs:
d[''.join(sorted(s))].append(s) # sorted(s) 相同的字符串分到同一组
return list(d.values())
```
```python
# 53.9 Spiral Matrix/螺旋矩阵 (Medium)
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
ans = list()
# 通过次数控制变量控制循环步数
n = len(matrix[0])
m = len(matrix)
# 初始化索引变量
i = 0
j = -1
# 移动方向变量
step = 1
while m > 0 and n > 0: # m n 变化的代表每一横纵可以行走的步数
# 横向循环
for jcnt in range(n):
# 先加再走,避免拐角重复
j += step
ans.append(matrix[i][j])
# 横向走完减少的是纵向可走的次数
m -= 1
# 纵向循环
for icnt in range(m):
# 先加再走,避免拐角重复
i += step
ans.append(matrix[i][j])
# 纵向走完减少的是横向可走的次数
n -= 1
# 改变方向
step = -step
return ans
```
```python
# 60 Permutation Sequence/排列序列 (Hard)
# 选择第一个元素为 1 后的排列有 (𝑛1)! 个
class Solution:
def getPermutation(self, n: int, k: int) -> str:
# 初始化数字列表
nums = list(range(1, n + 1))
# 转换为从 0 开始的索引
k -= 1
result = []
# 构造排列
for i in range(n, 0, -1):
fact = factorial(i - 1)
index = k // fact
result.append(str(nums.pop(index)))
k %= fact
return ''.join(result)
```
```python
# 119 Pascal's Triangle II/杨辉三角 II (Easy)
class Solution:
def getRow(self, rowIndex: int) -> List[int]:
f = [1] * (rowIndex + 1)
for i in range(2, rowIndex + 1):
for j in range(i - 1, 0, -1):
f[j] += f[j - 1]
return f
```
```python
# 0 反转字符串II/Reverse String II (easy)
# 给定一个字符串 s 和一个整数 k从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符;如果剩余字符少于 k 个,则将剩余字符全部反转;如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
class Solution:
def reverseStr(self, s: str, k: int) -> str:
# 字符串末尾如果超过最大长度,则会返回至字符串最后一个值,这个特性可以避免一些边界条件的处理。
def reverse_substring(text):
left, right = 0, len(text) - 1
while left < right:
text[left], text[right] = text[right], text[left]
left += 1
right -= 1
return text
res = list(s)
for cur in range(0, len(s), 2 * k):
res[cur: cur + k] = reverse_substring(res[cur: cur + k])
return ''.join(res)
```
```python
# 344.反转字符串/Reverse String (easy)
class Solution:
def reverseString(self, s: List[str]) -> None:
# Do not return anything, modify s in-place instead.
left, right = 0, len(s) - 1
# 该方法已经不需要判断奇偶数,经测试后时间空间复杂度比用 for i in range(len(s)//2)更低
# 因为while每次循环需要进行条件判断而range函数不需要直接生成数字因此时间复杂度更低。推荐使用range
while left < right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
```
```python
# 151 翻转字符串中的单词 Reverse Words in a String II $/? (Medium)
class Solution:
def reverseWords(self, str):
left = 0
n = len(str)
for i in range(n + 1):
if i == n or str[i] == ' ':
self.reverse(str, left, i - 1)
left = i + 1
self.reverse(str, 0, n - 1)
def reverse(self, str, left, right):
while left < right:
str[left], str[right] = str[right], str[left]
left += 1
right -= 1
```
```python
#R 28. 实现 strStr()next数组最长的相同的真前后缀长度
class Solution:
def getNext(self, next, s):
j = -1
next[0] = j
for i in range(1, len(s)):
while j >= 0 and s[i] != s[j+1]:
j = next[j]
if s[i] == s[j+1]:
j += 1
next[i] = j
def strStr(self, haystack: str, needle: str) -> int:
if not needle:
return 0
next = [0] * len(needle)
self.getNext(next, needle)
j = -1
for i in range(len(haystack)):
while j >= 0 and haystack[i] != needle[j+1]:
j = next[j]
if haystack[i] == needle[j+1]:
j += 1
if j == len(needle) - 1:
return i - len(needle) + 1
return -1
```
```python
# 459.重复的子字符串
class Solution:
def repeatedSubstringPattern(self, s: str) -> bool:
if len(s) == 0:
return False
nxt = [0] * len(s)
self.getNext(nxt, s)
if nxt[-1] != -1 and len(s) % (len(s) - (nxt[-1] + 1)) == 0:
return True
return False
def getNext(self, nxt, s):
nxt[0] = -1
j = -1
for i in range(1, len(s)):
while j >= 0 and s[i] != s[j+1]:
j = nxt[j]
if s[i] == s[j+1]:
j += 1
nxt[i] = j
return nxt
```
```python
# 162 Find Peak Element/寻找峰值 (Medium)
# 如果nums[i]<nums[i+1],那么在下标 [i+1,n1] 中一定存在至少一个峰值。
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
left, right = -1, len(nums) - 1 # 开区间 (-1, n-1)
while left + 1 < right: # 开区间不为空
mid = (left + right) // 2
if nums[mid] > nums[mid + 1]:
right = mid
else:
left = mid
return right
```
```python
# 166 Fraction to Recurring Decimal/分数到小数 (Medium)
class Solution:
def fractionToDecimal(self, numerator: int, denominator: int) -> str:
if numerator == 0: return "0"
res = []
# 首先判断结果正负, 异或作用就是 两个数不同 为 True 即 1 ^ 0 = 1 或者 0 ^ 1 = 1
if (numerator > 0) ^ (denominator > 0):
res.append("-")
numerator, denominator = abs(numerator), abs(denominator)
# 判读到底有没有小数
a, b = divmod(numerator, denominator)
res.append(str(a))
# 无小数
if b == 0:
return "".join(res)
res.append(".")
# 处理余数
# 把所有出现过的余数记录下来
loc = {b: len(res)}
while b:
b *= 10
a, b = divmod(b, denominator)
res.append(str(a))
# 余数前面出现过,说明开始循环了,加括号
if b in loc:
res.insert(loc[b], "(")
res.append(")")
break
# 在把该位置的记录下来
loc[b] = len(res)
return "".join(res)
```
```python
# 169 Majority Element/多数元素 (Easy)
# 摩尔投票法: 核心理念为 票数正负抵消 。
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
```
```python
# 243 Shortest Word Distance $/最短单词距离 (Easy)
# shortest distance between these two words in the list
class Solution:
def shortestDistance(self, words, word1, word2):
p1, p2 = -1, -1
res = float('inf') # 使用 float('inf') 表示正无穷
for i in range(len(words)):
if words[i] == word1:
p1 = i
elif words[i] == word2:
p2 = i
if p1 != -1 and p2 != -1:
res = min(res, abs(p1 - p2))
return res
```
```python
# 204 Count Primes/计数质数 (Easy)
def count_primes_py(n):
# 最小的质数是 2
if n < 2:
return 0
isPrime = [1] * n
isPrime[0] = isPrime[1] = 0 # 0和1不是质数先排除掉
# 埃式筛,把不大于根号 n 的所有质数的倍数剔除
for i in range(2, int(n ** 0.5) + 1):
if isPrime[i]:
isPrime[i * i:n:i] = [0] * ((n - 1 - i * i) // i + 1)
return sum(isPrime)
```
```python
# 0. Kth Largest Element in an Array/数组中的第K个最大元素 (Medium)
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
pq = [] # 将数组加入小顶堆堆中维护当前值最大的k个数
for num in nums:
heapq.heappush(pq, num) # 当前元素入堆
if len(pq) > k:
heapq.heappop(pq) # 堆中元素超过k个弹出最小的那个
return pq[0] # 最后堆顶的即为第k大的数
```
```python
# 241 Different Ways to Add Parentheses/为运算表达式设计优先级 (Medium)
# 输入expression = "2-1-1" 输出:[0,2]
# 解释: ((2-1)-1) = 0 (2-(1-1)) = 2
class Solution:
def diffWaysToCompute(self, input: str) -> List[int]:
# 如果只有数字,直接返回
if input.isdigit():
return [int(input)]
res = []
for i, char in enumerate(input):
if char in ['+', '-', '*']:
# 1.分解:遇到运算符,计算左右两侧的结果集
# 2.解决diffWaysToCompute 递归函数求出子问题的解
left = self.diffWaysToCompute(input[:i])
right = self.diffWaysToCompute(input[i+1:])
# 3.合并:根据运算符合并子问题的解
for l in left:
for r in right:
if char == '+':
res.append(l + r)
elif char == '-':
res.append(l - r)
else:
res.append(l * r)
return res
```
```python
# 264 Ugly Number II/丑数 II (Medium)
# 计算下一个丑数的候选集 res[a]⋅2 , res[b]⋅3 , res[c]⋅5 。
# 选择丑数候选集中最小的那个作为下一个丑数,填入 res 。
# 将被选中的丑数对应的指针向右移动一格。
class Solution:
def nthUglyNumber(self, n: int) -> int:
res, a, b, c = [1] * n, 0, 0, 0
for i in range(1, n):
n2, n3, n5 = res[a] * 2, res[b] * 3, res[c] * 5
res[i] = min(n2, n3, n5)
if res[i] == n2: a += 1
if res[i] == n3: b += 1
if res[i] == n5: c += 1
return res[-1]
```
```python
# 289 Game of Life/生命游戏 (Medium)
# 2表示活细胞变成死细胞3表示死细胞变成活细胞。
class Solution:
def gameOfLife(self, board: List[List[int]]) -> None:
m = len(board) # 行数
n = len(board[0]) # 列数
for i in range(m):
for j in range(n):
count = 0 # 统计每个格子周围八个位置的活细胞数每个格子计数重置为0
for x in range(-1, 2):
for y in range(-1, 2):
# 枚举周围八个位置其中去掉本身x = y = 0和越界的情况
if (x == 0 and y == 0) or i + x < 0 or i + x >= m or j + y < 0 or j + y >= n: continue
# 如果周围格子是活细胞1或者是活细胞变死细胞2都算一个活细胞
if board[i + x][j + y] == 1 or board[i + x][j + y] == 2: count += 1
if board[i][j] == 1 and (count < 2 or count > 3): board[i][j] = 2 # 格子本身是活细胞周围满足变成死细胞的条件标记为2
if board[i][j] == 0 and count == 3: board[i][j] = 3 # 格子本身是死细胞周围满足复活条件标记为3
for i in range(m):
for j in range(n):
# 死细胞为0活细胞变成死细胞为2都为偶数模2为0刚好是死细胞
# 活细胞为1死细胞变成活细胞为3都为奇数模2为1刚好是活细胞
board[i][j] %= 2
```
```python
# 346 Moving Average from Data Stream $/数据流中的移动平均值 (Easy)
class MovingAverage:
def __init__(self, size: int):
self.l = size
self.sum = 0
self.r = []
def next(self, val: int) -> float:
if len(self.r)==self.l:
self.sum -= self.r.pop(0) #移除队头元素这种简单情况可以用python list
self.sum += val
self.r.append(val)
return self.sum/len(self.r)
```
```python
# 498 Diagonal Traverse/对角线遍历 (Medium)
# 右斜对角线是横纵坐标和为定值一共有m+n1个对角线
class Solution:
def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]:
m, n, ans = len(mat), len(mat[0]), []
for k in range(m + n - 1):
if not k % 2:
ans += [mat[x][k-x] for x in range(min(m - 1, k), max(-1, k - n),-1)]
else:
ans += [mat[x][k-x] for x in range(max(0, k - n + 1), min(k + 1, m))]
return ans
```
```python
# 697 Degree of an Array/数组的度 (Easy)
# 数组的 度 的定义是指数组里任一元素出现频数的最大值,找到与 nums 拥有相同大小的度的最短连续子数组
# 要求的最短子数组的起始和终止位置,由出现次数最多的元素 第一次和最后一次出现的位置 确定。
class Solution:
def findShortestSubArray(self, nums: List[int]) -> int:
left, right = dict(), dict()
counter = collections.Counter()
for i, num in enumerate(nums):
if num not in left:
left[num] = i
right[num] = i
counter[num] += 1
degree = max(counter.values())
res = len(nums)
for k, v in counter.items():
if v == degree:
res = min(res, right[k] - left[k] + 1)
return res
```
```python
# 0 Shortest Completing Word/最短补全词 (Medium)
# 找到最短的包含所有字母的单词
class Solution:
def shortestCompletingWord(self, licensePlate: str, words: List[str]) -> str:
tmp=[]
for i in licensePlate:
if 'a'<=i<='z' or 'A'<=i<='Z':
tmp.append(i.lower())
words=sorted(words, key=lambda x:len(x))
for word in words:
l=tmp.copy()
for i in word:
if i in l:
l.remove(i)
if not l:
return word
```
```python
# 835 Image Overlap/图像重叠 (Medium)
# 转换 其中一个图像,将所有的 1 向左,右,上,或下滑动任何数量的单位;最大可能的重叠数量是多少
# 计算两个图像的1之间的移动距离取计数最大的移动方式
class Solution:
def largestOverlap(self, img1: List[List[int]], img2: List[List[int]]) -> int:
n = len(img1)
cnt = defaultdict(int)
one = []
for i in range(n):
for j in range(n):
if img1[i][j]:
one.append([i, j])
for i in range(n):
for j in range(n):
if img2[i][j]:
for a, b in one:
cnt[(i - a, j - b)] += 1
return max(cnt.values()) if cnt else 0
```
```python
# 892 Surface Area of 3D Shapes/三维形体的表面积 (Easy)
# 对于四个侧面的表面积,只有在相邻位置的高度小于 v 时,对应的那个侧面才会贡献表面积,且贡献的数量为 v - nv
class Solution:
def surfaceArea(self, grid: List[List[int]]) -> int:
N = len(grid)
ans = 0
for r in range(N):
for c in range(N):
if grid[r][c]:
ans += 2
for nr, nc in ((r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)):
if 0 <= nr < N and 0 <= nc < N:
nval = grid[nr][nc]
else:
nval = 0
ans += max(grid[r][c] - nval, 0)
return ans
```
```python
# 970 Powerful Integers/强整数 (Easy)
# 整数可以表示为 x^i + y^j
class Solution:
def powerfulIntegers(self, x: int, y: int, bound: int) -> List[int]:
ans = set()
a = 1
while a <= bound:
b = 1
while a + b <= bound:
ans.add(a + b)
b *= y
if y == 1:
break
if x == 1:
break
a *= x
return list(ans)
```
```python
# 1010 Pairs of Songs With Total Durations Divisible by 60/总持续时间可被 60 整除的歌曲对 (Medium)
# 可被 60 整除的歌曲对的数量,长度为 60 的数组 cnt 记录每个余数 x 出现的次数。
class Solution:
def numPairsDivisibleBy60(self, time: List[int]) -> int:
cnt = Counter()
ans = 0
for x in time:
x %= 60
y = (60 - x) % 60
ans += cnt[y]
cnt[x] += 1
return ans
```
```python
# 1013 Partition Array Into Three Parts With Equal Sum/将数组分成和相等的三个部分 (Easy)
# 依次寻找切分点i j
class Solution:
def canThreePartsEqualSum(self, A: List[int]) -> bool:
s = sum(A)
if s % 3 != 0:
return False
target = s // 3
n, i, cur = len(A), 0, 0
while i < n:
cur += A[i]
if cur == target:
break
i += 1
if cur != target:
return False
j = i + 1
while j + 1 < n: # 需要满足最后一个数组非空
cur += A[j]
if cur == target * 2:
return True
j += 1
return False
```
```python
#R 767 Reorganize String/重构字符串 (Medium)
# 重新排布其中的字母,使得两相邻的字符不同。
# 对于每一种元素,循环在不同桶中进行填充,由于桶的个数等于字符串中最多的元素的数目,因此每个桶中不会出现相同的元素,填充完毕后将桶依次相连即为答案
class Solution:
def reorganizeString(self, s: str) -> str:
cnt, idx = Counter(s), 0
bucketNum = cnt.most_common(1)[0][1] # 桶的数目等于字符串中最多的元素的数目
if bucketNum > (len(s) + 1) // 2: return ""
buckets = [[] for _ in range(bucketNum)]
for c, num in cnt.most_common():
for _ in range(num):
buckets[idx].append(c)
idx = (idx + 1) % bucketNum # 循环在不同桶中进行填充
return "".join(["".join(bucket) for bucket in buckets])
```
```python
#R 1053.9 Distant Barcodes/距离相等的条形码 (Medium)
# 请你重新排列这些条形码,使其中任意两个相邻的条形码不能相等。
# 我们先统计数组barcodes中各个数出现的次数然后按照它们出现的次数从大到小排序依次填入偶数后奇数
class Solution:
def rearrangeBarcodes(self, barcodes: List[int]) -> List[int]:
cnt = Counter(barcodes)
barcodes.sort(key=lambda x: (-cnt[x], x))
n = len(barcodes)
ans = [0] * len(barcodes)
ans[::2] = barcodes[: (n + 1) // 2]
ans[1::2] = barcodes[(n + 1) // 2:]
return ans
```
```python
# 1232.9 Remove Sub-Folders from the Filesystem/删除子文件夹 (Medium)
# 删除该列表中的所有子文件夹
class Solution:
def removeSubfolders(self, folder: List[str]) -> List[str]:
folder.sort()
ans = [folder[0]]
for f in folder[1:]:
m, n = len(ans[-1]), len(f)
if m >= n or not (ans[-1] == f[:m] and f[m] == '/'):
ans.append(f)
return ans
```
```python
# 1282 Group the People Given the Group Size They Belong To/用户分组 (Medium)
# groupSizes[i] 是第 i 个人所在的组的大小,返回用户分组[[5],[0,1,2],[3,4,6]]
class Solution:
def groupThePeople(self, groupSizes: List[int]) -> List[List[int]]:
mp, ans = defaultdict(list), []
for i, v in enumerate(groupSizes):
mp[v].append(i)
for k, lt in mp.items():
ans.extend(lt[i:i+k] for i in range(0, len(lt), k))
return ans
```
```python
# 1369.9 Increasing Decreasing String/上升下降字符串 (简单)
# 从 s 剩余字符中选出比上一个添加字符更大的 最小 字符,将它 接在 结果字符串后面。
# 创建一个大小为 26 的桶记录每种字符的数量。每次提取最长的上升或下降字符串时,我们直接顺序或逆序遍历这个桶。
class Solution:
def sortString(self, s: str) -> str:
num = [0] * 26
for ch in s:
num[ord(ch) - ord('a')] += 1
ret = list()
while len(ret) < len(s):
for i in range(26):
if num[i]:
ret.append(chr(i + ord('a')))
num[i] -= 1
for i in range(25, -1, -1):
if num[i]:
ret.append(chr(i + ord('a')))
num[i] -= 1
return "".join(ret)
```
```python
# 1452 People Whose List of Favorite Companies Is Not a Subset of Another List/收藏清单 (medium)
# 找出不是其他任何人收藏的公司清单的子集的收藏清单。 暴力匹配。中间加一点剪枝
class Solution:
def peopleIndexes(self, favoriteCompanies: List[List[str]]) -> List[int]:
a = list(set(f) for f in favoriteCompanies)
n = len(a)
res = []
for i in range(n):
for j in range(n):
flag = True
if i != j and len(a[i]) <= len(a[j]):
if a[i].issubset(a[j]): #### 偷懒,直接调库
flag = False
break
if flag == True:
res.append(i)
return res
```
```python
# 1966 未排序数组中的可被二分搜索的数 (中等)
# 二分搜索的方法是否可以判断 target 是否存在 sequence中。输出所有可以判断的数量
# 需要找到一个下标,他左边的数比他小,右边的数比他大,返回所有这样下标的数量。
# 分别从左往右和从右往左遍历分别记录起点到i最大值和j到终点的最小值如果一个数同时是从左往右的最大值和从右往左的最小值那么符合题意。
class Solution:
def binarySearchableNumbers(self, nums: List[int]) -> int:
n = len(nums)
l, r = float('-inf'), float('inf')
has = [0] * n
for i in range(n):
j = n-1-i
vi, vj = nums[i], nums[j]
if vi > l:
l = vi
has[i] += 1
if vj < r:
r = vj
has[j] += 1
return sum(i==2 for i in has)
```
```python
# 681.最近时刻 中等
# "01:34" 和 "12:09" 是合法的“1:34” 和 “12:9” 是不合法的。
# 利用当前出现过的数字构造下一个距离当前时间最近的时刻。每个出现数字都可以被无限次使用。
class Solution:
def nextClosestTime(self, time: str) -> str:
nums, hour, minute = set(time), int(time[:2]), int(time[3:])
m = hour * 60 + minute
while True:
m += 1
hour = m // 60 % 24
minute = m % 60
if hour < 10:
hour = '0' + str(hour)
else:
hour = str(hour)
if minute < 10:
minute = '0' + str(minute)
else:
minute = str(minute)
for i, ch in enumerate(hour + minute):
if ch not in nums:
break
if i == 3:
return hour + ':' + minute
```
# 链表
```python
# 203.移除链表元素
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
# 创建虚拟头部节点以简化删除过程
dummy_head = ListNode(next = head)
# 遍历列表并删除值为val的节点
current = dummy_head
while current.next:
if current.next.val == val:
current.next = current.next.next
else:
current = current.next
return dummy_head.next
```
```python
# 707.设计链表
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.dummy_head = ListNode()
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
current = self.dummy_head.next
for i in range(index):
current = current.next
return current.val
def addAtHead(self, val: int) -> None:
self.dummy_head.next = ListNode(val, self.dummy_head.next)
self.size += 1
def addAtTail(self, val: int) -> None:
current = self.dummy_head
while current.next:
current = current.next
current.next = ListNode(val)
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = ListNode(val, current.next)
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = current.next.next
self.size -= 1
```
```python
# 206.反转链表
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
cur = head
pre = None
while cur:
temp = cur.next # 保存一下 cur的下一个节点因为接下来要改变cur->next
cur.next = pre #反转
#更新pre、cur指针
pre = cur
cur = temp
return pre
```
```python
# 24. 两两交换链表中的节点
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
dummy_head = ListNode(next=head)
current = dummy_head
# 必须有cur的下一个和下下个才能交换否则说明已经交换结束了
while current.next and current.next.next:
temp = current.next # 防止节点修改
temp1 = current.next.next.next
current.next = current.next.next
current.next.next = temp
temp.next = temp1
current = current.next.next
return dummy_head.next
```
```python
19.删除链表的倒数第N个节点
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
# 创建一个虚拟节点,并将其下一个指针设置为链表的头部
dummy_head = ListNode(0, head)
# 创建两个指针,慢指针和快指针,并将它们初始化为虚拟节点
slow = fast = dummy_head
# 快指针比慢指针快 n+1 步
for i in range(n+1):
fast = fast.next
# 移动两个指针,直到快速指针到达链表的末尾
while fast:
slow = slow.next
fast = fast.next
# 通过更新第 (n-1) 个节点的 next 指针删除第 n 个节点
slow.next = slow.next.next
return dummy_head.next
```
```python
# 02.07. 链表相交
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
cur = headA
while cur: # 求链表A的长度
cur = cur.next
lenA += 1
cur = headB
while cur: # 求链表B的长度
cur = cur.next
lenB += 1
curA, curB = headA, headB
if lenA > lenB: # 让curB为最长链表的头lenB为其长度
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenB - lenA): # 让curA和curB在同一起点上末尾位置对齐
curB = curB.next
while curA: # 遍历curA 和 curB遇到相同则直接返回
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
```
```python
# 142.环形链表II
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# If there is a cycle, the slow and fast pointers will eventually meet
if slow == fast:
# Move one of the pointers back to the start of the list
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
# If there is no cycle, return None
return None
```
```python
2. Add Two Numbers两数相加
def addTwoNumbers(l1, l2):
dummy = ListNode() # 创建一个哑结点
current = dummy # 初始化指针,指向哑结点
carry = 0 # 进位值
# 遍历两个链表,直到两个链表都遍历完且没有进位
while l1 or l2 or carry:
# 获取当前位置的数字
val1 = l1.val if l1 else 0
val2 = l2.val if l2 else 0
# 计算当前位置的和,考虑进位
total = val1 + val2 + carry
carry, remainder = divmod(total, 10) # 计算进位和当前位置的数字
# 创建新的结点,并将其添加到结果链表中
current.next = ListNode(remainder)
current = current.next
# 移动指针到下一个位置
if l1:
l1 = l1.next
if l2:
l2 = l2.next
return dummy.next # 返回哑结点的下一个结点,即相加结果的链表的头结点
```
```python
#R 92 Reverse Linked List II/反转链表 II (Medium)
class Solution:
def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
p0 = dummy = ListNode(next=head)
for _ in range(left - 1):
p0 = p0.next
pre = None
cur = p0.next
for _ in range(right - left + 1):
nxt = cur.next
cur.next = pre # 每次循环只修改一个 next方便大家理解
pre = cur
cur = nxt
p0.next.next = cur
p0.next = pre
return dummy.next
```
```python
#R 146 LRU Cache/LRU 缓存机制 (Medium)
# get 如果关键字 key 存在于缓存中,则返回关键字的值,否则 -1 。
# put 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该逐出最久未使用的关键字。
class Node:
# 提高访问属性的速度,并节省内存
__slots__ = 'prev', 'next', 'key', 'value'
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.dummy = Node() # 哨兵节点 dummy.prev指向尾部dummy.next指向头部
self.dummy.prev = self.dummy
self.dummy.next = self.dummy
self.key_to_node = {}
# 获取 key 对应的节点,同时把该节点移到链表头部
def get_node(self, key: int) -> Optional[Node]:
if key not in self.key_to_node: # 没有这本书
return None
node = self.key_to_node[key] # 有这本书
self.remove(node) # 把这本书抽出来
self.push_front(node) # 放在最上面
return node
def get(self, key: int) -> int:
# 返回关键字的值
node = self.get_node(key) # get_node 会把对应节点移到链表头部
return node.value if node else -1
def put(self, key: int, value: int) -> None:
# 插入key-value 。如果关键字数量超过 capacity ,则应该逐出最久未使用的关键字
node = self.get_node(key)
if node: # 有这本书
node.value = value # 更新 value
return
self.key_to_node[key] = node = Node(key, value) # 新书
self.push_front(node) # 放在最上面
if len(self.key_to_node) > self.capacity: # 书太多了
back_node = self.dummy.prev
del self.key_to_node[back_node.key]
self.remove(back_node) # 去掉最后一本书
# 删除一个节点(抽出一本书)
def remove(self, x: Node) -> None:
x.prev.next = x.next
x.next.prev = x.prev
# 在链表头添加一个节点(把一本书放在最上面)
def push_front(self, x: Node) -> None:
x.prev = self.dummy
x.next = self.dummy.next
x.prev.next = x
x.next.prev = x
```
```python
# 234 Palindrome Linked List/回文链表 (Easy)
class Solution:
# 876. 链表的中间结点
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
# 206. 反转链表
def reverseList(self, head):
def isPalindrome(self, head: Optional[ListNode]) -> bool:
mid = self.middleNode(head)
head2 = self.reverseList(mid)
while head2:
if head.val != head2.val: # 不是回文链表
return False
head = head.next
head2 = head2.next
return True
```
```python
# 287 Find the Duplicate Number/寻找重复数 (Medium) nums 只有 一个重复的整数
# 将 i 和 nums[i] 视为链表中的父子节点。可转换为环形链表
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
slow = 0
fast = 0
while True:
# fast 前进两次slow 前进一次
fast = nums[fast]
fast = nums[fast]
slow = nums[slow]
if slow == fast:
break
# ptr == slow 时说明检测到重复元素,两个重复元素同时指向环的入口。
ptr = 0
while ptr != slow:
ptr = nums[ptr]
slow = nums[slow]
return ptr
```