大小顶堆的堆排序及堆的代码实现 Marathon

1.大顶堆/大根堆-排序

python

# 堆排序-大顶堆-升序排列

def bigHeapify(arr: [int], start: int, end: int) -> None:
    """向下调整,堆化,父节点与子节点大的比较并交换"""
    root = start
    # left child index
    child = root*2 + 1
    while child <= end:
        # 比较左右子节点的大值
        if child+1 <= end and arr[child] < arr[child+1]:
            child += 1
        if arr[root] < arr[child]:
            # 子节点更大,交换节点与子节点的值
            arr[root], arr[child] = arr[child], arr[root]
            # 更新节点到子节点位置,继续下一轮交换
            root = child
            child = root*2 + 1
        else:
            break

# 递归法
def bigHeapify_(arr, start, end):
    pos = start
    child = pos*2 + 1
    if child < end:
        right = child + 1
        if right < end and arr[right] > arr[child]:
            child = right
        if arr[child] > arr[pos]:
            arr[pos], arr[child] = arr[child], arr[pos]
            bigHeapify_(arr, pos, end)

def heapSort(arr: [int]) -> [int]:
    """大顶堆排序"""
    # 初始化非叶子节点
    first = (len(arr) >> 1) - 1
    for start in range(first, -1, -1):
        # 从下往上,从右至左对每个非叶子节点进行向下调整,循环构成大顶堆
        bigHeapify(arr, start, len(arr)-1)
    # 交换堆顶尾数据,堆数量--,重新堆化
    for end in range(len(arr)-1, 0, -1):
        # 交换堆顶堆尾数据, 构造大顶堆
        arr[0],arr[end] = arr[end], arr[0]
        # 重新调整堆,构造大顶堆
        bigHeapify(arr, 0, end-1)
    return arr


if __name__ == "__main__":
    import random
    nums = list(range(20))
    random.shuffle(nums)
    nums = [19, 1, 11, 4, 12, 8]
    orderNums = heapSort(nums)
    print(orderNums)

golang

func maxHeapSort(nums []int) []int {
	// max heap sort
	end := len(nums)-1
	// 从最后一个非叶子节点开始堆化
	for i:=end>>1;i>=0;i-- {
		maxHeapify(nums, i, end)
	}
	// 依次弹出堆顶元素,然后堆化,相当于依次把最大的放尾部
	for i:=end;i>=0;i-- {
		nums[0], nums[i] = nums[i], nums[0]
		end--
		maxHeapify(nums, 0, end)
	}
	return nums
}

func maxHeapify(nums []int, root, end int)  {
	// 大顶堆堆化,堆顶值小就一直下沉
	for {
		// 获取做左孩子
		child := root*2 + 1
		// 越界跳出
		if child > end {
			return
		}
		// 比较左右孩子节点,如果右孩子大则取右孩子与root比较,否则左右孩子左孩子大, child不用++
		if child < end && nums[child] <= nums[child+1] {
			child++
		}
		// 如果父节点值大于左右孩子的大值,说明,已经堆化
		if nums[root] > nums[child] {
			return
		}
		// 交换父节点与较大的子节点
		nums[root], nums[child] = nums[child], nums[root]
		// 更新父节点为孩子节点,继续与孙节点比较,不断往下沉
		root = child
	}
}

2.小顶堆/小根堆-排序

python

# 小顶堆-降序排列

def minHeapify(arr: [int], start: int, end: int):
    """大的节点往下沉,小的往上冒-小顶堆化,父节点与小的子节点比较并交换"""
    root = start
    child = root*2 + 1
    tmpVal = arr[start]
    while child <= end:
        if child + 1 <= end and arr[child+1] < arr[child]:
            child += 1
        if arr[child] < tmpVal:
            arr[root] = arr[child]
            root = child
            child = 2*root + 1
        else:
            arr[root] = tmpVal
            break
    else:
        arr[root] = tmpVal

def minHeapSort(arr: [int], k: int) -> [int]:
    """小顶堆,升序排列, topK"""
    # 以arr切片作为heap的初始序列
    heap = arr[:k]
    # 初始化topK的小顶堆的非叶子节点
    first = (k-2) >> 1
    # 小顶堆,堆化
    for i in range(first, -1, -1):
        minHeapify(heap, i, k-1)
    # 依次遍历arr的第k+1及以后的元素,入小顶堆顶,小顶堆化
    for i in range(k, len(arr)-1):
        if arr[i] > heap[0]:
            heap[0] = arr[i]
            minHeapify(heap, 0, k-1)
    # 排序heap, 小顶堆数量--
    for i in range(k-1, -1, -1):
        heap[0], heap[i] = heap[i], heap[0]
        minHeapify(heap, 0, i-1)
    return heap

def minHeapSort_(arr: [int]) -> [int]:
    """小顶堆,降序输出"""
    length = len(arr)
    # 初始化非叶子结点
    first = (length-2) >> 1
    # 遍历非叶子节点,小堆化
    for i in range(first, -1, -1):
        minHeapify(arr, i, length-1)
    # 排序数组, 小顶堆数量--
    for i in range(length-1, -1, -1):
        arr[0], arr[i] = arr[i], arr[0]
        minHeapify(arr, 0, i-1)
    return arr

if __name__ == "__main__":
    nums = [0, 8, 9, 12, 13, 15, 21, 23]
    # print(minHeapSort(nums, 5)) # top5, 前5的数
    # print(minHeapSort(nums, 8)) # k == len(nums), topK即为原数组的降序排列, copy
    print(minHeapSort_(nums))

golang

