关于堆结构的详解

一、定义

堆的定义

堆其实就是一棵完全二叉树(若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边),

定义为:具有n个元素的序列(h1,h2,...hn),当且仅当满足(hi>=h2i,hi>=h2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)时称之为堆

大顶堆

堆顶元素(即第一个元素)为最大项,并且(hi>=h2i,hi>=h2i+1)

小顶堆

堆顶元素为最小项,并且(hi<=h2i,hi<=2i+1)

二、构建堆(大顶堆)

方法

序列对应一个完全二叉树,从最后一个分支节点(n div 2)开始,到跟(1)为止,一次对每个分支节点进行调整(下沉),以便形成以每个分支节点为根的堆,当最后对树根节点进行调整后,整个树就变成一个堆

实例

先给出一个序列:45,36,18,53,72,30,48,93,15,35

要想此序列称为一个堆,我们按照上述方法,首先从最后一个分支节点(10/2),其值为72开始,一次对每个分支节点53,18,36,45进行调整(下沉)

图解流程



代码实现

/*根据树的性质建堆,树节点前一半一定是分支节点,即有孩子的,所以我们从这里开始调整出初始堆*/
 public static void adjust(List<Integer> heap){
    for (int i = heap.size() / 2; i > 0; i--)
        adjust(heap,i, heap.size()-1);    

    System.out.println("=================================================");
    System.out.println("调整后的初始堆:");
      print(heap);
  }
/**
 * 调整堆,使其满足堆得定义
 * @param i
 * @param n
 */
public static void adjust(List<Integer> heap,int i, int n) {    

    int child;
    for (; i <= n / 2; ) {
        child = i * 2;
        if(child+1<=n&&heap.get(child)<heap.get(child+1))
            child+=1;/*使child指向值较大的孩子*/
        if(heap.get(i)< heap.get(child)){
            swap(heap,i, child);
            /*交换后,以child为根的子树不一定满足堆定义,所以从child处开始调整*/
            i = child;    

        }  else break;
    }
}   

//把list中的a,b位置的值互换
public static void swap(List<Integer> heap, int a, int b) {
    //临时存储child位置的值
    int temp = (Integer) heap.get(a);   

    //把index的值赋给child的位置
    heap.set(a, heap.get(b));   

    //把原来的child位置的数值赋值给index位置
    heap.set(b, temp);
}   

三、堆排序

堆排序的性能介绍(适合处理数据量大的序列)

由于它在直接选择排序的基础上利用了比较结果形成。效率提高很大。它完成排序的总比较次数为O(nlog2n)。

堆排序需要两个步骤,一个建堆,而是交换重新建堆。比较复杂,所以一般在小规模的序列中不合适,但对于较大的序列,将表现出优越的性能
  

算法描述(建堆,交换重新建堆):

  • 初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大
  • 然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆
  • 依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列

代码实现

//对一个最大堆heap排序
public static void heapSort(List<Integer> heap) {    

   for (int i = heap.size()-1; i > 0; i--) {
    /*把根节点跟最后一个元素交换位置,调整剩下的n-1个节点,即可排好序*/
       swap(heap,1, i);
       adjust(heap,1, i - 1);
   }
}

四、堆排序的应用

场景:

如何从100万个数中找出最大的前100个数

算法分析:

先取出前100个数,维护一个100个数的最小堆,遍历一遍剩余的元素,在此过程中维护堆就可以了。

  • 取前m个元素(例如m=100),建立一个小顶堆
  • 顺序读取后续元素,直到结束。每次读取一个元素,如果该元素比堆顶元素小,直接丢弃
    如果大于堆顶元素,则用该元素替换堆顶元素,然后保持最小堆性质。
  • 最后这个堆中的元素就是前最大的100个

原文地址:https://www.cnblogs.com/sxkgeek/p/9662491.html

时间: 2024-10-11 09:43:39

关于堆结构的详解的相关文章

NetBios 的结构体详解(网络控制块NCB)

对之前网络基础编程用到控制块NCB进行介绍(补充): 在Win32环境下,使用VC++6.0进行NetBIOS程序开发时, 需要用到nb30.h文件和netapi32.lib静态链接库.前者定义了NetBIOS的所有函数声明和常数定义,后者定义了NetBIOS应用. Ncb的结构在nb30.h文件中定义.Ncb结构的定义:Ncb有64个字符,分为14个域(或称为字段)和一个10字节的保留域,表2.1显示了Ncb和它的域. 域名及字节偏移量说明. 各个命令详细说明: 1. 命令 NetBIOS命令

