二叉堆--最小堆的介绍和实现

233 阅读3分钟

一、什么是二叉堆(最小堆)?

二叉堆是一种完全二叉树,其中每个父节点的值都小于或等于其子节点的值。最小堆是二叉堆的一种,它的特点是每个节点的值都小于或等于其子节点的值,因此根节点的值是整个堆中的最小值。ps:最大堆正好相反,根节点为最大值。

二叉堆的特点

  1. 完全二叉树:二叉堆是一棵完全二叉树,意味着除了最后一层外,每一层的节点都被填满了,并且最后一层的节点从左到右依次排列。
  2. 堆的性质:对于最小堆,每个节点的值都不大于其任何一个子节点的值。因此,堆的根节点是最小值。
  3. 二叉树结构:每个节点至多有两个子节点,左子节点和右子节点。

为什么使用二叉堆?

二叉堆有着高效的插入、删除和访问最小(或最大)元素的特点。特别是在实现优先级队列时,最小堆提供了O(log n)的时间复杂度来插入和删除元素,因此它在处理动态优先级队列时非常有用。

二、二叉堆的基本操作

1. 插入操作

插入元素时,首先将元素插入到堆的末尾,然后通过上浮操作调整堆的结构,以保持堆的性质。上浮操作会将插入的元素与父节点进行比较,如果插入的元素小于父节点,则交换位置,直到堆的性质得到恢复。

2. 删除最小元素(根节点)

删除根节点(即最小值)时,我们首先将堆的最后一个元素移到根节点的位置,然后通过下沉操作调整堆的结构。下沉操作会将新的根节点与其较小的子节点交换,直到堆的性质恢复。

3. 获取最小元素

由于最小堆的根节点始终是最小值,获取最小元素的操作是O(1)的时间复杂度。

三、最小堆的JavaScript实现

下面我们将实现一个最小堆,支持插入、删除最小元素、获取最小元素和打印堆等操作。

class MinHeap {
    constructor() {
        this.heap = []; // 存储堆的数组
    }

    // 获取父节点的索引
    parent(index) {
        return Math.floor((index - 1) / 2);
    }

    // 获取左子节点的索引
    leftChild(index) {
        return 2 * index + 1;
    }

    // 获取右子节点的索引
    rightChild(index) {
        return 2 * index + 2;
    }

    // 判断一个节点是否有左子节点
    hasLeftChild(index) {
        return this.leftChild(index) < this.heap.length;
    }

    // 判断一个节点是否有右子节点
    hasRightChild(index) {
        return this.rightChild(index) < this.heap.length;
    }

    // 交换堆中两个元素的位置
    swap(index1, index2) {
        const temp = this.heap[index1];
        this.heap[index1] = this.heap[index2];
        this.heap[index2] = temp;
    }

    // 上浮操作:将元素上浮到合适的位置
    bubbleUp(index) {
        while (index > 0 && this.heap[this.parent(index)] > this.heap[index]) {
            this.swap(index, this.parent(index));
            index = this.parent(index);
        }
    }

    // 下沉操作:将元素下沉到合适的位置
    heapifyDown(index) {
        let smallest = index;
        if (this.hasLeftChild(index) && this.heap[this.leftChild(index)] < this.heap[smallest]) {
            smallest = this.leftChild(index);
        }
        if (this.hasRightChild(index) && this.heap[this.rightChild(index)] < this.heap[smallest]) {
            smallest = this.rightChild(index);
        }
        if (smallest !== index) {
            this.swap(index, smallest);
            this.heapifyDown(smallest);
        }
    }

    // 插入操作
    insert(value) {
        this.heap.push(value); // 将元素插入到堆的末尾
        this.bubbleUp(this.heap.length - 1); // 上浮调整堆
    }

    // 删除最小元素(根节点)
    removeMin() {
        if (this.heap.length === 0) return null;
        const min = this.heap[0]; // 获取最小元素
        this.heap[0] = this.heap[this.heap.length - 1]; // 将堆的最后一个元素放到根节点
        this.heap.pop(); // 删除最后一个元素
        this.heapifyDown(0); // 下沉调整堆
        return min;
    }

    // 获取最小元素(根节点)
    getMin() {
        return this.heap.length > 0 ? this.heap[0] : null;
    }

    // 打印堆的元素
    printHeap() {
        console.log(this.heap);
    }
}

// 示例:使用MinHeap实现优先级队列
const minHeap = new MinHeap();
minHeap.insert(10);
minHeap.insert(5);
minHeap.insert(8);
minHeap.insert(3);
minHeap.insert(7);
minHeap.insert(2);

console.log('最小元素:', minHeap.getMin()); // 输出: 2
minHeap.printHeap(); // 输出堆的结构 [ 2, 5, 3, 10, 7, 8 ]

console.log('删除最小元素:', minHeap.removeMin()); // 输出: 2
minHeap.printHeap(); // 输出堆的结构 [ 3, 5, 8, 10, 7 ]

console.log('删除最小元素:', minHeap.removeMin()); // 输出: 3
minHeap.printHeap(); // 输出堆的结构 [ 5, 7, 8, 10 ]