堆(优先队列)
问题:如何找到一串数据中最大的M个数或者最小的M个数?
答:可以构造一个包含M个数的堆或者直接将这串数据构造成一个堆
大根堆:父节点大于等于两个子节点,可以使用数组构造堆,
1、从下标为1的位置开始存储;第k个位置的父节点:k/2,子节点位置: 2k 和 2k+1
2、从下标为0的位置开始存储,第k个位置的父节点:(k-1)/2,子节点位置:2k+1 和 2k+2
通过上浮和下沉操作向堆添加元素和删除元素
添加元素:把元素放到数组最后一位,再上浮到合适的位置;最多需要lgN次(N为元素个数)
删除元素:数组的第一个元素和最后一个元素交换,然后将第一个元素下沉到合适的位置;最多需要2lgN次(每层两次)
堆的实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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] + "\t"); } 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(); } } }
有一串很大的数据,需要找出k个最大的或者最小的数?
大根堆:维护k个最小的数;如果加入的元素小于堆顶元素则替换堆顶元素,并下沉到合适位置
小根堆:维护k个最大的数;如果加入的元素大于堆顶元素则替换堆顶元素,并下沉到合适位置,如果加入的元素小于堆顶的元素则抛弃