《一文说透数据结构》系列之什么是堆?看这一篇就够了

本文将首先介绍什么是堆,然后介绍了堆的插入和删除操作,最后给出了堆的代码实现,并进行了测试。

什么是堆

堆是一颗完全二叉树,堆中某个节点的值总是不大于或不小于其父节点的值。根节点最大的堆叫做大根堆,根节点最小的堆叫做小根堆。
首先解释下什么是完全二叉树,设一颗二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。如下图所示,左侧的二叉树满足完全二叉树的定义,而右侧的不满足。

上图左侧便是一个小根堆,满足任意一个节点的值总是不小于其父节点的值。

堆的表示

一般的二叉树表示时需要首先定义节点结构,节点中包含指向父节点的指针,如下所示:

class Node<E>{
        E e;//节点储存的值
        Node left,right;//左右子节点
        public Node(E e){
            this.e = e;
            this.left = this.right = null;
        }
    }

但是堆并不是像树一样存储,其中没有使用父指针或者子指针,而是用数组来实现。怎么用数组来实现呢?先看一张图,如下:

我们从0开始对节点进行编号,寻找其中父子节点之间索引的对应关系。
首先,通过子节点的索引来找父节点的索引,设子节点的索引为i,则其父节点的索引为

int parentIndex = (i - 1) / 2;

然后,通过父节点的索引来找子节点的索引,设父节点的索引为p,则其孩子节点的索引为

int leftChildIndex  = 2 * p + 1;//左子节点
int rightChildIndex = 2 * p + 2;//右子节点

这样,通过子节点与父节点之间的索引关系,便相当于建立了父节点和子节点之间的指针,实现了用数组来存储堆这种数据结构。

堆的插入

对于堆来说,只有插入和删除两种操作,先谈一下堆的插入操作,此处以小根堆为例。

如上图1所示,在小根堆中插入元素0,首先将元素放置在二叉树最后一行的末尾,此时依然是完全二叉树;然后将该元素与父节点的值比较,若改节点的值小于父节点,则进行交换,如图3所示;之后再次与父节点进行对比交换,直至该节点的值大于等于父节点的值或者已经是根节点为止,如图4 所示。此时堆依然满足定义。

堆的删除

对于堆来说,删除元素是指移除根节点。以小根堆为例,是指移除根中最小值的节点,也就是根节点。移除很简单,之后我们要通过操作来使得堆依然满足定义。

首先删除堆中索引为0,也就是根节点,对于小根堆来说也就是最小值。然后将堆中最后一个元素填充至根节点的位置,如图3所示;之后比较该节点与左右子节点,若该节点大于左右子节点中较小的节点的值,则与该节点进行交换(小根堆中父节点永远与左右子节点中较小的那个子节点交换),如图4、5所示;直至满足该节点的值小于其左右子节点的值或者该节点左右子节点均为空。此时堆依然满足定义。

堆的实现

下面给出堆的代码实现,如下所示,实现了堆的插入和删除操作,并进行了测试。

package datastructures;

public class Heap {
    private int[] data;//存储堆的数组
    private int size;//堆中元素的数量
    public Heap(int capacity){
        data = new int[capacity];//初始化数组
        size = 0;//初始化数量
    }

    /**
     * 插入元素
     */
    public void insert(int value) throws Exception{
        if(size == data.length)
            throw new Exception("堆已满");
        else{
            data[size] = value;//将新插入的元素放在堆的末尾
            int i = size;
            size ++;
            while(i > 0){//对堆进行调整,直至满足条件
                int p = (i - 1) / 2;
                if(data[i] < data[p]){
                    int temp = data[i];
                    data[i] = data[p];
                    data[p] = temp;
                    i = p;
                }
                else
                    break;
            }
        }
    }

    /**
     * 删除堆中的元素
     * @return
     * @throws Exception
     */
    public int delMin() throws Exception{
        int res;
        if(size == 0)
            throw new Exception("为空");
        else{
            res = data[0];//返回索引为0的元素
            size -- ;
            data[0] = data[size];//将堆中最后一个元素填充至索引为0的位置
            int i = 0;
            while(2 * i + 1 < size){//对堆进行调整
                int left = 2 * i + 1;
                int right = 2 * i + 2;
                if(right < size && data[right] < data[left] && data[right] < data[i]){
                    int temp = data[i];
                    data[i] = data[right];
                    data[right] = temp;
                    i = right;
                }
                else if(data[left] < data[i] && (right >= size || data[right] >= data[left])){
                    int temp = data[i];
                    data[i] = data[left];
                    data[left] = temp;
                    i = left;
                }
                else
                    break;
            }
        }
        return res;
    }

    //测试
    public static void main(String[] args) throws Exception {
        Heap heap = new Heap(10);
        heap.insert(1);
        heap.insert(5);
        heap.insert(4);
        heap.insert(3);
        heap.insert(6);
        heap.insert(2);
        System.out.println(heap.delMin());
        System.out.println(heap.delMin());
        System.out.println(heap.delMin());
        System.out.println(heap.delMin());
        System.out.println(heap.delMin());
        System.out.println(heap.delMin());
    }

}

推荐阅读
为什么有红黑树?什么是红黑树?看完这篇你就明白了
《深入浅出话数据结构》系列之什么是B树、B+树?为什么二叉查找树不行?
都2020年了,听说你还不会归并排序?手把手教你手写归并排序算法
为什么会有多线程?什么是线程安全?如何保证线程安全?

觉得文章有用的话,点赞+关注呗,好让更多的人看到这篇文章,也激励博主写出更多的好文章。
更多关于算法、数据结构和计算机基础知识的内容,欢迎扫码关注我的原创公众号「超悦编程」。

