数据结构与算法——15.堆

这篇文章我们来讲一下堆

目录

1.堆的概述

2.堆的实现

3.总结


1.堆的概述

计算机科学中,堆是一种基于树的数据结构,通常用完全二叉树实现。堆的特性如下:

  • 在大顶堆中,任意节点C与它的父节点P符合P.value>=C.value
  • 在大顶堆中,任意节点C与它的父节点P符合P.value<=C.value
  • 最顶层的节点(没有父亲)称之为root根节点

注意:完全二叉树指除了最后一层,上层全部填满。并且最后一层填充时是从左往右填充。

堆是一种树形结构,但是我们依然可以用数组来存储它,下面看一下数组存储的特征:

如果从索引О开始存储节点数据

  • 节点 i 的父节点为floor((i- 1)/2),当i>0时
  • 节点 i 的左子节点为 2i+1,右子节点为 2i+2,当然它们得

如果从索引1开始存储节点数据

  • 节点 i 的父节点为 floor(i/2),当i >1时
  • 节点主的左子节点为2i,右子节点为2i+1,同样得

2.堆的实现

下面看一下具体的代码:

数据结构与算法——15.堆_第1张图片

代码如下:


import java.util.Arrays;

public class L17_MaxHeap {
    int[] array;//存储的数组
    int size;//有效元素个数

    /**下面是针对两种不同场景的两种构造方法*/
    public L17_MaxHeap(int capacity) {
        this.array = new int[capacity];
    }

    public L17_MaxHeap(int[] array) {
        this.array = array;
        this.size = array.length;
        heapify();
    }

    /**建堆操作*/
    private void heapify(){
        //找到最后一个非叶子节点  (size/2)-1 (索引0为起点的)
        for (int i =size/2-1; i >=0 ; i--) {
            down(i);
        }
    }

    /**删除堆顶元素*/
    public int poll(){
        int top = array[0];
        swap(0,size-1);//交换堆顶和数组中最后的一个元素
        size--;
        down(0);//让堆顶的元素(现在是最后的一个)进行下潜,到达此元素的正确位置
        return top;
    }

    /**删除指定位置处的元素*/
    public int poll(int index){//和上面的逻辑相似
        int deleted = array[index];
        swap(index,size-1);
        size--;
        down(index);
        return deleted;
    }

    /**返回指定位置处的元素*/
    public int peek(int index){
        return array[0];
    }

    /**替换堆顶的元素*/
    public void replace(int value){
        array[0] = value;
        down(0);
    }

    /**堆的尾部添加元素*/
    public boolean offer(int offered){
        if (size == array.length)
            return false;
        up(offered);
        size++;
        return true;
    }

    /**将offered元素上浮,直到offered小于父元素或者到达堆顶*/
    private void up(int offered){
        int child = size;//确定新加入元素的索引
        while (child > 0){
            int parent = (child-1)/2;//确定新加入元素父节点的索引
            if (offered>array[parent]){//如果加入元素大于其父节点,交换两个元素
                array[child] = array[parent];
            }else {
                break;//不大于,说明位置是对的,就退出循环
            }
            child = parent;//然后让加入元素的索引等于其父节点的索引
        }
        array[child] = offered;//对其赋值
    }

    /**将parent处的元素下潜,与两个孩子较大者交换,直到没孩子或者孩子没有它大*/
    private void down(int parent){
        int left = parent*2+1;//它的左孩子索引
        int right = parent*2+2;//它的右孩子索引
        int max = parent;//当前的父节点索引
        if (leftarray[max]){//左孩子大于父节点
            max = left;//交换左孩子与父节点的索引
        }
        if (rightarray[max]){
            max = right;
        }
        if (max != parent){//找到最大的孩子
            swap(max,parent);
            down(max);
        }
    }
    /**交换两个索引处的值*/
    private void swap(int i,int j){
        int a = array[i];
        array[i] = array[j];
        array[j] = a;
    }

    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7};
        L17_MaxHeap maxHeap = new L17_MaxHeap(array);
        System.out.println(Arrays.toString(maxHeap.array));
    }
}

关键在于构造和上浮和下潜这三个方法的书写。

3.总结

堆在数据结构上是和二叉树一样的,但是其数据的分布有自己的特点,根据这些特点,我们将其分为大顶堆和小顶堆。堆在实现上是用数组来实现的。我们需要牢牢的掌握堆元素下潜和上浮的代码书写。

你可能感兴趣的:(数据结构与算法,算法)