堆是一种图的树形结构
其实就是把线型的存储方式装换为树型的存储方式。
我们平常看到的都是以线型方式来存在的,就像下图这样子
那么树的存储结构就从线型变成了树状结构。
树的结构是一种非线型存储结构,存储的是具有“一对多”关系的数据元素的集合。
树的节点
结点:使用树结构存储的每一个数据元素都被称为“结点”。
父节点:像上图中6、5的父节点就是7。
子节点:那么7的子节点就是5、6。
根节点:每一个非空树都有且只有一个被称为根的结点。 像上图中7就是跟节点。
树根的判断依据为:如果一个节点没有父节点,那么这个节点就是整棵树的根节点。
叶子节点:如果节点没有任何子节点,那么此节点称为叶子节点(叶节点)。
堆又分为小顶堆和大顶堆
小顶堆:子节点必须大于父节点。如果父节点大于子节点,那么就要交换,(要从最后面开始) 直到符合规则。如果继续往里面添加数字,则要放到最后面,然后继续依次比较。
大顶堆则反之
来看一个大顶堆的动图吧
树的结构的特性如下:
- 一个父节点最多有2个子节点
- 如果是小顶堆,那么它的父节点 <= 子节点
如果是大顶堆,那么它的父节点 >= 子节点 就好比这样
小: 父节点 <= 子节点
大: 父节点 >= 子节点 - 它的结构都是从上到下,从左到右的。
上 -> 下
左 -> 右 - 该行不足就加一行
堆中最顶端的数据始终最小,所以无论数据量有多少,取出最小值的时间复杂度都为0(1)。
另外,因为取出数据后需要将最后的数据移到最顶端,然后一边比较它与子结点数据的大小,一边往下移动,所以取出数据需要的运行时间和树的高度成正比。假设数据量为n,根据堆的形状特点可知树的高度为log₂n,那么重构树的时间复杂度便为O(logn)
添加数据也一样。在堆的最后添加数据后,数据会一边比较它与父结点数据的大小,一边往上移动,直到满足堆的条件为止,所以添加数据需要的运行时间与树的高度成正比,也是O(logn)。
/**
* Java数组实现堆结构 下标格式如下 0 1 2 3 4 5 6
*/
public class MyHeap {
// find the index of left children
// 下标为i的节点的左侧子节点的下标
public static int left(int i) {
return (i + 1) * 2 - 1;
}
// find the index of right children
// 下标为i的节点的右侧子节点的下标
public static int right(int i) {
return (i + 1) * 2;
}
// find the index of parent
// 下标为i的节点的父节点的下标
public static int parent(int i) {
return (i - 1) / 2;
}
// build the max heap
// 构建某一数组的最大堆结构
public static void buildMaxHeap(int[] array, int length) {
// the last node index is array.length-1
// and his parent index is (array.length-1-1)/2
for (int i = parent(length - 1); i >= 0; i--) {
keepMaxHeap(array, i, length);
}
}
public static void keepMaxHeap(int[] array, int k, int length) {
int l = left(k);// k节点的左子节点
int r = right(k);// k节点的右子节点
int largest = k;// 假定k节点是k-l-r中的最大值节点
if (l < length && array[l] > array[largest]) {// 左子节点>最大值节点
largest = l;
}
if (r < length && array[r] > array[largest]) {// 右子节点>最大值节点
largest = r;
}
if (largest != k) {// 发现最大值节点不是k节点时 是左或右子节点 交换
swap(array, k, largest);
// int temp = array[k];
// array[k]= array[largest];// 新k节点为最大值节点
// array[largest] = temp;// 左或右子节点为原k节点值
// 迭代左或右子节点上的原k节点值
// 此时这个节点往下判断左右子节点
keepMaxHeap(array, largest, length);
}
}
// heap sort the array
public static void heapSort(int[] array) {
int length = array.length;
for (int i = 1; i <= length; i++) {
swap(array, length - i, 0);
buildMaxHeap(array, length - i);
}
}
// swap the value of index i and j in the array
public static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// print the array
public static void printArray(int[] array) {
for (int i : array) {
System.out.print(i + " ");
}
System.out.println("");
}
}
测试类:
public class HeapTest {
public static void main(String[] args) {
int[] array = {6,10,1,2,4,9,7,8,5,3};
// 从小到大排序使用大顶堆
// 从大到小排序使用小顶堆
MyHeap.buildMaxHeap(array,array.length);
System.out.println("生成大顶堆后的数据是: ");
MyHeap.printArray(array);
MyHeap.heapSort(array);
System.out.println("使用堆排序后的数据是: ");
MyHeap.printArray(array);
}
}
运行结果图: