# 两指针 ```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 中是否存在三个元素 a,b,c ,使得 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 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) # 请你返回 a,b 和 c 都 至少 出现过一次的子字符串数目。 # 滑动窗口的内层循环结束时,右端点固定在 right,左端点在 0,1,2,…,left−1 的所有子串都是合法的,这一共有 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] 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, acbd):392,如果考虑有多少个这样的子列要用动态规划(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 = 12,reverted_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] 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+n−1个对角线 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 ```