一、什么是堆
我们日常生活中经常见到各种堆,比如冬天的雪堆,施工的土堆,都是下面大,上面尖顶的样子,那么数据结构中的堆与日常生活中的堆有哪些异同呢?
1.堆的性质
- 堆是一个完全二叉树(除了最后一层,其他层从左到右的节点都是满的)
- 堆中的节点,都大于等于或小于等于其子节点
- 堆通常使用数组来存储
2.堆的分类
- 大顶堆
- 所有节点大于或等于其子节点
- 小顶堆
- 所有节点大于或小于其子节点
二、通过数组实现堆元素的存储
因为堆是一个完全二叉树
三、通过JAVA实现堆的数据结构
注:这里以小顶堆为例
1.定义堆结构:
- size : 树节点个数
- nodes : 节点值集合
public class MyArrayHeap<T> {
/**
* 节点个数
*/
private int size;
/**
* 节点数组
*/
private T[] nodes;
/**
* 最小容量
*/
private static final int MIN_CAPACITY = 8;
}
2.添加节点
(1)添加节点的流程图
- 将新节点添加到树的末尾,即数组的末尾
- 再将插入的节点与其父节点做对比,如果父节点大于插入的节点,则交换二者的位置,直到插入的节点比父节点小为止
(2)添加节点的实现
/**
* 添加节点
* @param element
* @return
*/
public boolean add(T element) {
//1.如果原数组是空数组,直接将元素插入进去,当作根节点
if (size == 0) {
nodes[0] = element;
return true;
}
//2.如果数组长度达到节点的个数,则扩容
if (size >= nodes.length ) {
ensureCapacity(size * 2 + 1);
}
//3.定义插入位置的索引index,默认插入到树的最后
int index = size;
Comparable<T> node = (Comparable<T>) element;
//4.循环比较插入节点与当前的父节点的大小,如果大于父节点,不用交换,否则,要交换位置
while (index > 0) {
//父节点的索引
int parent = (index - 1) / 2;
//父节点的元素值
T e = nodes[parent];
//如果当前插入节点(子节点)比父节点大,那么当前位置就是要插入的位置
if (node.compareTo(e) >= 0) {
break;
}
//插入节点位置与父节点位置交换
nodes[index] = e;
index = parent;
}
//最后得到插入的位置
nodes[index] = element;
return true;
}
3.删除节点
(1)删除节点的流程图
- 删除根节点
- 选出新的父节点
(2)删除节点的实现
/**
* 删除也叫删除最小元素
* @return
*/
public boolean remove() {
if (size == 0) {
return false;
}
//1.定义被最后替换的索引值
int index = 0;
//2.最后一个元素,也就是最后被移动的元素
T element = nodes[size - 1];
Comparable<T> node = (Comparable<T>) element;
//父节点个数half
int half = size / 2;
while (index < half) {
//子节点索引及子节点值
int child = index * 2 + 1;
T obj = nodes[child];
//右子节点索引
int rightChild = child + 1;
//如果右子节点比左子节点小,那么右子节点移动到左子节点的位置
if (rightChild < size && ((Comparable<T>) obj).compareTo(nodes[rightChild]) > 0) {
child = rightChild;
obj = nodes[child];
}
if (node.compareTo(obj) <= 0) {
break;
}
nodes[index] = obj;
index = child;
}
//最后一个元素仍然存储在数组最后一个位置上
nodes[index] = element;
size --;
return true;
}
4.完整实现
package main.java.com.shixinke.java.demo.dataStruct;
public class MyArrayHeap<T> {
/**
* 节点个数
*/
private int size;
/**
* 节点数组
*/
private T[] nodes;
/**
* 最小容量
*/
private static final int MIN_CAPACITY = 8;
public MyArrayHeap() {
clearHeap(MIN_CAPACITY);
}
public MyArrayHeap(int capacity) {
clearHeap(capacity);
}
public void clear() {
clearHeap(MIN_CAPACITY);
}
/**
* 获取节点个数
* @return
*/
public int size() {
return size;
}
/**
* 是否为空堆
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 添加节点
* @param element
* @return
*/
public boolean add(T element) {
//1.如果原数组是空数组,直接将元素插入进去,当作根节点
if (size == 0) {
nodes[0] = element;
size = 1;
return true;
}
//2.如果数组长度达到节点的个数,则扩容
if (size >= nodes.length ) {
ensureCapacity(size * 2 + 1);
}
//3.定义插入位置的索引index,默认插入到树的最后
int index = size;
Comparable<T> node = (Comparable<T>) element;
//4.循环比较插入节点与当前的父节点的大小,如果大于父节点,不用交换,否则,要交换位置
while (index > 0) {
//父节点的索引
int parent = (index - 1) / 2;
//父节点的元素值
T e = nodes[parent];
//如果当前插入节点(子节点)比父节点大,那么当前位置就是要插入的位置
if (node.compareTo(e) >= 0) {
break;
}
//插入节点位置与父节点位置交换
nodes[index] = e;
index = parent;
}
//最后得到插入的位置
nodes[index] = element;
size ++;
return true;
}
/**
* 删除也叫删除最小元素
* @return
*/
public boolean remove() {
if (size == 0) {
return false;
}
//1.定义被最后替换的索引值
int index = 0;
//2.最后一个元素,也就是最后被移动的元素
T element = nodes[size - 1];
Comparable<T> node = (Comparable<T>) element;
//父节点个数half
int half = size / 2;
while (index < half) {
//子节点索引及子节点值
int child = index * 2 + 1;
T obj = nodes[child];
//右子节点索引
int rightChild = child + 1;
//如果右子节点比左子节点小,那么右子节点移动到左子节点的位置
if (rightChild < size && ((Comparable<T>) obj).compareTo(nodes[rightChild]) > 0) {
child = rightChild;
obj = nodes[child];
}
if (node.compareTo(obj) <= 0) {
break;
}
nodes[index] = obj;
index = child;
}
//最后一个元素仍然存储在数组最后一个位置上
nodes[index] = element;
size --;
return true;
}
private void clearHeap(int capacity) {
size = 0;
ensureCapacity(capacity);
}
/**
* 扩容
* @param capacity
*/
private void ensureCapacity(int capacity) {
if (capacity < size) {
return;
}
T[] oldNodes = nodes;
nodes = (T[]) new Object[capacity];
for (int i = 0; i< size; i++) {
nodes[i] = oldNodes[i];
}
}
//打印节点
public void print() {
System.out.println();
System.out.print("[");
for (int i = 0; i < size; i++) {
System.out.print(i+"="+nodes[i]);
if (i < size -1) {
System.out.print(",");
}
}
System.out.print("]");
System.out.println();
}
//演示测试
public static void main(String[] args) {
MyArrayHeap<Integer> heap = new MyArrayHeap<Integer>();
heap.add(21);
heap.add(23);
heap.add(25);
heap.add(24);
heap.add(26);
heap.add(27);
heap.add(29);
heap.add(28);
heap.add(30);
//heap.print(); //[0=21,1=23,2=25,3=24,4=26,5=27,6=29,7=28,8=30]
heap.remove();
heap.print(); //[0=23,1=24,2=25,3=28,4=26,5=27,6=29,7=30]
}
}