数据结构 101:如何在 JavaScript 中构建最小堆和最大堆

简介: 数据结构 101:如何在 JavaScript 中构建最小堆和最大堆

什么是堆?

堆是一种高级的基于树的数据结构, 主要用于排序和实现优先级[队列]它们是具有以下特征的完全二叉树:

  • 除了叶节点(没有子节点的节点称为叶)之外,每个级别都被填充。
  • 每个节点最多有 2 个孩子。
  • 所有节点都尽可能靠左。这意味着每个孩子都在他父母的左边。

网络异常,图片无法展示
|

堆使用完全二叉树来避免数组中出现空洞。完全二叉树是每个结点至多有两个孩子的树,所有层的结点都是满的,除了叶子结点可以为空。堆是基于堆属性构建的,堆属性将父节点键与其子节点键进行比较。

重要的是要注意堆并不总是排序的。它们遵循的关键条件是最大或最小元素放置在根节点(顶部)上,具体取决于它是最大堆还是最小堆。堆数据结构与堆内存不同。

堆的优点和缺点

优点:

  • 垃圾收集在堆内存上运行以释放对象使用的内存。
  • 堆是灵活的,因为可以按任何顺序分配或删除内存。
  • 变量可以全局访问。
  • 它有助于找到最小和最大的数字。

缺点:

  • 与栈相比,堆需要更多的时间来执行。
  • 内存管理在堆内存中更为复杂,因为它是全局使用的。
  • 堆通常需要更多的时间来计算。

堆数据结构的应用

堆对于查找数组中的最小或最大元素非常有效,并且在顺序统计和选择算法中很有用。从堆中获取最小值/最大值的时间复杂度为O(1) ,(恒定时间复杂度)。

优先级队列是基于堆结构设计的。有效地插入 ( ) 和删除 ( ) 优先级队列中的每个元素需要O(log(n))时间。insert()``delete()

堆实现的优先级队列用于流行的算法,例如:

  • 普里姆算法
  • Dijkstra 算法
  • 堆排序算法

堆中的基本操作

以下是您在实现堆数据结构时可能使用的基本操作:

  • heapify 重新排列堆中的元素以保持堆属性。
  • insert 将一个项目添加到堆中,同时保持其堆属性。
  • delete 删除堆中的项目。
  • extract 返回一个项目的值,然后从堆中删除它。
  • isEmpty:  boolean,如果 boolean 为空则返回 true,如果有节点则返回 false。
  • size: 返回堆的大小。
  • getMax() 返回堆中的最大值

如何构建最大堆

最大堆中的元素遵循最大堆属性。这意味着父节点的键总是大于两个子节点的键。要构建最大堆:

  • 在堆的开头(根)创建一个新节点。
  • 给它赋值。
  • 将子节点的值与父节点进行比较。
  • 如果父节点的值小于任一子节点(向左或向右)的值,则交换节点。
  • 重复直到最大元素位于根父节点(然后你可以说堆属性成立)。

将新元素插入堆时也可以遵循这些步骤。这里的关键是无论在最大堆上执行什么操作,堆属性都必须保持。

移除/删除最大堆中的根节点:

  • 删除根节点。
  • 将最后一层的最后一个子节点移动到根。
  • 将父节点与其子节点进行比较。
  • 如果父节点的值小于子节点,则交换它们,并重复直到满足堆属性。

让我们看一下这在代码中是什么样子的。在下一节中,我们将使用[JavaScript] 实现最大堆。

在 JavaScript 中实现最大堆

在我们开始构建最大堆之前,先看看我们将要实现的一些方法以及它们的作用:

  • _percolateUp() 将堆属性从子节点恢复到根节点。
  • _maxHeapify() 将堆属性从特定节点向下恢复到叶节点。
  • insert() 将给定值附加到堆数组并根据元素的堆属性重新排列元素。在每次插入时,堆均匀增长,大小增加一。
  • getMax() 返回堆(根节点)中的最大值,不修改堆。注意这里的时间复杂度是常数时间O(1)
  • removeMax() 返回并删除堆中的最大值(想想pop())。该函数的时间复杂度为O(log(n))

如果堆大小大于一,它将最大值存储到一个变量中,将该值与最后一个叶子交换,然后从堆中删除最大值。

如果堆只有一个元素,则删除并返回该元素的值,最后一个条件是如果堆为空,则返回null。

__percolateUp()方法在每个父节点上递归[调用] ,直到到达根节点。对于要定位在 max-heap 属性之后的每个节点,我们在该数组的每个索引处调用该__maxHeapify()方法,从堆的底部开始。

