堆是一种数据结构,分为大根堆和小根堆。
下面以大根堆为例
1.大根堆有着如下特点:
a.顺序:堆顶元素永远是最大的。
b.形状:堆是一颗完全二叉树。
这两个特性保证了堆在插入和删除的过程中最大时间复杂度也是满足O(logn)的,所以是一种非常高效的数据结构格式。
2.更新堆的两种方式,分别对应了插入元素和删除堆顶元素操作:
自底向上
自顶向下
3.实现 可以使用数组作为隐式树(因为结构是完全二叉树)
如果当前元素的下标为i,那么:
当前元素的父亲下标:i - 1 / 2
当前元素的左子节点下标:i * 2 + 1
当前元素的右子节点下标:i * 2 + 2
小根堆同理
下面来看代码:
/**
* @author xuyp
*/
public class Heap {
// 数组存储结构
private int[] data;
// 当前元素数量
private int len;
// 容量
private int capacticy;
public Heap(int size) {
capacticy = size;
data = new int[capacticy];
len = 0;
}
/**
* 从最后一个节点的父节点len - 1 - 1 / 2 开始 反复调整堆 直到根节点0 为止
*/
private void shift() {
int parent = (len - 1 - 1) / 2;
while (parent >= 0) {
shiftDown(parent);
parent--;
}
}
private void shiftDown(int parent) {
int temp = data[parent];
for (int i = 2 * parent + 1; i < len; i = 2 * i + 1) {
// 取较大子节点的下标
if (i < len - 1 && data[i] < data[i + 1]) {
i++;
}
// 父节点大于左右孩子中的较大者 则调整结束
if (temp >= data[i]) {
break;
} else {
// 将左右孩子的较大者调整到父节点
data[parent] = data[i];
// 关键:修改parent值,以便继续向下调整
parent = i;
}
}
data[parent] = temp; // 被调整的结点的值放入最终位置
}
/**
* 插入元素入堆
* @param e
*/
public void add(int e) {
if (len >= capacticy) {
data = Arrays.copyOf(data, capacticy << 1);
}
data[len] = e;
len++;
shift();
}
/**
* 删除堆顶元素
*/
private void deleteTop() {
if (len <= 0)
return;
// 堆顶元素和最后一个元素交换
data[0] = data[len - 1];
len--;
shift();
}
/**
* 返回堆顶元素
* @return
*/
public int poll() {
int top = data[0];
deleteTop();
return top;
}
/**
* 堆排序
* @param arr
* @return
*/
public int[] heapSort(int[] arr) {
this.data = arr;
this.len = arr.length;
// 构建堆
shift();
for(int i = arr.length - 1; i >= 0; i--) {
arr[i] = poll();
}
return arr;
}
public static void main(String[] args) {
// 87,45,78,32,17,65,53,9,122
Heap heap = new Heap(8);
int[] arr = {87,45,78,32,17,65,53,9,122};
arr = heap.heapSort(arr);
for(int e : arr) {
System.out.print(e + " ");
}
}
}