Heap

堆定义:(这里只讲二叉堆)堆实为二叉树的一种,分为最小堆和最大堆,具有以下性质:

  • 任意节点小于/大于它的所有后裔,最小/大元在堆的根上。
  • 堆总是一棵完全二叉树

  将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的相关操作:

  • 建立
  • 插入
  • 删除

应用:

  • 堆排序
  • 优先队列
  • 合并容器元素
  • 找出第k大元素

Java实现:

/**
* Created by XuTao on 2018/11/5 22:10
* ···最小堆···
* 《注意: 实际上并不需要用节点来真正构造一颗树,我们只是在数组中操作排序,调整好的数组就是一个堆的层遍历结果》
*
* 插入:
* 也是插入末尾,然后调整,调整也应该是一个连续向上的过程,建树就是一个连续插入的过程
*
* 删除最小:
* 即删除root:
* 用末尾一个代替root,删除末尾,然后siftDown,如果子节点有更小的,每次只需要找到最小的子节点,然后交换即可。
*
* siftDown:
* 如果建树得以保证,那么如果子节点有更小的,每次只需要找到最小的子节点,然后交换即可。
* 如果是一个乱的树,那么就要考虑,比较麻烦, 解决方式:
* i 从 最后一个节点的父节点出开始迭代,直到i = 0;
* 每次检查时,将大的节点交换到末尾——就是要到底,如果大,就要成为叶节点,不是只交换一次(用循环)
*
* 那么就有两种构建方法:
* 1.乱序构建,调整( 一个一个添加(从数组中),添加到最后一个,树的最右最下方的那个,然后siftUp,从下往上调整就可以了 ,O(log2(n)))
* 2.一次一节点,依次调整
*
* <p>
* 思考题: 设计算法检查一个完全二叉树是不是堆,是的话是最大堆还是最小堆。
* 思路:元素1个,同为最大最小堆
* 元素>1个:
* 判断第一二个大小
* 第一个大: 可能为最大堆,然后递归校验,如果每一个节点都比子节点大,那么是最大堆,否则不是堆
* 第二个大: 可能为最小堆,然后递归校验,如果每一个节点都比子节点小,那么是最小堆,否则不是堆
* <p>
*
*时间复杂度分析:
* 建树:两种方式都是 O(nlog2(n))
* 插入: O(log2(n))
* 删除: O(log2(n))
*/ public class Heap {
private int[] data;
private final int maxSize = 128; //预设大小,足够就行
private int heapSize; //实际大小 public Heap(int[] input) {
data = new int[maxSize];
heapSize = input.length;
for (int i = 0; i < heapSize; i++) {//这个地方其实并不好,只是将传入的数组读入我的数组中,一方有不断插入操作,如果没有插入操作则不必要;
data[i] = input[i];
}
} public void build_1() {
/**
* 建树方法1:
* 每次插入一个节点
*/
int a = heapSize;
heapSize = 0;
for (int i = 0; i < a; i++) {
insert(data[i]);
}
} public void build_2() {
/**
* 建树方法2:
* 以原来的乱序进行调整:siftDown
*/
if (heapSize <= 1) return;
for (int i = getParent(heapSize - 1); i >= 0; i--) { // 从末元素的父节点开始,一次一次进行siftDown
siftDown(i);
}
} /**
* 由上而下调整, sift——筛
* @param start
*/
public void siftDown(int start) {
//start至少1子,不用担心溢出问题
while (getLeft(start) < heapSize) { //注意,这里必须是小于,不能等于,如果该节点的左节点是末尾节点则结束,条件是getLeft(start)==heapSize-1
int min = 0;//判别有没有发生交换的条件
//无右子
if (getRight(start) >= heapSize) {
if (data[start] > data[getLeft(start)]) {
min = getLeft(start);
swap(start, min);
}
}
//2子
else {
min = data[getLeft(start)] > data[getRight(start)] ? getRight(start) : getLeft(start);
if (data[start] > data[min]) {
swap(start, min);
}
}
if (min == 0) break;//满足堆条件,退出
start = min; //不满足堆条件,还可以调整,继续循环
}
} /**
* 由下而上调整
* @param start 开始的下标
*/
public void siftUp(int start) {
if (start <= 0) return;
while (data[start] < data[getParent(start)]) { //一直发生交换,直到满足条件
swap(start, getParent(start));
start = getParent(start);
if (start <= 0) break;// root
}
} public void insert(int a) {
/**
* 插入的话会使数组长度加一,比较麻烦,于是我建立一个比较大的树,用一个较大的量maxSize来限定堆的最大容量,用heapSize来声明实际的容量
*/
data[heapSize] = a;
siftUp(heapSize);
heapSize++;
} public int getLeft(int i) {
return 2 * i + 1;
} public int getRight(int i) {
return 2 * i + 2;
} public int getParent(int i) {
if (i == 0) return -1;
return (i - 1) >> 1; //除以2
} public void swap(int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
} public void display() {
for (int i = 0; i < heapSize; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
} public static void main(String[] args) {
int[] a = new int[]{8, 12, 2, 5, 3, 7, -1, 44, 23};
Heap heap = new Heap(a);
heap.display();
// heap.build_1();
heap.build_2();
heap.insert(-4);
heap.display();
}
}

