Python算法实现(二)—— 堆排序算法

二叉堆

二叉堆本质上是一种完全二叉树,它分为两个类型:最大堆和最小堆。

  1. 最大堆:最大堆任何一个父节点的值,都大于等于它左右孩子节点的值
  2. 最小堆:最小堆任何一个父节点的值,都小于等于它左右孩子节点的值。

二叉堆的根节点叫做堆顶,最大堆的堆顶是最大元素,最小堆的堆顶是最小元素。

堆的自我调整

插入节点

我们首先有一个最大堆,我们希望给这个堆插入一个元素,我们首先直接将这个新元素放置到堆的最下部,此时发现最下面的子堆不满足最大堆的定义,依次向上调整:首先交换80和45,然后交换80和72,最终满足条件,此时插入节点完毕。

Python算法实现(二)—— 堆排序算法_第1张图片

删除节点

首先我们要删除最小堆(如下图)的最顶端元素 0,我们首先将 0 与 3 进行交换,将最下面的元素交换到最上面。
Python算法实现(二)—— 堆排序算法_第2张图片
交换结果如下图,此时发现最上面的子堆不满足最小堆的定义:

Python算法实现(二)—— 堆排序算法_第3张图片
交换元素 1 和 3:

Python算法实现(二)—— 堆排序算法_第4张图片
一直向下调整,直到满足所有子堆最小堆的要求,得到最终结果:

Python算法实现(二)—— 堆排序算法_第5张图片

构建二叉堆

构建二叉堆也就是将一个无序的二叉树调整为二叉堆,本质上就是让所偶非叶子节点依次下沉,有两种方式——自下而上和自上而下。

自下而上就是首先看有叶子节点的最后一个堆,将其调整为一个最小堆(或最大堆),实际上就是和删除节点或插入节点时有类似的步骤,大家可以自己推一推。

堆的实现

二叉堆虽然是一棵完全二叉树,但它的存储方式并不是链式存储,二十顺序存储,也就是说二叉堆的所有节点都存储在数组当中。

我们经过观察很容易发现,所有子堆的子节点和父节点满足 {2n+1&2n+2 : n} 的关系,因此我们可以很容易得到一个节点的子节点或者父节点。因此数组和二叉树可以完全一一对应。

堆排序

思想:我们删除一个最大堆的堆顶(替换到最后面),经过自我调整,第二大的元素就会被交换上来,成为最大堆的新堆顶。
步骤

  1. 把无序数组构建成二叉堆(时间复杂度为 O ( n ) O(n) O(n)
  2. 循环删除对顶元素,移到集合尾部,调节堆产生新的堆顶(时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

堆排序的应用

我们还是看到 Leetcode 的 215 题:
Leetcode
我们直接看代码:

class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        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)

你可能感兴趣的:(python算法实现,数据结构,算法,二叉树,堆排序,其他)