【数据结构和算法14】堆结构(基于数组实现)

目录

1、有关二叉树和堆的介绍

2、大根堆的代码实现

3、小根堆的代码实现


1、有关二叉树和堆的介绍

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

  • 在大顶堆(大根堆)中,任意节点 C 与它的父节点 P 符合 P.value >= C.value

  • 而小顶堆(小根堆)中,任意节点 C 与它的父节点 P 符合 P.value <= C.value

  • 最顶层的节点(没有父亲)称之为 root 根节点

例1 - 满二叉树(Full Binary Tree)特点:每一层都是填满的

【数据结构和算法14】堆结构(基于数组实现)_第1张图片

 

例2 - 完全二叉树(Complete Binary Tree)特点:最后一层可能未填满,靠左对齐

【数据结构和算法14】堆结构(基于数组实现)_第2张图片

 

例3 - 大根堆

【数据结构和算法14】堆结构(基于数组实现)_第3张图片

 

例4 - 小根堆

【数据结构和算法14】堆结构(基于数组实现)_第4张图片

 

完全二叉树可以使用数组来表示

【数据结构和算法14】堆结构(基于数组实现)_第5张图片

 

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

    • 节点 i 的父节点为 floor((i-1)/2),当 i>0 时

    • 节点 i 的左子节点为 2i+1,右子节点为 2i+2,当然它们得 < size

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

    • 节点 i 的父节点为 floor(i/2),当 i > 1 时

    • 节点 i 的左子节点为 2i,右子节点为 2i+1,同样得 < size

2、大根堆的代码实现


​
/**
 * 使用数组实现堆数据结构
 * 定义一个用于存储整形数据的大根堆
 *
 * @author zjj_admin
 */
