Java 基于堆的最大最小优先队列

优先队列

解释: 在程序中需要有序的处理一批程序,但一定要求它们全部有序,或者是不一定要一次就将它们排序; 很多情况都是收集一些元素,然后处理最大/最小元素,也就是应该支持删除最大/最小元素和插入元素的一种数据结构。

定义: 优先队列是一种抽象数据类型(能对使用者隐藏数据类型),它表示了一组值和对这些值的操作,它的抽象层能使我们方便的将我们的应用程序和我们的具体实现分离开来

堆可以理解为是一种数组的表示方法,在二叉堆表示的数组中,每个元素都要保证大于等于/小于等于另外两个特定元素,相应的这些位置的元素又至少要大于等于/小于等于数组中的另外两个特定位置的元素,以此类推,如果我们将每个较大/较小的元素和两个较小/较大的元素用边连接起来我们就可以画出一颗二叉树的结构(注意:堆数组从1开始)
小顶堆: 每个节点都大于等于它的父节点 即 Key[i] <= Key[2i] && Key[i]<=Key[2i+1]
大顶堆: 每个节点都小于等于它的父节点 即Key[i] >= Key[2i] && Key[i] >=Key[2i+1]

二叉堆表示法: 在数组中我们只要将二叉树的节点按照层级顺序放入数组中,根节点在1,它的子节点在2,3,而子节点的子节点在4,5,6,7,以此类推
定义:二叉堆是一组能够用堆有序的完全二叉排序的元素,并在数组中按照层级排序

实现

首先我们要实现堆的有序化,但在这个过程中会遇到两个情况

  • 当某个节点优先级上升(或是在堆底新加入一个元素)时,我们需要由下至上恢复顺序

  • 当某个优先级下降(或是将根节点替换成一个较小的元素)时,我们需要由上至下恢复顺序

由下至上的堆有序化(上浮 最大优先队列)

这种状态打破的原因是某个节点变得比它的父节点更大(最小优先队列为更小),那么我们就需要通过交换它和它父节点的位置来修复;交换后它比它的两个子节点都大(一个是曾经的父节点,另一个比它更小,因为它是父节点的子节点),也可能交换后还是比它现在的父节点大,所以我们可以循环调用此操作,不断向上移动该结点,直到遇到更大的父节点

private void swim(int k) {
		while (k > 1 && less(k/2, k)) { // 这个地方k最小不能为1,就是因为数组的0位置处是不使用的
			exch(pq, k / 2, k);    //交换
			k = k / 2;
		}
	}

由上至下的堆有序化(下沉 最大优先队列)

这种是因为某个节点变得比它的两个子节点更小或是其中之一被打破了,所以我们通过将它和它的两个子节点种的较大者交换来交换顺序(最小优先队列为较小者);同样,交换后可能还是会子节点处继续打破堆有序,所以也需要循环调用此操作,直到遇到两个子节点都比它小或是达到了堆底

	private void sink(int k) {
		while (2 * k < N) {
			int j = 2 * k;
			if (j + 1 <= N && less(j,j+1))
				j++;
			if (!less(k,j))
				break;
			exch(pq, k, j);
			k = j;
		}
	}

最大优先队列

public class MaxPQ<Key extends Comparable<Key>> {
	private Key[] pq;
	private int N = 0;

	public MaxPQ(int maxN) { // 基于优先队列实现的二叉堆 根节点的位置是1
		pq = (Key[]) new Comparable[maxN + 1];
	}

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

	public int size() {
		return N;
	}

	public void insert(Key v) { // 将新元素插入到队尾并让新元素上浮到合适位置
		pq[++N] = v;
		swim(N);
	}

	private void swim(int k) {
		while (k > 1 && less(k/2, k)) { // 这个地方k最小不能为1,就是因为数组的0位置处是不使用的
			exch(pq, k / 2, k);
			k = k / 2;
		}
	}

