您的位置:首页 > 理论基础 > 数据结构算法

堆 (数据结构)

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

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

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

##1.1 逻辑定义

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

  (k_{i} <= k_{2i},k_{i} <= k_{2i+1})或者(k_i >= k_{2i},k_{i} >= k_{2i+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 测试结果

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: