数据结构与算法之堆: 实现最大堆类的两种方式及实现堆排序 (Ts, Py, Go版)

Typescript 版算法实现


1 )方案一:参考最小堆类实现最大堆类及实现排序
class MaxHeap {
    heap: number[] = [];
    // 交换节点位置
    swap(i, j) {
        [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
    }
    // 获得父节点
    getParentIndex(i) {
        return (i - 1) >> 1;
    }
    // 获取左子节点
    getLeftIndex(i) {
        return (i << 1) + 1; // 极客写法
    }
    // 获取右子节点
    getRightIndex(i) {
        return (i << 1) + 2;
    }
    // 向上移动
    shiftUp(index) {
        // 如果到了堆顶元素,index是0,则不要再上移了
        if(!index) return;
        const parentIndex = this.getParentIndex(index)
        if(this.heap[parentIndex] < this.heap[index]) {
            this.swap(parentIndex, index)
            this.shiftUp(parentIndex)
        }
    }
    // 下移
    shiftDown(index) {
        // 边界1:如果到了堆尾元素,则不要再下移了
        if(index >= this.heap.length - 1) return;
        const size = this.size();
        const leftIndex = this.getLeftIndex(index);
        const rightIndex = this.getRightIndex(index);
        if (leftIndex < size && this.heap[leftIndex] > this.heap[index]) {
            this.swap(leftIndex, index);
            this.shiftDown(leftIndex);
        }
        if (rightIndex < size && this.heap[rightIndex] > this.heap[index]) {
            this.swap(rightIndex, index);
            this.shiftDown(rightIndex);
        }
    }
    // 插入
    insert(value) {
        this.heap.push(value);
        this.shiftUp(this.heap.length - 1);
    }
    // 删除堆顶
    pop() {
        // pop()方法删除数组最后一个元素并返回,赋值给堆顶
        if (this.heap.length <= 1) return;
        this.heap[0] = this.heap.pop();
        // 对堆顶重新排序
        this.shiftDown(0);
    }
    // 获取堆顶
    peak() {
        return this.heap[0];
    }
    // 获取堆的大小
    size() {
        return this.heap.length;
    }
    sort(arr) {
    	// 1. 先构建一个最小堆
    	arr.forEach((n) => {
    		this.insert(n);
    	});
    	// 2. 开始依次排序,注意这个边界, pop不能是1,否则会造成死循环
    	const result = [];
    	while(this.heap.length > 1) {
    		result.push(this.peak());
    		this.pop();
    	}
    	// 3. 处理最后一个
    	result.push(this.peak());
    	this.heap = result;
    	return result;
    }
}

const mh = new MaxHeap();
mh.sort([1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98]);
console.log(mh.heap); // [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]
  • 实现最小堆类参考:https://blog.csdn.net/Tyro_java/article/details/133468244
  • 这里排序需要注意的是:先构建的最小堆,并不是排好序的,只是最小值在堆顶,O(logn)
  • 循环中每次把最小值取出来, 注意pop下移操作时的边界问题,O(nlogn)

2 )方案二:另一种方式实现最大堆类及实现排序

class MaxHeap {
  heap: number[] = []
  // 交换两个元素
  static swap (arr, i, j) {
    if (i === j) return;
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  // 构建最大堆的过程,递归
  static maxHeapify (Arr, i, size) {
    // 左节点(索引)
    const l = (i << 1) + 1
    // 右节点
    const r = (i << 1) + 2
    let largest = i;
    // 父节点i和左节点l做比较取最大
    if (l <= size && Arr[l] < Arr[largest]) largest = l;
    // 右节点和最大值比较
    if (r <= size && Arr[r] < Arr[largest]) largest = r;
    if (largest !== i) {
      MaxHeap.swap(Arr, i, largest);
      MaxHeap.maxHeapify(Arr, largest, size);
    }
  }
  sort () {
    const iArr = this.heap;
    const n = iArr.length;
    if (n <= 1) return iArr;
    // 这里,从最后一个父节点开始构建最大堆, 因为最后一个父节点到第一个父节点就是一次完整的构建最大堆的过程,完成后堆顶就是最大值
    for (let i = Math.floor(n / 2); i >= 0; i--) {
      MaxHeap.maxHeapify(iArr, i, n); // 这里最后一个参数 n 是完整的堆
    }
    // 减而治之
    for (let j = 0; j < n; j++) {
      MaxHeap.swap(iArr, 0, n - 1 - j); // 堆顶与最后一项互换, 堆顶被破坏, 堆顶变成最后一项
      MaxHeap.maxHeapify(iArr, 0, n - 1 - j - 1); // 这里逐渐缩小堆的规模,注意后面的减1,因为最后一个元素是排好序的最大值
    }
    return iArr
  }
}

const mh = new MaxHeap();
mh.heap = [1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98]
mh.sort();
console.log(mh.heap); // [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]
  • 这个排序的精髓是:在这个排序里先构建一次最大堆,获取排好序的最大值后,逐渐减而治之

Python3 版算法实现


1 ) 方案1

