数据结构与算法:优先队列(堆)

一种数据结构,特殊场景使用,同样是使用堆结构,利用堆的特性

文章目录

  • 概要
  • 概述
  • 图解优先队列
    • 构造初始堆
    • 进行入队列演示
    • 进行出队列演示
  • 代码实现
  • 堆 优先队列特点
  • 优先队列一般应用场景

概要

  • code语言:java、c
  • 测试环境:win、java8
  • 参考书籍:《数据结构与算法分析java语言描述》 原书第三版、《数据结构:使用C语言》、《算法导论》原书第三版
  • 参考链接:漫画:什么是优先队列?

概述

  • 需要优先有堆的知识才能更好理解堆支持的优先队列:数据结构与算法:堆排序
  • 一般队列:FIFO,先进先出(顺便考下,栈的特点是? – 后进先出哈哈哈)
  • 优先级队列:就是说出队列是需要按照一定规则的优先级咯。利用堆结构的话就能出两个类型的优先级队列:
    • 最大优先队列,无论入队顺序,当前最大的元素优先出队
    • 最小优先队列,无论入队顺序,当前最小的元素优先出队
  • 为什么用堆?
    • 线性数据结构时间复杂度高,最坏时间是O(N)
    • 最大堆的堆顶是整个堆中的最大元素
    • 最小堆的堆顶是整个堆中的最小元素

图解优先队列

这里以最大优先队列为例

构造初始堆

最大堆,构造方式这里不赘述,参看:数据结构与算法:堆排序

操作图如下:
假设原始数组情况:

3 6 4 5 7

数据结构与算法:优先队列(堆)_第1张图片
最后该初始堆对应的数组情况是:

7 6 3 5 4

进行入队列演示

假设这里入队一个8。
逻辑:把元素插入到最后,之后会导致堆结构被破坏,然后从下往上开始调整为最大堆结构,这个过程也叫做,上滤
数据结构与算法:优先队列(堆)_第2张图片
最后该堆对应的数组情况是:

8 6 7 5 4 3

注:入队列时,若数组长度不够,可能需要扩展数组长度

进行出队列演示

逻辑:总是出堆顶元素,这里也就是8出队。并用把最后一个元素放到空出来的堆顶上。之后会导致堆结构被破坏,需要自上而下重新调整为一个最大堆,这个过程叫下沉
数据结构与算法:优先队列(堆)_第3张图片

最后该堆对应的数组情况是:

7 6 3 5 4

代码实现

由于此前一篇介绍堆排序的文章实现的是最大堆,这里我就实现一个最小堆。
此数据结构稍微改动,就可以成为一个通用的数据结构了(加泛型等等)

package com.mym.practice.lock;

/**
 * 优先队列
 */
public class PriorityQueue {

    /**
     * 默认容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 存数据的数组
     */
    private int[] array;

    /**
     * 当前大小(实际大小)
     */
    private int currentSize;

    public PriorityQueue(){
        array = new int[DEFAULT_CAPACITY];
    }

    public PriorityQueue(int capacity){
        array = new int[capacity];
    }

    public PriorityQueue(int[] arr){
        currentSize = arr.length;
        this.array = new int[currentSize * 2];

        // 舍弃0位,方便二叉堆计算
        for(int i = 1; i < currentSize + 1; i++){
            array[i] = arr[i - 1];
        }

        // 构建初始堆
        buildHeap();
    }

    /**
     * 入队列
     * @param x 入队列数据
     */
    public void offer(int x){
        if(currentSize == array.length - 1){
            // 需要扩容
            enlargeArray(array.length * 2 + 1);
        }

        // 当前空位
        int hole = ++currentSize;
        // 重新调整为堆结构
        for(array[0] = x; x < array[hole / 2]; hole /= 2){
            array[hole] = array[hole / 2];
        }
        // 入队列,并删除临时使用的
        array[hole] = x;
        array[0] = 0;
    }

    /**
     * 出队列
     * @return
     */
    public int poll(){
        int result = array[1];
        // 更新
        array[1] = array[currentSize];
        array[currentSize] = 0;
        currentSize--;
        // 重新调整
        buildHeap();
        return result;
    }

    /**
     * 下滤,下沉
     * @param hole 当前位置
     */
    private void percolateDown(int hole){
        int child;
        int temp = array[hole];
        while(hole << 1 <= currentSize){
            child = hole << 1;      // 左孩。右孩节点 = 左孩节点 + 1
            if(child != currentSize && array[child + 1] < array[child]){
                child++;
            }
            if(array[child] < temp){
                array[hole] = array[child];
            }else{
                break;
            }
            hole = child;
        }
        array[hole] = temp;
    }

    /**
     * 构建堆
     */
    private void buildHeap(){
        for(int i = currentSize / 2; i > 0; i--){
            percolateDown(i);
        }
    }

    /**
     * 扩展数组
     * @param newSize
     */
    private void enlargeArray(int newSize){
        if(newSize <= array.length){
            return;
        }
        int[] ints = new int[newSize];
        for(int i = 0; i < array.length; i++){
            ints[i] = array[i];
        }
        array = ints;
    }

    /**
     * 删除最小的
     * @return 删除的数值
     */
    public int deleteMin(){
        return poll();
    }

    /**
     * 打印数组结构
     */
    public void print(){
        for(int i = 0; i < array.length; i++){
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int[] arr = {3,6,4,5,7};
        PriorityQueue priorityQueue = new PriorityQueue(arr);
        priorityQueue.print();

        int poll = priorityQueue.poll();
        System.out.println("出队列的是:" + poll);
        System.out.println("现堆数组情况:");
        priorityQueue.print();

        int min = priorityQueue.deleteMin();
        System.out.println("删除当前最小的元素是:" + min);
        System.out.println("现堆数组情况:");
        priorityQueue.print();

        priorityQueue.offer(3);
        System.out.println("入队列数后,现堆数组情况:");
        priorityQueue.print();
    }
}


执行结果

0 3 5 4 6 7 0 0 0 0 
出队列的是:3
现堆数组情况:
0 4 5 7 6 0 0 0 0 0 
删除当前最小的元素是:4
现堆数组情况:
0 5 6 7 0 0 0 0 0 0 
入队列数后,现堆数组情况:
0 3 5 7 6 0 0 0 0 0 

堆 优先队列特点

  • 入队列时间复杂度为logn(上滤为logn)
  • 出队列时间复杂度也为logn(下沉为logn)

优先队列一般应用场景

  • 哈哈,jdk1.5之后已经实现了并作为类库可以直接使用
  • 可以以某种权重作为排序字段来设计队列优先级的使用场景

你可能感兴趣的:(数据结构与算法)