数据结构_优先级队列(堆的性质、调整、创建、插入、删除及实现代码)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档



提示:以下是本篇文章正文内容,下面案例可供参考

一、优先级队列

1、概念

队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列;数据结构应该提供两个最基本的操作:1) 返回最高优先级对象,2) 是添加新的对象。这种数
据结构就是优先级队列(Priority Queue)

2、堆的概念

JDK1.8中的PriorityQueue底层使用了堆这种数据结构
 如果有一个关键码的集合合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >=K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆

1)性质

堆中某个节点的值总是不大于或不小于其父节点的值
堆总是一棵完全二叉树

2)堆的存储方式

 由堆的概念可知,堆是一棵完全二叉树,可以层序的规则采用顺序的方式来高效存储。

二、堆的创建及相关操作

1、堆向下调整

以小堆为例(根节点的左右子树已经满足)
在这里插入图片描述
过程:

操作1:parent标记需要调整的节点,child标记parent的左孩子.
操作2:如果,child < size,则进行以下过程

 (1) 若右孩子存在,存在找到左右孩子中最小的孩子,用child进行标识
 (2) 将parent与较小的孩子child比较;如果:parent小于较小的孩子child,调整结束;否则:交换parent与较小的孩子child,交换完成之后,parent中大的元素向下移动,可能导致子树不满足对的性质,因此需要继续向下调整,即parent = child;child = parent*2+1; 然后继续操作2。直到 child >size。
最坏的情况:从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(log2(N))

向下调整相关代码

  /**
     * 向下调整
     * 如果向下调整大根堆,时间复杂度为O(n)
     * @param parent
     * @param len
     */
private void siftDown(int parent,int len){
        int child = 2*parent+1;
        //至少有左孩子
        while (child<len){
            //左孩子和右孩子比较,如果如果右孩子的值比较大,则交换
            if (child+1< len && elem[child]<elem[child+1]){
                child = child +1;
            }
            //走完上述if语句,child一定保存的是左右孩子最大值的下标
            if (elem[child]>elem[parent]){
                swap(child,parent);
                parent = child;
                child = parent*2+1;
            }else{
                break;
            }
        }
    }

2、创建堆

创建堆的时间复杂度为T(N) = N -log2(N) = N
[注意:假设树的高度为h,第一层的节点最坏移动h-1层,每层节点的最坏移动层数相加即可得到时间复杂度]

    public void CreateHeap(){
        //节点下标i=len-1 ;父母节点下标为(i-1)/2
        for (int parent = (usedSize-1-1)/2; parent >=0 ; parent--) {
            siftDown(parent,usedSize);
        }
    }

3、堆的插入

堆的插入共两个步骤:

  1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
  2. 将最后新插入的节点向上调整,直到满足堆的性质
    在这里插入图片描述
    堆的插入相关代码
    //判断是否为满
    public boolean isFull(){
        return usedSize == elem.length;
    }
    public void push(int val){
        if (isFull()){
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize] = val;
        //向上调整
        siftUp(usedSize);
        usedSize++;
    }
//向上调整建立小根堆,时间复杂度O(N+NlogN)
    public void siftUp(int child){
        int parent = (child-1)/2;
        while (child >0){
            if (elem[child] >elem[parent]){
                swap(child,parent);
                child = parent;
                parent = (child-1)/2;
            }else {
                break;
            }
        }
    }

4、堆的删除

堆的删除一定删除的是堆顶元素。具体如下:

  1. 将堆顶元素对堆中最后一个元素交换
  2. 将堆中有效数据个数减少一个
  3. 对堆顶元素进行向下调整
    相关代码
 //删除
    public int pop(){
        if (isEmpty()){
            return -1;
        }
        int oldVal = elem[0];
        swap(0,usedSize-1);   //将第一个元素与最后一个元素交换
        usedSize--;
        siftDown(0,usedSize);
        return oldVal;
    }

    public  boolean isEmpty(){
        return usedSize == 0;
    }

三、PriorityQueue的特性

static void TestPriorityQueue(){
	// 创建一个空的优先级队列,底层默认容量是11
	PriorityQueue<Integer> q1 = new PriorityQueue<>();
	// 创建一个空的优先级队列,底层的容量为initialCapacity
	PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
	ArrayList<Integer> list = new ArrayList<>();
	list.add(4);
	list.add(3);
	list.add(2);
	list.add(1);
	// 用ArrayList对象来构造一个优先级队列的对象
	// q3中已经包含了三个元素
	PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
	System.out.println(q3.size());	
	//获取有效元素的个数
	System.out.println(q3.peek());
	//获取优先级最高的元素
}

PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器,即可调整为大根堆

  • 45
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值