算法与数据结构(六):堆

算法与数据结构(六):堆

  • 博主会对算法与数据结构会不断进行更新,敬请期待,如有什么建议,欢迎联系。

  • 堆的特性:

    堆的定义:

    堆是计算机科学中一类特殊的数据结构的统称,堆通常可以被看做是一棵完全二叉树的数组对象。

    堆的特性

    • 它是完全二叉树,除了树的最后一层结点不需要是满的,其它的每一层从左到右都是满的,如果最后一层结点不是满的,那么要求左满右不满。
    • 它通常用数组来实现。
      具体方法就是将二叉树的结点按照层级顺序放入数组中,根结点在位置1,它的子结点在位置2和3,而子结点的子结点则分别在位置4,5,6和7,以此类推。 如果一个结点的位置为k,则它的父结点的位置为[k/2],而它的两个子结点的位置则分别为2k和2k+1。这样,在不使用指针的情况下,我们也可以通过计算数组的索引在树中上下移动:从a[k]向上一层,就令k等于k/2,向下一层就令k等于2k或2k+1。
    • 每个结点都大于等于它的两个子结点。这里要注意堆中仅仅规定了每个结点大于等于它的两个子结点,但这两个子结点的顺序并没有做规定,跟我们之前学习的二叉查找树是有区别的 。

在这里插入图片描述

堆的实现细节:

  • 堆实现最主要的就是堆的上浮与下沉算法,由于父节点总是比子节点大;
  • 堆插入数据总是在数组的末尾插入,所以,当堆插入数据时,就要对新插入的数据进行上浮;
  • 当堆取出最大数据时,首先让头节点和最后的节点进行互换,堆的size-1,然后,让头节点进行下沉;

堆的实现代码如下:


package com.victor.heap;

/**
 * @description: 堆的实现
 * @author: victor
 * @date: 2020/12/12  15:11
 */
public class Heap<T extends Comparable<T>> {
    //定义一个数组
    private T[] items;
    //元素的个数
    private int N;

    @SuppressWarnings("unchecked")
    public Heap(int capacity) {
        this.items = (T[]) new Comparable[capacity + 1];
        this.N = 0;
    }

    //是否i坐标的值小于j坐标的值
    private boolean less(int i, int j) {
        return items[i].compareTo(items[j]) < 0;
    }

    /**
     * 交换坐标为i,j的元素
     *
     * @param i 坐标为i
     * @param j 坐标为j
     */
    private void exchange(int i, int j) {
        T temp = items[i];
        items[i] = items[j];
        items[j] = temp;
    }

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

    /**
     * 使用上浮算法,使堆中下标为k的元素处于正确的位置
     *
     * @param k 下标为k的元素
     */
    private void swim(int k) {
        //当k不为根节点时,上浮
        while (k > 1) {
            if (less(k / 2, k)) exchange(k / 2, k);
            else break;     //当k/2节点大于k节点时,停止循环
            k = k / 2;
        }
    }

    /**
     * 删除最大的元素
     *
     * @return 删除的最大元素
     */
    public T delMax() {
        T max = items[1];
        //让最大的元素与最后的元素交换
        exchange(1, N);
        //删除最大元素
        items[N] = null;
        N--;
        sink(1);
        return max;
    }

    /**
     * 采用下沉算法,使堆中下标为k的元素处于正确的位置
     *
     * @param k 下标为k的值
     */
    private void sink(int k) {

        while (2 * k <= N) {
            //获取当前节点子节点最大的节点
            int max;    //记录最大的节点
            if (2 * k + 1 <= N) {
                max = less(2 * k, 2 * k + 1) ? 2 * k + 1 : 2 * k;
            } else {
                max = 2 * k;
            }
            //如果最大的子节点小于k节点,则结束循环
            if (less(max, k)) break;
            //k节点与最大子节点进行交换
            exchange(k, max);
            //循环下一个节点
            k = max;
        }
    }
}

  • 由于,堆的父节点总是大于它的子节点,使用堆的第一个值总是最大的值,所以,根据这个规律可以运用的堆的知识对数组进行排序,因此,堆的排序代码如下:
package com.victor.heap;

/**
 * 堆排序
 *
 * @description:
 * @author: victor
 * @date: 2021/1/22  10:02
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class HeapSort {

    private static boolean less(Comparable[] heap, int i, int j) {
        return heap[i].compareTo(heap[j]) < 0;
    }

    private static void exchange(Comparable[] heap, int i, int j) {
        Comparable cache = heap[i];
        heap[i] = heap[j];
        heap[j] = cache;
    }

    /**
     * 对数组进行排序
     * 1. 首先创建一个堆数组
     * 2. 对堆进行下沉操作,形成堆
     * 3. 将首节点与尾结点进行交换,交换后进行首节点下沉,下沉的范围排除交换之前的首节点
     * 4. 下沉之后,排除尾结点,循环3
     *
     * @param source 源数组
     */
    public static void sort(Comparable[] source) {
        Comparable<Object>[] heap = new Comparable[source.length + 1];
        createHeap(source, heap);
        int N = heap.length - 1;
        while (N != 1) {
            exchange(heap, 1, N);
            N--;
            sink(heap, 1, N);
        }

        System.arraycopy(heap, 1, source, 0, source.length);

    }

    /**
     * 根据原数组source构造出数组heap
     *
     * @param source 原数组
     * @param heap   堆
     */
    private static void createHeap(Comparable[] source, Comparable[] heap) {
        System.arraycopy(source, 0, heap, 1, source.length);
        for (int i = (heap.length - 1) / 2; i > 0; i--) {
            sink(heap, i, heap.length - 1);
        }
    }

    /**
     * 下沉算法
     *
     * @param heap   堆
     * @param target 下沉的节点
     * @param range  要下沉的范围
     */
    private static void sink(Comparable[] heap, int target, int range) {
        while (2 * target <= range) {
            int max = 2 * target;
            int right;
            if ((right = max + 1) <= range)        //存在右子节点
                max = less(heap, max, right) ? right : max;
            if (less(heap, target, max))
                exchange(heap, max, target);
            else
                break;
            target = max;
        }
    }


}

  • 这里实现了堆的由大到小的排序,当然,堆也可以实现有小到大的排序,只要对上浮和下沉算法进行修改就能实现,这里不再赘述;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值