Java数据结构之优先级队列(堆)图文详解

Java数据结构之优先级队列(堆)图文详解

什么是优先级队列(堆)

优先级队列(堆)是一种非常重要的数据结构,它能够更好地管理数据,分配任务等。优先级队列的本质就是一种特殊的队列,它是一种可以根据元素的优先级来出队的数据结构。

通常情况下,队列中存储了一系列具有优先级的数据。当我们从队列中取出元素时,优先级高的元素会先出队。因此,我们需要一种数据结构,来对这些元素进行优先级处理。

在优先级队列中,堆是最常用的一种数据结构。堆本身就是一种树形结构,可以很好地管理优先级队列。

堆的基本概念

堆是一种满足两种性质的树形数据结构:

  1. 最大堆性质或最小堆性质

最大堆性质、最小堆性质可以根据实际情况来确定。在本文中我们以最大堆性质,也就是父节点的权值比子节点的权值大为例。

  1. 完全二叉树

完全二叉树可以这样理解:除了最后一层,其他每层都必须是完全填满的,最后一层仅允许出现最右侧的若干位置为空。

堆可以分为两种类型:大根堆(Max Heap)和小根堆(Min Heap)

  • 大根堆(Max Heap):在一个大根堆中,父节点的权值比子节点的权值大。
  • 小根堆(Min Heap):在一个小根堆中,父节点的权值比子节点的权值小。

在Java中,优先级队列可以通过java.util.PriorityQueue来实现,该类底层通过二叉小根堆实现。我们在实际开发过程中,只需要简单调用Java API中的方法来进行使用就好。

堆的应用

堆在数据结构中有广泛的应用,主要有以下两种:

  1. 堆排序

堆本身就是一种排序方法,也称为堆排序。它的时间复杂度是O(nlogn)。堆排序将一个无序的序列构建成一个堆,然后将堆的根节点输出,递归地输出剩余的元素。由于建堆的时间为O(n),输出堆顶元素后,可以每次将剩余堆的最后一个元素放到堆顶,再重新进行堆化。这个过程下来,只需要O(nlogn)的时间,即可对序列进行排序。

  1. 模拟优先级队列

在程序中,优先级队列是一种常见的数据结构,通常用于任务调度、网络通信等需要对任务进行优先级处理的场景。Java提供了PriorityQueue类来实现优先级队列,其底层是使用堆来实现的。我们只需要根据实际的需求,来实现对应的堆。

堆的实现

在Java中,堆的实现可以使用数组来完成,同时为了使数组下标从1开始,下标为0的位置不保存数值。

基本实现

public class Heap {

    // 数组存储堆的节点
    private int[] a;

    // 堆的大小
    private int n;

    // 堆的容量
    private int capacity;

    // 构造函数,初始化堆
    public Heap(int capacity) {
        this.a = new int[capacity + 1];
        this.n = 0;
        this.capacity = capacity;
    }

    // 获取堆中的最大值
    public int getMax() {
        if (n == 0) {
            return -1;
        }
        return a[1];
    }

    // 加入一个新的元素
    public boolean insert(int x) {
        if (n == capacity) {
            return false;
        }
        ++n;
        a[n] = x;
        // 堆化操作
        int i = n;
        while (i > 1 && a[i] > a[i/2]) {
            swap(a, i, i/2);
            i = i/2;
        }
        return true;
    }

    // 删除堆顶元素
    public boolean removeMax() {
        if (n == 0) {
            return false;
        }
        a[1] = a[n];
        --n;
        // 堆化操作
        heapify(a, n, 1);
        return true;
    }

    // 堆化操作
    private static void heapify(int[] a, int n, int i) {
        while (true) {
            int maxPos = i;
            if (i*2 <= n && a[i*2] > a[i]) {
                maxPos = i*2;
            }
            if (i*2+1 <= n && a[i*2+1] > a[maxPos]) {
                maxPos = i*2+1;
            }
            if (maxPos == i) {
                break;
            }
            swap(a, i, maxPos);
            i = maxPos;
        }
    }

    // 交换
    private static void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

堆排序实现

public static void heapSort(int[] a) {
        if (a.length <= 1) {
            return;
        }
        // 建堆
        buildHeap(a, a.length-1);
        // 排序
        for (int i = a.length-1; i >= 1; i--) {
            swap(a, 1, i);
            heapify(a, i-1, 1);
        }
    }

