数据结构——堆与PriorityQueue

目录

堆的概念

堆的性质

堆的存储 

堆的创建      

代码实现

属性定义 

向下调整

堆的插入 

向上调整  

删除结点

优先队列(PriorityQueue) 

PriorityQueue的特性 

常用接口介绍 

优先队列的实现 


堆的概念

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

堆的性质

  • 堆中的某个结点总是不大于(或不小于)其父结点的值
  • 堆是一颗完全二叉树

堆的存储 

由于堆是一颗完全二叉树,我们可以采用层序的规则来高效的存储

如图所示,如果对于非完全二叉树采用这样的方式存储,会导致许多的存储空间白白浪费 ,空间利用率较低.

假设i为节点在数组中的下标,则有:
如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

堆的创建      

 大根堆:二叉树根节点,比他左右子树的值都大

 小根堆:二叉树根节点,比他左右子树的值都小

   以{ 27,15,19,18,28,34,65,49,25,37 } 数组为例,我们分析大根堆的创建:在调整时,先找到左右孩子中最大的值,再与父结点比较,如果孩子结点的值大于父结点,父子交换。

 1.我们首先按照层序的方式构建完全二叉树       

2.我们找到最后一个度不为零的结点,首先调整最后一棵子树,然后向上遍历依次调整

   对于完全二叉树来说,用数组存储,数组的最后一个元素一定是二叉树最后一棵度为零的结点(叶子结点),那么他的父结点就是我们找的最后一刻度不为零的结点。parent = (i - 1) / 2;随后 parent--,操作剩下的子树;

具体的图解演示 

代码实现

属性定义 

由于堆是一个完全二叉树并且由数组存储,所以我们定义属性如下:

    private int[] elementDate;//数组
    private int size;         //有效值
    private int DEFAULT_CAPACITY = 10;//定义一个默认值

    public Heap() {
        this.elementDate =new int[DEFAULT_CAPACITY];
        this.size = 0 ;
    }
 public Heap(int [] array) {
        // 重新复制数组,防止外部修改数组对heap里数据的影响
        this.elementDate = Arrays.copyOf(array, array.length);
        this.size = elementDate.length;
        // 向下调整为堆结构
        int parent = (size - 1 - 1) / 2; //找到最后一个度不为零的结点
        for (int i = parent; i >= 0 ; i--) {
            // 向下调整的方法
            shiftDown(i);
        }
    }

向下调整

 private void shiftDown(int parent) {
        //非空校验
        if(parent < 0){
            return;
        }
        //由父结点找到左孩子结点
        int child = parent * 2 + 1;
        while(child < size){
           //判断是否有右孩子
            if(child + 1 < size){
           //如果右孩子的值大于左孩子,让child指向右孩子
                if(elementDate[child + 1] > elementDate[child]){
                    child++;
                }
            }
            //如果父节点大于孩子结点,直接退出循环
            if(elementDate[parent] >= elementDate[child]){
                break;
            }
            //孩子结点大于父结点,交换位置
            swap(elementDate,parent,child);
            //重置父亲,孩子结点
            parent = child;
            child = parent * 2 + 1;
        }
    }

 private void swap(int[] elementDate, int parent, int child) {
        int temp = elementDate[parent];
        elementDate[parent] = elementDate[child];
        elementDate[child] = temp;
    }

堆的插入 

插入一个新的元素之后,与他的父节点比较,如果没有父节点大,我们调整完成,否则,与父节点交换,向上调整

public void offer(int value){
      //判断数组是否已满
        if(isFull()){
      //扩容
            elementDate =Arrays.copyOf(elementDate,elementDate.length * 2);
        }
      //在size位置插入新的元素
        elementDate[size] = value;
        size++;
      //向上调整
        shiftUp(size - 1);
    }
    private boolean isFull() {
        return size == elementDate.length;
    }

向上调整  

 private void shiftUp(int child) {
       //判断是否越界
        if(child > size){
            return;
        }
        // 找到父节点
        int parent = (child - 1) / 2;
        while(child > 0 && parent >= 0){
         //两者进行判断
            if(elementDate[parent] >= elementDate[child]){
                break;
            }
        //交换
            swap(elementDate,parent,child);
        //重置父亲,孩子结点
            child = parent;
            parent = (child - 1) / 2;
        }
    }

删除结点

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

 

  public int poll(){
       //非空校验
        if(isEmpty()){
            throw new RuntimeException("数组为空");
        }
        //记录堆顶元素
        int value = elementDate[0];
        //交换第一个和最后一个元素
        swap(elementDate,0,size - 1);
        //有效值减一
        size--;
        //向下调整
        shiftDown(0);
        return value;
    }
   private boolean isEmpty() {
        return size == 0;
    }

优先队列(PriorityQueue) 

前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适。在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。

 

PriorityQueue的特性 

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的

常用接口介绍 

优先队列的实现 

作为底层结构封装优先级队列,所以上述我们实现的堆的增删等一系列操作,就是我们模拟实现优先队列

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值