class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
# 数组排序,固定一个数字后,通过双指针寻找另外两个数字
# 需要跳过重复数字
# 时间复杂度 O(n^2)
res = []
n = len(nums)
if n < 3:
return res
# 排序
nums.sort()
for i in range(n):
# 如果 nums[i]>0,后面三个数相加一定大于 0
if nums[i] > 0:
return res
# 跳过重复元素
if i > 0 and nums[i] == nums[i-1]:
continue
l, r = i+1, n-1
while l < r:
if (nums[i] + nums[l] + nums[r]) == 0:
res.append([nums[i], nums[l], nums[r]])
# 跳过重复元素
while l < n-1 and nums[l] == nums[l+1]:
l += 1
while r > 0 and nums[r] == nums[r-1]:
r -= 1
l += 1
r -= 1
elif (nums[i] + nums[l] + nums[r]) > 0:
r -= 1
else:
l += 1
return res
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
# 排序 + 双指针
res = nums[0] + nums[1] + nums[2]
dismin = abs(res - target)
n = len(nums)
nums.sort()
print(nums)
for i in range(n):
if i > 0 and nums[i] == nums[i-1]:
continue
l, r = i+1, n-1
while l < r:
ans = nums[i] + nums[l] + nums[r]
print(i,l,r,ans,res)
if ans == target:
return target
elif ans > target:
dis = ans - target
if dis < dismin:
dismin = dis
res = ans
while r > 0 and nums[r] == nums[r-1]:
r -= 1
r -= 1
else:
dis = target - ans
if dis < dismin:
dismin = dis
res = ans
while l < n-1 and nums[l] == nums[l+1]:
l += 1
l += 1
return res
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
# 字母异位词,字符串排序后的值相同,可作为 key,value 为 str list
res = dict()
# for s in strs:
# key = ''.join(sorted(s))
# if key in res.keys():
# res[key].append(s)
# else:
# res[key] = [s]
# 使用 26 个字母出现的次数组成 key
for s in strs:
count = [0] * 26
for ch in s:
count[ord(ch) - ord('a')] += 1
key = tuple(count)
if key in res.keys():
res[key].append(s)
else:
res[key] = [s]
return list(res.values())
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
# 把区间按照 start 排序,判断下一个区间的 start 是否小于前一个区间的 end,如果是,可以合并,否则不可以
if not intervals or not intervals[0]:
return [[]]
# 按照 start 排序
new_intervals = sorted(intervals, key=lambda arg:arg[0])
# intervals.sort(key=lambda x: x[0])
# print(new_intervals)
res = []
start = new_intervals[0][0]
end = new_intervals[0][1]
for i in range(1, len(new_intervals)):
# 如果当前区间start 小于等于 end,end 取最大值
if new_intervals[i][0] <= end:
end = max(new_intervals[i][1], end)
else:
res.append([start, end])
start = new_intervals[i][0]
end = new_intervals[i][1]
# 最后一个区间需要加入到 res 中
res.append([start, end])
return res
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
# 双指针,0 放到前面,2 放到后面
n = len(nums)
p1, p2 = 0, n-1
for i in range(n):
# 注意执行顺序,不能颠倒,2 换完可能是 0,需要继续执行下一个 if
while i <= p2 and nums[i] == 2:
nums[i], nums[p2] = nums[p2], nums[i]
p2 -= 1
if nums[i] == 0:
nums[i], nums[p1] = nums[p1], nums[i]
p1 += 1
return
# # all in [0, zero] = 0
# # all in (zero, i) = 1
# # all in (two, len - 1] = 2
# def swap(nums, index1, index2):
# nums[index1], nums[index2] = nums[index2], nums[index1]
# size = len(nums)
# if size < 2:
# return
# zero = -1
# two = size - 1
# i = 0
# while i <= two:
# # 这里只有是 0 和 1 的时候,i 才加 1,是 2 的话,需要继续判断
# if nums[i] == 0:
# zero += 1
# swap(nums, i, zero)
# i += 1
# elif nums[i] == 1:
# i += 1
# else:
# swap(nums, i, two)
# two -= 1
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
# 双指针,从后往前插入,可以保证 nums1 的元素不被覆盖
p1, p2 = m-1, n-1
tail = m+n-1
# 原数组为排序数组,p2 结束即可结束循环
while p2 >= 0:
# 注意 p1 先结束的 case
if p1 == -1:
nums1[tail] = nums2[p2]
p2 -= 1
elif nums1[p1] > nums2[p2]:
nums1[tail] = nums1[p1]
p1 -= 1
else:
nums1[tail] = nums2[p2]
p2 -= 1
tail -= 1
基数排序 or 桶排序
class Solution:
def majorityElement(self, nums: List[int]) -> int:
# 1、哈希表,时间复杂度 O(n), 空间复杂度 O(n)
# 2、排序,时间复杂度 O(nlogn), 空间复杂度 O(1)
# 3、分治,时间复杂度 O(nlogn), 空间复杂度 O(logn)
# 4、投票算法,时间复杂度 O(n), 空间复杂度 O(1)
candidate = 0
count = 0
for num in nums:
if count == 0:
candidate = num
if candidate == num:
count += 1
else:
count -= 1
return candidate
class Solution:
def largestNumber(self, nums: List[int]) -> str:
# 转换成字符串之后,按照两个字符串组合的大小排序
# map() 会根据提供的函数对指定序列做映射
strs = map(str, nums)
def cmp(a, b):
if a + b == b + a:
return 0
elif a + b > b + a:
return 1
else:
return -1
strs = sorted(strs, key=functools.cmp_to_key(cmp), reverse=True)
# 返回时注意首位是 0 时,直接返回 0
return ''.join(strs) if strs[0] != '0' else '0'
partition模板
def partition(nums, left, right):
pivot = nums[left]#初始化一个待比较数据
i,j = left, right
while(i < j):
while(i<j and nums[j]>=pivot): #从后往前查找,直到找到一个比pivot更小的数
j-=1
nums[i] = nums[j] #将更小的数放入左边
while(i<j and nums[i]<=pivot): #从前往后找,直到找到一个比pivot更大的数
i+=1
nums[j] = nums[i] #将更大的数放入右边
#循环结束,i与j相等
nums[i] = pivot #待比较数据放入最终位置
return i #返回待比较数据最终位置
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
# # 暴力解法
# nums.sort()
# n = len(nums)
# # print(nums)
# # res = sorted(nums)
# # print(nums)
# # print(res)
# return nums[n-k]
# # 快排
# def partition(nums, low, high):
# i, j = low, high
# pivot = nums[i]
# while i < j:
# # 倒序排列,小的放到后面,注意等号
# while i < j and nums[j] <= pivot:
# j -= 1
# nums[i] = nums[j]
# # 注意等号
# while i < j and nums[i] >= pivot:
# i += 1
# nums[j] = nums[i]
# nums[i] = pivot
# return i
# # 快速排序
# def quicksort(nums, left, right):
# if left < right:
# index = partition(nums, left, right)
# quicksort(nums, left, index-1)
# quicksort(nums, index+1, right)
# # topk 切分,获取第 k 大的数
# def find(nums, low, high):
# # 传进来的 low 和 high 是闭区间,注意等号
# if low <= high:
# index = partition(nums, low, high)
# # print(low, high, index)
# if index == k-1:
# return nums[index]
# elif index < k-1:
# return find(nums, index+1, high)
# else:
# return find(nums, low, index-1)
# n = len(nums)
# return find(nums, 0, n-1)
# # 最小堆,利用 API
# heap = []
# for num in nums:
# heapq.heappush(heap, num)
# if len(heap) > k:
# heapq.heappop(heap)
# return heap[0]
# 最大堆 & k-1次删除之后,剩下的堆顶元素即为目标值
# 构建大顶堆
def maxHeap(nums, i, size):
l, r = 2*i + 1, 2*i + 2
# print(i, l, r, nums)
large = i
if l < size and nums[l] > nums[large]:
large = l
if r < size and nums[r] > nums[large]:
large = r
# large != i,说明需要交换,递归继续构建大顶堆
if large != i:
nums[i], nums[large] = nums[large], nums[i]
maxHeap(nums, large, size)
def buildHeap(nums, size):
# 从最后一个非叶子节点向前遍历
# 最后一个非叶子节点是 size // 2 - 1
for i in range(size // 2 - 1, -1, -1):
maxHeap(nums, i, size)
n = len(nums)
buildHeap(nums, n)
# 删除 k-1次,剩下的堆顶为目标值
for i in range(k-1):
nums[0], nums[n-1] = nums[n-1], nums[0]
n = n-1
maxHeap(nums, 0, n)
return nums[0]
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
hashset = set()
for num in nums:
if num in hashset:
return True
else:
hashset.add(num)
return False
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
# 摩尔投票
# 对比之前的大于 n/2问题,差异是先选取两个候选元素
# 需要针对最后剩下的元素,统计出现次数是否大于 n/3
ans = []
element1, element2 = 0, 0
vote1, vote2 = 0, 0
for num in nums:
# 如果该元素为第一个元素,则计数加1
if vote1 > 0 and num == element1:
vote1 += 1
# 如果该元素为第二个元素,则计数加1
elif vote2 > 0 and num == element2:
vote2 += 1
# 选择第一个元素
elif vote1 == 0:
element1 = num
vote1 += 1
# 选择第二个元素
elif vote2 == 0:
element2 = num
vote2 += 1
# 如果三个元素均不相同,则相互抵消1次
else:
vote1 -= 1
vote2 -= 1
cnt1, cnt2 = 0, 0
for num in nums:
if vote1 > 0 and num == element1:
cnt1 += 1
if vote2 > 0 and num == element2:
cnt2 += 1
# 检测元素出现的次数是否满足要求
if vote1 > 0 and cnt1 > len(nums) / 3:
ans.append(element1)
if vote2 > 0 and cnt2 > len(nums) / 3:
ans.append(element2)
return ans
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
# # 排序
# if len(s) != len(t):
# return False
# s1 = []
# s2 = []
# for ch in s:
# s1.append(ch)
# for ch in t:
# s2.append(ch)
# s1.sort()
# s2.sort()
# for i in range(len(s)):
# if s1[i] != s2[i]:
# return False
# return True
# hash map
if len(s) != len(t):
return False
hashmap = dict()
for ch in s:
if ch not in hashmap.keys():
hashmap[ch] = 1
else:
hashmap[ch] += 1
for ch in t:
if ch in hashmap.keys():
hashmap[ch] -= 1
if hashmap[ch] < 0:
return False
else:
return False
return True
class Solution:
def hIndex(self, citations: List[int]) -> int:
# # 排序,从后向前遍历,初始 h=0, 如果citation[i] > h,说明找到了一个至少引用了 h+1
# sorted_citation = sorted(citations, reverse = True)
# h = 0; i = 0; n = len(citations)
# while i < n and sorted_citation[i] > h:
# h += 1
# i += 1
# return h
# 计数排序,维护一个引用次数对应文章数的数组,下标表示引用次数,值表示文章数
# 大于文章数的引用次数可限制到 n
# 计数之后,找到tot 文章数 大于等于 当前引用次数的 h
n = len(citations); tot = 0
counter = [0] * (n+1)
for c in citations:
if c >= n:
counter[n] += 1
else:
counter[c] += 1
for i in range(n, -1, -1):
tot += counter[i]
if tot >= i:
return i
return 0
class Solution:
def wiggleSort(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
# n = len(nums)
# # 排序,时间复杂度 O(nlogn)
# arr = sorted(nums)
# x = (n + 1) // 2
# # 相当于把分开的两个数组倒序之后再插入
# j, k = x - 1, n - 1
# for i in range(0, n, 2):
# nums[i] = arr[j]
# # 每次处理两个数
# if i + 1 < n:
# nums[i + 1] = arr[k]
# j -= 1
# k -= 1
# 无需对整个数组排序,通过快速选择找到数组的中位数,通过 3_way_partion 将数组分成三个部分,倒序穿插
# 快速选择
def partition(nums, begin, end, midIndex):
pivot = nums[begin]
i, j = begin, end-1
while i < j:
# 注意先判断 j,如果 pivot 是 begin
while i < j and nums[j] >= pivot:
j -= 1
nums[i] = nums[j]
while i < j and nums[i] <= pivot:
i += 1
nums[j] = nums[i]
nums[i] = pivot
if i == midIndex:
return
elif i > midIndex:
# 注意不包含 i
partition(nums, begin, i, midIndex)
else:
partition(nums, i+1, end, midIndex)
n = len(nums)
# 快速选择找到中位数
partition(nums, 0, n, n // 2)
mid = nums[n//2]
print(n//2, mid, nums)
# 3-way-partition
i, j, k = 0, 0, n-1
while j < k:
if nums[j] > mid:
nums[j], nums[k] = nums[k], nums[j]
k -= 1
elif nums[j] < mid:
nums[j], nums[i] = nums[i], nums[j]
i += 1
j += 1
else:
j += 1
# print(nums)
x = (n + 1) // 2
j, k = x - 1, n - 1
for i in range(0, n, 2):
nums[i] = arr[j]
# 每次处理两个数
if i + 1 < n:
nums[i + 1] = arr[k]
j -= 1
k -= 1