js数据结构基础,封装最小堆和最大堆

是什么

要封装这两个数据结构,首先要搞明白他们是什么,有什么特点

不管是最小堆还是最大堆,他们都是二叉树的衍生,在二叉树之上

他们在结构上要求每组节点都必须完整,即父节点必须存在左右两个子节点,除了最后一组,即最下层的最右侧的一组节点,可以只存在左节点,并且必须是左节点

数据上,最大堆要求父节点的key一定要大于等于子节点,最小堆则相反

最大堆和最小堆广泛运用于堆排序算法中,在求最大最小值时,效率比较高

具体封装---最小堆

之前我封装二叉搜索树时,使用的是链表形式的主体,通过left和right指针来查询子节点, 现在我们换一种方式,用数组来存储最小最大堆

我们给每个节点按从左到右,从上到下的顺序排序,就可以得到一个一维的数组,且每个节点的角标,都有规律可循 ,既然如此,能否将整个树结构转化为一维的数组,通过函数查询每个节点的子节点,最重要的是在插入和删除时,js的数组可以直接用index查找到对应元素,这完美满足的我们的需求!

获取节点角标

首先,我们封装最重要的基础方法,用来查询节点的父 / 左 / 右节点

左节点 = 当前节点角标 * 2 + 1 右节点 = 当前节点角标 * 2 + 2 父节点 = (当前节点角标 + 1) / 2

javascript 复制代码
    class MinHeep {
        constructor() {
            this.heep = []
        }

        getLeftIndex(index) {
            return (index * 2) + 1
        }

        getRightIndex(index) {
            return (index * 2) + 2
        }

        getParentIndex(index) {
            return Matn.floor((index - 1) / 2)
        }
    }

封装简单的基本方法

封一下size和isEmpty,方便使用,顺便假装一下私有属性

javascript 复制代码
        isEmpty() {
            return this.heep.length === 0
        }

        size() {
            return this.heep.length
        }

封装插入方法

在插入最小堆时,我们需要明确,怎么样插入才能保证最小堆的结构和数据性质保持不变呢?我的方法是,通过将节点插入到数组的末尾,即最后一个子节点的位置,在对比它的父节点,如果父节点大于插入节点的值,则将两者交换位置

kotlin 复制代码
        insert(item) {
            if(!item && item !== 0) return
                // 将插入值放到最后
                this.heep.push(item)
                let index = this.heep.length - 1
                this.shiftUP(index)
        }

        shiftUP(index) {
            let parentIndex = this.getParentIndex(index)
            // 当插入元素所在角标大于0且其值小于它父节点的值时,交换他两的位置
            while(index > 0 && this.heep[index] < this.heep[parentIndex]) {
                this.swap(this.heep, index, parentIndex)
                index = parentIndex
                parentIndex = this.getParentIndex(index)
            }
        }

        swap(arr, a, b) {
            let temp = arr[a]
            arr[a] = arr[b]
            arr[b] = temp
        }

封装弹出方法

封装弹出方法,即将数组的第一个元素,堆的根节点删除,并返回,最后对堆进行重新排序

我选择的排序方法是,将最后一个节点放在根节点上,然后向下查询,拿到该节点 / 左节点 / 右节点中最小的那个,如果根节点最小,则满足最小堆性质要求,结束排序,如果是左或右节点最小,则让最小的与该节点交换位置,直到该节点最小或者该节点的子节点为空

kotlin 复制代码
        extract() {
            if(this.isEmpty()) return
            if(this.size() === 1) return this.heep.pop()
            else {
                const m = this.heep[0]
                this.heep[0] = this.heep.pop()
                this.shiftDown(0)
                return m
            }
        }

        shiftDown(index) {
            let left = this.getLeftIndex(index)
            let right = this.getRightIndex(index)
            let min = index
            while(min < this.size()) {
                if(left < this.size() && this.heep[min] > this.heep[left]) {
                    min = left
                }
                if(right < this.size() && this.heep[min] > this.heep[right]) {
                    min = right
                }
                if(index !== min) {
                    this.swap(this.heep, min, index)
                    index = min
                    left = this.getLeftIndex(index)
                    right = this.getRightIndex(index)
                } else return
            }
        }

具体封装---最大堆

其实只需要把之前封装最小堆的一些条件判断改变就是最大堆了,我实在懒得再写一遍,大家如果看完想试试手,可以自己封装一下

其实,如果对比是自己封装在类中的方法的话,只需要继承,然后改一下这个方法就行了

kotlin 复制代码
    // 把 if(a > b) 改为 if(compare(a, b) === compareValue.bigger)
    const compareValue = {
        less: -1,
        equal: 0,
        bigger: 1
    }
    const compare = (a, b) => {
        if(a === b) return compareValue.equal
        else if(a > b) return compareValue.bigger
        else return compareValue.less
    }
    // 这样,只需要将 compare 函数改为以下即可,但这样改我觉得必须要加注释,说明大小值是相反的
    const compare = (a, b) => {
        if(a === b) return compareValue.equal
        else if(a < b) return compareValue.bigger
        else return compareValue.less
    }
相关推荐
IT码农-爱吃辣条3 小时前
Three.js 初级教程大全
开发语言·javascript·three.js
烛阴4 小时前
告别繁琐的类型注解:TypeScript 类型推断完全指南
前端·javascript·typescript
gnip4 小时前
工程项目中.env 文件原理
前端·javascript
月盈缺4 小时前
学习嵌入式的第二十二天——数据结构——双向链表
数据结构·学习·链表
JohnYan5 小时前
工作笔记 - CentOS7环境运行Bun应用
javascript·后端·容器
科大饭桶6 小时前
C++入门自学Day14-- Stack和Queue的自实现(适配器)
c语言·开发语言·数据结构·c++·容器
躲在云朵里`7 小时前
深入理解数据结构:从数组、链表到B树家族
数据结构·b树
东风西巷7 小时前
Rubick:基于Electron的开源桌面效率工具箱
前端·javascript·electron·软件需求
Miracle_G7 小时前
每日一个知识点:JavaScript 箭头函数与普通函数比较
javascript
unfetteredman7 小时前
Error: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found
前端·javascript·vite