堆的基础功能实现和优先级队列

1. 堆的插入与删除

1.1 堆的插入

        步骤:

        1、先将元素放入到底层空间中(注意:一般是放到整个二叉树的最后一个叶子节点的后边,其次存储空间不够时需要扩容)

        2、将最后新插入的节点向上调整,直到满足堆的性质(判断该节点所在的小二叉树根节点域该节点的关系是否满足该二叉树的性质),详细插入图解分析如下图所示:

        堆的插入代码如下图所示:

 

public void shiftUp(int child,int[] array) {
            // 找到child的双亲
            int parent = (child - 1) / 2;
            while (child > 0) {
                // 如果双亲比孩子大,parent满足堆的性质,调整结束
                if (array[parent] > array[child]) {
                    break;
                }
                else{
                // 将双亲与孩子节点进行交换
                    int t = array[parent];
                    array[parent] = array[child];
                    array[child] = t;
                // 小的元素向下移动,可能到值子树不满足对的性质,因此需要继续向上调增
                    child = parent;
                    parent = (child - 1) / 1;
                }
            }
        }

1.2 堆的删除

        思路:堆的删除a一定删除的是堆顶元素。具体分析如下:

        1、将堆顶元素a与堆中最后一个元素z交换

        2、将堆中有效数据个数减少一个(此时堆中的最后一个元素a被优化)

        3、对堆顶元素进行向下调整,其中删除图解如下图所示;

1.3 堆的常见习题 

 eg1.下列关键字序列为堆的是:()

        A: 100,60,70,50,32,65

        B: 60,70,65,50,32,100

        C: 65,100,70,32,50,60

        D: 70,65,100,32,50,60

        E: 32,50,100,70,65,60

        F: 50,100,70,65,60,32

由选项画图可知,只有a选项满足堆的基本性质;

eg2.已知小根堆为8,15,10,21,34,16,12,删除关键字8之后需重建堆,在此过程中,关键字之间的比较次数是()

A: 1         B: 2         C: 3         D: 4

分析如下:

1、该小根堆如下图所示:

2、删除8后(将末尾节点12与根节点8交换,并删除尾节点)

2.1 首先确定根节点

        15和10节点比较--->12与10进行比较

2.2 根节点的右叶子结点

        12节点与16

由此可得,一共比较三次;

eg3.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()

A: [3,2,5,7,4,6,8]

B: [2,3,5,7,4,6,8]

C: [2,3,4,5,7,8,6]

D: [2,3,4,5,6,7,8]

题解如下分析:

1、原题小根堆如下图所示:

删除堆顶元素0后为

按步骤向下调整

故此,答案选c

 2. 优先级队列(PriorityQueue)

2.1 PriorityQueue的特性

        Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的(主要是在多线程案例中,为了在定时器案例中保证多个线程在针对一个对象的应用中让代码按照我们的预期效果执行,而引入的),本文主要介绍PriorityQueue。优先级队列与其他接口的实现关系如下图所示:

                            

        关于PriorityQueue的使用要注意:

1. 使用时必须导入PriorityQueue所在的包,即:

2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出 ClassCastException异常

3. 不能插入null对象,否则会抛出NullPointerException

4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容

5. 插入和删除元素的时间复杂度为o(log 2 n)

6. PriorityQueue底层使用了堆数据结构

7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素

 2.2 PriorityQueue常用接口介绍

2.2.1 优先级队列的构造

        如下图所示,只是列出了PriorityQueue中常见的几种构造方式即相应的所实现的功能。

static void TestPriorityQueue(){
// 创建一个空的优先级队列,底层默认容量是11
        PriorityQueue<Integer> q1 = new PriorityQueue<>();
// 创建一个空的优先级队列,底层的容量为initialCapacity
        PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
        ArrayList<Integer> list = new ArrayList<>();
        list.add(4);
        list.add(3);
        list.add(2);
        list.add(1);
// 用ArrayList对象来构造一个优先级队列的对象
// q3中已经包含了三个元素
        PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
        System.out.println(q3.size());
        System.out.println(q3.peek());
    }

         注意:默认情况下,PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器

import java.util.Comparator;
import java.util.PriorityQueue;

// 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可
class IntCmp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}
public class TestPriorityQueue {
    public static void main(String[] args) {
        PriorityQueue<Integer> p = new PriorityQueue<>(new IntCmp());
        p.offer(4);
        p.offer(3);
        p.offer(2);
        p.offer(1);
        p.offer(5);
        System.out.println(p.peek());
    }
}