	private void sink(int k) {
		while (2 * k < N) {
			int j = 2 * k;
			if (j + 1 <= N && less(j,j+1))
				j++;
			if (!less(k,j))
				break;
			exch(pq, k, j);
			k = j;
		}
	}

	public Key delMax() { // 从数组顶端删去最大元素并将数组的最后一个元素放到顶端,减小堆的大小并让这个元素下沉到合适位置
		Key key = pq[1];
		exch(pq, 1, N--);
		pq[N + 1] = null;
		sink(1);
		return key;
	}

	private boolean less(int i,int j) {
		return pq[i].compareTo(pq[j]) < 0;
	}
	
	private static void exch(Comparable[] v, int i, int j) {
		Comparable temp = v[i];
		v[i] = v[j];
		v[j] = temp;
	}

	public static void show(Comparable[] a) {
		for (int i = 1; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println();
	}
		public static void main(String[] args) {
		MaxPQ mp = new MaxPQ<>(6);
	
		mp.insert(25);
		mp.insert(6);
		mp.insert(9);
		mp.insert(10);
		mp.insert(3);
		mp.insert(23);
		show(mp.pq);
		mp.delMax();
		System.out.println("删除最大节点:");
		show(mp.pq);
	}
}

输出

25 10 23 6 3 9 
删除最大节点:
23 10 9 6 3 null 

最小优先队列

public class MinPQ<Key extends Comparable<Key>> {
	private Key[] pq;
	private int N = 0;

	public MinPQ(int initCapacity) {
		pq = (Key[]) new Comparable[initCapacity + 1];
	}

	public MinPQ() {
		this(1);
	}

	public void insert(Key key) {
		if (N == pq.length - 1) {
			resize(2 * pq.length);
		}
		N++;
		pq[N] = key;
		swim(N);
	}

	/**
	 * @param i
	 */
	private void resize(int i) {         //扩容
		Key[] temp = (Key[]) new Comparable[i];
		for (int j = 0; j < pq.length; j++) {
			temp[j] = pq[j];
		}
		pq = temp;
	}

	public Key delMin() {    //删除最小节点 先将根节点和尾节点交换再弹出尾节点
		exch(pq, 1, N);
		Key min = pq[N];
		pq[N] = null;
		N--;
		skin(1);
		return min;
	}

	private void swim(int i) {
		while (i > 1 && greater(i / 2, i)) {   //最小优先队列是调整为比两个孩子节点小
			exch(i, i / 2);
			i = i / 2;
		}
	}

	private void exch(int i, int j) {
		Key temp = pq[i];
		pq[i] = pq[j];
		pq[j] = temp;
	}

	/**
	 * @param i
	 */
	private void skin(int i) {
		while (2 * i <= N) {
			int k = 2 * i;
			if (k < N && greater(k, k + 1)) { // 比较右节点小 取右节点
				k++;
			}
			if (greater(k, i)) { // 如果孩子节点大 不需要调整了
				break;
			}
			exch(pq, i, k);
			i = k;
		}
	}

	private boolean greater(int i, int k) {
		return pq[i].compareTo(pq[k]) > 0;
	}

	/**
	 * @param k
	 * @param i
	 */
	private static void exch(Comparable[] v, int k, int i) {
		Comparable temp = v[i];
		v[i] = v[k];
		v[k] = temp;
	}

	public static void show(Comparable[] a) {
		for (int i = 1; i < a.length; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println();
	}

	public int size() {
		return N;
	}

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

	public static void main(String[] args) {
		MinPQ<Integer> minPQ = new MinPQ<Integer>(6);
		minPQ.insert(25);
		minPQ.insert(6);
		minPQ.insert(9);
		minPQ.insert(10);
		minPQ.insert(3);
		minPQ.insert(23);

		show(minPQ.pq);
	    minPQ.delMin();
        System.out.println("删除最小节点:");
        show(minPQ.pq);
	}
}

输出

3 6 9 25 10 23 
删除最小节点:
6 10 9 25 23 null 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值