• 存储元素的数组

*/

private T[] items;

/**

  • 元素个数

*/

private int n;

/**

  • 构造器

  • @param capacity

*/

public MaxPriorityQueue(int capacity) {

this.items = (T[]) new Comparable[capacity + 1];

this.n = 0;

}

/**

  • 元素个数

  • @return

*/

public int size() {

return n;

}

/**

  • 是否为空

  • @return

*/

public boolean isEmpty() {

return n < 1;

}

/**

  • 插入元素

  • @param t

*/

public void insert(T t) {

// 在结尾插入

items[++n] = t;

// 让新元素上浮,找到合适的位置

swim(n);

}

/**

  • 删除最大元素并返回

  • @return

*/

public T delMax() {

// 最大元素

T max = items[1];

// 交换索引1处 和 最大索引处的元素,让完全二叉树中最右的元素成为临时根结点

exch(1, n);

// 删除交换后最大索引处的元素,即原根结点(最大元素)

items[n] = null;

// 元素数量减1

n–;

// 让新根结点下沉,找到合适位置

sink(1);

return max;

}

/**

  • 比较索引i处元素是否小于j处元素

  • @param i

  • @param j

  • @return

*/

private boolean less(int i, int j) {

return items[i].compareTo(items[j]) < 0;

}

/**

  • 交换索引i处和j处元素

  • @param i

  • @param j

*/

private void exch(int i, int j) {

T temp = items[i];

items[i] = items[j];

items[j] = temp;

}

/**

  • 让索引k处元素上浮到合适的位置

  • @param k

*/

private void swim(int k) {

// 循环比较索引k处元素和它父结点元素,如果父结点小,就交换位置

while (k > 1) {

// 比较当前结点与父结点

if (less(k / 2, k)) {

// 父比子小,交换位置

exch(k / 2, k);

}

k = k / 2;

}

}

/**

  • 让索引k处元素下沉到合适的位置

  • @param k

*/

private void sink(int k) {

// 循环比较当前结点k 和 max(左子结点2k,右子结点2k+1)的值,如果当前结点k小,就交换位置

// 循环条件 2*k <= n :表示存在 左子结点

while (2 * k <= n) {

// 获取当前结点中的较大 子结点

// 记录较大 子结点索引:默认为 左子结点

int max = 2 * k;

// 如果右结点存在,且左小于右,改变max

if (2 * k + 1 <= n && less(2 * k, 2 * k + 1)) {

max = 2 * k + 1;

}

if (!less(k, max)) {

// 当前结点不小于子结点,退出循环

break;

}

// 当前结点小于子结点,交换位置

exch(k, max);

// 交换k值,继续循环

k = max;

}

}

}

@Test

public void testMaxPriorityQueue() {

String[] arr = {“E”, “B”, “A”, “C”, “D”};

MaxPriorityQueue maxpq = new MaxPriorityQueue<>(20);

for (String s : arr) {

maxpq.insert(s);

}

System.out.println(maxpq.size());

String del;

while (!maxpq.isEmpty()) {

del = maxpq.delMax();

System.out.print(del + " ");

}

}

5

E D C B A

符合优先队列的要求:

插入时:在一端(尾部)

弹出时:在另一端(首部),并且首部都是最大的,即优先级最高的

9.2、最小优先队列


最小优先队列实现起来也比较简单,同样也可以基于堆来完成最小优先队列


最大堆

前面的堆中存放数据元素的数组要满足如下特性:

  1. 最大的元素放在数组的索引1处
  1. 每个结点的数据总是大于等于它的两个子结点的数据

最小堆

让堆中存放数据元素的数组满足如下特性:

  1. 最小的元素放在数组的索引1处
  1. 每个结点的数据总是小于等于它的两个子结点的数据

在这里插入图片描述

1)API设计

在这里插入图片描述

2)代码实现

package chapter07;

/**

  • @author 土味儿

  • Date 2021/9/9

  • @version 1.0

  • 最小优先队列

  • 以最小堆的方式实现

*/

