用堆模拟实现优先级队列-----java

 

目录

1、优先级队列概念

2、模拟实现优先级队列

3、堆的插入操作

4、堆的删除


1、优先级队列概念

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

JDK1.8中的PriorityQueue底层使用了堆这种数据结构。

2、模拟实现优先级队列

下面以小堆为例,即谁比较小,谁先出队列。(假设不考虑扩容)

public class MyPriorityQueue {
    //假设不考虑扩容
    int[] arr=new int[10];
    int size;         //元素个数

    public  MyPriorityQueue(){
        //空的优先级队列
        this.size=0;
    }

    //查看优先级队列最小值
    public int peek(int[] arr){
        if(this.size==0){
            throw new RuntimeException("空的");
        }
        //第一个元素为最小值
        return arr[0];
    }

    //传入数组以及待调整的结点的下标
    public static void adjustDown递归(int[] arr,int parentIdx,int size){
        int childLeftIdx=2*parentIdx+1;
        //1.说明没有左孩子,是一个叶子结点
        if(childLeftIdx>=size){
            return;
        }
        int childRightIdx=2*parentIdx+2;   //定义右孩子
        int minIdx=childLeftIdx;   //先假定左右孩子中最小的是左孩子
        //如果右子树存在且右子树比左子树小,重置最小节点
        if(childRightIdx<size&&arr[childLeftIdx]>arr[childRightIdx]){
            minIdx=childRightIdx;
        }
        //比较最小值结点与待调整结点的大小
        if(arr[parentIdx]<arr[minIdx]){
            return;
        }
        if(arr[parentIdx]>arr[minIdx]){
            int tmp=arr[parentIdx];
            arr[parentIdx]=arr[minIdx];
            arr[minIdx]=tmp;
        }
        //再递归判断被交换的结点是否满足堆的性质
        adjustDown递归(arr,minIdx,size);
    }

    //删除队首元素
    public int poll(){
        if(this.size==0){
            throw new RuntimeException("空的,删除错误");
        }
        //将待删除元素与最后一个叶子结点交换,然后向下调整
        int e=arr[0];
        arr[0]=arr[size-1];
        arr[size-1]=0;
        size--;
        adjustDown递归(arr,0,size);
        return e;
    }

    //插入元素
    //插入的最后的位置上,然后向上调整
    public void offer(int e){
        arr[size]=e;
        size++;
        int index=size-1;
        //index == 0 说明是根,不需要调整了
        while (index!=0) {
            // 找到child的双亲
            int parentIdx = (index - 1) / 2;
            // 如果双亲比孩子小,parent满足小堆的性质,调整结束
            if (arr[parentIdx] <= arr[index]) {
                break;
            } else{
                // 将双亲与孩子节点进行交换
                int tmp=arr[parentIdx];
                arr[parentIdx]=arr[index];
                arr[index]=tmp;
                // 大的元素向下移动
                index = parentIdx;
            }
        }
    }

测试:

public static void main(String[] args) {
        MyPriorityQueue mpq=new MyPriorityQueue();
        mpq.offer(6);
        mpq.offer(3);
        mpq.offer(7);
        mpq.offer(5);
        mpq.offer(2);
        mpq.offer(4);
        mpq.offer(1);
        mpq.offer(8);
        System.out.println("向队列中添加元素:");
        for(int i:mpq.arr){
            System.out.print(i+" ");
        }
        System.out.println();
        System.out.println("----------------------");
        System.out.println("查看最小值:");
        System.out.println(mpq.peek(mpq.arr));
        System.out.println("----------------------");
        System.out.println("被删除的元素为:");
        System.out.println(mpq.poll());
        System.out.println("----------------------");
        System.out.println("删除后的队列:");
        for(int i:mpq.arr){
            System.out.print(i+" ");
        }
        System.out.println();
        System.out.println("----------------------");
        System.out.println("插入元素后的队列:");
        mpq.offer(0);
        for(int i:mpq.arr){
            System.out.print(i+" ");
        }
        System.out.println();
        System.out.println("----------------------");
        System.out.println("出队:");
        while(mpq.size>0){
            System.out.println(mpq.poll());
        }
    }

 

可以看到,出队列时,谁小谁先出。

图解:

3、堆的插入操作

1. 先将元素放入到底层空间中
2. 将最后新插入的节点向上调整,直到满足堆的性质

 依次插入元素 ,如果比它的父节点小,就交换,当交换两个元素时,可能会改变堆的结构,如果交换后不符合小堆的性质,则继续交换,直到满足小堆的性质。

4、堆的删除

注意:堆的删除一定删除的是堆顶元素。具体如下:
1. 将堆顶元素对堆中最后一个元素交换
2. 将堆中有效数据个数减少一个
3. 对堆顶元素进行向下调整

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值