堆(优先队列)

问题:如何找到一串数据中最大的M个数或者最小的M个数?

:可以构造一个包含M个数的堆或者直接将这串数据构造成一个堆

大根堆:父节点大于等于两个子节点,可以使用数组构造堆,

1、从下标为1的位置开始存储;第k个位置的父节点:k/2,子节点位置: 2k 和 2k+1

2、从下标为0的位置开始存储,第k个位置的父节点:(k-1)/2,子节点位置:2k+1 和 2k+2

通过上浮和下沉操作向堆添加元素和删除元素

  添加元素:把元素放到数组最后一位,再上浮到合适的位置;最多需要lgN次(N为元素个数)

删除元素:数组的第一个元素和最后一个元素交换,然后将第一个元素下沉到合适的位置;最多需要2lgN次(每层两次)

堆的实现

import java.util.Arrays;

public class MaxPQ<T extends Comparable<T>>{
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    private T[] arr; // 用一个数组从下标为1的位置开始存储,当前节点k的父节点为k/2,子节点为2k和2k+1
    private int N = 0;

    @SuppressWarnings("all")
    public MaxPQ(){
        arr = (T[]) new Comparable[DEFAULT_INITIAL_CAPACITY];
    }

    @SuppressWarnings("all")
    public MaxPQ(int MaxN){
        arr = (T[]) new Comparable[MaxN+1];
    }
    public void insert(T t){
        int i = size() + 1;
        if(i>=arr.length)
            arr = Arrays.copyOf(arr, size()*2);
        arr[++N] = t;
        swim(N);        // 每次将添加的元素插入到最后的位置,然后上浮到合适的位置
    }

    public T delMax(){
        T max = arr[1]; // 返回最大的元素,如果要返回最小的元素,只需要改变上浮和下沉的比较
        exch(1, N--);   // 与数组最后的元素交换,数组长度减1,然后将这个元素下沉到合适的位置
        arr[N+1] = null;
        sink(1);
        return max;
    }

    public int size(){
        return N;
    }

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

    public void swim(int k){
        // 上浮的时候与父节点(arr[k/2])比较,大于父节点就上浮
        while(k>1 && arr[k/2].compareTo(arr[k])<0){
            exch(k/2, k);
            k = k/2;
        }
    }

    public void sink(int k){
        while(2*k <= N){  
            int j = 2*k;  
            // 注意j在这一步迭代中要小于剩余元素个数,否则会空指针异常
            // 用较大的子节点(2k或者2k+1)和父节点k比较
            if(j<N && arr[j].compareTo(arr[j+1])<0) j++;
            // 如果父节点大于两个子节点,则不用交换,否则与较大的子节点交换
            if(arr[k].compareTo(arr[j])>0) break; 
            exch(k, j);
            k = 2*k;
        }
    }

    public void exch(int i, int j){
        T temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public void printArr(){
        for(int i=1; i<=N; i++){
            System.out.print(arr[i] + "	");
        }
        System.out.println();
    }

    public static void main(String[] args){
        MaxPQ<String> arr = new MaxPQ<>(11);
        String[] strings = {"S","G","H","P","N","A","O","E","I","R","T","A"};
        for(String s: strings)
            arr.insert(s);
        arr.printArr();
        System.out.println("+++++++++");
        while(arr.size()!=0){
            arr.delMax();
            arr.printArr(); 
        }  
    }
}
View Code

有一串很大的数据,需要找出k个最大的或者最小的数?

大根堆:维护k个最小的数;如果加入的元素小于堆顶元素则替换堆顶元素,并下沉到合适位置

小根堆:维护k个最大的数;如果加入的元素大于堆顶元素则替换堆顶元素,并下沉到合适位置,如果加入的元素小于堆顶的元素则抛弃

java的优先队列 

原文地址:https://www.cnblogs.com/pineapple-chicken/p/14534022.html