二叉堆

什么是二叉堆?

二叉堆本质上是一种完全二叉树,它分为两个类型:
1.最大堆
2.最小堆
什么是最大堆呢?最大堆任何一个父节点的值,都大于等于它左右孩子节点的值。相反,最小堆就是任何一个父节点的值,都小于等于它左右孩子节点的值。
二叉堆的根节点叫做堆顶。
最大堆和最小堆的特点,决定了在最大堆的堆顶是整个堆中的最大元素;最小堆的堆顶是整个堆中的最小元素。

堆的自我调整

对于二叉堆,如下有几种操作:
插入节点
删除节点
构建二叉堆

这几种操作都是基于堆的自我调整。
下面让我们以最小堆为例,看一看二叉堆是如何进行自我调整的。
1.插入节点
二叉堆的节点插入,插入位置是完全二叉树的最后一个位置。比如我们插入一个新节点,值是 0。
在这里插入图片描述
这时候,我们让节点0的它的父节点5做比较,如果0小于5,则让新节点“上浮”,和父节点交换位置。
在这里插入图片描述
继续之前的上浮操作直到堆顶
在这里插入图片描述

2.删除节点

二叉堆的节点删除过程和插入过程正好相反,所删除的是处于堆顶的节点。比如我们删除最小堆的堆顶节点1。
在这里插入图片描述
这时候,为了维持完全二叉树的结构,我们把堆的最后一个节点10补到原本堆顶的位置。
接下来我们让移动到堆顶的节点10和它的左右孩子进行比较,如果左右孩子中最小的一个(显然是节点2)比节点10小,那么让节点10“下沉”。
在这里插入图片描述
继续让节点10和它的左右孩子做比较,左右孩子中最小的是节点7,由于10大于7,让节点10继续“下沉”。
在这里插入图片描述

堆的代码实现

二叉堆虽然是一颗完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二叉堆的所有节点都存储在数组当中。

数组中,在没有左右指针的情况下,如何定位到一个父节点的左孩子和右孩子呢?
像图中那样,我们可以依靠数组下标来计算。
假设父节点的下标是parent,那么它的左孩子下标就是 2parent+1;它的右孩子下标就是 2parent+2 。
比如上面例子中,节点6包含9和10两个孩子,节点6在数组中的下标是3,节点9在数组中的下标是7,节点10在数组中的下标是8。
7 = 32+1
8 = 3
2+2
刚好符合规律。

用java实现的动态数组
Array
public class Array<E> {
    private E[] data;
    private int size;

    public Array(int capacity) {
        data = (E[]) (new Object[capacity]);
        size = 0;
    }

    public Array() {
        this(10);
    }

    public int getSize() {
        return size;
    }

    public int getCapacity() {
        return data.length;
    }

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

    public void addLast(E e) {
        add(size, e);
    }

    public void addFirst(E e) {
        add(0, e);
    }

    public E get(int index) {
        if (index < 0 || index >= size)
            throw new IllegalArgumentException("add Failed,size is full");
        return data[index];
    }

    public E getLast() {
        return get(size-1);
    }
    public E getFirst() {
        return get(0);
    }

    public void set(int index, E e) {
        if (index < 0 || index >= size)
            throw new IllegalArgumentException("add Failed,size is full");
        data[index] = e;
    }

    public void add(int index, E e) {


        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add Failed,size is full");
        }

        if (size == data.length) {
            resize(2 * data.length);
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        data[index] = e;
        size++;
    }

    public void resize(int newCap) {
        E[] newData = (E[]) (new Object[newCap]);
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }

