2108 lines
76 KiB
Markdown
2108 lines
76 KiB
Markdown
# 两指针
|
||
```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<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)
|
||
# 请你返回 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]<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, 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]<nums[i+1],那么在下标 [i+1,n−1] 中一定存在至少一个峰值。
|
||
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+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
|
||
```
|
||
|
||
|
||
|
||
|