堆 (数据结构)

1 篇文章 1 订阅

堆 (数据结构)[工程下载>>>]


  堆(英语:Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。在队列中,调度程序反复提取队列中第一个作业并运行,因为实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权。堆即为解决此类问题设计的一种数据结构。

1.1 逻辑定义

  n个元素序列 k1,k2...ki...kn ,当且仅当满足下列关系时称之为堆:

   (ki<=k2i,ki<=k2i+1)(ki>=k2i,ki>=k2i+1),(i=1,2,3,4,...,n/2)

1.2 性质

  堆的实现通过构造二叉堆(binary heap),实为二叉树的一种;由于其应用的普遍性,当不加限定时,均指该数据结构的这种实现。这种数据结构具有以下性质。

任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
堆总是一棵完全树。即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入。

  将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

1.3 API介绍

  堆数据结构,如果数数元素是可以比较的,就不需要传入比较器对象,如果数据是可以比较的就要传入比较器,根据传入的比较器,可以实现大顶堆和小顶堆。

方法名作用时间复杂度
Heap()无参构造函数-
Heap(T t)构造函数-
Heap(T t, Comparator< T > comparator)构造函数-
Heap(Comparator< T > comparator)构造函数-
Heap(Collection< T > coll)构造函数-
Heap(Collection< T > coll, Comparator< T > comparator)构造函数-
Comparator< T > getComparator()获取比较器对象-
setComparator(Comparator< T > comparator)设置比较器对象-
shiftUp(int idx)向上调整堆 O(log(n))
shiftDown(int idx)向下调整堆 O(log(n))
int compare(Object o1, Object o2)比较两个数的大小-
add(T item)添加一个元素 O(log(n))
add(T[] arr)添加一组元素 O(nlog(n))
add(Collection< T > coll)添加一组元素 O(nlog(n))
T getTop()获取堆顶元素,但不删除 O(1)
T deleteTop()删除堆顶结点 O(log(n))
int size()获取堆的大小 O(1)
boolean isEmpty()判断堆是否为空 O(1)
clear()清空堆,如果有比较器,不会删除比较器-
delete()删除元素,并且清空比较器对象-
List< T > getData()获取堆中所有的数据-
toString()堆信息描述-

1.4 代码实现

package com.datastruct;

import java.util.*;

/**
 * 堆数据结构,如果数数元素是可以比较的,就不需要传入比较器对象,如果数据是可以比较的就要传入比较器,
 * 根据传入的比较器,可以实现大顶堆和小顶堆。
 * Author: 王俊超
 * Time: 2016-05-25 19:30
 * CSDN: http://blog.csdn.net/derrantcm
 * Github: https://github.com/Wang-Jun-Chao
 * Declaration: All Rights Reserved !!!
 */
public class Heap<T> {
    // 堆中元素存放的集合
    private List<T> data;
    // 比较器
    private Comparator<T> comparator;

    /**
     * 无参构造函数
     */
    public Heap() {
        data = new ArrayList<>(64);
    }

    /**
     * 构造函数
     *
     * @param t 元素
     */
    public Heap(T t) {
        this();
        add(t);
    }

    /**
     * 构造函数
     *
     * @param t          元素对象
     * @param comparator 比较器对象
     */
    public Heap(T t, Comparator<T> comparator) {
        this.comparator = comparator;
        add(t);
    }

    /**
     * 构造函数
     *
     * @param comparator 比较器对象
     */
    public Heap(Comparator<T> comparator) {
        if (comparator == null) {
            throw new NullPointerException("比较器不能为空");
        }

        this.comparator = comparator;
        this.data = new ArrayList<>(64);
    }

    /**
     * 构造函数
     *
     * @param arr 数据数组
     */
    public Heap(T[] arr) {
        if (arr == null) {
            throw new NullPointerException("数据据为空");
        }

        this.data = new ArrayList<>(arr.length);
        this.add(arr);
    }

    /**
     * 构造函数
     *
     * @param arr        数据数组
     * @param comparator 比较器对象
     */
    public Heap(T[] arr, Comparator<T> comparator) {
        if (arr == null) {
            throw new NullPointerException("数据据为空");
        }

        if (comparator == null) {
            throw new NullPointerException("比较器为空");
        }

        this.comparator = comparator;
        this.data = new ArrayList<>(arr.length);
        add(arr);
    }

    /**
     * 构造函数
     *
     * @param coll 数据集合
     */
    public Heap(Collection<T> coll) {
        if (coll == null) {
            throw new NullPointerException("数据据为空");
        }

        this.data = new ArrayList<>(coll.size());
        add(coll);
    }


    /**
     * 构造函数
     *
     * @param coll       数据集合
     * @param comparator 比较器对象
     */
    public Heap(Collection<T> coll, Comparator<T> comparator) {
        if (coll == null) {
            throw new NullPointerException("数据据为空");
        }

        this.comparator = comparator;
        this.data = new ArrayList<>(coll.size());
        add(coll);
    }


    /**
     * 获取比较器对象
     *
     * @return 比较器对象
     */
    public Comparator<T> getComparator() {
        return comparator;
    }

    /**
     * 设置比较器对象
     *
     * @param comparator 比较器对象
     */
    public void setComparator(Comparator<T> comparator) {
        this.comparator = comparator;
    }

    /**
     * 向上调整堆
     *
     * @param idx 被上移元素的起始位置
     */
    public void shiftUp(int idx) {
        // 检查是位置是否正确
        if (idx < 0 || idx >= data.size()) {
            throw new IllegalArgumentException(idx + "");
        }

        // 获取开始调整的元素对象
        T intent = data.get(idx);

        // 如果不是根元素,则需要上移
        while (idx > 0) {
            // 找父元素对象的位置
            int parentIdx = (idx - 1) / 2;
            // 获取父元素对象
            T parent = data.get(parentIdx);
            //上移的条件,子节点比父节点大,此处定义的大是以比较器返回值为准
            if (compare(intent, parent) > 0) {
                // 将父节点向下放
                data.set(idx, parent);
                idx = parentIdx;
                // 记录父节点下放的位置
            }
            // 子节点不比父节点大,说明父子路径已经按从大到小排好顺序了,不需要调整了
            else {
                break;
            }
        }

        // index此时记录是的最后一个被下放的父节点的位置(也可能是自身),
        // 所以将最开始的调整的元素值放入index位置即可
        data.set(idx, intent);
    }

    /**
     * 向下调整堆
     *
     * @param idx 被下移的元素的起始位置
     */
    public void shiftDown(int idx) {
        // 检查是位置是否正确
        if (idx < 0 || idx >= data.size()) {
            throw new IllegalArgumentException(idx + "");
        }

        // 获取开始调整的元素对象
        T intent = data.get(idx);
        // 获取开始调整的元素对象的左子结点的元素位置
        int leftIdx = idx * 2 + 1;
        // 如果有左子结点
        while (leftIdx < data.size()) {
            // 取左子结点的元素对象,并且假定其为两个子结点中最大的
            T maxChild = data.get(leftIdx);
            // 两个子节点中最大节点元素的位置,假定开始时为左子结点的位置
            int maxIdx = leftIdx;

            // 获取右子结点的位置
            int rightIdx = leftIdx + 1;
            // 如果有右子结点
            if (rightIdx < data.size()) {
                T rightChild = data.get(rightIdx);
                // 找出两个子节点中的最大子结点
                if (compare(rightChild, maxChild) > 0) {
                    maxChild = rightChild;
                    maxIdx = rightIdx;
                }
            }

            // 如果最大子节点比父节点大,则需要向下调整
            if (compare(maxChild, intent) > 0) {
                // 将较大的子节点向上移
                data.set(idx, maxChild);
                // 记录上移节点的位置
                idx = maxIdx;
                // 找到上移节点的左子节点的位置
                leftIdx = 2 * idx + 1;
            }
            // 最大子节点不比父节点大,说明父子路径已经按从大到小排好顺序了,不需要调整了
            else {
                break;
            }

        }
        // index此时记录是的最后一个被上移的子节点的位置(也可能是自身),
        // 所以将最开始的调整的元素值放入index位置即可
        data.set(idx, intent);
    }

    /**
     * 比较两个数的大小
     *
     * @param o1 数一
     * @param o2 数二
     * @return 比较结果
     */
    private final int compare(Object o1, Object o2) {
        return comparator == null ? ((Comparable<? super T>) o1).compareTo((T) o2)
                : comparator.compare((T) o1, (T) o2);
    }

    /**
     * 添加一个元素
     *
     * @param item 添加的元素
     */
    public void add(T item) {
        // 将元素添加到最后
        data.add(item);
        // 上移,以完成重构
        shiftUp(data.size() - 1);
    }


    /**
     * 向堆中添加元素
     *
     * @param arr 元素数组
     */
    public void add(T[] arr) {
        if (arr != null) {
            for (T t : arr) {
                add(t);
            }
        }
    }

    /**
     * 向堆中添加元素
     *
     * @param coll 元素集合
     */
    public void add(Collection<T> coll) {
        if (coll != null) {
            for (T t : coll) {
                add(t);
            }
        }
    }

    /**
     * 获取堆顶元素,但不删除
     *
     * @return 堆顶元素
     */
    public T getTop() {
        // 如果堆已经为空,就抛出异常
        if (data.isEmpty()) {
            throw new NoSuchElementException("堆已经为空");
        }

        return data.get(0);
    }

    /**
     * 删除堆顶元素
     *
     * @return 堆顶结点
     */
    public T deleteTop() {
        // 如果堆已经为空,就抛出异常
        if (data.isEmpty()) {
            throw new NoSuchElementException("堆已经为空");
        }

        // 获取堆顶元素
        T first = data.get(0);
        // 删除最后一个元素
        T last = data.remove(data.size() - 1);

        // 删除元素后,如果堆为空的情况,说明删除的元素也是堆顶元素
        if (data.size() == 0) {
            return last;
        } else {
            // 将删除的元素放入堆顶
            data.set(0, last);
            // 自上向下调整堆
            shiftDown(0);
            // 返回堆顶元素
            return first;
        }
    }

    /**
     * 获取堆的大小
     *
     * @return 堆的大小
     */
    public int size() {
        return data.size();
    }

    /**
     * 判断堆是否为空
     *
     * @return 堆是否为空
     */
    public boolean isEmpty() {
        return data.isEmpty();
    }

    /**
     * 清空堆,如果有比较器,不会删除比较器
     */
    public void clear() {
        data.clear();
    }

    /**
     * 删除元素,并且清空比较器对象
     */
    public void delete() {
        data.clear();
        comparator = null;
    }

    /**
     * 获取堆中所有的数据
     *
     * @return 堆中所在的数据
     */
    public List<T> getData() {
        return data;
    }

    /**
     * 堆信息描述
     *
     * @return 堆信息描述字符串
     */
    @Override
    public String toString() {
        return data.toString();
    }
}

1.5 测试

package com.datastruct;

/**
 * Author: 王俊超
 * Time: 2016-05-25 20:45
 * CSDN: http://blog.csdn.net/derrantcm
 * Github: https://github.com/Wang-Jun-Chao
 * Declaration: All Rights Reserved !!!
 */
public class Node {
    private int val;

    public Node() {
    }

    public Node(int val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return "" + val;
    }

    public int getVal() {
        return val;
    }

    public void setVal(int val) {
        this.val = val;
    }
}
package com.datastruct;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

/**
 * Author: 王俊超
 * Time: 2016-05-25 20:44
 * CSDN: http://blog.csdn.net/derrantcm
 * Github: https://github.com/Wang-Jun-Chao
 * Declaration: All Rights Reserved !!!
 */
public class HeapTest {

    // 产生的随机数数的个数
    private final static int NUM = 20;
    // 下边界,包含
    private final static int LOWER = 0;
    // 上边界,不包含
    private final static int UPPER = 100;


    private static List<Integer> getNumber() {
        List<Integer> arr = new ArrayList<>(NUM);
        Random random = new Random();

        for (int i = 0; i < NUM; i++) {
            arr.add(LOWER + random.nextInt(UPPER - LOWER));
        }
        return arr;
    }

    private List<Node> getNodes() {
        List<Integer> arr = getNumber();
        List<Node> nodes = new ArrayList<>();

        for (Integer i : arr) {
            nodes.add(new Node(i));
        }
        return nodes;
    }

    @Test
    public void testMaxHeapWithoutComparator() {
        List<Integer> arr = getNumber();
        System.out.println("HeapTest.testMaxHeapWithoutComparator");
        System.out.println(arr);
        Heap<Integer> maxHeap = new Heap<>(arr);
        System.out.println(maxHeap);
    }

    @Test
    public void testMaxHeapWithComparator() {
        List<Integer> arr = getNumber();
        System.out.println("HeapTest.testMaxHeapWithComparator");
        System.out.println(arr);
        Heap<Integer> maxHeap = new Heap<>(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println(maxHeap);
    }

    /**
     * 使用自定义类进行操作
     */
    @Test
    public void testMaxHeapWithComparator2() {
        List<Node> nodes = getNodes();

        System.out.println("HeapTest.testMaxHeapWithComparator2");
        System.out.println(nodes);
        Heap<Node> maxHeap = new Heap<>(nodes, new Comparator<Node>() {
            @Override
            public int compare(Node o1, Node o2) {
                return o1.getVal() - o2.getVal();
            }
        });
        System.out.println(maxHeap);
    }

    @Test
    public void testMinHeapWithComparator() {
        List<Integer> arr = getNumber();
        System.out.println("HeapTest.testMinHeapWithComparator");
        System.out.println(arr);
        Heap<Integer> maxHeap = new Heap<>(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(maxHeap);
    }

    /**
     * 使用自定义类进行操作
     */
    @Test
    public void testMinHeapWithComparator2() {
        List<Node> nodes = getNodes();

        System.out.println("HeapTest.testMaxHeapWithComparator2");
        System.out.println(nodes);
        Heap<Node> maxHeap = new Heap<>(nodes, new Comparator<Node>() {
            @Override
            public int compare(Node o1, Node o2) {
                return o2.getVal() - o1.getVal();
            }
        });
        System.out.println(maxHeap);
    }
}

1.6 测试结果

这里写图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值