图解数据结构(09) -- 优先队列

1、优先队列的特点

队列 的特点是 先进先出, 入队列,将新元素置于队尾:
在这里插入图片描述
出队列,队头元素最先被移出:
在这里插入图片描述
优先队列不再遵循先入先出的原则,而是分为两种情况:

  • 最大优先队列,无论入队顺序如何,都是当前最大的元素优先出队
  • 最小优先队列,无论入队顺序如何,都是当前最小的元素优先出队

例:
有一个最大优先队列,其中的最大元素是8,那么虽然8并不是队头元素,
但出队时仍然让元素8首先出队
在这里插入图片描述

2、优先队列的实现

二叉堆的特性:

  • 最大堆的堆顶是整个堆中的最大元素
  • 最小堆的堆顶是整个堆中的最小元素

可以用最大堆来实现最大优先队列:
每一次 入队 操作就是堆的 插入 操作;
每一次 出队 操作就是删除堆顶节点
入队操作 步骤如下:

  • 插入新节点5
    在这里插入图片描述
  • 新节点5“上浮”到合适位置
    在这里插入图片描述
    出队操作 步骤如下:
  • 让原堆顶节点10出队
    在这里插入图片描述
  • 把最后一个节点1替换到堆顶位置
    在这里插入图片描述
  • 节点1“下沉”,节点9成为新堆顶
    在这里插入图片描述
    二叉堆节点“上浮”和“下沉”的时间复杂度都是 O(logn)
    所以优先队列入队和出队的时间复杂度也是 O(logn)

3、优先队列的代码实现

采用数组来存储二叉堆的元素,当元素数量超过数组长度时,需要进行扩容来扩大数组长度

import java.util.Arrays;

//构建队列
public class TreeNode4 {
    private int[] array;
    private  int size;
    public TreeNode4(){
        //队列初始长度为32
        array = new int[32];
    }

    /**
     * “入队”操作
     * @param key  入队元素
     */
    public void enQueue(int key) {
        //队列长度超出范围,扩容
        if(size >= array.length){
            resize();
        }
        array[size++] = key;
        upAdjust();
    }

    /**
     * “出队”操作
     */
    public int deQueue() throws Exception {
       if(size <=0){
           throw  new Exception("The queue is empty!");
       }
       //获得栈顶元素
        int head = array[0];
       //让最后一个元素移动到栈顶
        array[0] = array[--size];
        downAdjust();
        return head;
    }

    /**
     * “上浮操作”
     */
    public void upAdjust() {
        int childIndex = size - 1;
        int parentIndex = (childIndex-1)/2;
        //temp :保存插入的叶子节点值,用于最后的赋值
        int temp = array[childIndex];
        while (childIndex > 0 && temp >array[parentIndex]){
            //无需真正交换,单项赋值即可
            array[childIndex] = array[parentIndex];
            childIndex = parentIndex;
            parentIndex = parentIndex/2;
        }
        array[childIndex] = temp;
    }

    /**
     * “下沉操作”
     */
    public void downAdjust() {
        //temp :保存父节点的值,用于最后的赋值
        int parentIndex = 0;
        int temp = array[parentIndex];
        int childIndex = 1;
        while (childIndex < size){
            //如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
            if(childIndex +1 <size && array[childIndex +1]>array[childIndex]){
                childIndex++;
            }
            //如果父节点大于任何一个孩子的值,直接跳出
            if(temp >= array[childIndex]){
                break;
            }
            //无须真正交换,单向赋值即可
            array[parentIndex] = array[childIndex];
            parentIndex = childIndex;
            childIndex = 2 * childIndex + 1;
        }
        array[parentIndex] = temp;
    }

    /**
     * “队列扩容”
     */
    public void resize() {
        //队列容量翻倍
        int newSize = this.size * 2;
        this.array = Arrays.copyOf(this.array,newSize);
    }

    public static void main(String[] args) throws Exception{
        TreeNode4 priorityQueue = new TreeNode4();
        priorityQueue.enQueue(3);
        priorityQueue.enQueue(5);
        priorityQueue.enQueue(10);
        priorityQueue.enQueue(2);
        priorityQueue.enQueue(7);
        System.out.println("出队元素:" + priorityQueue.deQueue());
        System.out.println("出队元素2:" + priorityQueue.deQueue());
       }
}

编译输出:
在这里插入图片描述

4、总结


  • 树是n个节点的有限集,有且仅有一个特定的称为根的节点。当n>1时,其余节 点可分为m个互不相交的有限集,每一个集合本身又是一个树,并称为根的子树;
  • 二叉树
    二叉树是树的一种特殊形式,每一个节点最多有两个孩子节点。二叉树包含完
    全二叉树和满二叉树两种特殊形式;
  • 二叉树的遍历方式
    根据遍历节点之间的关系,可以分为前序遍历、中序遍历、后序遍历、层序遍历这4种方式;
    从更宏观的角度划分,可以划分为深度优先遍历和广度优先遍历两大类;
  • 二叉堆
    二叉堆是一种特殊的完全二叉树,分为最大堆和最小堆
    在最大堆中,任何一个父节点的值,都大于或等于它左、右孩子节点的值。
    在最小堆中,任何一个父节点的值,都小于或等于它左、右孩子节点的值。
  • 优先队列
    优先队列分为最大优先队列和最小优先队列
    在最大优先队列中,无论入队顺序如何,当前最大的元素都会优先出队,这是 基于最大堆实现的;
    在最小优先队列中,无论入队顺序如何,当前最小的元素都会优先出队,这是 基于最小堆实现的。
    ———————————————————————————————————————
    内容来源:《漫画算法》
    关注公众号,回复 【算法】,获取高清算法书!
    在这里插入图片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章