索引优先队列 超大文件如何排序?

//索引优先队列 ,类似 带有键值对  key:index value:obj
//二叉堆实现
public class IndexMinPQ<T extends Comparable<T>> {
    int[] pqueue;       //索引优先队列  N->k , 从1开始 ,1为堆顶(最小)
    int[] k2n;          //k->N  ,从1开始,0是哨兵
    T items[];          //k->Item   ,从1开始,0是哨兵
    final int maxN;     //最大索引数
    int N;              //当前数据个数

    public IndexMinPQ(int maxN) {
        this.maxN = maxN;
        pqueue = new int[maxN + 1];
        k2n = new int[maxN + 1];
        items = (T[]) new Comparable[maxN + 1];

        for (int i = 0; i < maxN + 1; i++) {
            k2n[i] = -1;
        }
    }

    public int getMaxN() {
        return maxN;
    }

    public boolean contain(int k) {
        return k2n[k] != -1;
    }

    //k 自定义索引, k = [1 ~ maxN] ,下标从1开始
    //item 数据对象
    public void insert(int k, T item) {
        if (k < 1 || k > maxN)
            throw new RuntimeException("k=[1,maxN] K:" + k);
        if (contain(k)) {
            throw new RuntimeException("already contain K:" + k + " item:" + items[pqueue[k]]);
//            return;
        }
        N++;
        k2n[k] = N;         //k->N
        pqueue[N] = k;      //N->k
        items[k] = item;
        swimUp(N);
    }

    public void change(int k, T item) {
        items[k] = item;
        swimUp(k2n[k]);
        sinkDown(k2n[k]);
    }

    public T top() {
        if (N > 0)
            return items[pqueue[1]];
        return null;
    }

    public T delTop() {
        if (N < 1)
            return null;
        T top = items[pqueue[1]];   //保留删除的最顶
        exch(1, N);                 //最低的换到最顶,做sink
        items[pqueue[N]] = null;    //删掉换到最低的最顶
        k2n[pqueue[N]] = -1;
        pqueue[N] = 0;
        N--;
        sinkDown(1);
        return top;
    }

    public T del(int k) {
        if (k2n[k] == -1)
            return null;
        T tDel = items[k];
        pqueue[k2n[k]] = pqueue[N]; //最低的换到当前
        N--;
        swimUp(k2n[k]);             //将换过来的做 swim,sink ,堆有序化
        sinkDown(k2n[k]);
        k2n[k] = -1;                //删除目标k对应的n
        items[k] = null;
        return tDel;
    }

    public int topIndex() {
        if (N > 0)
            return pqueue[1];
        return -1;
    }

    public boolean isEmpty() {
        return N < 1;
    }

    public int size() {
        return N;
    }

    private void swimUp(int n) {
        int child = n;
        int parent = child / 2;
        while (parent > 0) {
            T pt = items[pqueue[parent]];
            T ct = items[pqueue[child]];
            if (pt.compareTo(ct) < 0)
                break;
            exch(parent, child);
            child = parent;
            parent = child / 2;
        }
    }

    private void sinkDown(int n) {
        int parent = n;
        int lchild = parent * 2;
        int rchild = lchild + 1;
        int targetChild;
        while (lchild <= N || rchild <= N) {
            T pt = items[pqueue[parent]];
            T tc = null;
            targetChild = lchild;       //默认用left child,当 lchild == rchild value 的时候,或不存在rchild的时候
            if (rchild < N) {           //targetChild ,找到2个child中更小的那个
                int rcmpl = items[pqueue[rchild]].compareTo(items[pqueue[lchild]]);
                if (rcmpl < 0)          //若 rchild 更小
                    targetChild = rchild;
            }
            tc = items[pqueue[targetChild]];
            if (pt.compareTo(tc) < 0)
                break;
            exch(parent, targetChild);
            parent = targetChild;
            lchild = parent * 2;
            rchild = lchild + 1;
        }
    }

    private void exch(int n1, int n2) {
        int tmp = pqueue[n1];       //[n]->k
        pqueue[n1] = pqueue[n2];
        pqueue[n2] = tmp;
        tmp = k2n[pqueue[n1]];      //[k]->n
        k2n[pqueue[n1]] = k2n[pqueue[n2]];
        k2n[pqueue[n2]] = tmp;
    }

    @Override
    public String toString() {
        return "IndexMinPQ{" +
                "pqueue[N]=k =" + Arrays.toString(pqueue) +
                ", \nk2n[k]=N =" + Arrays.toString(k2n) +
                ", \nitems[k]=O =" + Arrays.toString(items) +
                ", \nmaxN=" + maxN +
                ", \nN=" + N +
                '}';
    }

    public static void main(String[] args) {
        IndexMinPQ<Integer> ipq = new IndexMinPQ<>(10);
        ipq.insert(2, 22);
        ipq.insert(6, 66);
        ipq.insert(1, 11);
        ipq.insert(4, 44);
        System.out.println(ipq);

        ipq.change(1, 111);
        System.out.println(ipq);

        System.out.println("ipq.contain(2) :" + ipq.contain(2));

        System.out.println("ipq.top() :" + ipq.top()); //2
        System.out.println("ipq.topIndex() :" + ipq.topIndex()); //2
        System.out.println("ipq.delTop() :" + ipq.delTop());
        System.out.println(ipq);

        ipq.insert(10, 1);
//        ipq.insert(0, 1);
        System.out.println(ipq);

        while (!ipq.isEmpty()) {
            System.out.println("k: " + ipq.topIndex() + " v:" + ipq.delTop());
        }

        System.out.println("====");
        Random r = new Random(123);
        while (ipq.size() < ipq.getMaxN()) {
            ipq.insert(ipq.size() + 1, r.nextInt(100));
        }
        while (!ipq.isEmpty()) {
            System.out.println("k: " + ipq.topIndex() + " v:" + ipq.delTop());
        }
    }
}

 

带索引号的优先队列

key,索引号 用于找对应value

value, 任意可比较对象,优先队列根据对象的大小排序

优先队列中存储 key,根据key 值,找到在 item[] 中的对象,根据对象比较结果,进行排序(堆有序化,上浮,下沉)

也可以用change(k,item) 对堆中已存在的某个 key值对应的对象经行更新,更新后再次对该对象进行堆有序化处理

 

应用:

比如有1000个已经排序好的(从小达到)大文件,每个1G,文件内单条数据1M,而机器上的内存只有1G内存(单个文件比内存还大,无法完整载入内存),如何将这1000个文件排序并输出到1个文件中?

用这个 索引优先队列就可以完成,申请一个 1000个元素的索引优先队列,并同时打开这1000个文件,给文件按照从1-1000 编号,每个文件一次只读取1条数据到 索引优先队列,并且key值对应文件编号。

当1000个文件的1000条数据读入后,从索引优先队列中取出最小值,然后单独打开一个输出文件,将最小值输出,并再次从刚才读取最小值的文件编号的文件中再次读取下一跳数据,再插入到优先队列中。

如此反复,直到单个文件的所有数据读取并处理完成后,不再对这个文件进行处理,直到所有的这1000个文件都处理完成后,就可以完成有序合并。

posted on 2019-12-03 20:32  jald  阅读(329)  评论(0编辑  收藏  举报

导航