首页 > 其他分享 >数据结构第16节 最大堆

数据结构第16节 最大堆

时间:2024-07-08 13:00:40浏览次数:13  
标签:index 最大 16 int maxHeap heap 数据结构 public size

最大堆是一种特殊的完全二叉树数据结构,其中每个父节点的键值都大于或等于其子节点的键值。在Java中,最大堆通常用于实现优先队列,堆排序算法,或者在需要快速访问最大元素的应用场景中。

让我们通过一个具体的案例来说明最大堆的使用和实现:假设我们正在开发一个系统,用于实时分析用户行为数据,比如网站上的点击率。系统需要持续地接收点击事件,并能够快速报告过去一段时间内最高点击率的前N个页面。

实现最大堆

为了实现这个功能,我们可以使用最大堆来保存页面的点击率,堆顶元素总是保持为当前观察窗口内的最高点击率页面。当有新的点击事件到来时,我们将更新堆以反映最新的状态。

步骤:
  1. 初始化堆:创建一个最大堆,可以使用数组来实现。堆中第一个元素(索引1,因为索引0通常不使用)是堆顶,也就是当前最大值。

  2. 添加元素:当接收到新的点击事件时,将页面和其点击率插入堆中。为了保持堆的性质,需要执行“上浮”操作,即新插入的元素与其父节点比较,如果比父节点大,则交换它们的位置,继续向上比较直到满足堆的性质为止。

  3. 删除元素:当需要移除堆顶元素(即最大点击率的页面)时,用堆的最后一个元素替换堆顶元素,然后执行“下沉”操作,比较该元素与其子节点,如果子节点中有更大的,则与最大的子节点交换位置,继续向下比较直到满足堆的性质为止。

  4. 查询最大元素:堆顶元素始终是最大元素,可以直接访问而不影响堆的结构。

示例

下面是一个简单的最大堆实现示例,用于存储整数值:

public class MaxHeap {
    private int[] heap;
    private int size;

    public MaxHeap(int capacity) {
        heap = new int[capacity + 1]; // Index 0 is not used
        size = 0;
    }

    public void insert(int value) {
        if (size == heap.length - 1) {
            throw new IllegalStateException("Heap is full");
        }
        size++;
        heap[size] = value;
        siftUp(size);
    }

    private void siftUp(int index) {
        while (index > 1 && heap[index / 2] < heap[index]) {
            swap(index, index / 2);
            index /= 2;
        }
    }

    private void swap(int i, int j) {
        int temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }

    public int extractMax() {
        if (size == 0) {
            throw new IllegalStateException("Heap is empty");
        }
        int max = heap[1];
        heap[1] = heap[size];
        size--;
        siftDown(1);
        return max;
    }

    private void siftDown(int index) {
        while (index * 2 <= size) {
            int child = index * 2;
            if (child != size && heap[child] < heap[child + 1]) {
                child++;
            }
            if (heap[index] >= heap[child]) {
                break;
            }
            swap(index, child);
            index = child;
        }
    }
}

使用

public class Main {
    public static void main(String[] args) {
        MaxHeap maxHeap = new MaxHeap(10);
        maxHeap.insert(10);
        maxHeap.insert(20);
        maxHeap.insert(15);
        maxHeap.insert(30);
        
        System.out.println("Maximum value: " + maxHeap.extractMax()); // Should print 30
    }
}

在这个案例中,我们创建了一个最大堆并插入了一些元素,然后提取了最大值。这只是一个基础的实现,实际应用中可能需要考虑更多的边界情况和异常处理。

在之前的示例中,我们创建了一个基本的最大堆数据结构。现在,我们将进一步完善这个数据结构,使其更加健壮和实用,包括添加更多功能和错误处理机制。

完善最大堆数据结构

  1. 增加堆的动态扩容能力:当堆满时自动扩容。
  2. 添加获取堆大小的方法:方便外部了解堆的状态。
  3. 增强错误处理:在堆为空或满时抛出更有描述性的异常。
  4. 添加完整打印堆的方法:便于调试和验证。
更新后的Java代码:
public class MaxHeap {
    private int[] heap;
    private int size;
    private int capacity;

    public MaxHeap(int initialCapacity) {
        this.capacity = initialCapacity;
        this.heap = new int[initialCapacity + 1]; // Index 0 is not used
        this.size = 0;
    }

    public void insert(int value) {
        ensureCapacity();
        size++;
        heap[size] = value;
        siftUp(size);
    }

    private void ensureCapacity() {
        if (size == heap.length - 1) {
            int newCapacity = heap.length * 2;
            heap = Arrays.copyOf(heap, newCapacity);
            capacity = newCapacity - 1;
        }
    }