class MaxHeap:
    def __init__(self):
        self.heap = []

    # 交换节点位置
    def swap(self, i, j):
        self.heap[i], self.heap[j] = self.heap[j], self.heap[i]

    # 获得父节点
    def get_parent_index(self, i):
        return (i - 1) >> 1

    # 获取左子节点
    def get_left_index(self, i):
        return (i << 1) + 1

    # 获取右子节点
    def get_right_index(self, i):
        return (i << 1) + 2

    # 向上移动
    def shift_up(self, index):
        if index == 0:
            return
        parent_index = self.get_parent_index(index)
        if self.heap[parent_index] < self.heap[index]:
            self.swap(parent_index, index)
            self.shift_up(parent_index)

    # 下移
    def shift_down(self, index):
        if index >= len(self.heap) - 1:
            return
        size = self.size()
        left_index = self.get_left_index(index)
        right_index = self.get_right_index(index)
        if left_index < size and self.heap[left_index] > self.heap[index]:
            self.swap(left_index, index)
            self.shift_down(left_index)
        if right_index < size and self.heap[right_index] > self.heap[index]:
            self.swap(right_index, index)
            self.shift_down(right_index)

    # 插入
    def insert(self, value):
        self.heap.append(value)
        self.shift_up(len(self.heap) - 1)

    # 删除堆顶
    def pop(self):
        if len(self.heap) <= 1:
            return
        self.heap[0] = self.heap.pop()
        self.shift_down(0)

    # 获取堆顶
    def peak(self):
        return self.heap[0] if self.heap else None

    # 获取堆的大小
    def size(self):
        return len(self.heap)

    # 排序
    def sort(self, arr):
        for n in arr:
            self.insert(n)
        result = []
        while self.size() > 1:
            result.append(self.peak())
            self.pop()
        result.append(self.peak())
        self.heap = result
        return result

def main():
    mh = MaxHeap()
    sorted_array = mh.sort([1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98])
    print(sorted_array)  # 应该输出 [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]

if __name__ == "__main__":
    main()

2 )方案2

class MaxHeap:
    def __init__(self):
        self.heap = []

    # 交换两个元素
    @staticmethod
    def swap(arr, i, j):
        if i == j:
            return
        arr[i], arr[j] = arr[j], arr[i]

    # 构建最大堆的过程,递归
    @staticmethod
    def max_heapify(arr, i, size):
        l = (i << 1) + 1
        r = (i << 1) + 2
        largest = i
        if l <= size and arr[l] > arr[largest]:
            largest = l
        if r <= size and arr[r] > arr[largest]:
            largest = r
        if largest != i:
            MaxHeap.swap(arr, i, largest)
            MaxHeap.max_heapify(arr, largest, size)

    # 排序
    def sort(self):
        i_arr = self.heap
        n = len(i_arr)
        if n <= 1:
            return i_arr
        for i in range(n // 2, -1, -1):
            MaxHeap.max_heapify(i_arr, i, n - 1)
        for j in range(n):
            MaxHeap.swap(i_arr, 0, n - 1 - j)
            MaxHeap.max_heapify(i_arr, 0, n - 2 - j)
        return i_arr

def main():
    mh = MaxHeap()
    mh.heap = [1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98]
    sorted_array = mh.sort()
    print(sorted_array)  # 应该输出 [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]

if __name__ == "__main__":
    main()

Golang 版算法实现


1 ) 实现1

