堆 (数据结构)

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



  堆(英语: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(n?log(n))
add(Collection< T > coll) 添加一组元素 O(n?log(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 测试结果

时间: 2024-10-17 22:25:08

堆 (数据结构)的相关文章

堆数据结构+堆排序+最大优先队列的堆的实现

对于堆排序,首先要先知道什么是堆数据结构,堆数据结构就是一个完全二叉树,但是它有自己的性质. 例如最大堆的性质为:A[PARENT[i]]>=A[i]:即每一个结点的值大于等于其左右孩子的值,小于等于其父节点的值.我们在这里只讨论最大堆的情况.我们知道一颗完全二叉树对应一个最大堆的形式,我们要做的就是将二叉树转化为最大堆,这就是所谓的最大堆的维护,我们定义函数MaxheapFY(A,i)来进行操作. 代码: /** *MaxheapFY(A,i):维护位置i最大堆性质,此时假设left(i)和r

【上海交大oj】合并果子(堆数据结构)

4012. 合并果子 Description 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了.多多在合并果子时总共消耗的体力等于每次合并所耗体力之和. 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力.假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计

从一个集合中查找最大最小的N个元素——Python heapq 堆数据结构

Top N问题在搜索引擎.推荐系统领域应用很广, 如果用我们较为常见的语言,如C.C++.Java等,代码量至少也得五行,但是用Python的话,只用一个函数就能搞定,只需引入heapq(堆队列)这个数据结构即可.今天偶然看到这个库,特意记下之. 先看一个例子: 1 >>> import heapq 2 >>> nums = [1,8,2,23,7,-4,18,23,42,37,2] 3 >>> print heapq.nlargest(3, nums

优先队列(堆) -数据结构(C语言实现)

数据结构与算法分析 优先队列 模型 Insert(插入) == Enqueue(入队) DeleteMin(删除最小者) == Dequeue(出队) 基本实现 简单链表:在表头插入,并遍历该链表以删除最小元 时间代价昂贵 二叉查找树 二叉查找树支持许多不需要的操作,实现麻烦,不值得 最合适:二叉堆 二叉堆 堆的两种性质 结构性 完全二叉树:除底层外完全填满,底层也是从左至右填 完全二叉树的高为 log N 分布很有规律可以用数组实现 左儿子 = 2i 右儿子 = 2i + 1 堆序性 树的最小

二叉堆-数据结构-JavaScript版

/** * Created by caoke on 2015/11/21. */ //二叉树 特点父节点比子节点小 var Tree2=function(){ //初始化 二叉树的子元素 this.children=[]; } Tree2.prototype={ size:0, push:function(x){ var arr=this.children //自己节点的编号 var i=arr.length while(i>0){ //父节点的编号 var p=parseInt((i-1)/2

堆数据结构

以下是小根堆定义,包括向堆中插入元素,删除堆中元素,建立新堆,调整堆等函数. typedef int Elemtype; class MinHeap{ public: MinHeap() :elem(0), size(0){} void BuildMinHeap(vector<Elemtype> v); void Modify(int i); int Delete(); void Insert(Elemtype e); private: vector<Elemtype> elem;

数据结构之堆(Heap)的实现

堆数据结构是一种数组对象,它可以被视为一棵完全二叉树结构,所以堆也叫做二叉堆. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值. 2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆). 当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆.当父结点的键值总是小于或等于   任何一个子节点的键值时为最小堆. 最大堆和最小堆是堆数据结构的重点.堆排序中使用特别的多. 堆的存储一般是用一个数组实现的,当然也可以用链式存储,但是特别麻烦. 如下我

D&amp;F学数据结构系列——二叉堆

二叉堆(binary heap) 二叉堆数据结构是一种数组对象,它可以被视为一棵完全二叉树.同二叉查找树一样,堆也有两个性质,即结构性和堆序性.对于数组中任意位置i上的元素,其左儿子在位置2i上,右儿子在左儿子后的单元2i+1中,它的父亲在[i/2](向下取整)中. 因此,一个数据结构将由一个数组.一个代表最大值的整数.以及当前的堆的大小组成.一个典型的优先队列(priority queue)如下: 1 #ifndef _BinHeap_H 2 struct HeapStruct; 3 type

数据结构学习——堆

1 基本介绍 堆数据结构是一种数组对象,它可以被视为一颗完全二叉树.堆的访问可以通过三个函数来进行即, parent(i) return floor(i/2); left(i) return 2i; right(i) return 2i + 1; left操作可以通过一步左移操作完成,right操作可以通过左移并在地位+1实现,parent操作则可以通过把i右移一位得到.在实现中通常会使用宏或者内联函数来实现这三个操作. 二叉堆有两种,最大堆和最小堆.对于最大堆有 A[i] >= A[left(