    private void siftUp(int index) {
        while (index > 1 && heap[index / 2] < heap[index]) {
            swap(index, index / 2);
            index /= 2;
        }
    }

    private void swap(int i, int j) {
        int temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }

    public int extractMax() {
        if (isEmpty()) {
            throw new IllegalStateException("Heap is empty");
        }
        int max = heap[1];
        heap[1] = heap[size];
        size--;
        siftDown(1);
        return max;
    }

    private void siftDown(int index) {
        while (index * 2 <= size) {
            int child = index * 2;
            if (child != size && heap[child] < heap[child + 1]) {
                child++;
            }
            if (heap[index] >= heap[child]) {
                break;
            }
            swap(index, child);
            index = child;
        }
    }

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

    public int getSize() {
        return size;
    }

    public void printHeap() {
        for (int i = 1; i <= size; i++) {
            System.out.print(heap[i] + " ");
        }
        System.out.println();
    }
}

使用

public class Main {
    public static void main(String[] args) {
        MaxHeap maxHeap = new MaxHeap(10);
        maxHeap.insert(10);
        maxHeap.insert(20);
        maxHeap.insert(15);
        maxHeap.insert(30);
        
        System.out.println("Maximum value: " + maxHeap.extractMax()); // Should print 30
        maxHeap.printHeap(); // Print the current state of the heap
        
        try {
            maxHeap.extractMax();
            maxHeap.extractMax();
            maxHeap.extractMax();
            maxHeap.extractMax();
            maxHeap.extractMax(); // This should throw an exception since the heap is now empty
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());
        }
        
        System.out.println("Heap size: " + maxHeap.getSize()); // Should print 0
    }
}

在这个更新的版本中,我们增加了堆的动态扩容能力,提供了获取堆大小的方法,增强了错误处理,并添加了打印堆的方法,使最大堆成为一个更完整、更健壮的数据结构。

既然我们已经建立了一个基本的最大堆,现在我们可以进一步完善它,添加更多的实用功能和优化点,比如:

  1. 添加构建最大堆的方法:允许从一个数组快速构建最大堆。
  2. 增加堆排序功能:利用最大堆进行排序。
  3. 优化扩容策略:使用更高效的扩容策略。
  4. 添加清除堆中所有元素的方法:重置堆到初始状态。
  5. 增加查看但不移除最大值的方法:类似于队列的peek操作。

更新后的最大堆Java代码:

public class MaxHeap<T extends Comparable<T>> {
    private List<T> heap = new ArrayList<>();
    
    public MaxHeap() {}

    public MaxHeap(T[] items) {
        heap.addAll(Arrays.asList(items));
        buildHeap();
    }

    public void insert(T value) {
        heap.add(value);
        siftUp(heap.size() - 1);
    }

    private void siftUp(int index) {
        while (index > 0 && heap.get(parent(index)).compareTo(heap.get(index)) < 0) {
            swap(index, parent(index));
            index = parent(index);
        }
    }

    private void swap(int i, int j) {
        T temp = heap.get(i);
        heap.set(i, heap.get(j));
        heap.set(j, temp);
    }

    public T extractMax() {
        if (isEmpty()) {
            throw new IllegalStateException("Heap is empty");
        }
        T max = heap.get(0);
        heap.set(0, heap.get(heap.size() - 1));
        heap.remove(heap.size() - 1);
        siftDown(0);
        return max;
    }

    private void siftDown(int index) {
        int left = leftChild(index);
        while (left < heap.size()) {
            int right = rightChild(index);
            int largest = (right < heap.size() && heap.get(right).compareTo(heap.get(left)) > 0) ? right : left;
            largest = (heap.get(largest).compareTo(heap.get(index)) > 0) ? largest : index;
            if (largest == index) {
                break;
            }
            swap(index, largest);
            index = largest;
            left = leftChild(index);
        }
    }

    public boolean isEmpty() {
        return heap.isEmpty();
    }

    public int size() {
        return heap.size();
    }

    public void clear() {
        heap.clear();
    }

    public T peekMax() {
        if (isEmpty()) {
            throw new IllegalStateException("Heap is empty");
        }
        return heap.get(0);
    }

    public void buildHeap() {
        for (int i = parent(heap.size() - 1); i >= 0; i--) {
            siftDown(i);
        }
    }

    private int parent(int i) {
        return (i - 1) / 2;
    }

    private int leftChild(int i) {
        return 2 * i + 1;
    }

    private int rightChild(int i) {
        return 2 * i + 2;
    }