func minHeapify(arr []int, root, end int)  {
	// 小顶堆,堆化,非叶子节点大的下沉
	for {
		child := root*2 + 1
		// 越界跳出
		if child > end {
			return
		}
		// root的值和子节点的小值比较
		if child < end && arr[child] >= arr[child+1] {
			child++
		}
		// root节点已经小于子节点,跳出
		if arr[root] < arr[child] {
			return
		}
		// root节点大于等于子节点,交换
		arr[root], arr[child] = arr[child], arr[root]
		root = child
	}
}

func minHeapSort(arr []int) []int  {
	// 小顶堆,降序输出
	length := len(arr)
	// 非叶子节点初始化, int(n/2)
	first := (length-2) >> 1
	// 遍历所有非叶子节点,大的下沉
	for i:=first;i>=0;i-- {
		minHeapify(arr, i, length-1)
	}
	// 依次弹出堆尾节点,交换后继续堆化
	for i:=length-1;i>=0;i-- {
		arr[0], arr[i] = arr[i], arr[0]
		minHeapify(arr, 0, i-1)
	}
	return arr
}

func minHeapSort_(arr []int, k int) []int {
	// 小顶堆, top K
	heap := arr[:k]
	// 初始化非叶子节点
	first := (k-2) >> 1
	// 形成一个数量为k的小顶堆
	for i:=first;i>=0;i-- {
		minHeapify(heap, i, k-1)
	}
	// 遍历后续节点,只有大于堆顶的元素,放入堆顶,堆化
	for i:=k;i<len(arr)-1;i++ {
		if arr[i] > heap[0] {
			heap[0] = arr[i]
			minHeapify(heap, 0, k-1)
		}
	}
	// 依次弹出heap堆中的堆顶元素,弹出的元素就是topK
	for i:=k-1;i>=0;i-- {
		heap[0], heap[i] = heap[i], heap[0]
		minHeapify(heap, 0, i-1)
	}
	return heap
}

3.大顶堆功能实现

参考小顶堆的功能实现

4.小顶堆功能实现

实现功能:

  • 堆化,从n//2节点开始,和子节点比较,节点小则交换父节点与子节点,然后继续与孙节点比较,一直到叶子节点,总之遇到更小的子节点则下沉
  • 堆的插入,先加入到尾部,然后堆化
  • 堆的弹出或删除,弹出堆尾节点,保存堆顶节点,交换堆顶堆尾元素,重新堆化,让新堆顶元素下沉,堆化
  • 获取堆顶元素
  • 获取前k大元素,新建容器heap,获取到原数组的前k个元素切片,堆化heap,从k开始遍历arr,只要比heap[0]大,就令heap[0]等于该值,重新堆化,最后依次倒序弹出堆顶元素,每弹出一个,重新堆化(end--)

python

# -*- coding: utf-8 -*-


基于小顶堆的实现
实现功能:
-1.push(), 堆的插入
-2.pop(), 堆顶弹出/删除
-3.peak(), 获取堆顶元素
-4.kLargest(), 获取k个最大, 降序
-5.heapify(), 建堆


class MinHeap:
    def __init__(self, arr=None):
        self.heap = arr or []

    def _heapify(self, arr):
        """from the last non-leaf node, heapify"""
        if len(arr) > 0:
            end = len(arr) - 1
            first = end >> 1
            for i in range(first, -1, -1):
                self._siftdown(arr, i, end)

    def _siftdown(self, arr: [int], start: int, end: int):
        """cur root sink, compare with sub_node, if cur > sub_node, swap them, repeat, until to the leaf node"""
        root = start
        child = root*2 + 1
        while child <= end:
            if child+1 <= end and arr[child] > arr[child+1]:
                child += 1
            if arr[root] > arr[child]:
                arr[root], arr[child] = self._swap(arr[root], arr[child])
                root = child
                child = root*2 + 1
            else:
                break

    def _swap(self, a, b):
        a, b = b, a
        return a, b

    def heapify(self, arr):
        """transform arr to heap"""
        self._heapify(arr)

    def push(self, heap, item):
        """insert new item to heap's tail, heapify"""
        heap.append(item)
        self._heapify(heap)

    def pop(self, heap):
        """pop the last item of arr, and heapify itself"""
        if len(heap) == 0:
            raise Exception("IndexError, heap is empty")
        lastItem = heap.pop()
        if heap:
            peakItem = heap[0]
            heap[0] = lastItem
            self._heapify(heap)
            return peakItem
        return lastItem

    def peak(self, heap):
        """return peak of heap"""
        if len(heap) == 0:
            raise Exception("IndexError, heap is empty")
        return heap[0]

    def kLasrgest(self, arr, k):
        """arr -> heap, use minHeapSort to return k largest items"""
        if len(arr) == 0:
            return []
        if k == 1:
            result = max(arr)
            return [result]
        heap = arr[:k]
        self._heapify(heap)
        for i in range(k, len(arr)-1):
            if arr[i] > heap[0]:
                heap[0] = arr[i]
                self._siftdown(heap, 0, k-1)
        for i in range(k-1, -1, -1):
            heap[0], heap[i] = heap[i], heap[0]
            self._siftdown(heap, 0, i-1)
        return heap


if __name__ == "__main__":
    import heapq
    import random
    nums = list(range(100))
    random.shuffle(nums)
    randomNums = nums[:10]
    print(randomNums)

    heap = MinHeap()
    # heap.heapify(randomNums)
    # heap.push(randomNums, 1200)
    # print(randomNums)
    print(heap.kLasrgest(randomNums, 5))

参考:

原文地址:https://www.cnblogs.com/davis12/p/15665446.html