堆排序分为2个步骤
首先是构建大顶锥,然后从数组尾部开始便利,把大顶锥的顶点和尾部交换,就得到了一个由小到大的数组。
三分钟玩转堆排序原理及面试题(多图解释 Python实现)_码农富哥-CSDN博客
LeetCode—Python—215. 数组中的第K个最大元素(快排、堆排序)_IOT_victor的博客-CSDN博客
堆排序的两种方法实现(Python)以及面试中关于堆排序的相关题目_Matrix_cc的博客-CSDN博客
手写堆
前k个高频元素
堆排序的两种方法实现(Python)以及面试中关于堆排序的相关题目_Matrix_cc的博客-CSDN博客
class Solution(object):
import heapq
def push(self,value_key,min_heap,k):
if len(min_heap)<k:
#构造大小为k的堆
heapq.heappush(min_heap,value_key)
else:
if value_key[0]<min_heap[0][0]:
pass
else:
#pop出堆顶元素
min_value,min_key = heapq.heappop(min_heap)
#push进当前元素
heapq.heappush(min_heap,value_key)
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#分为2步,
#首先存成一个map,然后建立一个大小为k的堆,堆排序时间复杂度是o(nlogn)
#第一步:哈希表o(n)
dic={}
for i in range(len(nums)):
if nums[i] in dic:
dic[nums[i]]+=1
else:
dic[nums[i]]=1
#第二步:堆排序o(nlogn)。
#先把前k个元素放进去,然后后面的元素依次与堆顶元素进行比较
min_heap=[]
for key in dic.keys():
value=dic[key]
self.push((value,key),min_heap,k)
#取出来k
res=[]
for i in range(k):
res.append(min_heap[i][1])
return res
数据流中的中位数
手写堆(我)
参考
堆排序的两种方法实现(Python)以及面试中关于堆排序的相关题目_Matrix_cc的博客-CSDN博客
def shiftdown(nums,root,n):
while 2*root+1<n:
child=2*root+1
if child+1<n and nums[child+1]>nums[child]:
child=child+1
if nums[root]>=nums[child]:
break
nums[root],nums[child]=nums[child],nums[root]
root=child
def heapify(nums):
n=len(nums)
for i in range(n//2,-1,-1):
shiftdown(nums,i,n)
def heapsort(nums):
heapify(nums)
n=len(nums)
for i in range(n-1):
nums[0],nums[n-i-1]=nums[n-i-1],nums[0]
shiftdown(nums,0,n-i-1)
nums=[5,2,3,1,8]
heapsort(nums)
print(nums)
会议室
347 前k个高频元素
class Solution(object):
def push(self,val,minheap,k):
#用python的heap包构建堆
#如果堆列表的长度小于k,直接将值插入列表
if len(minheap)<k:
heapq.heappush(minheap,val)
else:
#如果堆列表长度大于k,如果val小于锥顶元素,跳过(肯定不是前k大)
if val[0]<=minheap[0][0]:
pass
else:
heapq.heapreplace(minheap, val)
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
# minheap=[]
# for i in range(len(nums)):
# self.push(nums[i],minheap,k)
#minheap就是前k大
dic={}
for i in range(len(nums)):
dic[nums[i]]=dic.get(nums[i],0)+1
minheap=[]
for key in dic:
# self.push((key,dic[key]),minheap,k)
self.push((dic[key],key),minheap,k)
res=[]
for i in range(k):
res.append(minheap[i][1])
return res
class Solution(object):
import heapq
def push(self,value_key,min_heap,k):
if len(min_heap)<k:
#构造大小为k的堆
heapq.heappush(min_heap,value_key)
else:
if value_key[0]<min_heap[0][0]:
pass
else:
#pop出堆顶元素
min_value,min_key = heapq.heappop(min_heap)
#push进当前元素
heapq.heappush(min_heap,value_key)
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#分为2步,
#首先存成一个map,然后建立一个大小为k的堆,堆排序时间复杂度是o(nlogn)
#第一步:哈希表o(n)
dic={}
for i in range(len(nums)):
if nums[i] in dic:
dic[nums[i]]+=1
else:
dic[nums[i]]=1
#第二步:堆排序o(nlogn)。
#先把前k个元素放进去,然后后面的元素依次与堆顶元素进行比较
min_heap=[]
for key in dic.keys():
value=dic[key]
self.push((value,key),min_heap,k)
#取出来k
res=[]
for i in range(k):
res.append(min_heap[i][1])
return res
295. 数据流的中位数
import heapq
class MedianFinder(object):
def __init__(self):
"""
initialize your data structure here.
"""
#用一个最大堆,一个最小堆。
#最大堆表示排序之后的list左边,最小堆表示排序之后的list右边。因为我们需要的是堆顶元素。
self.maxheap=[]
self.minheap=[]
#python heap表示的是最小堆,取个负数就是最大堆了。
def addNum(self, num):
"""
:type num: int
:rtype: None
"""
#先把元素添加到最大堆里面(最大堆是左半部分)
heapq.heappush(self.maxheap,-num)
#把最大堆中的最大元素添加到最小堆中
#先从最大堆中弹出堆顶元素
max_heap_top=heapq.heappop(self.maxheap)
#把最大堆中的堆顶元素加入最小堆
heapq.heappush(self.minheap,-max_heap_top)
#如果最小堆中的元素比最大堆多,则把最小堆中的最小元素移动到最大堆
if len(self.minheap)>len(self.maxheap):
min_heap_top = heapq.heappop(self.minheap)
heapq.heappush(self.maxheap, -min_heap_top)
def findMedian(self):
"""
:rtype: float
"""
if len(self.maxheap)>len(self.minheap):
return - self.maxheap[0]
else:
return (-self.maxheap[0]+self.minheap[0])/2
obj = MedianFinder()
obj.addNum(1)
obj.addNum(2)
param_2 = obj.findMedian()
print(param_2)
215 数组中第k大元素
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
#最大堆遍历到第k个
#用最小堆来代替(对num取负数)
maxheap=[]
for i in range(len(nums)):
heapq.heappush(maxheap,-nums[i])
for i in range(k):
maxval=heapq.heappop(maxheap)
return -maxval
253 会议室
23 合并k个有序链表
# 思路:每个链表的第一个结点入堆,进行比较
# 最小的出堆,然后找出堆链表中的后续入堆
# 在python3的堆中链表无法比较大小,我们传入的是val和他所在的list(这是第几个链表)方便查找后续
from heapq import *
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
minHeap = []
for index, node in enumerate(lists): # index 用来记录这是第几个链表, node 是每个链表的头结点
if node != None: # 只要头结点不空
heappush(minHeap ,(node.val, index)) # 将当前头结点的value以及该头结点所在的链表index入堆
linkedlistHead = ListNode(-1) # 创建用来存放结果的链表
linkedlistTail = linkedlistHead # 这个是尾结点
while minHeap:
val, index = heappop(minHeap) # 堆内链表value最小的出堆,找到他是第几个链表,好找他的下一个位置
linkedlistTail.next = lists[index] # 加入到了结果集合中
linkedlistTail = linkedlistTail.next # 开始加下一个位置
lists[index] = lists[index].next # 找到当前链表的下一个元素
if lists[index] != None: # 如果不空的话就让他入堆
heappush(minHeap, (lists[index].val, index)) # 下一个元素入堆
return linkedlistHead.next # 返回链表
作者:Fight_for_your_life
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/lc23-he-bing-kge-sheng-xu-lian-biao-by-fight_for_y/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
import heapq
dummy = ListNode(0)
p = dummy
head = []
for i in range(len(lists)):
if lists[i] :
heapq.heappush(head, (lists[i].val, i))
lists[i] = lists[i].next
while head:
val, idx = heapq.heappop(head)
p.next = ListNode(val)
p = p.next
if lists[idx]:
heapq.heappush(head, (lists[idx].val, idx))
lists[idx] = lists[idx].next
return dummy.next
373. 查找和最小的K对数字
class Solution:
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
"""
题目要求:从两个有序数组中每次选出一个元素,组成数对,返回两者相加之和前k个最小的数对并返回
:param nums1: 第一个有序的数组
:param nums2: 第二个有序的数组
:param k: 前k个最小的数对
:return: 返回前k小的每一个数对
"""
# 解题思路:暴力法很简单了,把所有的数对都找到并排序,然后返回TopK即可(可能会超时,你可以试试)
# 显然本题并不是想让我们用暴力法解决,通过常识和观察不难发现,最小的数对一定为(num1[0],num2[0]),第二小的必为(num1[0],num2[1])和(num1[1],num2[0])其中一个
# 依此类推,可以发现规律,对于当前选择的最小数对(num1[i],num2[j]),其下一个最小的数对必为(num1[i],num2[j+1])和(num1[i+1],num2[j])其中一个
# 接着就很容易形成一个基本的思路,用一个最小堆维护待选择的数对(以相加之和为排序准则)
# 每次选出当前最小数对(num1[i],num2[j])的同时将(num1[i],num2[j+1])和(num1[i+1],num2[j])一起进堆,直到拿到k个最小数对为止
# 但要注意,这个基本思路存在一个缺陷,就是可能会有重复进堆的数对
# 例如:当前最小数对为(num1[0],num2[1]),此时(num1[1],num2[1])和(num1[0],num2[2])进堆
# 接着下一个最小的数对若为(num1[1],num2[0]),此时此时(num1[1],num2[1])和(num1[2],num2[0])进堆,可以发现(num1[1],num2[1])重复进堆
# 显然会影响答案的准确性,至于如何处理有两种思路,最简单且容易理解的就是设置一个标记集合,标记数对是否进过堆,没进过才进堆
# 此思路对应解答如下:
n, m = len(nums1), len(nums2)
res = [] # 返回结果的列表
# 初始化小根堆,并将(num1[0]+num2[0],0,0)进堆(保留两者的下标,方便后面操作),默认以第一个元素大小为排序准则(即这里的两者之和)
# 当然也可以自定义排序规则(有兴趣可以了解,不展开,多啰嗦一句,一些特殊的题可能必须要自定义排序规则,不过很少见,你可以忽略)
heap = [(nums1[0] + nums2[0], 0, 0)]
visited = set() # 标记集合
while heap and len(res) < k:
_, i, j = heapq.heappop(heap)
if (i, j) in visited: # 进过堆直接跳过
continue
visited.add((i, j)) # 标记该位置进过堆
res.append([nums1[i], nums2[j]]) # 记录当前最小数对
# i, j没有越界,就将(num1[i],num2[j+1])和(num1[i+1],num2[j])一起进堆
if i + 1 < n:
heapq.heappush(heap, (nums1[i + 1] + nums2[j], i + 1, j))
if j + 1 < m:
heapq.heappush(heap, (nums1[i] + nums2[j + 1], i, j + 1))
return res # 返回结果
# 另一种非常巧妙的思路去解决重复进堆的问题,不如第一种思路那么直接,但是时间空间上要比第一种思路略好(可以击败100%,但总体上是同一量级上的)
# 我们将两个数组看作一个二维表格更好理解(自己画画看),比当前数对小的必然是它左侧或者下侧的其中一个(品一品应该很简单)
# 在分析为什么会出现重复进堆的时候,我们发现重复是因为选择下一个最小数对时有左和下两种选择,如果只有一个方向的选择的话,不就不会重复了么
# 这个思路主要是从这个角度出发思考问题,可以将num1的前k个索引数对(0,0),(1,0),…,(k−1,0)直接一起进堆
# 每次从堆中取出数对(num1[i],num2[j])时,只需要将num2索引j增加即可,这样就避免了重复加入元素的问题。
# n, m = len(nums1), len(nums2)
# res = []
# # 初始化小根堆,将num1的前k个索引数对全部进堆(注意num1长度不够k的话,就取其长度即可)
# heap = [(nums1[i] + nums2[0], i, 0) for i in range(min(n, k))]
# while heap and len(res) < k:
# _, i, j = heapq.heappop(heap)
# res.append([nums1[i], nums2[j]])
# # j不越界,只需要将(num1[i],num2[j+1])进堆即可
# if j + 1 < m:
# heapq.heappush(heap, (nums1[i] + nums2[j + 1], i, j + 1))
# return res
作者:sqsx
链接:https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/solution/dui-you-xian-dui-lie-de-ying-yong-liang-5v66b/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
元祖可以直接比较大小啊,用第一个元素去比较
这个题也是广搜, 队列不需要遍历完就能出结果就是广搜。
除了接鸡蛋这种判断大小的动态规划,其他的动态规划都可以用记忆化深搜解决
dfs就是递归,bfs就是循环。
class Solution(object):
def kSmallestPairs(self, nums1, nums2, k):
"""
:type nums1: List[int]
:type nums2: List[int]
:type k: int
:rtype: List[List[int]]
"""
#堆排序+广度优先遍历
import heapq
deq=[]
heapq.heappush(deq,(nums1[0]+nums2[0],0,0))
count=0
visited=set()
visited.add((0,0))
res=[]
while count<k and deq:
#弹出堆顶最小元素
_,i,j=heapq.heappop(deq)
res.append([nums1[i],nums2[j]])
count=count+1
#下一组
if i+1<len(nums1) and j<len(nums2) and (i+1,j) not in visited:
heapq.heappush(deq,(nums1[i+1]+nums2[j],i+1,j))
visited.add((i+1,j))
if i<len(nums1) and j+1<len(nums2) and (i,j+1) not in visited:
heapq.heappush(deq,(nums1[i]+nums2[j+1],i,j+1))
visited.add((i,j+1))
return res
6155. 找出数组的第 K 大和
贪心+子序列+堆
class Solution:
def kSum(self, nums: List[int], k: int) -> int:
n = len(nums)
maxSum = 0
for i in range(n):
if nums[i] > 0:
maxSum += nums[i]
nums[i] = abs(nums[i])
nums.sort()
import heapq
heap = []
heapq.heappush(heap, (0, 0))
for i in range(k - 1):
x, y = heapq.heappop(heap)
if y != n:
heapq.heappush(heap, (x + nums[y], y + 1))
if y:
heapq.heappush(heap, (x + nums[y] - nums[y - 1], y + 1))
x, y = heapq.heappop(heap)
return maxSum - x
作者:worcester_
链接:https://leetcode.cn/problems/find-the-k-sum-of-an-array/solution/python3zhuan-hua-wei-di-kxiao-zi-xu-lie-cye84/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。