玩转数据结构之Java实现二叉堆

文章介绍了如何使用Java自定义动态数组实现最大二叉堆,包括插入、删除、查找等操作,并基于最大堆构建了优先级队列,讨论了相关的时间复杂度。

前言

二叉堆是一颗完全二叉树,并满足堆中某个节点的值总是小于或等于父节点的值
二叉堆
最大堆:根节点元素最大值;
最小堆:根节点元素最小值;

代码实现

基于数组实现最大二叉堆

  • 自定义动态数组
package com.sjgd.heap;

/**
 * 自定义动态数组
 */
public class Array<E> {
    /**
     * 维护的数组
     */
    private E[] data;
    /**
     * 数组中元素的个数
     */
    private int size;

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

    public Array(E[] arr) {
        data = (E[]) new Object[arr.length];
        System.arraycopy(arr, 0, data, 0, arr.length);
        size = arr.length;
    }

    public Array() {
        this(10);
    }

    /**
     * 获取数组的融了
     *
     * @return
     */
    public int getCapacity() {
        return data.length;
    }

    /**
     * 获取数组中元素的个数
     *
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 判断数组是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }


    /**
     * 在数组的index索引插入一个新的元素
     */
    public void add(int index, E e) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add Failed, Require index >=0 and index <= size");
        }
        if (size == data.length) {
            //说明当前数组已满,需要进行扩容
            resize(2 * data.length);
        }
        //索引位置后面的元素都需要往后移1个位置
        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        data[index] = e;
        size++;
    }

    /**
     * 向一个位置添加元素E
     */
    public void addFirst(E e) {
        add(0, e);
    }

    /**
     * 向最后一个位置添加元素E
     *
     * @param e
     */
    public void addLast(E e) {
        add(size, e);
    }

    /**
     * 根据index获取索引对应的元素E
     *
     * @param index
     * @return
     */
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Get Failed, Index is illegal!");
        }
        return data[index];
    }

    /**
     * 交换数组中两个元素的位置
     */
    public void swap(int i, int j) {
        if (i < 0 || i >= size || j < 0 || j >= size) {
            throw new IllegalArgumentException("The Index is not Illegal");
        }
        E e = data[i];
        data[i] = data[j];
        data[j] = e;
    }

    /**
     * 修改index索引位置对应的元素
     *
     * @param index
     * @param e
     */
    public void set(int index, E e) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Add Failed, Require index >=0 and index <= size");
        }
        data[index] = e;
    }


    /**
     * 判断当前数组是否包含元素E
     *
     * @param e
     * @return
     */
    public boolean contains(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 查询元素E对应的下标位置
     *
     * @param e
     * @return
     */
    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("remove Failed, Require index >=0 and index < size");
        }
        E ret = data[index];
        //从删除索引位置之后元素均向前移一位
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;
        //进行空间释放
        data[size] = null;
        //当数组元素到容量1/4时,进行缩容操作
        if (size == data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }
        return ret;
    }

    /**
     * 移除第一个元素
     *
     * @return
     */
    public E removeFirst() {
        return remove(0);
    }

    /**
     * 移除最后一个元素
     *
     * @return
     */
    public E removeLast() {
        return remove(size - 1);
    }

    /**
     * 移除对应元素e
     *
     * @param e
     */
    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }


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

    /**
     * 对数组进行动态扩容或缩容
     *
     * @param newCapacity
     */
    private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }
}

  • 实现最大二叉堆
package com.sjgd.heap;

/**
 * 基于数组实现最大堆
 * on 2023/2/8
 */
public class MaxHeap<E extends Comparable<E>> {
    private Array<E> array;

    public MaxHeap(int capacity) {
        array = new Array<>(capacity);
    }

    public MaxHeap() {
        array = new Array<>();
    }

    public MaxHeap(E[] arr) {
        array = new Array<>(arr);
        for (int i = parent(array.getSize() - 1); i >= 0; i--) {
            siftDown(i);
        }
    }

    /**
     * 返回堆中元素的个数
     *
     * @return
     */
    public int size() {
        return array.getSize();
    }

    /**
     * 返回一个布尔值,判断当前堆是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return array.isEmpty();
    }

    /**
     * 返回完全二叉树的数组表示中,一个索引所表示的元素的父节点所有的索引
     *
     * @return
     */
    private int parent(int index) {
        if (index == 0) {
            throw new IllegalArgumentException("index 0 doesn't have parent.");
        }
        return (index - 1) / 2;
    }

    /**
     * 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
     *
     * @return
     */
    private int leftChild(int index) {
        return index * 2 + 1;
    }

    /**
     * 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
     *
     * @return
     */
    private int rightChild(int index) {
        return index * 2 + 2;
    }

    /**
     * 往最大堆中添加元素e
     *
     * @param e
     */
    public void add(E e) {
        array.addLast(e);
        siftUp(array.getSize() - 1);
    }


    public E removeMax() {
        E result = getMax();
        //先交换最大值和最后一个元素的位置
        array.swap(0, array.getSize() - 1);
        //删除最后一个元素
        array.removeLast();
        siftDown(0);
        return result;
    }

    public E getMax() {
        if (array.getSize() == 0) {
            throw new IllegalArgumentException("The MaxHeap is Empty!");
        }
        return array.get(0);
    }

    /**
     * 取出堆中最大元素,并替换成元素e
     *
     * @return
     */
    public E replace(E e) {
        E result = getMax();
        array.set(0, e);
        siftDown(0);
        return result;
    }


    /**
     * 不断与左右子节点中较大的比较并交换,直到满足最大堆要求;
     *
     * @param index
     */
    private void siftDown(int index) {
        while (leftChild(index) < array.getSize()) {
            int leftChildIndex = leftChild(index);
            int rightChildIndex = leftChildIndex + 1;
            if (rightChildIndex < array.getSize() && array.get(leftChildIndex).compareTo(array.get(rightChildIndex)) < 0) {
                leftChildIndex = rightChildIndex;
            }
            if (array.get(index).compareTo(array.get(leftChildIndex)) > 0) {
                break;
            }
            array.swap(index, leftChildIndex);
            index = leftChildIndex;

        }
    }

    /**
     * 不断与父节点元素进行对比,如果大于父节点元素不断交换两者位置,直到满足最大堆要求;
     *
     * @param index
     */
    private void siftUp(int index) {
        while (index > 0 && array.get(index).compareTo(array.get(parent(index))) > 0) {
            array.swap(index, parent(index));
            index = parent(index);
        }
    }
}

  • 基于MaxHeap实现优先级队列;
package com.sjgd.heap;

/**
 * @author 基于MaxHeap实现优先级队列
 * on 2023/2/8
 */
public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {
    private MaxHeap<E> maxHeap;

    public PriorityQueue() {
        maxHeap = new MaxHeap<>();
    }

    @Override
    public int getSize() {
        return maxHeap.size();
    }

    @Override
    public boolean isEmpty() {
        return maxHeap.isEmpty();
    }

    @Override
    public void enqueue(E e) {
        maxHeap.add(e);
    }

    @Override
    public E dequeue() {
        return maxHeap.removeMax();
    }

    @Override
    public E getFront() {
        return maxHeap.getMax();
    }
}

时间复杂度

入队出队:O(logn)

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值