深度之眼讲解笔记——堆排序

堆排序

深度之眼讲解笔记:

在这里插入图片描述

二叉堆

在这里插入图片描述

堆的自我调整

插入节点:

在这里插入图片描述
以最小堆为例子,不满足规则的向下调整。插入0,比较0和5,不符合最小堆的规则,交换0和5,在0,3,6中判断,不满足规则,交换0和3,检查3和5,满足不用交换。在1,0,2,中判断,不满足规则,交换1和0.检查1,6,3,满足规则不交换。

删除节点:

在这里插入图片描述
从堆中删除节点一般是pop出堆顶,即弹出堆顶数。
在这里插入图片描述
交换1和10,删除末尾的1,(其实不需要管末尾这个数字就好)
在这里插入图片描述
对比10,2,3,将10与最小的数2交换(若与3交换,2和3依旧不满足),对比10和7,8,与最小的7 做交换。

构建二叉堆:

在这里插入图片描述
两种常见方法:自下而上,自上而下。这里讲最常见的自下而上操作。
在这里插入图片描述
首先看最下面的树,对比10,6,9,第一个堆顶是10,将10与6交换,10前一个数是3,对比3,2,8,交换3与2。再看1,6,5满足,不变。看7,1,3,交换1和7.
在这里插入图片描述
7交换下后进行检查,7,6,5不满足最小堆定义,向下调整。交换7和5.
在这里插入图片描述

堆的代码实现

在这里插入图片描述
数组存储:根据下标关系
当下标是从0开始:0,1,2……
比如父节点6,下标n为3,子节点9和10下标分别为7,8。关系为2n+1,2n+2.
知道子节点下标求父节点:-1或者-2后除以二向下取整。例如:子节点下标为n,则父节点为
在这里插入图片描述
当下标从1开始,1,2,3,……
则下标关系变化为:得到父节点下标n,子节点为2n, 2n+1
相应求父节点下标也要变化。

堆排序

在这里插入图片描述
最大堆,删除10时,跟2交换,排序。

复杂度

在这里插入图片描述

代码实现堆排序

方法1:

参考链接:https://www.jianshu.com/p/d174f1862601

from collections import deque

def swap_param(L, i, j):
    L[i], L[j] = L[j], L[i]
    return L

def heap_adjust(L, start, end):
    temp = L[start]

    i = start
    j = 2 * i

    while j <= end:
        if (j < end) and (L[j] < L[j + 1]):
            j += 1
        if temp < L[j]:
            L[i] = L[j]
            i = j
            j = 2 * i
        else:
            break
    L[i] = temp


def heap_sort(L):
    L_length = len(L) - 1 # 10-9=1

    first_sort_count = int(L_length / 2) # 4
    for i in range(first_sort_count): # 0,1,2,3
        heap_adjust(L, first_sort_count - i, L_length)

    for i in range(L_length - 1):
        L = swap_param(L, 1, L_length - i)
        heap_adjust(L, 1, L_length - i - 1)

    return [L[i] for i in range(1, len(L))]


def main():
    L = deque([50, 16, 30, 10, 60,  90,  2, 80, 70])
    L.appendleft(0)
    print(heap_sort(L))


if __name__ == '__main__':
    main()

方法2:见下题

LeetCode215题目:数组中第k个最大元素

题目:

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

示例 1:

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

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

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array

class Solution:
    # def __init__(self):

    def findKthLargest(self, nums, k):
        # # 快速排序
        # self._k = len(nums) - k
        # 10 - 2 = 8 求第2大的话就是下标为8(下标从0开始)
        # return self.quicksort(nums, 0, len(nums)-1)
        # 堆排序
        self._k = len(nums) - k # 下标为8
        return self.heapsort(nums)
    def heapsort(self, nums):
        self.build_heap(nums)
        for i in range(len(nums)-1, self._k-1, -1): # range(9, 7, -1) # 即9,8
            nums[i], nums[0] = nums[0], nums[i]
            #这里排除最后一个元素
            self.max_heapify(nums, 0, i)
            # 0的位置是父节点下标,i的位置是排序的总长度
        # 循环结束后,下标8存储倒数第2大元素
        return nums[self._k] # 返回下标为8的元素

    def build_heap(self, nums):
        length = len(nums) # 长度是10
        for i in range((length-1)//2, -1, -1): # 最后一个-1为倒序意思
            # 节点的父节点是(长度-1)/2向下取整
            # range(10,0,-1)意思是从10倒序,取到不到0的数。即10-1
            # 这里就是循环从最后一个父节点(length-1)//2到第一个父节点0
            # 建堆:从最后一个节点的父节点开始向上调整:
            # 最后一个父节点下标为(length-1)//2
            self.max_heapify(nums, i, length) # i是父节点下标

    # 调整操作
    def max_heapify(self, nums, i, length):  # 最大堆 # 是父节点下标
        left = 2 * i + 1  # 左孩子
        right = 2 * i + 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))
发布了6 篇原创文章 · 获赞 0 · 访问量 62

猜你喜欢

转载自blog.csdn.net/qq_35493320/article/details/104086242