public class MinPriorityQueue<T extends Comparable> {

/**

  • 存储元素的数组

*/

private T[] items;

/**

  • 元素个数

*/

private int n;

/**

  • 构造器

  • @param capacity

*/

public MinPriorityQueue(int capacity) {

this.items = (T[]) new Comparable[capacity + 1];

this.n = 0;

}

/**

  • 元素个数

  • @return

*/

public int size() {

return n;

}

/**

  • 是否为空

  • @return

*/

public boolean isEmpty() {

return n < 1;

}

/**

  • 插入元素

  • @param t

*/

public void insert(T t) {

// 在结尾插入

items[++n] = t;

// 让新元素上浮,找到合适的位置

swim(n);

}

/**

  • 删除最小元素并返回

  • @return

*/

public T delMin() {

// 最小元素

T min = items[1];

// 交换索引1处 和 最大索引处的元素,让完全二叉树中最右的元素成为临时根结点

exch(1, n);

// 删除交换后最大索引处的元素,即原根结点(最小元素)

items[n] = null;

// 元素数量减1

n–;

// 让新根结点下沉,找到合适位置

sink(1);

return min;

}

/**

  • 比较索引i处元素是否小于j处元素

  • @param i

  • @param j

  • @return

*/

private boolean less(int i, int j) {

return items[i].compareTo(items[j]) < 0;

}

/**

  • 交换索引i处和j处元素

  • @param i

  • @param j

*/

private void exch(int i, int j) {

T temp = items[i];

items[i] = items[j];

items[j] = temp;

}

/**

  • 让索引k处元素上浮到合适的位置

  • @param k

*/

private void swim(int k) {

// 循环比较索引k处元素和它父结点元素,如果父结点大,就交换位置

while (k > 1) {

// 比较当前结点与父结点

if (less(k, k/2)) {

// 父比子大,交换位置

exch(k, k/2);

}

k = k / 2;

}

}

/**

  • 让索引k处元素下沉到合适的位置

  • @param k

*/

private void sink(int k) {

// 循环比较当前结点k 和 min(左子结点2k,右子结点2k+1)的值,如果当前结点大,就交换位置

// 循环条件 2*k <= n :表示存在 左子结点

while (2 * k <= n) {

// 获取当前结点中的较小 子结点

// 记录较小 子结点索引:默认为 左子结点

int min = 2 * k;

// 如果右结点存在,且左大于右,改变min

if (2 * k + 1 <= n && less(2 * k +1, 2 * k)) {

min = 2 * k + 1;

}

if (!less(min, k)) {

// 当前结点不大于子结点,退出循环

break;

}

// 当前结点大于子结点,交换位置

exch(k, min);

// 交换k值,继续循环

k = min;

}

}

}

@Test

public void testMaxPriorityQueue() {

String[] arr = {“E”, “B”, “A”, “C”, “D”};

//MaxPriorityQueue queue = new MaxPriorityQueue<>(20);

MinPriorityQueue queue = new MinPriorityQueue<>(20);

for (String s : arr) {

queue.insert(s);

}

System.out.println(queue.size());

String del;

while (!queue.isEmpty()) {

//del = queue.delMax();

del = queue.delMin();

System.out.print(del + " ");

}

}

5

A B C D E

9.3、索引优先队列


在最大优先队列和最小优先队列中,可以分别快速访问到队列中最大元素和最小元素

但是有一个 缺点:就是没有办法通过索引访问已存在于优先队列中的对象,并更新它们

为了实现这个目的,在优先队列的基础上,学习一种新的数据结构,索引优先队列

以最小索引优先队列实现

1)实现思路

  • 步骤一

  • 存储数据时,给每一个数据元素关联一个整数

例如:insert(int k,T t),可以看做 k 是 t 关联的整数,那么实现需要通过 k 这个值,快速获取到队列中 t 这个元素,此时 k 这个值需要具有唯一性

最直观的想法就是可以用一个T[] items 数组来保存数据元素,在 insert(int k,T t) 完成插入时,可以把 k 看做是 items 数组的索引,把 t 元素放到 items 数组的索引 k 处,这样再根据 k 获取元素 t 时就很方便了,直接就可以拿到 items[k] 即可

在这里插入图片描述

  • 步骤二

  • 步骤一完成后的结果,虽然给每个元素关联了一个整数,并且可以使用这个整数快速的获取到该元素,但是 items 数组中的元素 顺序是随机的并不是堆有序的

  • 增加一个数组 int[] pq,来 保存每个元素在 items 数组中的索引,pq 数组需要 堆有序

例如:pq[1] 对应的数据元素 items[pq[1]] 要小于等于 pq[2] 和 pq[3] 对应的数据元素 items[pq[2]] 和 items[pq[3]]

在这里插入图片描述

  • 步骤三

  • 通过步骤二的分析,可以发现,其实通过上浮和下沉做堆调整的时候,调整的是 pq 数组

