堆儿

  • 堆的概念
  • 堆的操作
    • 向下调整
    • 搞一个大堆
    • 向上调整
    • 入堆
    • 出堆
  • 使用Java中的堆
  • top-K问题
    • 分享一个题目
  • 堆排序

堆的概念

堆就是优先级队列

  1. 堆在逻辑上是一棵完全二叉树
  2. 堆在物理上是储存在数组中的
  3. 进行堆操作时,可以将数组写作完全二叉树,运用双亲的下标操作
    双亲下标 parent
    左孩子下标 = parent * 2 + 1;
    右孩子下标 = parent * 2 + 2;

    知道孩子下标(左孩子和右孩子都行)
    parent = ( child - 1 ) / 2;
  4. 大根堆
    任意节点的值都大于其孩子节点的值(左右孩子值的大小比较无要求)

堆的操作

向下调整

前提:左右子树必须已经是一个堆

如某节点的左右子树都是大堆,将这个树向下调整成大堆

  1. parent是开始节点的下标,child是其左孩子的下标
  2. 如果child超过或等于数组长度,说明没有左孩子,因为是完全二叉树,也没有右孩子,即说明这树是大堆喽
  3. 如果child<size,说明有左孩子
  4. 先判断其有没有右孩子,如果有并且大于左孩子,则child++,成为右孩子下标;如果没有,child依旧是左孩子下标
  5. 判断parent与child节点的大小,如果parent节点小于child节点,交换值,继续向下调整,parent标记子孩子,child向下标记新的子孩子(因为子树的节点变换了,不一定是大堆了,要继续将其变为大堆);如果parent节点大于child节点,直接break退出循环,此时它就是一个大堆
    public void adjustDown(int[] array, int begin, int size) {int parent = begin;int child = parent * 2 + 1;while (child < size) {if (child + 1 < array.length && array[child + 1] > array[child]) {child++;}if (array[parent] < array[child]) {int temp = array[parent];array[parent] = array[child];array[child] = temp;parent = child;child = child*2+1;}else{break;}}}

搞一个大堆

  1. 堆就是一个数组,创建字段
  2. 找最后的一棵子树,child为最后一个节点的下标,然后找到parent是父亲节点的下标,进行向下调整,调整成大堆
  3. 对于每一棵子树进行向下调整,直至最后一棵树(parent=0,即parent标记第一个节点)
public class MyHeap {public int[] elem;public int useSize;public MyHeap() {this.elem = new int[10];}//建一个最大堆public void creatHeap(int[] array){for (int i = 0 ; i < array.length; i++) {this.elem[i] = array[i];this.useSize++;}//确定最后一棵子树的父树int child = this.useSize-1;int parent = (child-1)/2;//对于每棵子树进行调换大小的操作for( ;parent>=0;parent--){adjustDown(parent,this.useSize);}}//向下调整public void adjustDown(int parent,int size){int child = parent*2+1;while(child < size) {if (child + 1 < len && this.elem[child + 1] > this.elem[child]) {child++;}if (this.elem[parent] < this.elem[child]) {int temp = this.elem[parent];this.elem[parent] = this.elem[child];this.elem[child] = temp;parent = child;child = parent * 2 + 1;} else {break;}}}

向上调整

前提:这个树已经是一个堆
如某树是一个大堆
向上调整用于 入堆或改变原来堆的叶子节点,再将其变为大堆