2.2.2 插入/删除/获取优先级最高的元素

        如下图所示是相应方法的展示及功能介绍:

            

         相应代码功能实现如下图所示:

    static void TestPriorityQueue2(){
        int[] arr = {4,1,9,2,8,0,7,3,6,5};
// 一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
// 否则在插入时需要不多的扩容
// 扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低
        PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
        for (int e: arr) {
            q.offer(e);
        }
        System.out.println(q.size()); // 打印优先级队列中有效元素个数
        System.out.println(q.peek()); // 获取优先级最高的元素
// 从优先级队列中删除两个元素之和,再次获取优先级最高的元素
        q.poll();
        q.poll();
        System.out.println(q.size()); // 打印优先级队列中有效元素个数
        System.out.println(q.peek()); // 获取优先级最高的元素
        q.offer(0);
        System.out.println(q.peek()); // 获取优先级最高的元素
// 将优先级队列中的有效元素删除掉,检测其是否为空
        q.clear();
        if(q.isEmpty()){
            System.out.println("优先级队列已经为空!!!");
        } else {
            System.out.println("优先级队列不为空");
        }
    }

 2.2.3 PriorityQueue的扩容方式

        注意:以下是JDK 1.8中,PriorityQueue的扩容方式:

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    private void grow(int minCapacity) {
        int oldCapacity = queue.length;
// Double size if small; else grow by 50%
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                (oldCapacity + 2) :
                (oldCapacity >> 1));
// overflow-conscious code
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        queue = Arrays.copyOf(queue, newCapacity);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

 优先级队列的扩容说明(上述代码小结):

  • 如果容量小于64时,是按照oldCapacity的2倍方式扩容的

  • 如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的

  • 如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容

3. 堆的应用

        PriorityQueue的实现:用堆作为底层结构封装优先级队列 

3.1 堆排序

        堆排序即利用堆的思想来进行排序,总共分为两个步骤:

1. 建堆

        升序:建大堆

        降序:建小堆

2. 利用堆删除思想来进行排序 建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序,其中过程详解如下图所示:

4. 关于java对象的比较 

4.1 基于Comparble接口类的比较

Comparble是JDK提供的泛型的比较接口类,源码实现具体如下:

public interface Comparable<E> {
    // 返回值:
    // < 0: 表示 this 指向的对象小于 o 指向的对象
    // == 0: 表示 this 指向的对象等于 o 指向的对象
    // > 0: 表示 this 指向的对象大于 o 指向的对象
    int compareTo(E o);
}

         对与用户所使用的自定义类型,如果要想按照大小与方式进行比较时:在定义类时,实现Comparble接口即可,然后在类中重写compareTo方法,例如以下代码:

public class Card implements Comparable<Card> {
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
    // 根据数值比较,不管花色
    // 这里我们认为 null 是最小的
    @Override
    public int compareTo(Card o) {
        if (o == null) {
            return 1;
        }
        return rank - o.rank;
    }
    public static void main(String[] args){
        Card p = new Card(1, "♠");
        Card q = new Card(2, "♠");
        Card o = new Card(1, "♠");
        System.out.println(p.compareTo(o)); // == 0,表示牌相等
        System.out.println(p.compareTo(q)); // < 0,表示 p 比较小
        System.out.println(q.compareTo(p)); // > 0,表示 q 比较大
    }
}

4.2 基于比较器比较 

按照比较器方式进行比较,具体步骤如下:

  • 用户自定义比较器类,实现Comparator接口
public interface Comparator<T> {
    // 返回值:
// < 0: 表示 o1 指向的对象小于 o2 指向的对象
// == 0: 表示 o1 指向的对象等于 o2 指向的对象
// > 0: 表示 o1 指向的对象等于 o2 指向的对象
    int compare(T o1, T o2);
}
  • 覆写Comparator中的compare方法,代码如下(注意:Comparator是java.util 包中的泛型接口类,使用时必须导入对应的包):
import java.util.Comparator;
class Card {
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
}
public class CardComparator implements Comparator<Card> {
    
    // 根据数值比较,不管花色
// 这里我们认为 null 是最小的
    @Override
    public int compare(Card o1, Card o2) {
        if (o1 == o2) {
            return 0;
        } if
        (o1 == null) {
            return -1;
        }
        if (o2 == null) {
            return 1;
        } 
        return o1.rank - o2.rank;
    }
    public static void main(String[] args){
        Card p = new Card(1, "♠");
        Card q = new Card(2, "♠");
        Card o = new Card(1, "♠");
// 定义比较器对象
        CardComparator cmptor = new CardComparator();
// 使用比较器对象进行比较
        System.out.println(cmptor.compare(p, o)); // == 0,表示牌相等
        System.out.println(cmptor.compare(p, q)); // < 0,表示 p 比较小
        System.out.println(cmptor.compare(q, p)); // > 0,表示 q 比较大
    }
}