堆(Heap)详解——Java实现的更多相关文章

  1. 详解Java GC的工作原理+Minor GC、FullGC

    详解Java GC的工作原理+Minor GC.FullGC 引用地址:http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html J ...

  2. Protocol Buffer技术详解(Java实例)

    Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...

  3. 详解Java中的clone方法

    详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...

  4. java基础(十五)----- Java 最全异常详解 ——Java高级开发必须懂的

    本文将详解java中的异常和异常处理机制 异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常. Java异常的分类和类结构图 1.Java中的所 ...

  5. 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)

    在Thread中有异常处理器相关的方法 在ThreadGroup中也有相关的异常处理方法 示例 未检查异常 对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行 在主线程中能不能捕获呢? 我 ...

  6. 第三节:带你详解Java的操作符,控制流程以及数组

    前言 大家好,给大家带来带你详解Java的操作符,控制流程以及数组的概述,希望你们喜欢 操作符 算数操作符 一般的 +,-,*,/,还有两个自增 自减 ,以及一个取模 % 操作符. 这里的操作算法,一 ...

  7. 第十八节:详解Java抽象类和接口的区别

    前言 对于面向对象编程来说,抽象是它的特征之一. 在Java中,实现抽象的机制分两种,一为抽象类,二为接口. 抽象类为abstract class,接口为Interface. 今天来学习一下Java中 ...

  8. 详解java动态代理机制以及使用场景

    详解java动态代理机制以及使用场景 https://blog.csdn.net/u011784767/article/details/78281384 深入理解java动态代理的实现机制 https ...

  9. 【转帖】windows命令行中java和javac、javap使用详解(java编译命令)

    windows命令行中java和javac.javap使用详解(java编译命令) 更新时间:2014年03月23日 11:53:15   作者:    我要评论 http://www.jb51.ne ...

  10. 超全详解Java开发环境搭建

    摘自:https://www.cnblogs.com/wangjiming/p/11278577.html 超全详解Java开发环境搭建   在项目产品开发中,开发环境搭建是软件开发的首要阶段,也是必 ...

随机推荐

  1. 利用DNSLOG获取看不到的信息(给盲注带上眼镜)

    一.前言 本文原创作者:sucppVK,本文属i春秋原创奖励计划,未经许可禁止转载! 毕业设计总算搞得差不多了,这个心累啊.这不,完成了学校的任务,赶紧回来给蛋总交作业.今天给大家分享一个姿势吧,不是 ...

  2. ASP.NET MVC动态加载数据

    ASP.NET MVC动态加载数据,一般的做法是使用$.each方法来循环产生tabel: 你可以在html时先写下非动态的部分:  Source Code 上图中,有一行代码: <tbody ...

  3. alexnet- tensorflow

    alexnet 在 imagenet上夺冠是卷积神经网络如今这么火热的起点. 虽然卷积神经网络很早就被提出来,但是由于计算能力和各方面原因,没有得到关注. alexnet 为什么能取得这么好的成绩,它 ...

  4. python 学习 argparse

    1. argparse python 中用于解析命令行参数和选项的标准模块 创建一个解析器对象,解析器类ArgumentParser add_argument() 方法用来指定程序接受那些命令行参数, ...

  5. Java SE之正则表达式四:获取

    /** * * @author Zen Johnny * @date 2018年4月29日 下午4:51:08 * */ package demo.regex; import java.util.re ...

  6. 牛客寒假算法基础集训营4 G(最小生成树)

    题目链接 题目要求的是得到k种不同的元素,(题解)将每种类型视为一个点,跑一边克鲁斯卡尔即可. #include <set> #include <map> #include & ...

  7. Lattice

    Lattice是一个无环WFSA,结点可以是hmm状态.hmm(音素).词,每个结点是一段音频在某个时间的对齐 用训练好的声学模型.现成的语言模型和发音字典构建解码网络(wfst),最后将提取的测试集 ...

  8. 【mmall】Guava框架

    Guava 简介:http://www.yiibai.com/guava 本项目主要用到了Guava缓存

  9. sql-connectionStrings

    <connectionStrings> <add name="ClassReservatConnectionString" connectionString=&q ...

  10. Social Grouping for Multi-Target Tracking and Head Pose Estimation in Video(翻译)

    0 - ABSTRACT 许多计算机任务在缺少上下文信息的情况下的处理会更加困难.例如,在多相机跟踪任务下,行人可能在不同照相机下面因为有这不同的姿势和灯光条件而看起来很不一样.类似地,在低分辨率高角 ...