堆、优先队列的实现


堆 Heap

heap 是一个抽象的数据结构,或者说是逻辑上的数据结构,并不是一个物理上真实存在的数据结构。
heap 其实有很多种实现方式,比如 binomial heap, Fibonacci heap 等等。
但是面试最常考的,也是最经典的,就是二叉堆,也就是用一棵完全二叉树来实现的。

Java中的优先队列PriorityQueue的实现就是使用的


特点

在这里插入图片描述

  1. 完全二叉树
  2. 任意节点都优于它的所有孩子
  3. 一般使用数组来实现

已知父节点下标计算左右孩子节点

1. 从 1 下标开始
leftChild = 2Pa
rightChild = 2
Pa
Pa = Child / 2
2. 从 0 下标开始
leftChild =2*(Pa+1) - 1 = 2Pa+1
rightChild =2
(Pa+1) + 1 - 1 = 2*Pa+2
Pa = (Child + 1)/2 -1 =( Child - 1) / 2

从 0 下标开始的节点下标计算可以 从 1 下标开始计算推理而出,先转换为下标 1 的表示方式(+1),通过计算转换为下标1开始的父节点或孩子节点后再(-1)转换为 0 下标的方式表示


基本操作

  1. 创建(从最后一个节点的父节点开始,b)
  2. 增加(a)
  3. 删除(b)

核心:

a. 向上调整
b. 向下调整

以下都以大顶堆为例


堆的创建

  1. 选择初始开始结点下标 initIndex(最后一个节点的父节点)
  2. 以该位置开始进行更新(向下调整)
  3. initIndex–
  4. 继续更新,直到完成栈顶

从1开始 :initIndex = size/2
从0开始 :initIndex = (size - 1) / 2

增加结点

  1. 插入到最后的位置
  2. 更新堆 (向上调整)

删除结点

分为 2 中类型,删除堆顶和任意位置删除

去除堆顶
  1. 使用最后的结点替换堆顶
  2. 更新堆 (向下调整)
去除任意结点

一般不进行这个操作,但可以实现待添加


向上调整

只需要当前结点和其父亲结点进行比较,arr[current] > arr[Pa] 则交换并继续向上,反之结束

向下调整

需要和两个孩子结点相比较,这儿我们先比较两个孩子节点,arr[current] < arr[Child] 则交换,继续向上,反之结束。

示例代码

package com.company;

import sun.plugin.javascript.navig.Link;

import java.util.*;

public class Main {
    public static  void main(String[] args)
    {
        int[] arr = {0,1,2,3,4,5,6,7};
        System.out.println(Arrays.toString(arr));
        Heap heap = new Heap(arr);
        System.out.println(Arrays.toString(heap.arr));
        heap.offer(10);
        System.out.println(Arrays.toString(heap.arr));
        heap.pop();
        System.out.println(Arrays.toString(heap.arr));
    }

}
// 大顶堆
class Heap{
    int[] arr;  // 存放堆
    int size;   // 堆大小

    public Heap(){};
    public Heap(int[] tarr){
        this.size = tarr.length;
        this.arr = Arrays.copyOf(tarr,tarr.length + 10); // 初始化 多加10个空间
        int lastIndex = tarr.length-1; // 最末尾下标  是  长度减一
        for(int i= (lastIndex-1)/2; i>= 0;i--) // 从 (lastIndex-1)/2 开始向下调整
            adjustDown(i);

    };

    public void adjustUp(int loc) // 从下往上调整
    {// 只需要和父节点相比较
        for(int pa=(loc-1)/2; pa>=0;pa = (pa-1)/2)
        {
            if(isBig(loc, pa)){
                swap(loc, pa);
                loc = pa; // 依次向上
            }
            if(pa == 0)
                return;
        }
    }

    public void adjustDown(int loc) // 从上往下调整
    {// 需要和两个孩子结点相比较,这儿我们先比较两个孩子节点
        for(int ch=2*loc+1; ch < size; ch=2*loc+1)
        {
            if(ch+1 < size && isBig(ch+1, ch))
                ch++;
            if(isBig( loc , ch)) break;
            else{
                swap(loc,ch);
                loc = ch; // 依次向下
            }
        }
    }

    public void offer(int data) // 加入数据,首先加入到末尾
    {
        int loc = size;
        arr[size++] = data;
        if(size == 1)
            return;
        adjustUp(loc);
    }

    public int pop()            // 弹出堆顶
    {
        int ret = arr[0];
        arr[0] = arr[size-1];
        size--;
        adjustDown(0);
        return ret;
    }
    
    public boolean isBig(int a, int b) // 比较
    {// 现在是大顶堆,arr[a] > arr[b] 换位 < 符号就是小顶堆
        return arr[a] > arr[b] ? true:false;
    }

    public void swap(int ia,int ib)
    {// 辅助交换(注:其实可以不用交换,使用一个临时空间的方式)
        // 但是使用 交换更好理解代码
        int tmp = arr[ia];
        arr[ia] =  arr[ib];
        arr[ib] = tmp;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值