1. 简介
https://blog.csdn.net/qq_28063811/article/details/93034625/
https://blog.csdn.net/weixin_50941083/article/details/121096435
堆是一种完全二叉树的数据结构。由于完全二叉树的性质,对于每个节点,都能推算出其父节点和左右子节点的索引。因此堆可以用数组来表示。
大顶堆:每个节点的值都大于或者等于它的左右子节点的值。
小顶堆:每个节点的值都小于或者等于它的左右子节点的值。
2. 特点
对于一个完全二叉树上的节点i
,其左右子节点index为2*i+1
和2*i+2
,父节点index为(i-1)//2
假设i
所在层为n
(该层元素个数为$ 2 ^ {n} $,等于其所有上层元素个数和➕1),i
所在层前方有k
个元素。则有 $i = 2^{n} - 1 + k$
对于其左子节点j
, 有 $ j = 2^{n+1} - 1 + 2*k$。所以 $ j = 2 * i + 1 $,右子节点为 $ 2 * i + 2$
同理算出父节点index
最后一个父节点为尾节点的父节点,即为$ (len - 1 - 1) // 2 = len // 2 - 1$
大顶堆堆顶的元素是最大的元素,小顶堆堆顶的元素是最小的元素
堆排序的基本思想是:
将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。
复杂度分析:
交换及重建大顶堆的过程中,需要交换n-1次,重建大顶堆的过程根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为O(nlog(n))
heap_insert
初始化大顶堆时间复杂度为O(nlog(n))。 heapify
则为O(n)
3. 实现 1. heap_insert初始化 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 36 37 38 39 40 41 42 43 from typing import List def heap_sort (arr: List ): for index in range (1 , len (arr)): heap_insert(arr, index) heapsize = len (arr) - 1 while heapsize > 0 : arr[0 ], arr[heapsize] = arr[heapsize], arr[0 ] heapsize -= 1 heapify(arr, heapsize)def heap_insert (arr: List , index: int ): """调整索引为index处, 使得arr[0:index+1]为大顶堆 适用于初始阶段, 调整整个arr成为大顶堆. """ parent_index = (index-1 ) // 2 while parent_index >= 0 and arr[index] > arr[parent_index]: arr[index], arr[parent_index] = arr[parent_index], arr[index] index = parent_index parent_index = (index-1 ) // 2 def heapify (arr: List , index: int ): """对于一个大顶堆, 堆顶和index+1值交换后, 重新调整, 使得arr[0:index+1]为大顶堆。 复杂度为log(n) """ i = 0 while 2 *i+1 <= index: if 2 *i+2 <= index: swap_index = 2 *i+2 if arr[2 *i+2 ] > arr[2 *i+1 ] else 2 *i+1 else : swap_index = 2 *i+1 if arr[i] < arr[swap_index]: arr[i], arr[swap_index] = arr[swap_index], arr[i] i = swap_index else : break
2. heapify初始化 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 36 37 38 from typing import List def heap_sort (arr: List ): build_max_heap(arr) heapsize = len (arr) - 1 while heapsize > 0 : arr[0 ], arr[heapsize] = arr[heapsize], arr[0 ] heapsize -= 1 heapify(arr, 0 , heapsize)def build_max_heap (arr: List ): """从第一个非叶子节点开始,从右至左,从下至上调整子树为大根堆,最终整个堆为大根堆 """ i = len (arr) // 2 - 1 while i >= 0 : heapify(arr, i, len (arr)-1 ) i -= 1 def heapify (arr: List , start: int , index: int ): """重新调整, 使得arr[start:index+1]为大顶堆 """ i = start while 2 *i+1 <= index: if 2 *i+2 <= index: swap_index = 2 *i+2 if arr[2 *i+2 ] > arr[2 *i+1 ] else 2 *i+1 else : swap_index = 2 *i+1 if arr[i] < arr[swap_index]: arr[i], arr[swap_index] = arr[swap_index], arr[i] i = swap_index else : break
4. 解题
https://leetcode.cn/problems/kth-largest-element-in-an-array/
给定整数数组 nums
和整数 k
,请返回数组中第 **k**
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
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 36 37 38 39 40 41 42 def build_max_heap (arr ): """从第一个非叶子节点开始,从右至左,从下至上调整子树为大根堆,最终整个堆为大根堆 """ i = len (arr) // 2 - 1 while i >= 0 : heapify(arr, i, len (arr)-1 ) i -= 1 def heapify (arr, start, index ): """重新调整, 使得arr[start:index+1]为大顶堆 """ i = start while 2 *i+1 <= index: if 2 *i+2 <= index: swap_index = 2 *i+2 if arr[2 *i+2 ] > arr[2 *i+1 ] else 2 *i+1 else : swap_index = 2 *i+1 if arr[i] < arr[swap_index]: arr[i], arr[swap_index] = arr[swap_index], arr[i] i = swap_index else : break class Solution (object ): def findKthLargest (self, nums, k ): """ :type nums: List[int] :type k: int :rtype: int """ build_max_heap(nums) heapsize = len (nums) - 1 idx = k while heapsize > len (nums) - k - 1 : nums[0 ], nums[heapsize] = nums[heapsize], nums[0 ] heapsize -= 1 heapify(nums, 0 , heapsize) return nums[-k]