文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

从二叉堆进阶到堆排序与优先队列,前端大佬都这样学...

2024-12-02 03:37

关注

大根堆

在是完全二叉树的前提下,其节点值大于其左右子节点值,称为大根堆。在大根堆中根节点是所有堆节点中的最大值。

小根堆

在是完全二叉树的前提下,其节点值小于其左右子节点值,称为小根堆。在小根堆中根节点是所有堆节点中的最小值。

二、二叉堆的存储

上述阐述了二叉堆其实就是一棵完全二叉树,然后数据满足某些特性,那么在编程中应该如何存储这些数据呢?这些数据间满足那些关系呢?

1.以数组结构存储二叉堆

在编程中我们会以数组存储二叉堆,以上述的最大堆为例,存储到数组中为如下结构:

2.二叉堆中父子节点间满足何种关系

既然数据将存储到数组中,那么就必须知道父子节点之间的关系,即:

(1)在知道父节点的索引的时候必须找到其对应的子节点;

(2)在知道子节点的索引的时候找到其对应的父节点;

如下的二叉堆,设置其根节点索引为0,然后依次递增:

已知某节点的索引为i,则其左右子节点索引为:

已知某节点的索引为i,则其父节点索引为:

三、堆的基本操作

当遇到二叉堆的时候,我们应该学会如何构建一个大根堆(小根堆),这个时候就涉及到加堆和减堆的过程,下面让从加堆和减堆开始,从而使我们能够丝滑般的了解整个二叉堆的构建过程。(注:下面的例子以大根堆为例)

// 注:此处是封装的交换数组中元素的方法
// 数组中交换数据
function swap(arr, i, j) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

1.加堆

加堆的过程实际上就是再往堆里面加入一个新的元素,然后维护该堆结构的过程,实际上就是在数组尾部加入一个元素,然后通过不断上浮,使其又调整为二叉堆结构的过程。


// 大根堆加堆过程中
function heapInsert(arr, index) {
// 比较当前位置和其父位置,若大于其父位置,则进行交换,并将索引移动到其父位置进行循环,否则跳过
// 结束条件是比其父位置小或者到达根节点处
while (index > 0 && arr[index] > arr[parseInt((index - 1) / 2)]) {
swap(arr, index, parseInt((index - 1) / 2));
index = parseInt((index - 1) / 2);
}

return arr;
}

有了加堆的过程,很容易我们就可以实现创建二叉堆,如下所示:

// 创建二叉堆
function createBigTopHeap(arr) {
for (let i = 0; i < arr.length; i++) {
arr = heapInsert(arr, i);
}

return arr;
}


const arr = [1, 5, 9, 10, 8, 12];

console.log(createBigTopHeap(arr)); // [ 12, 9, 10, 1, 8, 5 ]

2.减堆

减堆的过程就是将堆顶元素A和最后元素B交换,然后删除A元素并将B元素调整到合适的位置的过程,实际上就是将堆顶数据交换后,下沉数据的过程

// 减堆过程
// size指的是这个数组前多少个数构成一个堆
// 如果想把堆顶弹出,则把堆顶和最后一个数交换,把size减1,然后从0位置经历一次heapify,调整一下,剩余部分变成大顶堆
function heapify(arr, index, size) {
// 获取其左节点索引
let left = 2 * index + 1;
while (left < size) {
// 判断一下左右子节点哪个值最大,获取其最大值
let largest = (left + 1 < size && arr[left] < arr[left + 1]) ? left + 1 : left;
// 判断左右子节点最大值与当前要比较的节点哪个大
largest = arr[index] > arr[largest] ? index : largest;

// 如果最大值索引和传进来的索引一样,则该值到达指定位置,直接结束循环
if (index === largest) {
break;
}

// 进行交换,改变索引及其左子节点
swap(arr, index, largest);
index = largest;
left = 2 * index + 1;
}

return arr;
}

const arr = [1, 5, 9, 10, 8, 12];

console.log(createBigTopHeap(arr));

// 交换堆顶数据与最后位置数据
swap(arr, 0, arr.length - 1);

// 通过下沉的方式获取新的堆结果
console.log(heapify(arr, 0, arr.length - 1));

四、叉堆的用途

因为二叉堆能够高效的找出最大值和最小值,所以可用于堆排序和构建优先队列。

1.堆排序

堆排序是常见的一种排序算法,整个堆排序过程如下所示:

function heapSort(arr) {
if (arr === null || arr.length === 0) {
return [];
}

// 首先创建大根堆
for (let i = 0; i < arr.length; i++) {
arr = heapInsert(arr, i);
}

let size = arr.length; // 这个值用来指定多少个数组构成堆

// 由于当前已经是大根堆,所以直接交换
swap(arr, 0, --size);

while (size > 0) {
// 重新变成大顶堆
heapify(arr, 0, size);
// 进行交换
swap(arr, 0, --size);
}

return arr;
}

const arr = [1, 5, 9, 10, 8, 12];

console.log(heapSort(arr)); // [ 1, 5, 8, 9, 10, 12

2.优先队列

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。通常采用堆数据结构来实现。

优先队列的插入

优先队列中元素的插入就是上述heapInsert过程,通过将元素添加到数组末尾,然后通过上浮的方式将内容插入到合适位置。

优先队列中元素的删除

优先队列中元素的删除就是将根节点的元素与最后元素交换,然后将交换后的根节点下沉,调整到合适位置,重新构成二叉堆。

来源:前端点线面内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