如果需要对 items 中的元素进行修改,比如让 items[0]=“H”,那么很显然,需要对 pq 中的数据做堆调整,而且是调整 pq[9] 中元素的位置。但现在就会遇到一个问题,修改的是 items 数组中 0 索引处的值,如何才能快速的知道需要挑中 pq[9] 中元素的位置呢?

  • 最直观的想法就是遍历 pq 数组,拿出每一个元素和 0 做比较,如果当前元素是0,那么调整该索引处的元素即可,但是效率很低

  • 可以 另外增加一个数组 int[] qp,用来存储 pq 的 逆序

例如:

在pq数组中:pq[1] = 6;那么在qp数组中,把6作为索引,1作为值,结果是:qp[6]=1;

  • 当有了 qp 数组后,如果修改 items[0]=“H”,那么就可以先通过索引 0,在 qp 数组中找到 qp 的索引:qp[0]=9, 那么直接调整 pq[9] 即可

在这里插入图片描述

  • 总结

  • 得到 items 队列中的最小元素:items[pq[1]]

  • 得到 items 队列中的最 k 个元素:items[pq[k]]

在这里插入图片描述

  • 修改 items 数组中的最 k 个元素为 t :

1、items[k] = t

2、通过 qp 逆序数组,找到 pq 对应位置:pq[qp[k]],并调整 pq 堆

3、更新 qp 逆序数组

在这里插入图片描述

2)API设计

在这里插入图片描述

3)代码实现

package chapter07;

/**

  • @author 土味儿

  • Date 2021/9/9

  • @version 1.0

  • 索引优先队列

  • 以最小堆方式实现

*/

public class IndexMinPriorityQueue<T extends Comparable> {

/**

  • 存储元素的数组

*/

private T[] items;

/**

  • 保存 items 中元素的索引

*/

private int[] pq;

/**

  • pq数组 的逆序

  • pq中的值为qp的索引,pq的索引为qp的值

*/

private int[] qp;

/**

  • 元素的数量

*/

private int n;

/**

  • 构造器

  • @param capacity

*/

public IndexMinPriorityQueue(int capacity) {

this.items = (T[]) new Comparable[capacity + 1];

this.pq = new int[capacity + 1];

this.qp = new int[capacity + 1];

this.n = 0;

// 初始化qp为-1

for (int i = 0; i < qp.length; i++) {

qp[i] = -1;

}

}

/**

  • 元素数量

  • @return

*/

public int size() {

return n;

}

/**

  • 队列是否为空

  • @return

*/

public boolean isEmpty() {

return n < 1;

}

/**

  • 获取队列 pq 中索引i对应元素的值

  • @param i

  • @return

*/

public T get(int i) {

if (i < 0 || i > pq.length) {

return null;

}

return items[pq[i]];

}

/**

  • 判断堆中索引i处元素是否小于j处

  • @param i

  • @param j

  • @return

*/

private boolean less(int i, int j) {

// 实际比较的是items中元素的值,而堆中存的是items元素的索引

return items[pq[i]].compareTo(items[pq[j]]) < 0;

}

/**

  • 交换堆中索引i处与j处的值

  • @param i

  • @param j

*/

private void exch(int i, int j) {

// i = 1; j = 3;

// 交换前

// pq[1] = 5 —> qp[5] = 1

// pq[3] = 7 —> qp[7] = 3

// 交换后

// pq[1] = 7 —> qp[7] = 1 => qp[pq[1]] = 1 => qp[pq[i]] = i

// pq[3] = 5 —> qp[5] = 3 => qp[pq[3]] = 3 => qp[pq[j]] = j

// 交换pq

int temp = pq[i];

pq[i] = pq[j];

pq[j] = temp;

// 交换qp

qp[pq[i]] = i;

qp[pq[j]] = j;

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

image.png

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

image.png

  • Kafka的集群
  • 第一个Kafka程序
  • image.png

afka的生产者

image.png

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

image.png

image.png

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

image.png

  • Kafka实战之削峰填谷

image.png

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
qqZ4qZ-1713421354705)]

[外链图片转存中…(img-7QAKNaPn-1713421354705)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

[外链图片转存中…(img-td3sVZCh-1713421354706)]

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

[外链图片转存中…(img-n0fSXFgd-1713421354706)]

  • Kafka的集群
  • 第一个Kafka程序
  • [外链图片转存中…(img-b9YLOtZa-1713421354706)]

afka的生产者

[外链图片转存中…(img-9mhskg3n-1713421354706)]

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

[外链图片转存中…(img-PpMo9Eed-1713421354706)]

[外链图片转存中…(img-IxPaLqlV-1713421354706)]

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-cNWtsp1r-1713421354707)]

  • Kafka实战之削峰填谷

[外链图片转存中…(img-nU2dsK6K-1713421354707)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