    public List<T> sort() {
        List<T> sorted = new ArrayList<>();
        while (!isEmpty()) {
            sorted.add(extractMax());
        }
        return sorted;
    }
}

使用方式:

public class Main {
    public static void main(String[] args) {
        Integer[] numbers = {10, 20, 15, 30, 40};
        MaxHeap<Integer> maxHeap = new MaxHeap<>(numbers);
        
        System.out.println("Maximum value: " + maxHeap.peekMax()); // Should print 40
        System.out.println("Extracted max value: " + maxHeap.extractMax()); // Should print 40
        System.out.println("New maximum value: " + maxHeap.peekMax()); // Should print 30
        
        System.out.println("Sorted list: " + maxHeap.sort()); // Should print [30, 20, 15, 10]
    }
}

在这个版本中,我们使用了ArrayList代替原始数组,允许动态添加和移除元素,同时提供了构建最大堆、堆排序、查看最大值而不移除等功能,使得最大堆更加通用和强大。

标签:index,最大,16,int,maxHeap,heap,数据结构,public,size
From: https://blog.csdn.net/hummhumm/article/details/140259958

相关文章

  • PHP数据结构当中的栈
    本文由 ChatMoney团队出品栈(Stack)是一种后进先出(LastInFirstOut,LIFO)的数据结构,它只允许在一端(称为栈顶)进行插入和删除操作。栈的应用非常广泛,例如在编程语言的函数调用中,每次函数调用都会将一个新的帧压入栈中,当函数返回时,该帧会被弹出。此外,栈还常用于解决某些算法问题,......
  • PHP数据结构之栈
    本文由 ChatMoney团队出品栈(Stack)是一种后进先出(LastInFirstOut,LIFO)的数据结构,它只允许在一端(称为栈顶)进行插入和删除操作。栈的应用非常广泛,例如在编程语言的函数调用中,每次函数调用都会将一个新的帧压入栈中,当函数返回时,该帧会被弹出。此外,栈还常用于解决某些算法问题,......
  • 蓝牙发射接收器芯片TD5165A—拓达半导体
     随着科技的不断发展,越来越多的人开始将电视机作为娱乐和休闲活动的重要设备。然而,电视机的内置音响系统通常无法提供给用户高质量的音频体验,此时需要搭配蓝牙发射接收器使用,本文将详细介绍这款蓝牙发射接收器的应用场景、产品功能和与配对兼容性相关的问题。    首......
  • 代码随想录算法训练营第56天 | 42. 接雨水 、84.柱状图中最大的矩形
    图论理论基础大家可以在看图论理论基础的时候,很多内容看不懂,例如也不知道看完之后还是不知道邻接矩阵,邻接表怎么用,别着急。理论基础大家先对各个概念有个印象就好,后面在刷题的过程中,每个知识点都会得到巩固。https://www.programmercarl.com/kamacoder/图论理论基础.html......
  • P4688 Ynoi2016 掉进兔子洞
    P4688Ynoi2016掉进兔子洞经典莫队加bitset。思路不难发现最终答案就是:\[(r_1-l_1+1)+(r_2-l_2+1)+(r_3-l_3+1)-3\timessize\]其中\(size\)表示3个区间内出现了多少个公共元素。看到这么多区间,不妨有把区间拆下来搞莫队的想法。先不考虑询问个数的限制,我们考虑使用......
  • 【无人机通信】基于哈里斯鹰算法实现无人机辅助可见光通信NOMA 的总速率最大化附matla
     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,代码获取、论文复现及科研仿真合作可私信。......
  • 关于数据结构的学习心得
    介绍在备赛xcpc时,其实除了数据结构以外,绝大部分常用的大纲知识都学习了,但数据结构确实是练得最多的,本文主要介绍一下个人是如何学习数据结构的。数据结构概述数据结构大概是很多人比较抵触系统学习的东西,因为许多数据结构来说,光是板子就比其他领域的算法长很多。比如线段树,可......
  • 数据结构:二叉树的顺序结构及代码实现
    一:二叉树的顺序结构普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操......
  • 数据结构:二叉树的链式结构及代码实现
    一.二叉树的链式结构1.1前置说明在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头......
  • 代码随想录算法训练营第55天 | 42. 接雨水 、84.柱状图中最大的矩形
    接雨水接雨水这道题目是面试中特别高频的一道题,也是单调栈应用的题目,大家好好做做。建议是掌握双指针和单调栈,因为在面试中写出单调栈可能有点难度,但双指针思路更直接一些。在时间紧张的情况有,能写出双指针法也是不错的,然后可以和面试官在慢慢讨论如何优化。https://p......