【JavaScript数据结构——堆(优先队列)】

堆的结构、堆的分类、堆的实现


1、堆的结构:完全二叉树

在这里插入图片描述
所以可以用数组来存储:

(从数组索引0开始)

节点父节点索引 parentIndex = [(index - 1) / 2]

左子节点索引leftIndex = index * 2 + 1

右子节点索引 rightIndex = index * 2 + 2


2、堆的分类:大顶堆,小顶堆

这也是为何堆可以用来实现优先队列的原因。优先队列的入队和出队凭借队列的优先级,优先级最高的出队,入队的元素要凭借优先级再次排列。这和堆的特征完美符合,以小顶堆为例,堆顶是最小的元素,也即元素越小优先级越高,也是按照优先级排列,插入堆的元素也要按优先级大小重排。故用堆来实现优先队列。


3、堆的实现

3.1基本功能

class Heap {
  constructor(compare) {} //构造函数,能够接受自定义的优先级比较方法
  push(item) {} // 入队
  pop() {} // 返回优先级最高元素,出队
  peek() {} // 返回优先级最高元素,但不出队
  bubbleDown(index){}//由下往上调整优先级
  bubbleUp(index){}//由上往下调整优先级
  size() {} // 当前队列的大小
}

3.2如何入队

(1)元素直接push入数组末尾

(2)然后bubbleDown,由下往上调整优先级

3.3如何找到优先级最高的元素并出队

(1)data[0]就是优先级最高的

(2)出队步骤:保存要返回的data[0],将末尾元素赋给0位置,然后bubbleUp,由上往下调整优先级

3.3如何bubbleUp

针对末尾元素,开启循环,将它和它的父节点比较,如果比父节点优先级高,则swap,并且转战到父节点继续向上比较,直到优先级小于等于父节点或者到堆顶为止。

时间复杂度O(logn)

3.4如何bubbleDown

针对堆顶元素,开启循环,将它和左子节点和右子结点比较,找出优先级最高的那个,然后swap,并且转战到被交换的子结点,继续往下比较,直到子结点优先级都没它高,或者到末尾。

时间复杂度O(logn)

4.完整代码实现

class Heap {
    constructor(comparator = (a, b) => a - b, data = []) {
        this.data = data;
        this.comparator = comparator;//比较器
        this.heapify();//堆化
    }

    heapify() {
        if (this.size() < 2) return;
        for (let i = Math.floor(this.size()/2)-1; i >= 0; i--) {
            this.bubbleDown(i);//bubbleDown操作
        }
    }

    peek() {
        if (this.size() === 0) return null;
        return this.data[0];//查看堆顶
    }

    push(value) {
        this.data.push(value);//加入数组
        this.bubbleUp(this.size() - 1);//调整加入的元素在小顶堆中的位置
    }

    pop() {
        if (this.size() === 0) {
            return null;
        }
        const result = this.data[0];
        const last = this.data.pop();
        if (this.size() !== 0) {
            this.data[0] = last;//交换第一个元素和最后一个元素
            this.bubbleDown(0);//bubbleDown操作
        }
        return result;
    }

    bubbleUp(index) {
        while (index > 0) {
            const parentIndex = (index - 1) /2;//父节点的位置
            //如果当前元素比父节点的元素小,就交换当前节点和父节点的位置
            if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {
                this.swap(index, parentIndex);//交换自己和父节点的位置
                index = parentIndex;//不断向上取父节点进行比较
            } else {
                break;//如果当前元素比父节点的元素大,不需要处理
            }
        }
    }

    bubbleDown(index) {
        const lastIndex = this.size() - 1;//最后一个节点的位置
        while (true) {
            const leftIndex = index * 2 + 1;//左节点的位置
            const rightIndex = index * 2 + 2;//右节点的位置
            let findIndex = index;//bubbleDown节点的位置
            //找出左右节点中value小的节点
            if (
                leftIndex <= lastIndex &&
                this.comparator(this.data[leftIndex], this.data[findIndex]) < 0
            ) {//先和左子节点比较
                findIndex = leftIndex;
            }
            if (
                rightIndex <= lastIndex &&
                this.comparator(this.data[rightIndex], this.data[findIndex]) < 0
            ) {//再和右子结点比较
                findIndex = rightIndex;
            }
            if (index !== findIndex) {//说明需要被交换
                this.swap(index, findIndex);//交换当前元素和左右节点中value小的
                index = findIndex;
            } else {
                break;
            }
        }
    }

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

    size() {
        return this.data.length;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值