加强堆结构说明

加强,结构,说明 · 浏览次数 : 125

小编点评

**加强堆结构说明** **作者:Grey** **博客:**与堆和堆排序相关的问题基础的堆结构可以实现数据入堆和出堆以后(即: 调用堆的 pop 和 push 方法),使用O(logN)的时间复杂度可以将堆调整好,如果使用的是 Java 语言,可以用 java.util 包中的 PriorityQueue 实现堆的所有操作。但是,在实际场景中,有一种情况是:在已知的一个堆中,堆中任意一个元素变换后,也要以O(logN)的时间复杂度把堆结构调整正确。这是 Java 语言自带的堆结构(PriorityQueue)无法做到的,这就引入了「加强堆」的概念。 **加强堆的完整代码:** ```java import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; public class Code_CustomHeap { // 自己手写堆 public static class HeapGreater<T> { private ArrayList<T> heap; private HashMap<T, Integer> indexMap; // 元素在堆中的位置 private int heapSize; // 和heap配合使用 private Comparator<? super T> comp; public HeapGreater(Comparator<T> c) { heap = new ArrayList<>(); indexMap = new HashMap<>(); comp = c; } public boolean isEmpty() { return heapSize == 0; } public int size() { return heapSize; } public boolean contains(T obj) { return indexMap.containsKey(obj); } public T peek() { return heap.get(0); } public void push(T obj) { heap.add(obj); indexMap.put(obj, heapSize); heapInsert(heapSize++); } public T pop() { T ans = heap.get(0); swap(0, heapSize - 1); indexMap.remove(ans); heap.remove(--heapSize); if (obj != replace) { // obj == replace表示删掉的是最后一个位置的数据,此时不需要进行resign操作 heap.set(index, replace); indexMap.put(replace, index); resign(replace); } } public void resign(T obj) { heapInsert(indexMap.get(obj)); heapify(indexMap.get(obj)); } // 请返回堆上的所有元素 public List getAllElements() { List<T> ans = new ArrayList<>(); for (T c : heap) { ans.add(c); } return ans; } private void heapInsert(int index) { while (comp.compare(heap.get(index), heap.get((index - 1) / 2)) < 0) { swap(index, (index - 1) / 2); index = (index - 1) / 2; } } private void heapify(int index) { int left = index * 2 + 1; while (left < heapSize) { int best = left + 1 < heapSize && comp.compare(heap.get(left + 1), heap.get(left)) < 0 ? (left + 1) : left; best = comp.compare(heap.get(best), heap.get(index)) < 0 ? best : index; if (best == index) { break; } swap(best, index); index = best; left = index * 2 + 1; } } private void swap(int i, int j) { T o1 = heap.get(i); T o2 = heap.get(j); heap.set(i, o2); heap.set(j, o1); indexMap.put(o2, i); indexMap.put(o1, j); } } } ``` **使用加强堆来解决的问题示例:** ``` 使用加强堆解决 topK 问题更多算法和数据结构笔记参考资料算法和数据结构体系班-左程云。 ```

正文

加强堆结构说明

作者:Grey

原文地址:

博客园:加强堆结构说明

CSDN:加强堆结构说明

关于堆和堆排序的说明

可以参考这篇博客:与堆和堆排序相关的问题

基础的堆结构可以实现数据入堆和出堆以后(即: 调用堆的 pop 和 push 方法),使用O(logN)的时间复杂度可以将堆调整好,如果使用的是 Java 语言,可以用 java.util 包中的 PriorityQueue 实现堆的所有操作。

但是,在实际场景中,有一种情况是:在已知的一个堆中,堆中任意一个元素变换后,也要以O(logN)的时间复杂度把堆结构调整正确。这是 Java 语言自带的堆结构(PriorityQueue)无法做到的,这就引入了「加强堆」的概念。「加强堆」提供如下方法

    public void resign(T obj) {
      
    }

这个方法表示,对于堆中任意的一个元素 obj,如果调整了其对应的数值,整个堆结构还能在时间复杂度O(logN)下调整好。