    // 建堆
    private static void buildHeap(int[] a, int n) {
        for (int i = n/2; i >= 1; i--) {
            heapify(a, n, i);
        }
    }

示例说明

示例一:使用堆实现TopK

假设我们有一个数据集合,我们需要从中找出前K个最大的数。这个时候,我们就可以使用堆来实现。

比如,假设数据集合为{1,3,5,7,2,4,6,8,9,0},我们需要找出前3个最大的数。那么我们就可以使用一个大小为3的最小堆来存储数据,每当插入一个新数据时,我们比较其与当前堆的根节点的大小,如果小于等于根节点,则跳过,否则则将该数据插入到堆中,并将堆中最小的数据删除。最终得到的就是前3个最大的数:{7,8,9}。

示例二:使用优先级队列模拟数据中心的任务调度

在某个数据中心中,需要处理很多任务,但是不同的任务的优先级不同。优先级高的任务需要先执行,优先级低的任务则需要等待。这个时候,我们可以使用优先级队列来模拟任务调度。

我们可以定义一个Task类,存储任务的信息,包括任务的优先级、创建时间、执行时间等等。然后使用PriorityQueue来存储这些任务。每当有新的任务进入时,我们可以将其加入到PriorityQueue中,PriorityQueue会自动根据优先级来排序。然后我们创建一个单独的线程来不断地从PriorityQueue中获取任务,执行任务,并更新PriorityQueue。

例如,我们有以下三个任务:

Task 1:
Priority: 2
CreateTime: 2021-10-01 08:00:00
ExecuteTime: 2021-10-01 08:10:00

Task 2:
Priority: 1
CreateTime: 2021-10-01 08:00:00
ExecuteTime: 2021-10-01 08:30:00

Task 3:
Priority: 3
CreateTime: 2021-10-01 08:00:00
ExecuteTime: 2021-10-01 08:20:00

首先,我们将三个任务加入PriorityQueue中,PriorityQueue会自动根据优先级排序。然后,我们开启一个线程来处理任务。该线程首先从PriorityQueue中获取优先级最高的任务,也就是Task 3;然后执行Task 3,并更新PriorityQueue;接着获取Task 1,执行Task 1,并更新PriorityQueue;最后获取Task 2,执行Task 2,并更新PriorityQueue。这个过程下来,所有的任务都会被正确地执行。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java数据结构之优先级队列(堆)图文详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 从零学JSON之JSON数据结构

    从零学JSON之JSON数据结构 什么是JSON? JSON全称为JavaScript Object Notation,即JavaScript对象表示法。它是一种轻量级的数据交换格式,具有可读性高、易于开发和解析的特点。JSON格式通常用于客户端和服务器之间的数据传输,可以支持多种编程语言。如下是一个简单的JSON格式示例: { "name&quo…

    数据结构 2023年5月17日
    00
  • C语言 超详细总结讲解二叉树的概念与使用

    C语言 超详细总结讲解二叉树的概念与使用 1. 什么是二叉树? 二叉树是一种树状数据结构,其中每个节点最多有两个子节点,被称为左子节点和右子节点。具有以下几个特点: 每个节点最多有两个子节点; 左子节点可以为空,右子节点也可以为空; 二叉树的每个节点最多有一个父节点; 二叉树通常定义为递归模式定义,即每个节点都可以看做一棵新的二叉树。 2. 二叉树的遍历方式…

    数据结构 2023年5月17日
    00
  • 深入理解Objective-C中类的数据结构

    深入理解Objective-C中类的数据结构 在Objective-C中,类作为面向对象编程的基础,是必不可少的概念。理解Objective-C中类的数据结构,对于开发者理解iOS应用程序的底层原理,以及编写高质量代码具有重要的意义。 类的数据结构 一个Objective-C类由以下几部分组成: isa指针:指向该类对象的元类,元类是描述一个类的对象。isa…

    数据结构 2023年5月17日
    00
  • Oracle 11g Release (11.1) 索引底层的数据结构

    我来为您详细讲解“Oracle 11g Release (11.1) 索引底层的数据结构”的完整攻略。 索引底层数据结构简介 在Oracle数据库中,索引底层数据结构是B树(B-Tree)。B树是一种常用的多路平衡查找树,它的特点是每个节点都有多个子节点,能够自动调整高度,保持所有叶子节点到根节点的距离相等。在B树中,每个节点都有一个关键字列表和一个指向子节…

    数据结构 2023年5月17日
    00
  • qqwry.dat的数据结构图文解释第2/2页

    首先,对于“qqwry.dat的数据结构图文解释第2/2页”这个主题,我们需要先对其进行一些介绍。 qqwry.dat是一种IP地址转换工具,它可以将一个给定的IP地址转换成一个物理地址。它的数据结构是一种二叉查找树,在此二叉查找树中每个节点保存了一个IP地址段和该段IP地址所对应的物理地址的信息。这个数据结构的结构图可以在“qqwry.dat的数据结构图文…

    数据结构 2023年5月17日
    00
  • Android开发数据结构算法ArrayList源码详解

    Android开发数据结构算法ArrayList源码详解 概述 Android开发中,大量使用到了数据结构和算法,ArrayList便是其中的一种非常重要的数据结构。ArrayList是Java中非常重要且使用率非常高的一种数据结构,Android开发中也经常使用它来存储数据。本文将深入探究ArrayList的源码,帮助读者更好地理解其工作原理和使用方法。 …

    数据结构 2023年5月17日
    00
  • C++深入分析讲解链表

    C++深入分析讲解链表 链表概述 链表是数据结构中最基本和重要的一种,它的实现可以分为链表的节点和链表的指针。每个节点都记录着链表中的一个元素,并带有一个指向下一个节点的指针,这样就可以通过遍历指针,达到遍历链表的目的。 链表数据结构 在C++中,链表可以通过结构体或者类来实现,比如以下这个结构体实现的单向链表: struct Node { int data…

    数据结构 2023年5月17日
    00
  • C++数据结构之单链表的实现

    C++数据结构之单链表的实现可分为以下步骤: 1. 定义链表节点类 链表节点类需要包含两个成员变量,一个是存储数据的变量,另一个是指向下一个节点的指针变量。同时,需要实现构造函数和析构函数。 class Node{ public: int data; // 存储节点数据 Node* next; // 指向下一个节点的指针 Node(int data):dat…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部