NetBios 的结构体详解

[NetBios 的结构体详解] 1.结构体. 2.命令 NetBIOS命令的使用方式有两种,即等待和非等待(或称为同步与异步)方式. 如果命令码的高阶位是0时是等待方式,命令将阻止提交命令的应用程序继续执行,直到该NetBIOS命令执行完毕后才允许这个应用程序继续执行.NetBIOS每次只能处理一个等待方式命令. 如果命令码的高阶位是二进制1时则是非等待方式,命令由NetBIOS在内部排队,并不阻止提交命令的应用程序继续执行.应用程序可通过对命令结束标志字段值的轮询来了解命令执行的状态(详见命

Android目录结构(详解)

Android目录结构(详解) 下面是HelloAndroid项目在eclipse中的目录层次结构: 由上图可以看出项目的根目录下共有九个文件(夹),下面就这九个文件(夹)进行详解: 1.1src文件夹和assets文件夹: 每个Android程序都包含资源目录(src)和资产目录(assets),资源和资产听起来感觉没有多大差别,但在存储外部内容时用资源(src)比较多,其中它们的区别在于存放在资源(src)下的内容可以通过应用程序的R类进行访问,而存放在资产(assets)下的内容会保持原始

jvm堆内存优化详解

在日常的运维工作中用到tomcat,都需要对tomcat中的jvm虚拟机进行优化,只有知道需要优化参数的具体用处,才能深刻体会优化jvm的意义所在. 在平常的工作中我们谈对jvm的优化,主要是针对java的堆内存的优化和垃圾回收机制的优化. JVM堆内存示意图: JVM的堆内存的组成: young generation:新生代 eden:伊甸园区 surived:存活区 其中存活区有2个,第1个为S0,第2个为S1 old generation:老年代 permanent generation:

struct sk_buff结构体详解

struct sk_buff是linux网络系统中的核心结构体,linux网络中的所有数据包的封装以及解封装都是在这个结构体的基础上进行. struct sk_buff_head  {     struct sk_buff *next;     struct sk_buff *prev;          __u32 qlen;     spinlock_t lock; } struct sk_buff {     struct sk_buff *next;     struct sk_buff

Java堆和栈详解

Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用. 堆内存用于存放由new创建的对象和数组. 在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或 者对象在堆内

【STL学习】堆相关算法详解与C++编程实现(Heap)

转自:https://blog.csdn.net/xiajun07061225/article/details/8553808 堆简介 堆并不是STL的组件,但是经常充当着底层实现结构.比如优先级队列(Priority Queue)等等. 堆是一种完全二叉树,因此我们可以用数组来存储所有节点.在这里的实现中,采用了一个技巧:将数组中索引为0的元素保留,设置为极大值或者为极小值(依据大顶堆或者小顶堆而定).那么当某个节点的索引是i时,其左子节点索引为2*i,右子节点索引为2*i+1.父节点是i/2

C#类与结构再次详解

类和结构是 .NET Framework 中的常规类型系统的两种基本构造.  两者在本质上都属于数据结构,封装着一组整体作为一个逻辑单位的数据和行为.  数据和行为是该类或结构的"成员",它们包含各自的方法.属性和事件等(本主题后面列出了这些内容). 类或结构的声明类似于蓝图,用于在运行时创建实例或对象.  如果定义一个名为 Person 的类或结构,则 Person 为类型名称.  如果声明并初始化 Person 类型的变量 p,则 p 称为 Person 的对象或实例.  可以创建

Linux下DIR,dirent,stat等结构体详解

摘要: 最近在看Linux下文件操作相关章节,遇到了这么几个结构体,被搞的晕乎乎的,今日有空,仔细研究了一下,受益匪浅. DIR结构体类似于FILE,是一个内部结构,以下几个函数用这个内部结构保存当前正在被读取的目录的有关信息(摘自<UNIX环境高级编程(第二版)>).函数 DIR *opendir(const char *pathname),即打开文件目录,返回的就是指向DIR结构体的指针,而该指针由以下几个函数使用: 最近在看Linux下文件操作相关章节,遇到了这么几个结构体,被搞的晕乎乎