一、堆排序
堆实际上就是一个完全二叉堆,它分为两个类型:
1、最大堆:任意的父节点的值都大于等于它左右孩子节点的值。
2、最小堆:任意的父节点的值都小于等于它左右孩子节点的值。
二叉堆的根节点叫做堆顶。
根据最大堆和最小堆的特点,可以得出最大堆的顶点是整个堆中的最大元素,最小堆中的顶点是整个堆中最小的元素。
最大堆满足:parent >= 2*parent+1 (左节点) && parent >= 2*parent+2(右节点)
最小堆满足:parent <= 2*parent+1(左节点)&& parent <= 2*parent+2 (右节点)
最大堆:参照下图大顶堆
索引: | 0 | parent | 2 | 3 | 4 | 2*parent+1 | 2*parent+2 |
数列: | 9 | 6 | 8 | 3 | 4 | 5 | 3 |
最小堆:参照下图小顶堆
索引: | 0 | parent | 2 | 2*parent+1 | 2*parent+2 | 5 |
数列: | 4 | 6 | 5 | 7 | 9 | 8 |
堆的自我调整:如何从一个无序的序列转换成上述的最大堆和最小堆的过程。
下面转自:图解排序算法
- 给定一个无序列表结构如下:将其转换成最大堆:(一般升序采用最大堆,降序采用最小堆)
- 从最后一个非叶子节点开始(就是图中红色的6),从左到右,从上到下依次进行调整,调整的规则就是按照:最大堆满足的特点
最大堆满足:parent >= 2*parent+1 (左节点) && parent >= 2*parent+2(右节点)
- 然后继续找到第二个非叶子节点4,选择最大的元素进行交换,因此9和4进行调换,有下面结果
- 4和9交换后,会导致下面的4,5,6不满足最大堆的条件,因此继续按照规则进行调整;将4和6进行交换;
此时就可以将最初无序的列表变成一个最大堆结构:
再将堆顶元素与末尾元素进行交换,使得末尾元素最大,然后继续调整堆,再将堆顶元素与末尾元素进行交换,反复重复这个操作;
- 将堆顶元素9与末尾元素4进行交换,就把9从树中踢出。
- 重新按照最大堆的定义对结构进行调整;
- 再将顶点元素8与末尾元素5进行交换,就得到第二大元素8
- 继续重复上述操作,最后得到一个有序序列
代码实现:题目来源于leetcode:215
在未排序的数组中找到第k个最大的元素,注意,你需要找的是数组排序后的第k个最大的元素,而不是第k个
不同的元素。
例如:输入:[3,2,1,5,6,4] 和 k=2
输出:5
# 利用堆排序进行求解
class Solution(object):
def findKthLargest(self,nums,k):
# 堆排序
self._k = len(nums)-k
return self.heapsort(nums)
# 执行堆排序
def heapsort(self,nums):
self.build_heap(nums)
for i in range(len(nums)-1,self._k-1,-1):
nums[i],nums[0] = nums[0],nums[i]
# 这里要排除最后一个元素
self.max_heapify(nums,0,i)
return nums[self._k]
def build_heap(self,nums):
length = len(nums)
for i in range(((length-1) // 2),-1,-1):
# 从最后一个节点的父节点开始向上调整
self.max_heapify(nums,i,length)
def max_heapify(self,nums,i,length):
left = i*2 + 1
right = i*2 + 2
if left < length and nums[left] > nums[i]:
largest = left
else:
largest = i
if right < length and nums[right] > nums[largest]:
largest = right
if largest != i:
nums[i],nums[largest] = nums[largest],nums[i]
# 子树也需要按照最大堆规则进行调整
self.max_heapify(nums,largest,length)
if __name__ == '__main__':
s = Solution()
print(s.findKthLargest([3,2,3,1,2,4,5,5,6],4))
另一种简洁做法:
class Solution:
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
return heapq.nlargest(k, nums)[-1]