package main

import (
	"fmt"
)

type MaxHeap struct {
	heap []int
}

// 交换节点位置
func (h *MaxHeap) swap(i, j int) {
	h.heap[i], h.heap[j] = h.heap[j], h.heap[i]
}

// 获得父节点
func (h *MaxHeap) getParentIndex(i int) int {
	return (i - 1) >> 1
}

// 获取左子节点
func (h *MaxHeap) getLeftIndex(i int) int {
	return (i << 1) + 1
}

// 获取右子节点
func (h *MaxHeap) getRightIndex(i int) int {
	return (i << 1) + 2
}

// 向上移动
func (h *MaxHeap) shiftUp(index int) {
	if index == 0 {
		return
	}
	parentIndex := h.getParentIndex(index)
	if h.heap[parentIndex] < h.heap[index] {
		h.swap(parentIndex, index)
		h.shiftUp(parentIndex)
	}
}

// 下移
func (h *MaxHeap) shiftDown(index int) {
	if index >= len(h.heap)-1 {
		return
	}
	size := h.size()
	leftIndex := h.getLeftIndex(index)
	rightIndex := h.getRightIndex(index)
	if leftIndex < size && h.heap[leftIndex] > h.heap[index] {
		h.swap(leftIndex, index)
		h.shiftDown(leftIndex)
	}
	if rightIndex < size && h.heap[rightIndex] > h.heap[index] {
		h.swap(rightIndex, index)
		h.shiftDown(rightIndex)
	}
}

// 插入
func (h *MaxHeap) insert(value int) {
	h.heap = append(h.heap, value)
	h.shiftUp(len(h.heap) - 1)
}

// 删除堆顶
func (h *MaxHeap) pop() {
	if len(h.heap) <= 1 {
		return
	}
	h.heap[0] = h.heap[len(h.heap)-1]
	h.heap = h.heap[:len(h.heap)-1]
	h.shiftDown(0)
}

// 获取堆顶
func (h *MaxHeap) peak() *int {
	if len(h.heap) == 0 {
		return nil
	}
	return &h.heap[0]
}

// 获取堆的大小
func (h *MaxHeap) size() int {
	return len(h.heap)
}

// 排序
func (h *MaxHeap) sort(arr []int) []int {
	for _, n := range arr {
		h.insert(n)
	}
	result := make([]int, 0, len(arr))
	for h.size() > 1 {
		result = append(result, *h.peak())
		h.pop()
	}
	result = append(result, *h.peak())
	h.heap = result
	return result
}

func main() {
	mh := &MaxHeap{}
	sortedArray := mh.sort([]int{1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98})
	fmt.Println(sortedArray) // 应该输出 [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]
}

2 )实现2

package main

import (
	"fmt"
)

type MaxHeap struct {
	heap []int
}

// 交换两个元素
func (h *MaxHeap) swap(arr []int, i, j int) {
	if i == j {
		return
	}
	arr[i], arr[j] = arr[j], arr[i]
}

// 构建最大堆的过程,递归
func (h *MaxHeap) maxHeapify(arr []int, i, size int) {
	l := (i << 1) + 1
	r := (i << 1) + 2
	largest := i
	if l <= size && arr[l] > arr[largest] {
		largest = l
	}
	if r <= size && arr[r] > arr[largest] {
		largest = r
	}
	if largest != i {
		h.swap(arr, i, largest)
		h.maxHeapify(arr, largest, size)
	}
}

// 排序
func (h *MaxHeap) sort() []int {
	iArr := h.heap
	n := len(iArr)
	if n <= 1 {
		return iArr
	}
	for i := n / 2; i >= 0; i-- {
		h.maxHeapify(iArr, i, n-1)
	}
	for j := 0; j < n; j++ {
		h.swap(iArr, 0, n-1-j)
		h.maxHeapify(iArr, 0, n-2-j)
	}
	return iArr
}

func main() {
	mh := &MaxHeap{
		heap: []int{1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98},
	}
	sortedArray := mh.sort()
	fmt.Println(sortedArray) // 应该输出 [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值