class maxHeap {
    constructor() {
        this.heap = [];
        this.elements = 0;
    };
    insert(val) {
        if (this.elements >= this.heap.length) {
            this.elements = this.elements + 1;
            this.heap.push(val);
            this.__percolateUp(this.heap.length - 1);
        }
        else {
            this.heap[this.elements] = val;
            this.elements = this.elements + 1;
            this.__percolateUp(this.elements - 1);
        }
    };
    getMax() {
        if (this.elements !== 0)
            return this.heap[0];
        return null;
    };
    removeMax() {
        let max = this.heap[0];
        if (this.elements > 1) {
            this.heap[0] = this.heap[this.elements - 1];
            this.elements = this.elements - 1;
            this.__maxHeapify(0);
            return max
        } else if (this.elements === 1) {
            this.elements = this.elements - 1;
            return max;
        } else {
            return null;
        }
    };
    __percolateUp(index) {
        const parent = Math.floor((index - 1) / 2);
        if (index <= 0)
            return
        else if (this.heap[parent] < this.heap[index]) {
            let tmp = this.heap[parent];
            this.heap[parent] = this.heap[index];
            this.heap[index] = tmp;
            this.__percolateUp(parent);
        }
    };
    __maxHeapify(index) {
        let left = (index * 2) + 1;
        let right = (index * 2) + 2;
        let largest = index;
        if ((this.elements > left) && (this.heap[largest] < this.heap[left])) {
            largest = left
        }
        else if ((this.elements > right) && (this.heap[largest] < this.heap[right]))
            largest = right
        else if (largest !== index) {
            const tmp = this.heap[largest];
            this.heap[largest] = this.heap[index];
            this.heap[index] = tmp;
            this.__maxHeapify(largest);
        }
    };
    buildHeap(arr) {
        this.heap = arr;
        this.elements = this.heap.length;
        for (let i = this.heap.length - 1; i >= 0; i--) {
            this.__maxHeapify(i);
        }
    };
};
let heap = new maxHeap();
复制代码

如何构建最小堆

直观上,我们可以说最小堆中的元素遵循最小堆属性,因为这与最大堆相反。父节点的键总是小于两个子节点的键。要构建最小堆:

  • 在堆的末尾(最后一层)创建一个新的子节点。
  • 将新键添加到该节点(将其附加到数组)。
  • 向上移动子节点,直到到达根节点并且堆属性得到满足。

删除/删除最小堆中的根节点:

  • 删除根节点。
  • 将最后一个孩子的钥匙移到根。
  • 将父节点与其子节点进行比较。
  • 如果父节点的值大于子节点,则交换它们,并重复直到满足堆属性。

在 JavaScript 中实现最小堆

在我们开始构建最小堆之前,请注意它的实现类似于最大堆。minHeapify()恢复堆属性。getMin()返回堆(根节点)中的最小值而不修改堆。并removeMin()删除最小值并返回它。

class minHeap {
    constructor() {
        this.heap = []
        this.elements = 0;
    };
    insert(val) {
        if (this.elements >== this.heap.length) {
            this.elements = this.elements + 1
            this.heap.push(val);
            this.__percolateUp(this.heap.length - 1);
        }
        else {
            this.heap[this.elements] = val;
            this.elements = this.elements + 1;
            this.__percolateUp(this.elements - 1);
        }
    };
    getMin() {
        if (this.heap.length !== 0)
            return this.heap[0];
        return null;
    }
    removeMin() {
        const min = this.heap[0];
        if (this.elements > 1) {            
            this.heap[0] = this.heap[this.elements - 1];
            this.elements = this.elements - 1;
            this.__minHeapify(0);
            return min;
        } else if (this.elements == 1) {
            this.elements = this.elements - 1;
            return min;
        } else {
            return null;
        }
    };
    __percolateUp(index) {
        let parent = Math.floor((index - 1) / 2);
        if (index <= 0)
            return
        else if (this.heap[parent] > this.heap[index]) {
            let tmp = this.heap[parent];
            this.heap[parent] = this.heap[index];
            this.heap[index] = tmp;
            this.__percolateUp(parent);
        }
    };
    __minHeapify(index) {
        let left = (index * 2) + 1;
        let right = (index * 2) + 2;
        let smallest = index;
        if ((this.elements > left) && (this.heap[smallest] > this.heap[left])) {
            smallest = left;
        }
        if ((this.elements > right) && (this.heap[smallest] > this.heap[right]))
            smallest = right;
        if (smallest !== index) {
            let tmp = this.heap[smallest];
            this.heap[smallest] = this.heap[index];
            this.heap[index] = tmp;
            this.__minHeapify(smallest);
        }
    }
    buildHeap(arr) {
        this.heap = arr;
        this.elements = this.heap.length;
        for (let i = this.heap.length - 1; i >= 0; i--) {
            this.__minHeapify(i)
        }
    }
};
let heap = new minHeap();
heap.insert(12);
heap.insert(10);
heap.insert(-10);
heap.insert(100);
console.log(heap.getMin()); //you should get -10
let newheap = new minHeap();
let arr = [12, 6, 8, 3, 16, 4, 27];
newheap.buildHeap(arr) //builds this new heap with elements from the array
console.log(newheap.getMin()) //this logs 3
newheap.removeMin();
console.log(newheap.getMin())
复制代码

堆挑战:将最大堆转换为最小堆

让我们通过动手挑战来进一步学习。我们的目标是将最大堆转换为最小堆。跟随我们的代码解决方案,看看它是如何完成的。

问题陈述: 实现一个函数convertMax(maxHeap),将二进制最大堆转换为二进制最小堆,其中maxHeap是格式中的数组maxHeap(即父项大于其子项)。您的输出应该是一个转换后的数组。

