本片是对上一篇的补充,实现的是一个小方法,就是将用户传来的数组变成堆,其实有一种比较简单的方法,就是将数组的元素进行遍历,然后将其一个一个加入到堆当中,但此做法时间复杂度为O(nlogn),相对于第二种做法时间比较长,第二中做法叫做heapify,时间复杂度为O(n),实现的思路还是挺容易的,就是先将用户传来的数组转换成动态数组,因为我的最大堆底层用的是自定义的动态数组类,然后此时,数组中的元素排列并不符合堆的特点,所以我们要重新对数组进行堆化,也就是将节点进行下沉(siftdown)操作<上篇博文有详细介绍>,首先,我们需要抛弃所有叶子节点(直接从其父节点开始,节省时间),所以,先找到最后一个节点的父节点,从它开始进行节点下沉操作,直到结束。该方法不难。
首先是Array类中,写一个构造方法,参数为数组
//传一个数组,将其转换成动态数组
public Array(E arr[]) {
data=(E[]) (new Object[arr.length]);
for(int i=0;i<arr.length;i++) {
data[i]=arr[i];
}
size=arr.length;//注意,这里需要维护一下size!!
}
然后是MaxHeap类中添加构造方法,此方法也是关键的实现数组转换成堆的关键
public MaxHeap(E arr[]) {
array=new Array<>(arr);
//直接从尾节点的父亲节点开始遍历
for(int i=parent(array.getSize()-1);i>=0;i--) {
siftdown(i);//该方法的详情参考上一篇堆排序的博文
}
}
//接着,我们来看一下测试类,首先定义一个testHeap方法,该方法主要是为了测试直接遍历数组元素添加到堆的方法与heapify方法的性能差异,所以需要传两个参数,第一个是需要测试的数据,也就是testData,然后是一个boolean型的变量,用以表示是否需要使用heapify方法进行转换。
import java.util.Random;
public class test {
private static double testHeap(Integer[] testData,boolean isHeapify) {
long time=System.nanoTime();
MaxHeap<Integer> heap=null;
if(isHeapify) {
heap=new MaxHeap<Integer>(testData);
}
else {
//如果用户传来的是false,就使用上文所说的第一种方法进行转换
heap=new MaxHeap<Integer>();
for (Integer num : testData) {
heap.add(num);
}
}
//以下部分是为了测试堆的添加是否有错误
int[] arr = new int[testData.length];
for(int i = 0 ; i < testData.length ; i ++)
arr[i] = heap.extractMax();
for(int i = 1 ; i < testData.length ; i ++)
if(arr[i-1] < arr[i])
throw new IllegalArgumentException("Error");
System.out.println("Test MaxHeap completed.");
long time2=System.nanoTime();
return (time2-time)/1000000000.0;
}
public static void main(String[] args) {
int n=10000;
Integer[] arr=new Integer[n];
for(int i=0;i<arr.length;i++) {
arr[i]=new Random().nextInt(Integer.MAX_VALUE);
}
double time1=testHeap(arr,false);
System.out.println("直接添加"+time1);
double time2=testHeap(arr,true);
System.out.println("堆化添加"+time2);
}
}
测试结果如下
可以看到,heapify的方法相对于直接添加还是比较快的。