普通堆结构之所以无法做到,是因为普通的堆结构没有记录任意一个数据所在的位置信息,所以无法从对应的位置进行堆结构调整。所以,「加强堆」结构引入了一个 HashMap

HashMap<T, Integer> indexMap; // 元素在堆中的位置

有了这个HashMap, 就可以很方便得到某个数据项在堆中的位置是什么,在堆的poppush方法中,要把HashMap的逻辑加入

    public void push(T obj) {
      heap.add(obj);
        // obj 这个数据在堆中是什么位置
      indexMap.put(obj, heapSize);
      heapInsert(heapSize++);
    }

    public T pop() {
      T ans = heap.get(0);
      swap(0, heapSize - 1);
      // 要把对应的obj在堆中直接删除
      indexMap.remove(ans);
      heap.remove(--heapSize);
      heapify(0);
      return ans;
    }

更重要的是,在堆的 heapifyheapInsert 操作中,涉及到的堆中两个元素的交换,也要把堆中元素的位置进行交换

// 不要忘记把堆中元素的位置也要做交换!!!!
    private void swap(int i, int j) {
      T o1 = heap.get(i);
      T o2 = heap.get(j);
      heap.set(i, o2);
      heap.set(j, o1);
      indexMap.put(o2, i);
      indexMap.put(o1, j);
    }

以上是铺垫,到了最核心的resign方法,其逻辑如下

    public void resign(T obj) {
      heapInsert(indexMap.get(obj));
      heapify(indexMap.get(obj));
    }

整个过程也非常好理解,就是找到变化的那个数据项的位置,然后执行heapifyheapInsert,由于变换过程无非是变大或者变小,所以找到变换的位置,heapifyheapInsert操作只会执行一个操作!另外一个操作进去以后会直接跳出。

加强堆的完整代码如下(支持泛型):

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

public class Code_CustomHeap {

  // 自己手写堆
  public static class HeapGreater<T> {

    private ArrayList<T> heap;
    private HashMap<T, Integer> indexMap; // 元素在堆中的位置
    private int heapSize; // 和heap配合使用
    private Comparator<? super T> comp;

    public HeapGreater(Comparator<T> c) {
      heap = new ArrayList<>();
      indexMap = new HashMap<>();
      comp = c;
    }

    public boolean isEmpty() {
      return heapSize == 0;
    }

    public int size() {
      return heapSize;
    }

    public boolean contains(T obj) {
      return indexMap.containsKey(obj);
    }

    public T peek() {
      return heap.get(0);
    }

    public void push(T obj) {
      heap.add(obj);
      indexMap.put(obj, heapSize);
      heapInsert(heapSize++);
    }

    public T pop() {
      T ans = heap.get(0);
      swap(0, heapSize - 1);
      indexMap.remove(ans);
      heap.remove(--heapSize);
      heapify(0);
      return ans;
    }

    public void remove(T obj) {
      T replace = heap.get(heapSize - 1);
      int index = indexMap.get(obj);
      indexMap.remove(obj);
      heap.remove(--heapSize);
      if (obj != replace) { // obj == replace表示删掉的是最后一个位置的数据,此时不需要进行resign操作
        heap.set(index, replace);
        indexMap.put(replace, index);
        resign(replace);
      }
    }

    public void resign(T obj) {
      heapInsert(indexMap.get(obj));
      heapify(indexMap.get(obj));
    }

    // 请返回堆上的所有元素
    public List<T> getAllElements() {
      List<T> ans = new ArrayList<>();
      for (T c : heap) {
        ans.add(c);
      }
      return ans;
    }

    private void heapInsert(int index) {
      while (comp.compare(heap.get(index), heap.get((index - 1) / 2)) < 0) {
        swap(index, (index - 1) / 2);
        index = (index - 1) / 2;
      }
    }