原文地址:https://www.cnblogs.com/exzlc/p/12234620.html

时间: 2024-10-01 03:10:56

《一文说透数据结构》系列之什么是堆?看这一篇就够了的相关文章

【转】【修真院“善良”系列之十八】WEB程序员从零开始到就业的全资料V1.0——只看这一篇就够了!

这是两年以来,修真院收集整理的学习资料顺序.以CSS15个任务,JS15个任务为基础,分别依据要完成任务的不同的技能点,我们整理出来了这么一篇在学习的时候需要看到的资料. 这是Version 1.0,接下来会优化和更新到2.0.现在只有WEB,接着会推出Java. 任务地址在这里. 任务体系是从简单到难,官网提供了更多更详细的资料. 资料 css部分任务1:九宫格--用html+css制作一个网页通过这个任务能学到:1.网页是由什么组成的:2.如何生成一个网页:3.如何访问一个网页:4.html

【Java面试题系列】:Java基础知识面试题,看这一篇就够了

文中面试题从茫茫网海中精心筛选,如有错误,欢迎指正! 1.前言 参加过社招的同学都了解,进入一家公司面试开发岗位时,填写完个人信息后,一般都会让先做一份笔试题,然后公司会根据笔试题的回答结果,确定要不要继续此次面试,如果答的不好,有些公司可能会直接说"技术经理或者总监在忙,你先回去等通知吧",有些公司可能会继续面试,了解下你的项目经验等情况. 至少在工作的前5年甚至更久,面试一般不会跳过笔试题这个环节(大牛,个别公司除外),我自己也记不清自己面试过多少家公司,做过多少份面试题了,导致现

D&amp;F学数据结构系列——红黑树

红黑树 定义:一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树: 1)每个结点不是红的就是黑的 2)根结点是黑的 3)每个叶结点是黑的 4)如果一个结点是红的,它的两个儿子都是黑的(即不可能有两个连续的红色结点) 5)对于每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点 性质: 这些约束确保了红黑树的关键特性: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长.结果是这个树大致上是平衡的.因为操作比如插入.删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上

D&amp;F学数据结构系列——B树(B-树和B+树)介绍

B树 定义:一棵B树T是具有如下性质的有根树: 1)每个节点X有以下域: a)n[x],当前存储在X节点中的关键字数, b)n[x]个关键字本身,以非降序存放,因此key1[x]<=key2[x]<=...<=keyn[x][x], c)leaf[x],是一个布尔值,如果x是叶子的话,则它为TRUE,如果x为一个内节点,则为FALSE. 2)每个内节点包含n[x]+1个指向其子女的指针c1[x],c2[x],...,cn[x]+1[x].叶节点没有子女,故它们的ci域无意义. 3)各关键

D&amp;F学数据结构系列——二叉堆

二叉堆(binary heap) 二叉堆数据结构是一种数组对象,它可以被视为一棵完全二叉树.同二叉查找树一样,堆也有两个性质,即结构性和堆序性.对于数组中任意位置i上的元素,其左儿子在位置2i上,右儿子在左儿子后的单元2i+1中,它的父亲在[i/2](向下取整)中. 因此,一个数据结构将由一个数组.一个代表最大值的整数.以及当前的堆的大小组成.一个典型的优先队列(priority queue)如下: 1 #ifndef _BinHeap_H 2 struct HeapStruct; 3 type

D&amp;F学数据结构系列——前驱和后继

前驱和后继 本文所述为二叉排序树的前驱和后继,如果想了解二叉排序树的概念,可以参考我的博文*** 给定一个二叉查找树中的结点,有时候要求找出在中序遍历顺序下它的后继.如果所有的关键字均不同,则某一结X点的后继就是所有(结点值)大于X的结点中最小的那个. 包含两种情况: 情况一:结点X的右子树非空,则X的后继是其右子树中最左的结点 情况二:结点X的右子树为空,设X的后继为Y.则Y是X的最低祖先结点,且Y的左儿子也是X的祖先(X自身也可以看做是X的祖先) 1 BT* TreeSuccessor(BT

Spring Boot干货系列:(一)优雅的入门篇

Spring Boot干货系列:(一)优雅的入门篇http://www.cnblogs.com/zheting/p/6707032.html  全篇参考:http://www.cnblogs.com/zheting/category/966890.html 前言 Spring一直是很火的一个开源框架,在过去的一段时间里,Spring Boot在社区中热度一直很高,所以决定花时间来了解和学习,为自己做技术储备.   正文 首先声明,Spring Boot不是一门新技术,所以不用紧张.从本质上来说,

Autofac全面解析系列(版本:3.5) – [使用篇(推荐篇):4.类型关联(服务暴露)]

前言 autofac Autofac是一套高效的依赖注入框架. Autofac官方网站:http://autofac.org/ Autofac在Github上的开源项目:https://github.com/autofac/Autofac Autofac安装:通过VS的Nuget可以很方便的获取. 类型关联(服务暴露) 关于“类型关联(服务暴露)”这个名字,是源于官网上的exposes一词,有点词穷,希望大家在看完博客后能够提供更加贴切的描述名称. 前面的autofac系列文章一直有提到直接注册

Android学习系列(39)--Android主题和样式之系统篇(上)

Android学习系列(39)--Android主题和样式之系统篇(上) [基于最新的Android4.4的源码分析] 每家公司或者每个移动团队无不想开发出一套自己的UI框架,融入自己的设计和特性,这必然会去修改android的ui.所以,学习和理解android的UI设计是最基础和非常有必要的.android ui设计最重要的就是主题和样式. 1.位置在Android的frameworks/base/core/res/res/values目录下有一下几个文件: 1 2 3 4 themes.x