初识数据结构之堆_Java

  • 特点
  1. 堆是一棵完全二叉树,即除了最下边一层其余都是满的状态,最下边一层满足最后一个元素的左侧都是满状态。
  2. 堆有别于二叉树的一个特点就是其父节点是大于两个子节点的,两个子节点的左右分步没有硬性要求
  3. 堆通常以数组进行实现,常见的规律有 假设一个节点所在的索引为k,则其父节点所在的索引为k/2,两个子节点的索引分别为2k2k+1

自己实现堆

public class Heap<T extends Comparable<T>> {

    // 用来存储元素的数组
    private T[] items;
    // 用来存储堆中元素的个数
    private int size;

    public Heap(int capacity) {
        this.items = (T[]) new Comparable[capacity + 1];
        this.size = 0;
    }

    /**
     * 交换数组索引i和索引j处的元素
     * @param i 索引
     * @param j 索引
     */
    private void exchange(int i, int j) {
        T temp = this.items[i];
        this.items[i] = this.items[j];
        this.items[j] = temp;
    }


    /**
     * 删除堆中最大的元素,并返回
     * @return 堆中最大的元素
     */
    public T deleteMax() {
        // 交换最大的元素和最大索引处的值
        T maxItem = items[1];
        exchange(1, size);
        // 删除最大的元素,让完全二叉树中最右侧的元素变为临时的根节点
        items[size] = null;
        this.size --;
        // 通过下沉算法调整堆,让根节点重新处于正确的位置,让堆重新有序
        this.sink(1);
        return maxItem;
    }

    /**
     * 向堆中插入一个元素
     * @param t 插入的元素
     */
    public void insert(T t) {
        items[++ size] = t;
        swim(size);
    }

    /**
     * 使用上浮算法,将索引index处的元素不断的与其父节点进行比较,最终能够在堆中处于正确的位置
     * @param index 索引
     */
    private void swim(int index) {
        while (index > 1) {
            // 与父节点进行比较
            if (items[index].compareTo(items[index/2]) > 0) {
                exchange(index, index/2);
            }
            index = index / 2;
        }
    }

    /**
     * 使用下沉算法,将索引index处的元素不断的
     * @param index
     */
    private void sink(int index) {
        // 通过循环弊端的遍历当前索引的两个子节点
        while (2*index <= size) {
            // 获取两个子节点中较大的那个
            int maxChildIndex;
            if (2*index+1 <= size) {
                maxChildIndex = items[2*index].compareTo(items[2*index+1]) > 0 ? 2*index : 2*index+1;
            } else {
                maxChildIndex = 2*index;
            }

            // 比较当前索引处的值和两个子节点最大的值的关系
            if (items[index].compareTo(items[maxChildIndex]) < 0) {
                // 当前索引比子节点的最大值小,就往两个子节点的最大者底下沉
                exchange(index, maxChildIndex);
                index = maxChildIndex;
            } else {
                break;
            }
        }
    }

    /**
     * 将原始的数组进行堆排序并返回
     * @param source 原始数组
     */
    public static void sort(Comparable[] source) {
        // 1. 构建堆
        // 将source数组中的元素拷贝到堆数组中,形成一个无序待排序的堆
        Comparable[] heap = new Comparable[source.length + 1];
        System.arraycopy(source, 0, heap, 1, source.length);
        // 对堆中的元素做下沉调整(从堆长度的一般开始,往索引1处进行扫描)
        for (int i=(heap.length)/2; i>0; i--) {
            sinkSort(heap, i, source.length);
        }

        // 2. 堆排序
        // 记录未排序的数组最大索引
        int maxIndex = source.length;
        while (maxIndex != 1) {
            // 交换1索引处和未排序的最大索引处的值
            Comparable temp = heap[1];
            heap[1] = heap[maxIndex];
            heap[maxIndex] = temp;
            maxIndex --;
            // 对交换之后的索引1处元素进行下沉调整,调整的区域为未排序区域
            sinkSort(heap, 1, maxIndex);
        }

        // 3. 将排序好的数组转到原数组中
        System.arraycopy(heap, 1, source, 0, source.length);
    }

    /**
     * 堆排序所需的下沉排序
     * @param heap 堆数组
     * @param targetIndex 需要做下沉的元素索引
     * @param range 做下沉的范围,0-range
     */
    private static void sinkSort(Comparable[] heap, int targetIndex, int range) {
        while (2*targetIndex <= range) {
            int maxChildIndex;
            if (2*targetIndex+1 < range) {
                maxChildIndex = heap[2*targetIndex].compareTo(heap[2*targetIndex+1])>0 ? 2*targetIndex : 2*targetIndex+1;
            } else {
                maxChildIndex = 2*targetIndex;
            }

            if (heap[targetIndex].compareTo(heap[maxChildIndex]) >= 0) break;

            // 元素位置交换
            Comparable temp = heap[targetIndex];
            heap[targetIndex] = heap[maxChildIndex];
            heap[maxChildIndex] = temp;

            targetIndex = maxChildIndex;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啪!~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值