日常系列LeetCode《11·堆和优先队列篇》
Posted 常某某的好奇心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列LeetCode《11·堆和优先队列篇》相关的知识,希望对你有一定的参考价值。
数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)
总结
有限容量的小顶堆:最大k个元素
有限容量的大顶堆:最小k个元素
堆化时间复杂度:o(n)
lc 1046:最后一块石头的重量
https://leetcode.cn/problems/last-stone-weight/
提示:
1 <= stones.length <= 30
1 <= stones[i] <= 1000
#方案一:排序
class Solution:
def lastStoneWeight(self, stones: List[int]) -> int:
n=len(stones)
if n == 1: return stones[0]
#o(n * nlogn)
for i in range(n-1): #比较次数
#每次都进行排序
stones.sort(reverse=False)
#
if stones[n-2]==0:break #优化:例如000001
stones[n-1]=stones[n-1]-stones[n-2]
stones[n-2]=0
return stones[n-1]
#方案二:大顶堆
import heapq
class Solution:
def lastStoneWeight(self, stones: List[int]) -> int:
n=len(stones)
if n == 1: return stones[0]
#o(n),o(nlogn)
s=[-x for x in stones]
heapq.heapify(s)#大堆化
while len(s)>1:
y=heapq.heappop(s)
x=heapq.heappop(s)
if x!=y:heapq.heappush(s,y-x)
#
return -s[0] if s else 0
lc 215 【剑指 076】:数组中的第 K 个
https://leetcode.cn/problems/kth-largest-element-in-an-array/
提示:
1 <= k <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
#方案一:排序(不满足)
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
#o(logn)/o(n),o(nlogn)
nums.sort()
return nums[len(nums)-k]
#方案二:小顶堆 + 大顶堆(不满足)
import heapq
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
n=len(nums)
#o(k)/o(n-k),o(nlogk)
if k<=n-k+1: #小顶堆
pq=nums[:k]
heapq.heapify(pq)
for num in nums[k:]:#key:放k+1元素,更新后,去头(‘第k-1大’)
heapq.heappush(pq,num)
heapq.heappop(pq)
#
return pq[0]
else:#大顶堆优化:当k比较大的时候——第k大——>第n-k+1小
pq=[-x for x in nums[:n-k+1]]
heapq.heapify(pq)
for num in nums[n-k+1:]:
heapq.heappush(pq,-num)#key:-
heapq.heappop(pq)
return -pq[0]
#方案三:二路快排
import random
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
n=len(nums)
#o(1),o(n)
left,right=0,n-1
target=n-k
while True:
pivot=self.pivot(nums,left,right)
if pivot==target:return nums[pivot]
elif pivot>target:right=pivot-1
else:left=pivot+1
def pivot(self,nums,lo,hi):
i=random.randint(lo,hi)
nums[i],nums[hi]=nums[hi],nums[i]
pivot=nums[hi]
less=great=lo
while great<=hi-1:
if nums[great]<pivot:
nums[less],nums[great]=nums[great],nums[less]
less+=1
great+=1
nums[less],nums[hi]=nums[hi],nums[less]
return less
lc 347【剑指 060】【top100】:前 K 个高频元素
https://leetcode.cn/problems/top-k-frequent-elements/
提示:
1 <= nums.length <= 105
k 的取值范围是 [1, 数组中不相同的元素的个数]
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
进阶:
你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
#方案一:小顶堆
import collections
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
#o(n),o(nlogk)
knums=[]
count=collections.Counter(nums)#MAP
for num,cnt in count.items(): #dict结构
heapq.heappush(knums,(cnt,num))
if len(knums)>k:heapq.heappop(knums) #维护k个
return [x[1] for x in knums]
#方案二:二路快排
import collections
import random
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
#o(n)
count=collections.Counter(nums)#MAP
nums=[(cnt,num) for num,cnt in count.items()] #dict结构
#o(n)
left,right=0,len(nums)-1
target=len(nums)-k #升序
while True:
pivot=self.pivot(nums,left,right)
if pivot == target:
break
elif pivot < target:
left = pivot+1
else:
right = pivot - 1
return [x[1] for x in nums[-k:]] #区别:nums[-k:][1]为其中第二个dict
def pivot(self,nums,lo,hi):
i=random.randint(lo,hi)
nums[i], nums[hi] = nums[hi], nums[i]
pivot = nums[hi][0] #key
less = great = lo
while great <= hi - 1:
if nums[great][0] < pivot:
nums[less], nums[great] = nums[great], nums[less]
less += 1
great += 1
nums[less], nums[hi] = nums[hi], nums[less]
return less
lc 973:最接近原点的 K 个点
https://leetcode.cn/problems/k-closest-points-to-origin/
提示:
1 <= k <= points.length <= 10^4
-10^4 < xi, yi < 10^4
#方案一:大顶堆
import heapq
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
#o(k)
pq=[(-x**2-y**2,i) for i,(x,y) in enumerate(points[:k])] #key:i<->(x,y)
heapq.heapify(pq)#大顶堆
#o(nlogk)
for i in range(k,len(points)):
x,y=points[i]
dist=-x**2-y**2
heapq.heappushpop(pq,(dist,i))
return [points[i] for (_,i) in pq]
#方案二:二路快排
import heapq
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
#o(1),o(n)
left, right = 0, len(points) - 1
target = len(points) - k
while True:
pivot = self.pivot(points, left, right)
if pivot == target:
break
elif pivot < target:
left = pivot + 1
else:
right = pivot - 1
return [x for x in points[-k:]]
def pivot(self, nums, lo, hi) -> None:
i = random.randint(lo, hi)
nums[i], nums[hi] = nums[hi], nums[i]
pivot = nums[hi][0] ** 2 + nums[hi][1] ** 2 #key
less = great = lo
while great <= hi - 1:
dist = nums[great][0] ** 2 + nums[great][1] ** 2
# 降序排列
if dist > pivot:
nums[less], nums[great] = nums[great], nums[less]
less += 1
great += 1
nums[less], nums[hi] = nums[hi], nums[less]
return less
lc 703 【剑指 059】:数据流中的第 K 大
https://leetcode.cn/problems/kth-largest-element-in-a-stream/
提示:
1 <= k <= 10^4
0 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
-10^4 <= val <= 10^4
最多调用 add 方法 104 次
题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素
#方案一:普通排序
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.data=nums #动态数组
self.k=k
#o(n)/o(logn),o(nlogn)
def add(self, val: int) -> int:
self.data.append(val)
self.data.sort()
return self.data[len(self.data)-self.k]
# Your KthLargest object will be instantiated and called as such:
# obj = KthLargest(k, nums)
# param_1 = obj.add(val)
#方案二:插入排序(超时)
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.data=nums #动态数组
self.k=k
self.data.sort()
#o(n)/o(logn),o(nlogn)
def add(self, val: int) -> int:
n=len(self.data)
self.data.append(val)#扩容n+1
#
j=n
while j>=1 and val<self.data[j-1]:
self.data[j]=self.data[j-1]
j-=1
self.data[j]=val
#
return self.data[n+1-self.k]
# Your KthLargest object will be instantiated and called as such:
# obj = KthLargest(k, nums)
# param_1 = obj.add(val)
#方案三:小顶堆
import heapq
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.k=k
self.pq=[] #注:位置(动态维护)
#o(k),o(nlogk)
for num in nums:
self.add(num)
def add(self, val: int) -> int:
if len(self.pq)<self.k:
heapq.heappush(self.pq,val)
else:
heapq.heappushpop(self.pq,val)
return self.pq[0]
# Your KthLargest object will be instantiated and called as such:
# obj = KthLargest(k, nums)
# param_1 = obj.add(val)
lc 295【剑指 41】:数据流的中位数
https://leetcode.cn/problems/find-median-from-data-stream/
进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
#方案一:普通排序
class MedianFinder:
def __init__(self):
self.data=[]
def addNum(self, num: int) -> None:
self.data.append(num)
#o(n)/o(logn),o(nlogn)
def findMedian(self) -> float:
self.data.sort()
n=len(self.data)
if n%2==1:
return self.data[n//2]
else:
return (self.data[n//2-1]+self.data[n//2])/2
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
#方案二:插入排序(超时)
class MedianFinder:
def __init__(self):
self.data=[]
#o(1),o(n)
def addNum(self, num: int) -> None:
n = len(self.data)
self.data.append(num)
j = n
while j >=1 and num < self.data[j - 1]:
self.data[j] = self.data[j - 1]
j -= 1
self.data[j] = num
def findMedian(self) -> float:
n=len(self.data)
if n%2==1:
return self.data[n//2]
else:
return (self.data[n//2-1]+self.data[n//2])/2
# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()
#方案三:大顶堆+小顶堆
class MedianFinder:
def __init__(self):
#key:len(大顶堆)=len(小顶堆)/len(小顶堆)+1
self.min_heap = []
self.max_heap = []
#o(logn)
def addNum(self, num: int) -> None:
#第一轮
if not len(self.max_heap):
heapq.heappush(self.max_heap,以上是关于日常系列LeetCode《11·堆和优先队列篇》的主要内容,如果未能解决你的问题,请参考以下文章
堆和优先级队列3:不泡妹子都要会的LeetCode7道题之一