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. 三十八、Linux 线程——线程属性初始化、销毁、设置和获得分离属性

    38.1 线程属性初始化和销毁 #include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); int pthread_ ...

  2. 关于JSON CSRF的一些思考

    CSRF作为常见漏洞,一直受到关注和研究,JSON是一种应用广泛的轻量级数据交换格式,当CSRF去POST一段JSON,情况可能会变得有些不一样:此次就一种特殊情况下的CSRF进行分析,权当抛砖引玉. ...

  3. grep 打印相关行内容

    grep 打印相关行数常用参数: -r:关键字 -c:打印符合要求的行数 -i:忽略大小写 -n:输出行和行号 -v:打印不符合要求的行,即反选 -A:后跟数字(有无空格都可以),例如 -A3表示打印 ...

  4. [C++]Linux之头文件sys/types.h[/usr/include/sys]

    1.查找<sys/types.h>文件 一般地,Linux的C头文件<sys/types.h>路径在如题的途径:/usr/include/sys下,然而博主[Linux For ...

  5. PHP7语法知识(四):目录文件操作、Cookie与Session、MySQL数据库的使用、Redis数据库、PHP处理XML与JSON

    目录文件操作 一.目录 1.判断文件类型: 2.创建和删除目录: 3.打开读取和关闭目录 4.获得路径中目录部分 5.目录磁盘空间 二.文件操作 1.打开文件: 2.读取文件: 3.获得文件属性: 4 ...

  6. vi常用指令

    vi filename 打开文件 i 进入编辑模式 esc 退出编辑模式 : 进入指令模式 wq 保存退出 q!不存盘强制退出vi 按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字 ...

  7. react踩坑记录——使用fetch获取json数据报错

    报错: 原因其实是list.json文件路径错误,该文件路径是相对于index.html的,而不是App.js或者index.js.

  8. Microsoft SQL - 查询与更新

    查询与更新(Query & Update) 转义引号 SQL语句中字符串只能使用单引号,如果需要转义,可用单引号转义单引号. 查询(Inquire) 以下公式中的c指代列名. 规则 1.查询语 ...

  9. MySql cmd下的学习笔记 —— 有关子查询的操作(where型,from型,exists型子查询)

    先找到goods表 查询goods_id最大的商品 where型的子查询 查询goods_id最大的商品(不能用排序) 把两步写成一步,就是子查询 from型子查询 查找出每种cat_id下goods ...

  10. 已安装nginx支持https配置 the "ssl" parameter requires ngx_http_ssl_module

    原文链接:https://blog.seosiwei.com/detail/28 nginx已安装,ssl模块未安装的解决方法: 如果需要在linux中编译自己的nginx服务器,请参照:https: ...