【数据结构_12】优先级队列

一、优先级队列的使用

 输出:

通过以上例子我们可以看出,优先级队列是一个“特殊”的队列,正常的队列的顺序是“先进先出”,优先级队列,出队列的顺序和入队列的是不一样的,是把队列中的每个元素,都约定了“优先级”,出队列的时候把优先级最高的元素先出队列。

如果仅仅是把 “ 整数 / 字符串 ”放到优先级队列,元素自身就带有规则。如果优先级队列中的元素是对象,我们就需要掂量以下具体的规则了。

二、优先级队列的原理
优先级队列并不是通过顺序表/链表来表示的,因为如果是通过遍历顺表或者链表来把最小的元素找到并进行删除,这样的思路固然可以,但他的时间复杂度是O(N)。

*通常我们认为O(1)是最小的,P(logN) 当N是非常大的时候,无线趋近于水平线 ,而O(N)比logN大很多

因此,我们是通过“堆”来实现优先级队列的。

1.堆的概念

堆是一个完全二叉树,他是通过数组的方式来存储的完全二叉树

针对完全二叉树,可以不适用“孩子表示法”,而是可以通过更简单的“数组”的方式来表示

把这个树层序遍历,遍历的结果,依次添加到数组中,中间如果遇到空节点,把空节点也添加到数组中。

*为什么前面不用数组的方式来表示二叉树呢?

e.g.

比方说图上这棵树,如果用数组去表示,那么我们就会得到如下这一个数组:

放进去null是为了维护父子之间的下标关系,通过举例发现:普通的二叉树,可能包含大量的null,导致整个数组,里面也包含大量的null,导致整体的内存利用率降低。

而堆是一个完全二叉树,所以不会出现内存利用率低的情况。

通过数组,我们可以更快地得到父节点与子节点的关系:

设父节点的下标为parent,那么左子树的下标就是2*parent+1,右子树的下标就是2*parent+2。

设子节点下标为child,父节点的下标也就是(child-1)/2。(*因为0/2=0,1/2=0)

2.对于堆来说,约定,任何一个节点的值都是小于(或大于)两个子节点的值(这俩子节点谁大谁小没关系)

堆的树根节点(数组的[0]下标的元素,就是整棵树的最小值)

在这颗树中,我们可以通过O(1)复杂度就能找到值最小的元素—>优先级队列

小堆示例:

大堆示例:

接下来我们来考虑如何用代码来实现堆

对于给定的一个任意数组,将他调整成符合堆要求的结构

(1)首先,我们来考虑一个最简单的情况:有一个数组,除了0下标的元素之外,其他的元素都满足小堆的要求。

如图所示,针对这种情况,此时我们的方案就是“向下调整”:
1.先确定要比较的子节点是哪一个?

(a)如果只有左子树,那么要比较的就是这个左子树

(b)如果同时又左右子树,先比较左右子树哪个更小,然后取更小的那个

(*此处不考虑只有右子树的情况,因为堆是完全二叉树)

2.拿着刚才这个更小的左右子树的节点,和根节点进行比较和交换

如果发现根节点比子节点大,那就交换这两个节点的值

重复此过程直到该结构符合要求。

*向下调整/下沉式调整:进行向下调整的前提是除了根节点之外,其他的节点,都已经符合堆的要求,随便给个数组,直接向下调整一次,是不足以构建出整个堆的。

对于堆来说,数组是一个完全二叉树,取出整个数组中最后一个“非叶子节点”,从这个节点开始,从后往前地进行向下调整。由于当前是从最后一个非叶子节点开始进行调整的,此时意味着该系欸但的子节点一定是叶子(叶子意味着没有子树),因此叶子自身就可以认为是一个合法的堆。

一个向下调整的过程:

总结:构建堆的基本思路:

1.从最后一个非叶子节点出发,从后往前遍历,针对每个节点,进行向下调整

2.所谓的向下调整,找出子树中的最小值,和根节点比较,如果根节点大,就和刚才的子树节点交换,持续往下进行直到根节点比子树小,或者没有子树的时候

左子树下标:2*parent+1

右子树下标:2*parent+2

package PriorityQueue;

public class Heap {

    //先写一个向下调整的方法
    public static void ShiftDown(int[] array, int index){
        int parent = index;
        int child = parent *2+1;
        while(child <array.length){
            if ((child+1<array.length) && array[child + 1] < array[child] ) {
                child = child +1;
                }
            if(array[parent]<= array[child]){
                break;
            }else{
                int tmp = array[child];
                array[child] = array[parent];
                array[parent] = tmp;
                parent = child;
                child = 2*parent+1;
            }
            }
        }

        public static void createHeap(int[] array){
        int lastLeaf = array.length -1;
        int lastParent = (lastLeaf-1)/2;
        for(int i = lastParent;i>=0;i--){
            ShiftDown(array,i);
        }
        }

    public static void main(String[] args) {
        int[] array ={1,5,6,2,4,3};
        createHeap(array);
        for(int i=0;i<array.length;i++){
            System.out.print(array[i]+"  ");
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值