什么是堆?
优先队列:特殊的队列,取出元素的顺序是依照元素的优先权(关键字)大小,而不是进入队列的先后顺序。
堆的定义如下:具有n个元素的序列(h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,…,n/2)时称之为堆。
由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。
堆结构特性:
①用数组表示的完全二叉树;
②任一节点的关键字是其子树所有节点的最大值(或最小值)。
完全二叉树满足以下条件:
①所有叶子结点都出现在k或k-1层,而且从1到k-1层都必须达到最大节点数;
②第k层可以不是满的,但第k层的所有节点都必须集中在最左边;
③叶结点必须出现在最下层或次下层
最大堆的操作:
①插入:
算法:将新增节点插入到其父节点到根节点的有序序列中,每次插入都要与其父节点比较,如果大于父节点,则互换位置;
②删除:
从最大堆中取出键值为最大的元素,并删除一个节点,用最大堆的最后一个元素从根节点开始向上过滤下层节点。
最大堆的建立:
将已经存在的N个元素按最大堆的要求存放在一维数组中。
方法一:
通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间复杂度最大时为O(NlogN);
方法二:在线性时间下建立最大堆
①将N个元素按输入顺序存入,先满足完全二叉树的结构特性;
②调整各节点位置,以满足最大堆的有序特性。
调整算法:
①从最后一个节点的父节点(k=(array.length - 2) / 2)开始比较,是否大于左右孩子(左右孩子先进行比较,大的与父节点比较);
a.大–>位置不变
b.小–>交换位置
②当换完位置后此结点仍有子节点,重复①步骤;
③k–,循环重复①②步骤,直到K<0,结束循环。
堆排序:
最大堆建立好以后,每次将堆顶位置与末尾节点交换位置,并且保存在临时数组中,然后将剩余的(N-1)个元素继续调整成最大堆,直到所有元素有序。
代码实现:
public class Duipaixv {
private int[] buildMaxHeap(int[] array) {
for (int i = (array.length - 2) / 2; i >= 0; i--) {
adjustDownToUp(array, i, array.length);
}
return array;
}
private void adjustDownToUp(int[] array, int k, int length) {
int temp = array[k];
for (int i = 2 * k + 1; i < length - 1; i = 2 * i + 1) {
if (i < length && array[i] < array[i + 1]) {
i++;
}
if (temp >= array[i]) {
break;
} else {
array[k] = array[i];
k = i;
}
}
array[k] = temp;
}
// 堆排序
public int[] heapSort(int[] array) {
array = buildMaxHeap(array); // 初始建堆,array[0]为第一趟值最大的元素
for (int i = array.length - 1; i > 1; i--) {
int temp = array[0];
array[0] = array[i];
array[i] = temp;
adjustDownToUp(array, 0, i);
}
return array;
}
// 删除堆顶元素操作
public int[] deleteMax(int[] array) {
array[0] = array[array.length - 1];
array[array.length - 1] = -99999;
adjustDownToUp(array, 0, array.length);
return array;
}
// 插入操作:向大根堆array中插入数据data
public int[] insertData(int[] array, int data) {
array[array.length - 1] = data;
int k = array.length - 1;
int parent = (k - 1) / 2;
while (parent >= 0 && data > array[parent]) {
array[k] = array[parent];
k = parent;
if (parent != 0) {
parent = (parent - 1) / 2;
} else {
break;
}
}
array[k] = data;
return array;
}
public void toString(int[] array) {
for (int i : array) {
System.out.print(i + " ");
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Duipaixv hs = new Duipaixv();
int[] array = { 87, 45, 78, 32, 17, 65, 53, 9, 122 };
System.out.print("构建大根堆:");
hs.toString(hs.buildMaxHeap(array));
System.out.print("\n" + "删除堆顶元素:");
hs.toString(hs.deleteMax(array));
System.out.print("\n" + "插入元素63:");
hs.toString(hs.insertData(array, 63));
System.out.print("\n" + "大根堆排序:");
hs.toString(hs.heapSort(array));
}
}