堆排序

堆排序描述

堆排序是利用堆这种数据结构所设计的一种排序算法。堆实际上是一个完全二叉树结构

问:那么什么是完全二叉树呢?答:假设一个二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树

image-20210911103831357

我们知道堆是一个完全二叉树了,那么堆又分两种堆:大顶堆小顶堆它们符合一个重要的性质:

  • 小顶堆满足:Key[i] <= key[2i + 1] && Key[i] <= key[2i + 2] (父节点小于子节点)
  • 大顶堆满足:Key[i] >= key[2i + 1] && Key[i] >= Key[2i + 2] (父节点大于子节点)

大顶堆最大的元素在跟节点,堆的性质决定了大顶堆中节点一定大于等于其子节点,反之,小顶堆的最小元素在根节点。我们来看看大顶堆和小顶堆的示意图:

image-20210911104452575

堆排序基本思想及步骤

堆排序有以下几个核心的步骤:

  1. 将待排序的数组初始化为大顶堆,该过程即建堆。
  2. 将堆顶元素与最后一个元素进行交换,除去最后一个元素外可以组建为一个新的大顶堆。(获得当前最大的元素
  3. 将第二步堆顶元素跟最后一个元素交换后,新建立的堆不是大顶堆,需要重新建立大顶堆。重复上面的流程,直到堆中仅剩下一个元素。

若二叉树的节点数量为l,则二叉树中,最后一个非叶子节点为l//2 -1

建堆代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution(object):
def heap_sort(self,nums):
i,l = 0,len(nums)
self.nums = nums

# 构造大顶堆,从非叶子节点开始倒序遍历,因此是 l//2 -1 就是最后一个非叶子节点
for i in range(l//2 -1, -1, -1):
self.build_heap(i,l-1)
print(self.nums)
print("--")
# 上面的循环完成了大顶堆的构造,那么就开始把根节点跟末尾节点交换,然后重新调整大顶堆
for j in range(l-1, -1, -1):
nums[0],nums[j] = nums[j],nums[0]
self.build_heap(0,j-1) # 从后向前依次调整堆
return nums

def build_heap(self,i,l):
'''
构建大顶堆
'''
nums = self.nums
left,right = 2 * i +1, 2 * i + 2 # 左右子节点的下标
large_index =i # 父节点下标

if left <= l and nums[i] < nums[left]: # 如果父节点比左孩子小,记录左孩子
large_index = left

if right <= l and nums[left] < nums[right]: # 如果左孩子比右孩子小,记录右孩子
large_index = right

# 通过上面跟左右节点比较后,得出三个元素之间较大的下标,
# 如果较大下表不是父节点的下标,说明交换后需要重新调整大顶堆
if large_index != i:
nums[i], nums[large_index] = nums[large_index], nums[i]
self.build_heap(large_index,l)

输入:arr = [4, 6, 7, 2, 9, 8, 3, 5],输出:

image-20210911144037905

347. 前 K 个高频元素

描述

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

1
2
3
4
5
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

输入: nums = [1], k = 1
输出: [1]

解法

参考评论区:347. 前 K 个高频元素,直接排序 +堆排序 + 快速排序,Python4种 + Java2种解法~ - 前 K 个高频元素 - 力扣(LeetCode) (leetcode-cn.com)

方法一:直接排序
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)
1
2
3
4
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count = collections.Counter(nums)
return [item[0] for item in count.most_common(k)]
方法二:堆排序1
  1. 记录每个数字出现的次数
  2. 把数字和对应出现次数放入堆中
  3. 返回堆的前k大元素
1
2
3
4
5
6
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count = collections.Counter(nums)
heap = [(val, key) for key, val in count.items()]
return [item[1] for item in heapq.nlargest(k, heap)]

方法三:堆排序2
  1. 记录每个数字出现的次数
  2. 把数字和对应的出现次数放到堆中(小顶堆)
  3. 如果堆已满(大小>=k)且当前数的次数比堆顶大,用当前元素替换堆顶元素
  4. 返回堆中的数字部分
1
2
3
4
5
6
7
8
9
10
11
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count = collections.Counter(nums)
heap = []
for key, val in count.items():
if len(heap) >= k:
if val > heap[0][0]:
heapq.heapreplace(heap, (val, key))
else:
heapq.heappush(heap, (val, key))
return [item[1] for item in heap]

NC119 最小的K个数

描述

给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

  • 0 <= k <= input.length <= 10000
  • 0 <= input[i] <= 10000

解法:堆排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14

class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
# print(tinput)
import heapq
heapq.heapify(tinput) # 默认是小根堆
res = []

for i in range(k):
t = heapq.heappop(tinput)
res.append(t)

return res

求前k个最大的数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Solution:
def getMaxNumbers_Solution(self,tinput,k):
'''
获取前k个大的数
'''
import heapq

neg_tinput = [-1*t for t in tinput]
print(neg_tinput)
heapq.heapify(neg_tinput)
res = []

for i in range(k):
t = heapq.heappop(neg_tinput)
res.append(t*-1)
return res

215. 数组中的第K个最大元素

描述

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例:

1
2
3
4
5
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

解法:堆排序

python默认是小顶堆,需要调整一下

1
2
3
4
5
6
7
8
9
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
import heapq
nums = [-1*t for t in nums]
heapq.heapify(nums)
res = 0
for _ in range(k):
res = heapq.heappop(nums)
return -1*res

692. 前K个高频单词

描述

给一非空的单词列表,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

解法:重写Python比较方法

参考评论区

1

作者

bd160jbgm

发布于

2021-09-10

更新于

2021-09-27

许可协议