建议读者先去下载《啊哈算法》看大概在P182页的堆,什么是最小堆?
ps:如果想进来学习什么是堆的童鞋们,你们不需要再继续往下面阅读啦,对你们有意义的是第一行哦~随后我将此本算法书会长传到csdn上哦~
而已经学习过数据结构,对堆已经有了初步认识的童鞋,可以仔细读读一下内容哦,以下主要是堆的应用。以及为什么要用堆~
最小堆就是 所有的父节点都要比子节点要小,反之所有的父节点都要比子节点大的完全二叉树是最大堆
回顾看了看后,真的时候恍然大悟,最后就把最重要的捞出来把
像这样支持插入和寻找最大值和最小值元素的数据 结构称之为优先队列。
如果使用普通队列来实现这两个功能,那么寻找最大元素或者最小元素都要枚举整个队列,这样的时间复杂度比较高。如果是已经排序好的数组,那么插入一个元素需要移动很多元素,时间复杂度依旧很高,而堆就是一种优先队列的实现,可以很好的解决这两个问题。
堆还经常用来寻找第k大或第k小的元素,比如现在要找第k大的元素,现在只需要建立一个个数为k的最小堆,堆顶元素就是第k大的元素。例如现在有10个数,要找第3大的元素,那么就先建立一个数目为3个的最小堆,然后拿第4个元素和堆顶元素比较,如果比他小这个数就丢弃,如果比他大,就将第4个元素替换原来的堆顶,然后重新调整堆,后面的5-10个元素一次这样重复,遍历完成后,堆顶元素就是第k大的元素。
相反的,寻找第k小的元素,建立数目为k的最大堆,比堆顶大的元素丢弃,小的替换后重新向下调整堆
那么,求k个最大元素和k个最小元素,则遍历完成后,最小堆和最大堆中的元素就是k个最大元素和最小元素。
还要记录的一点就是向下调整和向上调整。
向下调整是从堆顶开始,将最小的元素放在父节点上,直至当前节点是叶子节点为止。
向下调整主要是用于删除堆顶元素,在堆顶上重新添加一个新的元素或者删除堆顶元素时候用到(删除堆顶元素是,将最后一个元素放在堆顶元素,再使用向下调整,保证最小堆或者最大堆),还用于创建一个最大或最小堆
向上调整用于在末尾增加一个元素时候用到。
还有使用堆排序的时间复杂度是NO(logN),例如从小到大排列,有两种方法,一种就是用最小堆,每次去堆顶元素,
还有一种就是更好的方法就是使用最大堆,因为从小到大排序,大的在后面,小的在前面。那么每次将堆顶元素放在和最后一个元素替换,即时heap[0]和heap[n]替换。然后n--,在从heap[0]向下调整堆。
下面贴下代码吧(这个代码中,我是将数组从1开始的,heap[0]可以忽略掉,就是执行过后将0忽略,只是个占位的而已,不是数据。。)
import sun.plugin.javascript.navig.Array;
import java.util.Arrays;
//建立最小堆(数组从1开始)
public class Test3 {
public static void main(String[] args){
Test3 t = new Test3();
int heap[]={0,45,35,67,33,87,8,4,2,5,12};
String str = Arrays.toString(heap);
System.out.println(str);
t.swap(heap,2,3);
for(int i=heap.length-1/2; i>=1; i--){
t.sift_down(heap,i);
}
String str2 = Arrays.toString(heap);
System.out.println(str2);
//测试向上调整,添加一个数据时
int heap2[]={0,2,5,4,33,12,8,35,45,67,87,1};
t.sift_up(heap2,heap2.length-1);
String str3 = Arrays.toString(heap2);
System.out.println(str3);
}
public int delete_min(int[] heap){
int t = heap[1];
//将最后一个元素赋值给堆顶
heap[1]=heap[heap.length-1];
//将heap的长度减1 n--
//因为heap的不是全局变量
sift_down(heap,1);
return t;
}
//向上调整(添加元素)
public void sift_up(int[] heap, int i){
int n = heap.length-1;
int flag = 0;
if(i == 1)return ;//如果时候对顶就直接返回
while(i != 1 && flag==0){
if(heap[i] < heap[i/2]){
swap(heap,i,i/2);
}else{
flag=1;
}
i=i/2;
}
}
//向下调整(删除堆顶元素,添加一个元素需要用到向下调整)
public void sift_down(int[] heap,int i){
int temp,flag=0;//flag标记是否继续向下调整
int n = heap.length-1;
while(i*2 <= n && flag == 0){
temp = i;
if(heap[i*2] < heap[i]){
temp = i*2;
}
if(i*2+1 <= n){
if(heap[i*2+1] < heap[temp]){
temp = i*2+1;
}
}
if(temp != i){//如果当前节点不是最小节点
swap(heap,temp,i);
i = temp;//更变当前节点,继续向下调整
}
else{
flag = 1;
}
}
}
public void swap(int[] heap,int a,int b){
int t = heap[a];
heap[a] = heap[b];
heap[b] = t;
}
}