一、解释
在上一篇博文堆的预备知识 中,我们谈到,堆实际上可以理解为一个数组表示的完全二叉树。他也具有树的一些性质。在这一篇中主要来记录一些堆的基本操作。其中会涉及到上篇博文中阐述到的部分完全二叉树的性质。
二、代码
1、建立一个大根堆
大根堆是堆的两种形式之一。
根节点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆(大顶堆)。
大根堆要求根节点的关键字既大于或等于左子树的关键字值,又大于或等于右子树的关键字值。
构建思路:
给我们一个任意的数组arr。将其调整为大根堆。操作如下:
a、以该数组为基础开始构建,起初默认堆大小为0,然后插入第一个元素arr[1],堆大小加一;
b、此时插入第二个元素(即数组的第二个元素)arr[1],堆大小加一,并对该元素做调整,大根堆的任意一个节点要小于其父节点元素。所以判断 arr[(1-1)/2](即父节点位置元素,这里计算方法在上篇博文中有阐述) 是否小于arr[1],若父节点元素比当前元素小,则交换。
c、插入第i+1个元素 判断 arr[(i-1)/2](即父节点位置元素) 是否小于arr[i],若父节点元素比当前 i 位置元素小,则交换,然后以父节点元素位置开始继续向上比较,直至小于某个元素,或到达堆顶停止。
代码如下:
//构建大根堆
public static void heapInit(int[] arr){
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
}
//插入数据 这里旨在将一个数组调整为大根堆 默认数据存储在数组中 index为要插入的数据的位置
public static void heapInsert(int arr[],int index){
//只要该元素大于他的父元素 则将该元素与父元素交换 并从父元素位置继续开始比较
while(arr[index]>arr[(index-1)/2]) {
Sort_logarithmic_device.swap(arr, index, (index-1)/2);
index=(index-1)/2;
}
}
2、堆元素调整
当某个堆中某个位置元素改变时,需要重新做调整,使其继续成为一个堆。
调整思路(当i位置元素改变时):
a、判断 已改变元素是否大于 父节点元素,若大于父节点元素则做建堆中的插入操作,即向上调整。
b、若该元素小于父节点元素,则将其与左右孩子中较大的元素做比较,若该元素最大,则结束。
若左右孩子中某个位置元素最大,则将最大元素位置与该元素位置交换,并将需要调整的位置改变,继续做上述操作。直至无左右孩子或已经为最大元素停止。
代码:
//调整数据 调整index位置的元素 堆大小为heapSize 默认参数传入
public static void heapify(int arr[],int index,int heapSize){
//若需要调整位置的元素比父元素大 则向上调整 即同插入操作
if (arr[(index-1)/2]<arr[index]) {
heapInsert(arr, index);
}else{//否则向下调整
while((index*2+1)<heapSize){//当左孩子存在时
int left=index*2+1;//左孩子位置
int largest=(left+1)<heapSize&&arr[left+1]>arr[left]
?(left+1):left;//左右孩子较大一个元素位置 无右孩子时 最大为左孩子位置
if (arr[largest]<arr[index]) {//当左右孩子最大元素小于需要调整的位置元素时 退出
break;
}
//否则进行交换 并将需要调整的元素位置变为新位置 继续比较
Sort_logarithmic_device.swap(arr, index, largest);
index=largest;
}
}
}
3、弹堆顶操作
弹出堆顶实际就是删除操作。
算法思路:
a、将堆顶元素与堆最后一个位置元素交换,并堆大小减一,再对堆顶位置做调整操作。使得剩余元素依旧为一个堆。
代码:
//弹出堆顶
public static int deleteHeapTop(int arr[],int heapSize){
int top=arr[0];
Sort_logarithmic_device.swap(arr, 0, --heapSize);
heapify(arr, 0, heapSize);
return top;
}