4.3 三种方式对比 

4.4集合框架中PriorityQueue的比较方式

        集合框架中的PriorityQueue底层使用堆结构,因此其内部的元素必须要能够比大小,PriorityQueue采用了:Comparble和Comparator两种方式。

        Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法

        同时也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口并覆写compare方法。

// JDK中PriorityQueue的实现:
public class PriorityQueue<E> extends AbstractQueue<E>
        implements java.io.Serializable {
    // ...
    // 默认容量
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    // 内部定义的比较器对象,用来接收用户实例化PriorityQueue对象时提供的比较器对象
    private final Comparator<? super E> comparator;
    // 用户如果没有提供比较器对象,使用默认的内部比较,将comparator置为null
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    } 
    // 如果用户提供了比较器,采用用户提供的比较器进行比较
    public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
    // Note: This restriction of at least one is not actually needed,
    // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    } 
    // ...
    // 向上调整:
    // 如果用户没有提供比较器对象,采用Comparable进行比较
    // 否则使用用户提供的比较器对象进行比较
    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }
    // 使用Comparable
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    } 
    // 使用用户提供的比较器对象进行比较
    @SuppressWarnings("unchecked")
    private void siftUpUsingComparator(int k, E x) {
        }while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = x;
}

ps:本次的学习就到这里了,喜欢的话还请大家一键三连哦!!1 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/234800.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

30 张图解 HTTP 常见的面试题

前言 在面试过程中&#xff0c;HTTP 被提问的概率还是比较高的 我搜集了 5 大类 HTTP 面试常问的题目&#xff0c;同时这 5 大类题跟 HTTP 的发展和演变关联性是比较大的&#xff0c;通过问答 图解的形式由浅入深的方式帮助大家进一步的学习和理解 HTTP 协议。 HTTP 基本概…

【数学建模】《实战数学建模:例题与讲解》第十讲-时间序列预测(含Matlab代码)

【数学建模】《实战数学建模&#xff1a;例题与讲解》第十讲-时间序列预测&#xff08;含Matlab代码&#xff09; 基本概念移动平均&#xff08;Moving Average, MA&#xff09;:指数平滑法&#xff08;Exponential Smoothing&#xff09;:季节性调整&#xff08;Seasonal Adju…

Anaconda安装

1.Anaconda下载路径 官网最新版本&#xff1a;https://www.anaconda.com/products/distribution/ 官网历史版本&#xff1a;https://repo.anaconda.com/archive/ 清华大学开源软件镜像站&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 2.和python版本关系…

GD32F30X-RT-Thread学习-线程管理

1. 软硬件平台 GD32F307E-START Board开发板MDK-ARM Keil 2.RT-Thread Nano 3.RT-Thread 内核学习-线程管理 ​ 在多线程操作系统中&#xff0c;可以把一个复杂的应用分解成多个小的、可调度的、序列化的程序单元&#xff0c;当合理地划分任务并正确地执行时&#xff0c;这…

Java JVM类加载机制原理剖析

目录 前言一、什么是类加载二、类加载子系统三、类的加载过程2.1、加载2.2、验证2.3、准备2.4、解析2.5、初始化 四、类加载器(ClassLoader) 前言 Java类要加载到JVM中的&#xff0c;会经过一系列的加载过程&#xff0c;这个过程就是在类加载子系统中实现的&#xff0c;当我们用…

函数栈帧的创建和销毁

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 1. 什么是函数栈帧 2. 理解函数栈帧能解决什么问题呢&#xff1f; 3. 函数栈帧的创建和销毁解析 3.1 什么是栈&#xff1f; 3.2 认识相关寄存器和汇编指令 3.3…

【深度学习】注意力机制(一)

本文介绍一些注意力机制的实现&#xff0c;包括SE/ECA/GE/A2-Net/GC/CBAM。 目录 一、SE&#xff08;Squeeze-and-Excitation&#xff09; 二、ECA&#xff08;Efficient Channel Attention&#xff09; 三、GE&#xff08;Gather-Excite&#xff09; 四、A2-Net(Double A…

USB基础知识点介绍