    private void heapify(int index) {
      int left = index * 2 + 1;
      while (left < heapSize) {
        int best =
            left + 1 < heapSize && comp.compare(heap.get(left + 1), heap.get(left)) < 0
                ? (left + 1)
                : left;
        best = comp.compare(heap.get(best), heap.get(index)) < 0 ? best : index;
        if (best == index) {
          break;
        }
        swap(best, index);
        index = best;
        left = index * 2 + 1;
      }
    }

    private void swap(int i, int j) {
      T o1 = heap.get(i);
      T o2 = heap.get(j);
      heap.set(i, o2);
      heap.set(j, o1);
      indexMap.put(o2, i);
      indexMap.put(o1, j);
    }
  }
}

使用加强堆来解决的问题示例,见使用加强堆解决 topK 问题

更多

算法和数据结构笔记

参考资料

算法和数据结构体系班-左程云

与加强堆结构说明相似的内容:

加强堆结构说明

加强堆结构说明 作者:Grey 原文地址: 博客园:加强堆结构说明 CSDN:加强堆结构说明 关于堆和堆排序的说明 可以参考这篇博客:与堆和堆排序相关的问题 基础的堆结构可以实现数据入堆和出堆以后(即: 调用堆的 pop 和 push 方法),使用O(logN)的时间复杂度可以将堆调整好,如果使用的

8.4 ProcessHeap

ProcessHeap 是`Windows`进程的默认堆,每个进程都有一个默认的堆,用于在进程地址空间中分配内存空间。默认情况下`ProcessHeap`由内核进行初始化,该堆中存在一个未公开的属性,它被设置为加载器为进程分配的第一个堆的位置(进程堆标志),`ProcessHeap`标志位于`PEB`结构中偏移为`0x18`处,第一个堆头部有一个属性字段,这个属性叫做`ForceFlags`属性偏

如何通过jstat命令进行查看堆内存使用情况?

摘要:jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。 本文分享自华为云社区《JVM之通过jstat命令进行查看堆内存使用情况》,作者:共饮一杯无 。 基本概念 jstat是JDK自带的一个轻量级小工具。它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性

[转帖]【JVM】类加载机制

什么是类的加载 将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供

[转帖]JVM类加载机制

概述 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Cl

Unlink原理和一些手法

Unlink原理和一些手法 ✅简单介绍一下unlink相关的知识 unlink是利用glibc malloc 的内存回收机制造成攻击的,核心就在于当两个free的堆块在物理上相邻时,会将他们合并,并将原来free的堆块在原来的链表中解链,加入新的链表中其目的是把一个双向链表中的空闲块拿出来(例如 f

安全测试常态化落地方案及日常推进机制

数据安全法实施后,国家监管部门加强了对企业数据安全的监管力度。在这个大的背景下,为保障物流体系系统安全,提前规避安全风险,由测试组牵头制定安全测试流程规范并持续推进安全测试常态化。

2023年主要网络安全趋势

2023年,网络安全仍然是企业在加强数字防御任务中的重点。随着勒索软件攻击持续上升,零信任模型变得更加普遍,越来越多的公司开始使用在线技术来自动化他们的运营,而这也导致大量数据存在于互联网中,在一定程度上造成了数据的泄露和失窃,这对于小型企业、个人和大公司来说竟已经是司空见惯的事情。在2022年第一

基于uniapp+vue3自定义增强版table表格组件「兼容H5+小程序+App端」

vue3+uniapp多端自定义table组件|uniapp加强版综合表格组件 uv3-table:一款基于uniapp+vue3跨端自定义手机端增强版表格组件。支持固定表头/列、边框、斑马纹、单选/多选,自定义表头/表体插槽、左右固定列阴影高亮显示。支持编译兼容H5+小程序端+App端。 如下图:

Redis 菜鸟进阶

Redis 菜鸟进阶 背景 最近产品一直要优化性能,加强高可用. 有一个课题是Redis高可用与性能调优. 我这边其实获取到的内容很有限. 最近济南疫情严重,自己锁骨骨折. 然后通勤时间基本上都用来查资料了. 但是毕竟水平有限..简单整理总结一下. 估计纰漏很多..需要进一步学习. 单点 观点: R