    public boolean contains(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e))
                return true;
        }
        return false;
    }

    public int find(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e))
                return i;
        }
        return -1;
    }

    public E remove(int index) {
        if (index < 0 || index > size)
            throw new IllegalArgumentException("add Failed,size is full");
        E res = data[index];
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        data[size] = null;
        size--;
        if (size == data.length / 4 && data.length / 2 != 0)
            resize(data.length / 2);
        return res;
    }

    public E removeFirst() {
        return remove(0);
    }

    public E removeLast() {
        return remove(size - 1);
    }

    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }
    public void swap(int i,int j){
        if(i<0||i>=size||j<0||j>=size){
            throw  new IllegalArgumentException("index is illegal");
        }
        E temp=data[i];
        data[i]=data[j];
        data[j]=temp;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("Array:size=%d,capacity=%d\n", size, data.length));
        res.append('[');
        for (int i = 0; i < size; i++) {
            res.append(data[i]);
            if (i != size - 1) {
                res.append(',');
            }

        }
        res.append(']');
        return res.toString();
    }

}
MaxHeap
import java.util.Random;
public class MaxHeap<E extends Comparable<E>> {
    private Array<E> data;
    public MaxHeap(int capcity) {
        data = new Array<>(capcity);
    }
    public MaxHeap() {
        data = new Array<>();
    }

    //任意数组变成MaxHeap
    public MaxHeap(E[] arr){
        data=new Array<>(arr);
        for (int i=parent(arr.length-1);i>=0;i--){
            siftDown(i);
        }
    }

    public int size() {
        return data.getSize();
    }

    public boolean isEmpty() {
        return data.isEmpty();
    }

    //返回该节点的父亲节点的索引
    private int parent(int index) {
        if (index == 0) {
            throw new IllegalArgumentException("index=0 has not parent");
        }
        return (index - 1) / 2;
    }

    //返回左孩子所在的节点索引
    private int leftChild(int index) {
        return index * 2 + 1;
    }

    private int rightChild(int index) {
        return index * 2 + 2;
    }

    //向堆中添加元素
    public void add(E e) {
        data.addLast(e);
        siftUp(data.getSize() - 1);
    }

    private void siftUp(int k) {
        while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0) {
            data.swap(k, parent(k));
            k = parent(k);
        }
    }
    public E findMax(){
        if(data.getSize()==0){
            throw new IllegalArgumentException("can not findMax when heap is empty");
        }
        return data.get(0);
    }
    public E extractMax(){
        E ret=findMax();
        data.swap(0,data.getSize()-1);
        data.removeLast();
        siftDown(0);
        return ret;
    }
    private void siftDown(int k){
        while (leftChild(k)<data.getSize()){
            int j=leftChild(k);
            if(j+1<data.getSize()&&data.get(j+1).compareTo(data.get(j))>0){
                j=rightChild(k);
            }
            //data[j]是leftChild和rightChild中的较大值
            if(data.get(k).compareTo(data.get(j))>=0){
                break;
            }else{
                data.swap(k,j);
                k=j;
            }
        }
    }
    //用元素e来替换最大元素
    public E replace(E e){
        E ret=findMax();
        data.set(0,e);
        siftDown(0);
        return ret;
    }


    private  static double testMaxHeap(Integer[] testData,boolean isHeapify){
        long startTime=System.nanoTime();
        MaxHeap<Integer> maxHeap;
        if(isHeapify){
            maxHeap=new MaxHeap<>(testData);
        }else{
            maxHeap=new MaxHeap<>();
            for(int num:testData){
                maxHeap.add(num);
            }
        }
//        int[] arr=new int[testData.length];
//        for(int i=0;i<testData.length;i++){
//            arr[i]=maxHeap.extractMax();
//        }
//        for (int i=1;i<testData.length;i++){
//            if(arr[i-1]<arr[i]){
//                throw new IllegalArgumentException("error");
//            }
//        }
        System.out.println("MaxHeap is complete");
        long endTime=System.nanoTime();
        return (endTime-startTime)/1000000000.0;
    }


    public static void main(String[] args) {
        int n=1000000;
        Integer[] testData=new Integer[n];
        Random random=new Random();
        MaxHeap<Integer> maxheap=new MaxHeap<>();
        for(int i=0;i<n;i++){
            testData[i]=random.nextInt(Integer.MAX_VALUE);
        }
        double time1=testMaxHeap(testData,false);
        System.out.println("maxHeap without heapify"+time1+"s");
        double time2=testMaxHeap(testData,true);
        System.out.println("maxHeap without heapify"+time2+"s");

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值