public class MaxHeap {
​
    /**
     * 用于存储数据
     */
    private int[] array;
​
​
    /**
     * 堆中的数据个数
     */
    private int size;
​
​
    public MaxHeap(int capacity) {
        this.size = 0;
        this.array = new int[capacity];
        //调整堆的结构
        adjust();
    }
​
    /**
     * 获取堆中的元素个数
     *
     * @return
     */
    public int size() {
        return size;
    }
​
    /**
     * 查看堆顶元素
     *
     * @return
     */
    public int peek() {
        if (size <= 0) {
            throw new NullPointerException("没有数据了");
        }
        return array[0];
    }
​
​
    /**
     * 返回并移除堆顶元素
     *
     * @return
     */
    public int poll() {
        if (size <= 0) {
            throw new NullPointerException("没有数据了");
        }
        int top = array[0];
        //让第一个数据和左后一个数据做交换
        exchange(0, size - 1);
        size--;
        // 重新对第一个元素做下潜操作
        down(0);
        return top;
    }
​
    /**
     * 返回并移除指定索引的数据
     * 1、先将索引 index 和最后一个数据进行交换
     * 2、移除最后一个数据,及删除了开始索引为 index 的数据
     * 3、对索引 index 的数据进行上浮操作,当不发生节点交换时再进行下潜操作
     *
     * @param index
     * @return
     */
    public int poll(int index) {
        if (index < 0) {
            throw new IndexOutOfBoundsException("index 必须不小于 0");
        }
        // 当索引数据
        if (index > size - 1) {
            throw new NullPointerException("没有数据了");
        }
        //先将索引 index 和最后一个数据进行交换
        exchange(index, size - 1);
        //移除最后一个数据,及删除了开始索引为 index 的数据
        int removed = array[size - 1];
        size--;
        //对索引 index 的数据进行上浮操作,当不发生节点交换时再进行下潜操作
        int newParent = up(index);
        down(newParent);
        return removed;
    }
​
    /**
     * 向堆中添加一个元素
     *
     * @param value
     * @return
     */
    public boolean offer(int value) {
        if (size >= array.length) {
            return false;
        }
        array[size] = value;
        size++;
        //上浮最后一个元素
        up(size - 1);
        return true;
    }
​
    /**
     * 调整堆结构,让其满足大根堆
     * 方法:从最后一个非叶子节点开始,依次向前做下潜操作
     * 在数组中,左后一个非叶子节点的索引为:(size/2  - 1)
     */
    private void adjust() {
        for (int i = size / 2 - 1; i >= 0; i--) {
            //依次做下潜操作即可
            down(i);
        }
    }
​
​
    /**
     * 堆节点做下潜操作,让父节点数据不小于子节点数据
     *
     * @param parent
     */
    private void down(int parent) {
        if (2 * parent + 1 < size && array[parent] < array[2 * parent + 1]) {
            // 交换位置
            exchange(parent, 2 * parent + 1);
​
            // 做下潜操作
            down(2 * parent + 1);
        } else if (2 * parent + 2 < size && array[parent] < array[2 * parent + 2]) {
            // 交换位置
            exchange(parent, 2 * parent + 2);
​
            // 做下潜操作
            down(2 * parent + 2);
        }
    }
​
    /**
     * 上浮操作,并返回上浮终点索引
     *
     * @param child
     * @return 上浮终点索引
     */
    private int up(int child) {
        int parent = (child - 1) / 2;
        while (parent >= 0 && array[child] > array[parent]) {
            exchange(parent, child);
            child = parent;
            parent = (child - 1) / 2;
        }
        return Math.max(child, 0);
    }
​
    /**
     * 交换堆中两个位置的数据
     *
     * @param i
     * @param j
     */
    private void exchange(int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
​
    @Override
    public String toString() {
        int[] a = new int[size];
        System.arraycopy(array, 0, a, 0, size);
        return Arrays.toString(a);
    }
}

3、小根堆的代码实现


/**
 * 使用数组实现堆数据结构
 *
 * @author zjj_admin
 */
public class MinHeap {
​
    /**
     * 用于存储数据
     */
    private int[] array;
​
​
    /**
     * 堆中的数据个数
     */
    private int size;
​
​
    public MinHeap(int capacity) {
        this.size = 0;
        this.array = new int[capacity];
        //调整堆的结构
        adjust();
    }
​
    /**
     * 获取堆中的元素个数
     *
     * @return
     */
    public int size() {
        return size;
    }
​
    /**
     * 查看堆顶元素
     *
     * @return
     */
    public int peek() {
        if (size <= 0) {
            throw new NullPointerException("没有数据了");
        }
        return array[0];
    }
​
​
    /**
     * 返回并移除堆顶元素
     *
     * @return
     */
    public int poll() {
        if (size <= 0) {
            throw new NullPointerException("没有数据了");
        }
        int top = array[0];
        //让第一个数据和左后一个数据做交换
        exchange(0, size - 1);
        size--;
        // 重新对第一个元素做下潜操作
        down(0);
        return top;
    }
​
    /**
     * 返回并移除指定索引的数据
     * 1、先将索引 index 和最后一个数据进行交换
     * 2、移除最后一个数据,及删除了开始索引为 index 的数据
     * 3、对索引 index 的数据进行上浮操作,当不发生节点交换时再进行下潜操作
     *
     * @param index
     * @return
     */
    public int poll(int index) {
        if (index < 0) {
            throw new IndexOutOfBoundsException("index 必须不小于 0");
        }
        // 当索引数据
        if (index > size - 1) {
            throw new NullPointerException("没有数据了");
        }
        //先将索引 index 和最后一个数据进行交换
        exchange(index, size - 1);
        //移除最后一个数据,及删除了开始索引为 index 的数据
        int removed = array[size - 1];
        size--;
        //对索引 index 的数据进行上浮操作,当不发生节点交换时再进行下潜操作
        int newParent = up(index);
        down(newParent);
        return removed;
    }
​
    /**
     * 向堆中添加一个元素
     *
     * @param value
     * @return
     */
    public boolean offer(int value) {
        if (size >= array.length) {
            return false;
        }
        array[size] = value;
        size++;
        //上浮最后一个元素
        up(size - 1);
        return true;
    }
​
    /**
     * 调整堆结构,让其满足大根堆
     * 方法:从最后一个非叶子节点开始,依次向前做下潜操作
     * 在数组中,左后一个非叶子节点的索引为:(size/2  - 1)
     */
    private void adjust() {
        for (int i = size / 2 - 1; i >= 0; i--) {
            //依次做下潜操作即可
            down(i);
        }
    }
​
​
    /**
     * 堆节点做下潜操作
     *
     * @param parent
     */
    private void down(int parent) {
        if (2 * parent + 1 < size && array[parent] > array[2 * parent + 1]) {
            // 交换位置
            exchange(parent, 2 * parent + 1);
​
            // 做下潜操作
            down(2 * parent + 1);
        } else if (2 * parent + 2 < size && array[parent] > array[2 * parent + 2]) {
            // 交换位置
            exchange(parent, 2 * parent + 2);
​
            // 做下潜操作
            down(2 * parent + 2);
        }
    }
​
    /**
     * 上浮操作,并返回上浮终点索引
     *
     * @param child
     * @return 上浮终点索引
     */
    private int up(int child) {
        int parent = (child - 1) / 2;
        while (parent >= 0 && array[child] < array[parent]) {
            exchange(parent, child);
            child = parent;
            parent = (child - 1) / 2;
        }
        return Math.max(child, 0);
    }
​
    /**
     * 交换堆中两个位置的数据
     *
     * @param i
     * @param j
     */
    private void exchange(int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
​
    @Override
    public String toString() {
        int[] a = new int[size];
        System.arraycopy(array, 0, a, 0, size);
        return Arrays.toString(a);
    }
}

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