优先队列(Priority Queue)最大堆最小堆

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lp15203883326/article/details/81369969

建议读者先去下载《啊哈算法》看大概在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;
    }
}

猜你喜欢

转载自blog.csdn.net/lp15203883326/article/details/81369969
今日推荐