优先队列

踏踏实实积累,不要浮躁

(1):适用场景

许多应用程序都需要处理有序的元素,但不一定要求他们全部有序,或者是不一定要一次将他们排序。很多情况下我们会收集一些元素,处理当前键值的最大值,然后再收集更多的元素再处理当前键值的最大元素在任务调度中很适用,还有在图的搜索算法和一些数据压缩算法都能用到优先队列

(2):思想利用数组来存放元素(下标0位置不存放元素),k,2k,2k+1分别是完全二叉树中父节点和两个子节点的数组下标,在插入元素和删除元素的操作中都通过sink()或者是swim()方法来维持堆的有序性,本质上还是比较和交换,只不过抽象了一层更高级的API来实现堆的一些性质(计算机里面也很喜欢玩概念)

(3):代码实现  利用完全二叉树来实现堆的基本API 主要关注思想方法考虑的还不够完善

package sort;

import tools.Util;

/**
 * 基于完全二叉树来实现堆,目前只考虑数字不考虑复杂的应用情况
 * 有序堆:父节点大于等于两个子节点
 */
public class MaxPQ {
    /**
     * arr 存放树种元素的数组
     * N 用于记录数组中的元素个数
     */
    private Object[] arr;
    private int N = 0;
    public MaxPQ(int MaxN){
        arr = new Object[MaxN + 1];
    }

    public boolean isEmpty(){
        return N == 0;
    }

    public int size(){
        return N;
    }

    public void insert(int v){
        arr[++N] = v;
        swim(N);
    }

    /**
     * 删除最大值并把最大值返回
     * @return  最大值
     */
    public int delMax(){
        int max = (Integer) arr[1];
        Util.swap(arr,1,N--); // N 的值已经减1
        arr[N+1] = null;
        sink(1);
       return max;
    }

    private void swim(int k){
        while (k > 1 && (Integer)arr[k/2] < (Integer) arr[k]){
            Util.swap(arr,k/2,k);
            k = k/2;
        }
    }

    private void sink(int k){

        while (2*k + 1 <= N){
            int j = 2*k;
            if(j <= N && (Integer)arr[j] < (Integer) arr[j+1]){
                j ++; // 找到两个父节点中较大的值
            }
            if((Integer)arr[k] > (Integer) arr[j]){
                break;
            };
            Util.swap(arr,j,k);
            k = j;
        }
    }

    public static void main(String[] args) {
        MaxPQ pq = new MaxPQ(5);
        pq.insert(1);
        pq.insert(2);
        pq.insert(3);
        pq.insert(4);
        pq.insert(5);
        Util.println(pq.arr);
        int max = pq.delMax();
        Util.println(pq.arr);
        pq.delMax();
        Util.println(pq.arr);
        pq.delMax();
        Util.println(pq.arr);
    }
}

运行结果如下:

说明:

1: N 变量用来记录数组中存放的元素个数

2:arr[] 数组中第一个位置不存放元素

3:swim()方法思想:在数组中插入一个元素之后,通过跟父节点进行比较如果大于就跟父节点进行交换,保证新数据的有序性

4:del()方法 中的sink() 因为将数组中最后一个元素跟第一个元素进行交换,然后跟子节点中最大的元素进行交换,依次进行保证新数据集的有序性 这里指的有序是保证第一个元素是最大的

5:上面的API还只是最初级的,考虑不完善比如

a:当数组元素为空的时候没有进行判断;

b:没有提供自动扩容的API,当满足一定条件是增大数组arr 当删除操作进行之后判断元素个数满足一定条件将数组大小减小

c:insert 插入元素的时候如果数组大小已经满了应该删除其中最小的元素然后再进行插入操作

 

思考:优先级队列的用处,如果要在100G的数据集里面找出最大的10个元素有哪些方法

1:将全部数据集进行排序 由于内存有限,可以利用分治的思想,将100G的数据切分成100个大小为1G的数据集,然后分别对1G的数据集进行排序取最大的10个最后对挑选出来的前10大元素进行排序找出最终最大的10个 元素

2:用mapreduce等分布式计算框架能轻松解决,本质上也是利用了分治思想来处理大规模数据集

3:利用优先级队列,只需要大小为11的数组就能解决(利用两个堆)

思想很重要:一定要多思考,算法不好学但是不要放弃!!!

文章我会继续完善的,先就写这么多吧。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值