  1. 向上调整的起点child,如果child<=0,说明完成调整了
  2. 比较孩子节点与父亲节点的大小,如果孩子节点>父亲节点,交换;否则,直接break退出循环
  3. 交换节点后,新的父亲节点为原parent的父亲节点,新的孩子节点为原parent,进行循环
  4. 由于原本就是一个大堆,只要孩子节点没交换,一定小于父亲节点,所以不用左右比较
    public void adjustUp(int[] arrays, int child){int parent = (child-1)/2;while(child > 0){if(arrays[parent] < arrays[child]){int temp = arrays[parent];arrays[parent] = arrays[child];arrays[child] = temp;child = parent;parent = (child-1)/2;}else{break;}}}

入堆

数组尾插,不够扩容,向上转型

//this.elem已经是一个大堆啦public void adjustUp(int child){int parent = (child-1)/2;while(child > 0){if(this.elem[child] > this.elem[parent]){int temp = this.elem[child];this.elem[child] = this.elem[parent];this.elem[parent] = temp;child = parent;parent = (child-1)/2;}else{break;}}}public void push(int val){if(isFull()){this.elem = Arrays.copyOf(this.elem,2*this.elem.length);}this.elem[this.useSize] = val;this.useSize++;adjustUp(this.useSize-1);}public boolean isFull(){return this.useSize == this.elem.length;}

出堆

  1. 判断堆是否为空
  2. 交换堆首元素与堆尾的元素,即交换数组第一个元素和最后一个元素
  3. 堆的长度-1
  4. 向下调整
//this.elem是一个大堆public void pop(){if(isEmpty()){return;}int temp = this.elem[0];this.elem[0] = this.elem[this.useSize -1];this.elem[this.useSize -1] = temp;this.useSize--;adjustDown(0,this.useSize);}public boolean isEmpty(){return this.useSize == 0;}

使用Java中的堆

默认是一个小堆

PriorityQueue<Integer> minHeap = new PriorityQueue<>();

PriorityQueue的构造方法



如果堆中的元素无法直接比较的话,可以用Java对象比较的两种方法

  1. 新建一个比较类(实现Comparator接口,重写compare()方法)或 使用匿名内部类(重写compare()方法)
  2. 不可以直接用比较的对象类实现Comparator接口

由图可知:

  • 如果Compare(入队元素,父元素)>= 0 ;入队元素直接放入最后一个位置
  • 如果 Compare(入队元素,父元素)< 0;入队元素和父元素交换
  • Compare(o1, o2) o1 - o2 是建小根堆; o2 - o1 是建大根堆
class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}}
class AgeComparator implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {//o1是入队的,o2是原来的//用于建最小堆  排序就是从小到大  return o1.getAge()-o2.getAge();//用于建最大堆  排序就是从大到小  //  return o2.getAge()-o1.getAge();}
}public static void main(String[] args) {//建最小堆PriorityQueue<Student> maxHeap = new PriorityQueue<>(new AgeComparator());maxHeap.offer(new Student("dong",18));maxHeap.offer(new Student("gang",20));maxHeap.offer(new Student("hui",16));System.out.println(maxHeap);}
    public static void main(String[] args) {PriorityQueue<Student> heap = new PriorityQueue<>(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {// 建最小堆return o1.getAge()-o2.getAge();}});}
  1. 需要比较的对象的类实现Comparable接口,重写CompareTo()方法

  • compareTo(原来存在的父亲元素)
  • 入堆元素.compareTo() >= 0 ; 入队元素在最后一个位置
  • 入堆元素.compareTo() < 0;入堆元素和父亲元素交换
class Student implements Comparable<Student>{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {//建小堆  排序就是从小到大return this.getAge()-o.getAge();//建大堆  排序就是从大到小return o.getAge()-this.getAge();}
}public static void main(String[] args) {Student student1 = new Student("拉塞尔",29);Student student2 = new Student("亚历山大",27);Student[] students = {student1,student2};Arrays.sort(students);System.out.println(Arrays.toString(students));}

top-K问题

一组数据中找前k个最大(最小)的数

  1. 若找前k个最小的数,建立一个长度为k的最大堆
  2. 遍历数组,前k个元素建堆,堆顶元素是这个堆中最大的元素
  3. 堆建好后继续遍历数组,如果元素比堆顶的元素小,出堆顶元素,入该元素,以此循环
  4. 遍历完成数组,最后的堆中就是所求的元素
    public PriorityQueue<Integer> topK(int[] array,int k){PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, new Comparator<Integer>() {@Override// 最大堆public int compare(Integer o1, Integer o2) {return o2-o1;}});for (int i = 0; i < array.length; i++) {if(maxHeap.size() < k){maxHeap.offer(array[i]);}else{if(maxHeap.peek()>array[i]){maxHeap.poll();maxHeap.offer(array[i]);}}}return maxHeap;}

分享一个题目

top-K问题

    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k){PriorityQueue<List<Integer>> maxHeap = new PriorityQueue<>(k, new Comparator<List<Integer>>() {@Overridepublic int compare(List<Integer> o1, List<Integer> o2) {// 建一个最大堆return (o2.get(0)+o2.get(1)) - (o1.get(0)+o1.get(1));}});for (int i = 0; i < nums1.length; i++) {for (int j = 0; j < nums2.length; j++) {if(maxHeap.size() < k){List<Integer> list = new LinkedList<>();list.add(nums1[i]);list.add(nums2[j]);maxHeap.offer(list);}else{List<Integer> list = maxHeap.peek();int count = list.get(0) + list.get(1);if(nums1[i] + nums2[j] < count){List<Integer> list1 = new LinkedList<>();list1.add(nums1[i]);list1.add(nums2[j]);maxHeap.poll();maxHeap.offer(list1);}}}}List<List<Integer>> list = new LinkedList<>();for (int i = 0; i < k && !maxHeap.isEmpty(); i++) {list.add(maxHeap.poll());}return list;}

此时堆顶元素就是这个数组第k小的元素

堆排序

从小到大排列,此时this.elem是一个大堆了!!!

  1. 交换 堆顶元素(第一个元素) 和 堆尾(最后一个元素),此时堆尾的元素就是此数组中最大的元素
  2. 再对堆顶元素进行向下调整(注意范围要-1,最后一个元素不参与此调整)
  3. end–,再交换第一个元素和倒数第二个元素,此时倒数第二个元素就是第二大
  4. 依次循环,直到end=0为止
    public void sort(int[] array){int end = array.length-1;while(end>0) {int temp = array[0];array[0] = array[end];array[end] = temp;adjustDown(array, 0, end);end--;}}

认真生活,就能找到生活藏起来的糖果。

人的一生会遇到三种人,一个惊艳了时光,一个温柔了岁月,一个讲懂了“堆”相关推荐

  1. 四种人适合一起创业,三种人绝对不能合伙

    导读: 创业过程中存在着太多的不确定因素,对于单个创业者来说,创业风险是巨大的,因此,许多创业者喜欢与别人合作进行"共同创业",一个合适的创业伙伴能够极大的提升创业的成功率 创业过 ...

  2. 鬼谷子:深交四种人,绝交三种人

    导读: 鬼谷子是中国历史上一位极具神秘色彩的人物,被誉为千古奇人,创建鬼谷门派,长于持身养性,精于心理揣摩,深明刚柔之势,通晓纵横捭阖之术,独具通天之智.鬼谷子身怀旷世绝学,智慧卓绝,精通百家学问,是 ...

  3. 鬼谷子:深交四种人,绝交三种人!

    和什么样的人在一起,就有什么样的人生. 朋友是我们选择的亲人. 日夜相处,耳濡目染,朋友的模样,就是自己的模样. 跟着蜜蜂能找到花蜜,跟着苍蝇只能找到水沟. 选择朋友,就是选择自己的命运. 不是所有人 ...

  4. 人的一生,有三件事情不能等

    人的一生,有三件事情不能等. 第一是"贫穷" 贫穷不能等,因为一但时间久了,你将习惯贫穷,到时不但无法突破自我,甚至会抹杀了自己的梦想,而庸庸碌碌的过一辈子...... 第二是&q ...

  5. 脸上有酒窝,脖子后有痣,胸前有颗痣,此三种人不能错过

         脸上有酒窝,脖子后有痣,胸前有颗痣,此三种人不能错过!        传说苦情痣.酒窝的来历是这样的: 相传人死后,过了鬼门关便上了黄泉路,路上盛开着只见花,不见叶的彼岸花.花叶生生两不见, ...

  6. 这个世界依聪明才智的先天高下得三种人:先知先觉的发明家,后知后觉的宣传家,不知不觉的实践家...

    这个世界依聪明才智的先天高下得三种人:先知先觉的发明家,后知后觉的宣传家,不知不觉的实践家

  7. 网上赚钱的门路方法,大部分人都是利用这三种方法!

    想通过网络来挣钱,无外乎以下方法:用时间来赚钱.用钱来生钱.通过推广赚钱.而每一种挣钱方式,都是需要有一定付出的,只有付出了才会有收获.本文就来给大家介绍一下想要通过网赚挣钱,我们都应该了解些什么?网 ...

  8. 盛大半年员工扩招1千 首富陈天桥只招三种人

    在参加完一个行业大会之后,张燕梅觉得有些不可思议,她很奇怪,一个主题这么棒的大会,居然会办成"这个样子","如果是我,早就自己打包辞职了". 这时的张燕梅已经从 ...

  9. 使用key 发smtp.sendgrid.net_【点亮人生】微信上从来不发朋友圈的男人,不是低调,十有八九是这三种人...

    纵看古今,人生皆戏,戏映人生.关注"大鼎豫剧"公益平台,每天免费为您提供经典戏剧.曲艺.新闻轶事及深度哲文,为您的生活增光添彩. 文 | 佚名 来源 | 历史趣 随着微信的广泛使用 ...

最新文章

  1. 面试使用计算机,面试相关之计算机基础
  2. JVM---StringTable(字符串常量池)
  3. 除了芯片 我们还应关注哪些核心技术
  4. 在Xamarin.iOS项目中使用预设数据库
  5. ​基于BCI的现代神经反馈有助于认知增强(二)
  6. sqluldr2支持mysql吗_Oracle SQLULDR2 以及 SQLLDR 进行导入导出的功能说明
  7. ElasticSearch---------------------Elasticsearch Clients---------------------JAVA API
  8. java 实现斐波切纳数列,求解这个算斐波切纳兔子有关问题的算法错哪了(和stl迭代器貌似有点关系)...
  9. 每一个正整数可以表示为3个三角形数之和
  10. python中数据用折线图表示_用python处理文本数据
  11. DiscoveryService.getRemoteAdvertisements是否会获得本地通告?
  12. SpringMVC之数据传递三Ajax与Controller交互
  13. 大工17春计算机文化基础,大工17春《计算机文化基础》在线测试
  14. 以太坊代币事件监控_以太坊程序员的常见误解
  15. solr 6.4 mysql_solr6.4.1搜索引擎(2)首次同步mysql数据库
  16. 短短几分钟快速带你掌握C语言指南针!
  17. linux移动文件 rf参数_linux下文件的复制、移动与删除
  18. 雅可比迭代法和高斯赛德尔迭代法
  19. Web移动开发基本概念
  20. java中文getbytes为3_Java getBytes方法详解(字符集问题)

热门文章

  1. 素描的诀窍-第一章 作画步骤
  2. 全球及中国商业无线雷管行业研究及十四五规划分析报告
  3. Chrome插件manifest.json文件详解
  4. 二叉树应用-Huffman树类模板的实现(数据结构基础 第6周)
  5. mysql远程访问权限开启
  6. 7月以后考研复习这样规划,离上岸更近一步!
  7. 中兴交换机创建acl操作以及引用至端口操作方式
  8. html5 jssdk,微信公众号与HTML 5混合模式揭秘1——如何部署JSSDK
  9. 第四天 可用试验环境和创建快照
  10. 一个模仿探探头像编辑效果解析