【JAVA】数据结构——优先级队列(堆的应用)

关于堆的7种基本排序方式及相关面试题:【JAVA】数据结构——堆的排序及相关面试题

目录

1.二叉树的顺序存储

2.堆的概念及应用

优先级队列(堆)实现方式:常见的是使用堆来构建

(1)向下调整:根据parent向下创建堆

(2)建堆:创建大根堆

(3)向上调整:根据child向上创建大根堆

(4)放元素val:首先考虑数组是否满了,再放入元素(放末尾),然后重新建堆

(5)获取队头元素:也即是根节点

(6)出元素:保证每次出最大值(大根堆)或者最小值(小根堆)

3.java中的优先级队列:PriorityQueue implements Queue

4. 堆的其他应用-TopK 问题


1.二叉树的顺序存储

1.1存储方式:用层序遍历的方式将二叉树放入数组中,保存二叉树的结构(也即是用堆表示

1.2下标关系:

已知孩子child节点下标,则parent = (child-1)/2

已知双亲parent节点下标,则左孩子:left = 2*parent+1; 右孩子:right = 2*parent +2;


2.堆的概念及应用

逻辑上是一颗完全二叉树,物理上是保存在数组中的;

堆的基本作用是快速找到集合中的最值

优先级队列(堆)实现方式:常见的是使用堆来构建

前提:左右子树必须已经是堆(大小堆),才能进行调整;

(1)向下调整:根据parent向下创建堆

时间复杂度:最坏:O(log(N))

public class testHeap {
    public int[] elem;
    public int usedSize;

    public testHeap() {
        this.elem = new int[10];
    }

    //parent:每棵树的根节点,len:每棵树调整的结束位置
    //向下调整注意,child下标不能超出数组长度
    public void shiftDown(int parent,int len) {
        int child = 2*parent+1; //孩子下标
        //当孩子下标小于数组长度,则需进行判断是否符合大根堆
        while (child < len) {  //进入循环,至少有一个孩子(左孩子)
            if (child+1 < len && elem[child] < elem[child+1]) { //孩子下标没超出数组范围,并且左孩子<右孩子,则继续向后进行,向下调整
                child++;//保证当前左右孩子最大值的下标
            }
            if (elem[child] > elem[parent]) { //如果孩子节点值》父亲节点则进行交换
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;  

                parent = child;  //交换结束,继续向下移动,parent= child 向下调整,再求解其子树下标
                child = 2*parent+1; //左孩子下标
            }else {  //如果elem[child] 《 elem[parent],则 已经是大根堆了,只需退出即可
                break;
            }
        }
    }

(2)建堆:创建大根堆

时间复杂度:理论:O(n*log(n));实际:每一层节点个数*每个节点调整的高度O(N)


    public void createHeap(int[] array) {  //创建一个大根堆函数,也即是将数组元素放入大根堆
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
        //parent = (child-1)/2,child = usedsize-1
        for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) { //此处parent下标是最后一个父亲节点下标
            shiftDown(parent,usedSize);
        }
    }

(3)向上调整:根据child向上创建大根堆

    public void shiftUp(int child) {
        int parent = (child-1)/2;
        while (child > 0) {  //进入循环,至少有一个孩子(左孩子),孩子下标是>0的
            //如果孩子节点值》父亲节点则进行交换
            if (elem[child] > elem[parent]) { //还是创建大根堆,孩子节点大则进行交换
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
   //交换结束,说明当前子二叉树满足大堆要求,则向上调整;当parent= child 时,是向下调整,child = 2*parent+1
                child = parent;  
                parent = (child-1)/2;
            }else {  //如果elem[child] 《 elem[parent],则 已经是大根堆了,只需退出即可
                break;
            }
        }
    }
 

(4)放元素val:首先考虑数组是否满了,再放入元素(放末尾),然后重新建堆

    public void offer(int val) {
        if (isFull()) { //首先判断数组满不满,满了则需扩容
            //扩容
            elem = Arrays.copyOf(elem,2*elem.length);    //2倍扩容
        }
        //放元素,则有效数据多了一个
        elem[usedSize++] = val; //扩容结束放元素,在位置elem[usedSize++]放val
        shiftUp(usedSize-1); // 因为已知最后一个下标所以向上调整: usedSize-1为下标 
    }

    public boolean isFull() {
        return usedSize == elem.length;
    }

(5)获取队头元素:也即是根节点

//获取队头元素,也即是elem[0]根节点元素
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("优先级队列为空");
        }
        return elem[0];
    }

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

(6)出元素:保证每次出最大值(大根堆)或者最小值(小根堆)

//交换根节点和最后一个元素的位置,变成了出最后一个元素,而此时已不符合大小堆的要求,因此需要重新创建大小根堆。
    public int poll() {
        if (isEmpty()) {
            throw new RuntimeException("优先级队列为空");
        }
        int tmp = elem[0]; //交换0下标元素和最后一个元素
        elem[0] = elem[usedSize-1];
        elem[usedSize-1] = tmp;
        usedSize--;
        shiftDown(0,usedSize); //usedSize结束位置
        return tmp;
    }

3.java中的优先级队列:PriorityQueue implements Queue

错误处理抛出异常返回特殊值
入队列add(e)offer(e)
出队列remove()poll()
队首元素element()peek()

4. 堆的其他应用-TopK 问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值