示例输入:

maxHeap = [9,4,7,1,-2,6,5]
复制代码

示例输出:

result = [-2,1,5,9,4,6,7]
复制代码

网络异常,图片无法展示
|

问题:

function convertMax(maxHeap) {
    return maxHeap
}
复制代码

解决方案:

function minHeapify(heap, index) {
    var left = index * 2;
    var right = (index * 2) + 1;
    var smallest = index;
    if ((heap.length > left) && (heap[smallest] > heap[left])) {
        smallest = left
    }
    if ((heap.length > right) && (heap[smallest] > heap[right]))
        smallest = right
    if (smallest != index) {
        var tmp = heap[smallest]
        heap[smallest] = heap[index]
        heap[index] = tmp
        minHeapify(heap, smallest)
    }
    return heap;
}
function convertMax(maxHeap) {
    for (var i = Math.floor((maxHeap.length) / 2); i > -1; i--)
        maxHeap = minHeapify(maxHeap, i)
    return maxHeap
}
复制代码

****可以运行上面的代码解决方案。 我们可以将 given 视为maxHeap一个规则的元素数组并对其重新排序,以便它准确地表示一个最小堆。该函数通过在每个节点上convertMax()调用函数从最低父节点恢复所有节点上的堆属性。minHeapify()

构建堆的时间复杂度是O(n) 。这个问题也是如此。

function minHeapify(heap, index) {
    var left = index * 2;
    var right = (index * 2) + 1;
    var smallest = index;
    if ((heap.length > left) && (heap[smallest] > heap[left])) {
        smallest = left
    }
    if ((heap.length > right) && (heap[smallest] > heap[right]))
        smallest = right
    if (smallest != index) {
        var tmp = heap[smallest]
        heap[smallest] = heap[index]
        heap[index] = tmp
        minHeapify(heap, smallest)
    }
    return heap;
}
function convertMax(maxHeap) {
    for (var i = Math.floor((maxHeap.length) / 2); i > -1; i--)
        maxHeap = minHeapify(maxHeap, i)
    return maxHeap
}
var maxHeap = [9,4,7,1,-2,6,5]
console.log(convertMax(maxHeap))
-->
[ -2, 1, 4, 5, 7, 6, 9 ]


相关文章
|
6天前
|
存储 JavaScript 前端开发
使用Vue.js构建交互式前端界面的技术探索
【5月更文挑战第20天】Vue.js是一款渐进式JavaScript框架,擅长构建交互式前端界面。其核心特性包括响应式数据绑定、组件化开发、指令系统和虚拟DOM,简化开发并提升性能。通过Vue CLI创建项目,拆分组件,结合数据绑定和事件处理实现交互,使用Vue Router管理路由,Vuex进行状态管理,能高效构建现代Web应用。
|
2天前
|
JavaScript 前端开发 NoSQL
构建基于Node.js的全栈应用:从前端到后端的完整指南
【5月更文挑战第24天】本文是关于使用Node.js构建全栈应用的指南,涵盖前端(React或Vue)、后端(Node.js + Express)和数据库(MongoDB)的选型与实现。文章介绍了项目结构、前端组件化开发、后端API接口编写、前后端联调及部署上线的注意事项,帮助读者掌握全栈开发流程。
TU^
|
3天前
|
存储 机器学习/深度学习 算法
数据结构~~二叉树-堆
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
TU^
11 1
|
3天前
|
存储 算法 索引
[数据结构]——二叉树——堆的实现
[数据结构]——二叉树——堆的实现
|
4天前
|
存储 Web App开发 JavaScript
构建基于Node.js的实时通信系统:技术详解
【5月更文挑战第22天】构建基于Node.js的实时通信系统,利用WebSocket协议和Socket.IO库实现全双工通信。系统采用Node.js作为服务器环境,处理高并发,结合WebSocket进行高效数据交换。Socket.IO提供WebSocket封装,保证兼容性。系统架构包括客户端(使用WebSocket连接服务器)、Node.js服务器(处理连接、消息、认证和数据存储)和数据库。开发流程包括环境搭建、服务器和客户端开发,最后部署测试。该系统可为在线聊天、视频会议等场景提供流畅交互体验,未来可优化性能和扩展性。
|
5天前
|
存储 算法 分布式数据库
【数据结构】堆(Heap)
【数据结构】堆(Heap)
|
6天前
|
存储 机器学习/深度学习 算法
数据结构与算法⑬(第四章_中_续二)堆解决Topk问题+堆的概念选择题
数据结构与算法⑬(第四章_中_续二)堆解决Topk问题+堆的概念选择题
12 3
|
6天前
|
存储 算法
数据结构与算法⑪(第四章_中)堆的分步构建
数据结构与算法⑪(第四章_中)堆的分步构建
10 0
|
6天前
|
存储 移动开发 算法
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构(下)
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构
18 0
|
6天前
|
机器学习/深度学习 算法 搜索推荐
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构(上)
数据结构与算法⑩(第四章_上)树和二叉树和堆的概念及结构
10 0