优先级队列

1.概念

队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。
在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。
 

2.优先级队列的模拟实现

JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。

3.堆的概念

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一
个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大
堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

 4.堆的存储方式

1.堆是一颗完全二叉树,可以采用层序的规则去顺序存储.

对于非完全二叉树,则不适合顺序存储的方式进行存储,因为为了还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低

2.数组的下标映射(可以找规律去推理)

数组下标从0 开始,0 到 n - 1,n为节点数

如果i 为0 , i下标只能表示根节点; 

如果 2 * i + 1 < n, 则节点 i 的左孩子是 2 * i + 1,如果不在节点范围内,没有左孩子

如果 2 * i + 2 < n, 则节点 i 的右孩子是 2 * i + 2,如果不在节点范围内,没有右孩子

如果i不为0, i下标表示孩子节点,双亲节点是(i - 2) / 2;

5.堆的创建

1.堆的向下调整和创建

这里是建立小根堆,把整个数组当成一颗二叉树, 依次从最后一颗子树的根节点到最上面的第一颗子树,开始向下调整.(此过程需要循环)

对于一颗子树而言,向下调整过程是

parent指向双亲节点,child指向孩子节点,不确定左还是右,size是指树的节点总数

(1) 让parent标记需要调整的节点,child标记parent的左孩子(parent如果有孩子一定是先有左孩子)

(2)如果parent的左孩子存在,即child < size , 进行以下操作,直到parent没有左孩子

    1.如果parent右孩子存在,找到左右孩子最小的孩子,让child指向

    2.parent与较小的孩子child比较,

        如果parent < child ,调整结束,子树后面的节点都已经满足了小根堆的性质了

        如果parent >= child ,交换parent和child ,交换完成之后;由于parent中大的元素向下移动了,子树后面的结构可能不满足堆的性质,因此需要继续向下调整,即parent = child ; child = parent * 2 + 1; 然后重复步骤(2)

import java.util.Arrays;

public class Heap {

    private int[] elem;
    private int size;

    public Heap(int[] array) {
        this.elem = array;
        this.size = array.length;
    }

    public void CreateHeap() {
        for (int i = (elem.length - 2) / 2; i >= 0; i--) {
            shiftDown(i);
        }

    }

    public void shiftDown(int parent) {
        int child = 2 * parent + 1;

        while (child < size) {
            if (child + 1 < size && elem[child + 1] < elem[child]) {
                child = child + 1;
            }
            if (elem[parent] < elem[child]) {
                break;
            }
            int temp = elem[parent];
            elem[parent] = elem[child];
            elem[child] = temp;

            parent = child;
            child = parent * 2 + 1;
        }
    }

    public static void main(String[] args) {
        int[] array = {10,91,48,56,23,41};
        Heap heap = new Heap(array);
        heap.CreateHeap();
        System.out.println(Arrays.toString(array));
    }
}

注意:在调整以parent为根的二叉树,必须要满足parent的左子树和右子树已经是堆了才可以向下调整.否则最大或者最小元素在最下面一层的情况,交换只能到上一层,无法成为堆.

这就是把整个数组当成一颗二叉树, 依次从最后一颗子树的根节点到最上面的第一颗子树,开始向下调整的原因,确保在调整以parent为根的二叉树,必须要满足parent的左子树和右子树已经是堆了.

2.建堆的时间复杂度

3.堆的插入创建和删除

1.堆的插入

1.先将元素放入到底层空间中(树的最后一层),空间不够需要扩容

2.将最后新插入的节点向上调整,节点向上交换移动,直到满足堆的性质

 

//小根堆
    public void shiftUp(int child) {
        //child 是第一个节点,不需要调整
        if (child == 0) {
            return;
        }
        int parent = (child - 1) / 2;

        while (child > 0) {
            if (elem[parent] < elem[child]) {
                break;
            } else {
                int temp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = temp;
                // 小的元素向下移动,可能以parent为孩子的子树不满足堆的性质,因此需要继续向上调整
                child = parent;
                parent = (child - 1) / 2;
            }
        }
    }

2.堆的删除(都是删除堆顶元素)

1.将堆顶元素和堆中最后一个元素交换

2.将堆中有效数据个数size减少1

3.对现在交换过后的堆顶元素进行向下调整

//删除堆顶元素
public int poll() {
    int oldValue = array[0];
    array[0] = array[--size];
    shiftDown(0);
    return oldValue;
}
//取堆顶元素
public int peek() {
    return array[0];
}
//插入元素
public void offer(int e) {
    array[size++] = e;
    shiftUp(size - 1);
}

 6.常用集合PriorityQueue介绍

1.特性

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,本文主要介绍PriorityQueue。

关于PriorityQueue的使用要注意:
1. 使用时必须导入PriorityQueue所在的包,即:

import java.util.PriorityQueue;


2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常
3. 不能插入null对象,否则会抛出NullPointerException
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5. 插入和删除元素的时间复杂度为
6. PriorityQueue底层使用了堆数据结构
7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素

2.构造方法和常用的方法 

 

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨落寒山意

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值