215. 数组中的第k个最大元素
215. 数组中的第K个最大元素 - 力扣(LeetCode)
python
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
# 就是堆排序的过程:堆排序第一轮以后,会把最大值推到堆顶[0],然后开始下沉到每一个元素正确排序
# 这里求第k大的数,所以这里的下沉仅仅是往下走k-1个元素,此时的塔尖就是最大的元素
n = len(nums)
# 最后一个非叶子节点是n//2-1
for i in range(n//2-1,-1,-1):
self.heapify(nums,n,i)
for i in range(n-1,n-k,-1):
nums[0],nums[i] = nums[i],nums[0]
self.heapify(nums,i,0)
return nums[0]
def heapify(self,nums,n,i):
cur = i
left = 2*i+1
right = 2*i+2
if left<n and nums[cur]<nums[left]:
cur = left
if right<n and nums[cur]<nums[right]:
cur = right
if cur != i:
nums[i],nums[cur] = nums[cur],nums[i]
self.heapify(nums,n,cur)
时间复杂度:O(nlogn) # 本质就是堆排序
空间复杂度:O(logn) # 栈空间
利用堆
python
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
min_heaq = []
for n in nums:
heapq.heappush(min_heaq,n) # 默认最小堆
if len(min_heaq)>k:
# 弹掉堆顶最小值
heapq.heappop(min_heaq)
return min_heaq[0]
快排
python
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
# 在快排中,每找到一个基准元素的位置i,则前i个元素都比nums[i]小,后n-i-1个元素比nums[i]大
# 如果要找第k个最大元素,仅仅令n-i-1=k-1,则此时的基准元素就是我们的目标值
# 则令i=n-k
# n-k就是我们的目标下标
# 如果当前基准元素的下标比我们的目标下标来的小,那么我们就去右区间找,否则就去左区间
n = len(nums)
return self.quicksort(nums,0,n-1,n-k)
def quicksort(self,nums,left,right,target):
if left==right:
return nums[left]
pivot = nums[left]
i,j = left,right
while i<j:
# 找到右侧第一小的元素
while i<j and nums[j]>=pivot: j-=1
# 找到左侧第一大的元素
while i<j and nums[i]<=pivot: i+=1
if i<j:
nums[i],nums[j] = nums[j],nums[i]
# i=j
nums[left],nums[i] = nums[i],nums[left]
if i==target:
return nums[i]
elif i>target:
return self.quicksort(nums,left,i-1,target)
else:
return self.quicksort(nums,i+1,right,target)
时间复杂度:O(n) # 不是很理解,但是这个在leetcode上时间会超时,必须按照官方的一个特殊的边界条件去写,利用=的判断,写do-while循环,才可以通过全是1的那个示例
空间复杂度:O(logn) # 栈空间
python
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
# 在快排中,每找到一个基准元素的位置i,则前i个元素都比nums[i]小,后n-i-1个元素比nums[i]大
# 如果要找第k个最大元素,仅仅令n-i-1=k-1,则此时的基准元素就是我们的目标值
# 则令i=n-k
# n-k就是我们的目标下标
# 如果当前基准元素的下标比我们的目标下标来的小,那么我们就去右区间找,否则就去左区间
n = len(nums)
return self.quicksort(nums,0,n-1,n-k)
def quicksort(self,nums,left,right,target):
if left==right:
return nums[left]
pivot = nums[left]
i,j = left-1,right+1
while i<j:
# 找到右侧第一小的元素
j-=1
while i<j and nums[j]>pivot: j-=1
# 找到左侧第一大的元素
i+=1
while i<j and nums[i]<pivot: i+=1
if i<j:
nums[i],nums[j] = nums[j],nums[i]
# Hoare 划分的核心差异:结束时,数组被切分为 [left, j] 和 [j+1, right]
# 注意:这里 j 这个位置上的元素不一定恰好就是 pivot,但它完美地划清了界限!
if j>=target:
return self.quicksort(nums,left,j,target)
else:
return self.quicksort(nums,j+1,right,target)
347. 前k个高频元素
python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
# 简单思路:先求出频率字典,然后排序,取k个
# 如果直接使用sorted,那么其实本质用的是快排,所以时间复杂度就是nlog(n)
d = {}
for i in nums:
d[i] = d.get(i,0)+1
d_list = sorted(d.items(),key = lambda x:x[1],reverse = True)
res = []
for i in range(k):
res.append(d_list[i][0])
return res
时间复杂度:O(nlogn)
空间复杂度:O(n)
但是其实没必要存储所有的元素,我们仅仅需要存储前k个高频元素,利用堆的思想。
python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
d = {}
for i in nums:
d[i] = d.get(i,0)+1
min_heaq = []
for key,value in d.items():
heapq.heappush(min_heaq,(value,key)) # 按照频率排序
if len(min_heaq)>k:
heapq.heappop(min_heaq)
res = [0]*k
# 按照高频到低频排序
for i in range(k-1,-1,-1):
res[i] = heapq.heappop(min_heaq)[1]
return res
时间复杂度:O(nlogn)
空间复杂度:O(n) # 因为字典还是需要O(n),heapq需要O(k)
295. 数据流的中位数
python
class MedianFinder(object):
# 其实维护两个堆就可以
# 一个存小于等于中位数的元素【大顶堆】【默认是小顶堆,所以存负数】
# 一个存大于中位数的元素【小顶堆】【上面的比下面的元素个数最多多1】
def __init__(self):
self.min_heaq=[]
self.max_heaq=[]
def addNum(self, num):
"""
:type num: int
:rtype: None
"""
if not self.min_heaq or num<=-self.min_heaq[0]:
heapq.heappush(self.min_heaq,-num)
if len(self.min_heaq)>len(self.max_heaq)+1:
heapq.heappush(self.max_heaq,-heapq.heappop(self.min_heaq))
else:
heapq.heappush(self.max_heaq,num)
if len(self.min_heaq)<len(self.max_heaq):
heapq.heappush(self.min_heaq,-heapq.heappop(self.max_heaq))
def findMedian(self):
"""
:rtype: float
"""
if len(self.min_heaq)==len(self.max_heaq):
return (-self.min_heaq[0]+self.max_heaq[0])/2.0
else:
return -self.min_heaq[0]
时间复杂度:
- addNum: O(logn) # n 为累计添加的数的数量,每增加一个元素,仅仅调整一次堆,所以是logn
- findMedian: O(1)
空间复杂度:O(n) # 两个堆的开销