本文主要介绍USB2.0相关的知识点。 USB 2.0介绍 USB 2.0是一种通用串行总线&#xff08;Universal Serial Bus&#xff09;的接口标准&#xff0c;是USB&#xff08;Universal Serial Bus&#xff09;技术的第二代版本。它于2000年4月发布&#xff0c;是USB 1.1的升级版本。 …

交易历史记录20231208 记录

昨日回顾&#xff1a; SELECT TOP 10000 CODE,成交额排名,净流入排名,代码,名称,DDE大单金额,涨幅,所属行业,主力净额,DDE大单净量,CONVERT(DATETIME, 最后涨停时间, 120) AS 最后涨停时间 FROM dbo.全部&#xff21;股20231208_ALL WHERE 连板天 > 1AND DDE大单净量 > …

通信:mqtt学习网址

看这个网址&#xff1a;讲的很详细&#xff0c;后面补实战例子 第一章 - MQTT介绍 MQTT协议中文版 (gitbooks.io)https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html

3.DevEco Studio安装鸿蒙手机app本地模拟器

配合Intel CPU启动模拟器 解决措施 打开任务管理器&#xff0c;在“性能”选项&#xff0c;检查CPU虚拟化是否已经启用。如果未启用&#xff0c;需要进入电脑的BIOS中&#xff0c;将CPU的“Intel Virtualization Technology”选项开启。 点击New Emulator 文档中心 解决措施…

python+pytest接口自动化(12)-自动化用例编写思路 (使用pytest编写一个测试脚本)

经过之前的学习铺垫&#xff0c;我们尝试着利用pytest框架编写一条接口自动化测试用例&#xff0c;来厘清接口自动化用例编写的思路。 我们在百度搜索天气查询&#xff0c;会出现如下图所示结果&#xff1a; 接下来&#xff0c;我们以该天气查询接口为例&#xff0c;编写接口测…

Android Studio连接MYSQL数据库

首先导入mysql的jar包&#xff0c;这里连接的是8版本的。 这里之前到如果mysql的jar包了 首先跳到Project模式&#xff1a; 直接复制粘贴到这里&#xff1a; 这里之前到如果了。想删掉重新导入一次&#xff0c;但是报错,什么ioexception。这里将Project Structure中的Moudle中的…

【每日一题】—— D. Divide and Equalize(Codeforces Round 903 (Div. 3))(数学、数论)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

【分治】最接近点对Python实现

文章目录 [toc]问题描述一维最接近点对算法Python实现 二维最接近点对算法分治算法时间复杂性Python实现 问题描述 给定平面上 n n n个点&#xff0c;找其中的一对点&#xff0c;使得在 n n n个点组成的所有点对中&#xff0c;该点对的距离最小 一维最接近点对算法 Python实…

探索无监督域自适应,释放语言模型的力量:基于检索增强的情境学习实现知识迁移...

深度学习自然语言处理 原创作者: Xnhyacinth 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;如何有效地进行无监督域自适应(Unsupervised Domain Adaptation, UDA) 一直是研究的热点和挑战。无监督域自适应的目标是在目标域无标签的情况下&#xff0c;将源域的知识…

docker安装elasticsearch和kibana

docker系列 1、CentOS7安装docker 2、docker安装rabbitmq 3、docker安装mysql docker安装elasticsearch和kibana docker系列一、安装elasticsearch二、安装kibana三、安装ik分词器1、分词器说明2、安装分词器 本篇文章所采用的elasticsearch和kibana版本以及ik分词器都是7.12.…

AS安装目录

编辑器&#xff1a; sdk: gradle: gradle使用的jdk目录&#xff1a;Gradle使用的jdk是android studio安装目录下的jbr 成功项目的android studio配置&#xff1a;

动态内存的管理malloc、free、calloc、realloc

身在井隅&#xff0c;心向星光 眼里有诗&#xff0c;自在远方 目录 动态内存的简单介绍 动态内存的优势 可以控制内存的大小 可以多次利用这部分空间 动态内存函数malloc、free malloc开辟函数 free释放函数 动态内存函数calloc、realloc calloc开辟函数 realloc调整函数 动…

生产问题: 利用线程Thread预加载数据缓存,其它类全局变量获取缓存偶发加载不到

生产问题: 利用线程Thread预加载数据缓存偶发加载不到 先上代码 public class ThreadTest {//本地缓存Map<String, Object> map new HashMap<String, Object>();class ThreadA implements Runnable{Overridepublic void run() {System.out.println("Thread…