基本定义:
堆的概念:
1. 父节点的值总是会大于等于或小于等于子节点的值[最大堆是父节点大于等于子节点的值;最小堆的时候,父节点小于等于子节点的值]
2. 堆本身为一个完全二叉树( complete binary tree),是完美平衡的。
注:
在具有N个节点的堆,其深度为floor( )。
在通常情况下,堆通常用顺序表来存储。
堆的实现
由于堆的目的是快速的插入与删除数据,故遍历与查找很难实现。
插入元素:
为了给堆添加一个新结点,首先将它添加到堆的末尾,然后不断比较新结点与其父结点的大小。若新结点小于父结点,则新结点的位置即是堆的末尾。若新结点的值大于父结点,则将父结点的值赋值给尾端结点,并比较父结点的父结点的值与插入元素的大小。按照这种方式不断向上级比较,直到找到比新结点的值大的父结点。
删除元素:
我们有时需要从堆中删除最大元素,也就是这个堆的根结点。删除这个根结点后,就必须要重建这课树来保持堆的特征。删除根结点后堆的重建过程按照如下几步进行:
- 第一步:删除根结点,并将尾结点的值赋给根结点,并设为当前结点
- 第二步:比较当前结点的左右两个子孩子的值,选取较大的子孩子与当前结点交换位置,交换后当前结点的下标为其原来的较大的孩子结点的下标。此时分为两种情况:
- 情况1:当前结点仅存在左孩子。此时当前结点需要与其左孩子比较值的大小,若小于左孩子,则交换两者位置。
- 情况2:当前结点存在左右两个孩子结点,此时具体步骤同第二步一样。
- 第三步:循环第二步,直到当前结点为叶子结点或当前结点仅存在一个左孩子并且其值大于其左孩子结点的情况
堆排序:先调整为堆,在依次调整
代码实现:
import java.util.Arrays;
//构造最大堆
public class Heap{
private static int SIZE=10;
private int[] arr;
private int size=0;
public Heap() {
this.arr=null;
}
@SuppressWarnings("unchecked")
public Heap(int size,int[] arr) {
if(size<arr.length) {
throw new ArrayIndexOutOfBoundsException("数组长度设置过小");
}else {
if(size<SIZE) {
this.arr=new int[SIZE];
}else {
this.arr=new int[size];
}
for(int i=0;i<arr.length;i++) {
this.arr[i]=arr[i];
}
this.size=arr.length;
}
}
//将数组调整为堆
public void to_heap() {
int index=size/2-1;
for(int i=index;i>=0;i--) {
just_heap(i);
}
}
public void just_heap(int index) {
int ele=arr[index];
int index2=index;
while((index+1)*2-1<size) {
index2=(index+1)*2-1;
if(index2+1<size&& arr[index2]<arr[index2+1]) {
index2++;
}
if(arr[index2]>ele) {
arr[index]=arr[index2];
index=index2;
}else {
break;
}
}
arr[index]=ele;
}
//打印数组
public void print() {
for( int i=0;i<size;i++) {
System.out.print(this.arr[i]+" ");
}
System.out.println();
}
//删除根节点
public int remove() {
if(size==0) {
throw new NullPointerException("堆为空");
}else {
int res=arr[0];
arr[0]=arr[size-1];
size--;
int index=0;
int temp=arr[0];
int left;
while((index+1)*2-1<size) {
left=(index+1)*2-1;
if(left+1<size && arr[left+1]>arr[left]) {
left++;
}
if(temp<arr[left]) {
arr[index]=arr[left];
index=left;
}else {
break;
}
}
arr[index]=temp;
return res;
}
}
public void add(int ele) {
if(size==0) {
arr[0]=ele;
size++;
}else {
if(size==this.arr.length) {
throw new IndexOutOfBoundsException("数组已满");
}else {
arr[size]=ele;
int index=size;
size++;
int parent;
while((index-1)/2>0) {
parent=(index-1)/2;
if(arr[parent]<ele) {
arr[index]=arr[parent];
index=parent;
}
else {
break;
}
}
arr[index]=ele;
}
}
}
//降序打印
public void sort() {
int[] arr=Arrays.copyOf(this.arr,size); //进行浅拷贝
for( int i=size-1;i>0;i--) {
int temp=arr[i];
arr[i]=arr[0];
arr[0]=temp;
System.out.print(arr[i]+" ");
adjust(arr,0,i-1);
}
System.out.print(arr[0]);
System.out.println();
}
public static void adjust(int[] arr,int start,int end) {
int temp=arr[start];
for(int i=start*2+1;i<=end;i=i*2+1) {
if(i+1<=end &&arr[i+1]>arr[i]) { //取子节点的最大值,此处必须为&&以保证索引不会越界
i++;
}
if(arr[i]>temp) { //将子节点的值,给父节点
arr[start]=arr[i];
start=i;
}else {
break;
}
}
arr[start]=temp;//将temp放到合适的位置
}
}
class TestHeap{
public static void main(String args[]) {
int[] arr={2,3,5,1,45,67,30,6,24,21,51,31,11};
Heap heap=new Heap(arr.length+5,arr);
heap.to_heap();
heap.print();
heap.remove();
heap.print();
heap.sort();
heap.print();
heap.add(33);
heap.print();
}
}