java用数组实现优先级队列(小顶堆)

2019 精帖
2 1890

优先级队列

        普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。通常采用堆数据结构来实现。

        优先级队列 是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。

操作:

        1.往队列中添加数据

        2.从队列中获取数据

        优先级队列通常采用堆数据结构处理

        用数组实现


文档下载:java数组实现优先级队列(小顶堆).docx

插入数据:

算法:

    先将数据插入到数组末尾,然后进行上浮操作,

    如果父节点大于大于该子节点,则执行上浮操作,直到该节点为根节点。

//交换
private void exchange(int i,int j) {
                   queue[i]=queue[i]+queue[j];
                   queue[j]=queue[i]-queue[j];
                   queue[i]=queue[i]-queue[j];
         }
//上浮
private void swim(int k) {
             while (k > 1 && compare(k>>1, k)) {
                      exchange(k, k>>1);
                 k = k>>1;
             }
         }

s.png


从队列中获取数据

算法:

        把数组首节点和尾节点交换,删除尾节点,队列元素个数-1,此时队列可能不满足优先队列的条件,需要执行下沉操作。

        具体是先比较下沉节点的的两个子节点的元素大小,选择较小的子节点交换,反复进行此操作,直到树的叶子节点。

private void sink(int k) {
             while (k<<1 <= n) {
                 int j = k<<1;
                 if (j < n && compare(j, j+1))
                          j++;
                 if (!compare(k, j))
                          break;
                 exchange(k, j);
                 k = j;
             }
         }

dd.png

代码实现:

package com.itdragon.controller;
/**
 * 优先级队列(小顶堆)
 * @author JIA_JIAJIA
 * @website http://www.jiajiajia.club
 * @da2019年5月11日
 */
public class PriorityQueue {
	/**
	 * 队列元素
	 */
	private int queue[];
	/**
	 * 队列长度
	 */
	int n;
	/**
	 * 循序打印数组中的值,相当于二叉树的层级遍历
	 */
	public void printf() {
		for(int i=1;i<n+1;i++) {
			System.out.print(queue[i]+" ");
		}
	}
	
	/**
	 * 初始化队列大小
	 * @param size
	 */
	public PriorityQueue(int size) {
		/**
		 * 初始化队列的元素数组,和队列大小
		 */
		queue=new int[size+1];
		n=0;
	}
	/**
	 * 插入数据到队列中
	 * @param data
	 * @return
	 */
	public boolean insert(int data) {
		/**
		 * 如果数组容量不够则进行扩容操作(在原来数组容量的基础上+10)
		 */
		if (n >= queue.length - 1)
			queue=java.util.Arrays.copyOf(queue,queue.length+10);
		queue[++n] = data;
	    swim(n);
	    System.out.println(isPriorityQueue());
	    return true;
	}
	
	/***
	 * 从队列中获取数据
	 * @return
	 */
	public int getDate() {
		int data = queue[1];
	    exchange(1, n);
	    n--;
	    sink(1);
	    queue[n+1]=0;
	    if ((n > 0) && (n == (queue.length - 1) / 4))
	    	queue=java.util.Arrays.copyOf(queue,queue.length/2);
	    System.out.println(isPriorityQueue());
	    return data;
	}
	/**
	 * 比较元素的大小,queue[i]>queue[j]返回true,queue[i]<queue[j]返回false
	 * @param i
	 * @param j
	 * @return
	 */
	public boolean compare(int i,int j) {
		return queue[i]>queue[j]?true:false;
	}
	
	/**
	 * 交换queue中i位置和j位置的值
	 * @param i
	 * @param j
	 */
	private void exchange(int i,int j) {
		/**
		 * 不用第三个变量做中间变量,交换两个整数变量的值
		 * 例:
		 * a=a+b;
		 * b=a-b;
		 * a=a-b;
		 * 这样a,b的值就交换了
		 */
		queue[i]=queue[i]+queue[j];
		queue[j]=queue[i]-queue[j];
		queue[i]=queue[i]-queue[j];
	}
	
	/**
	 * 上浮操作
	 * @param k 数组下标k
	 */
	private void swim(int k) {
		/**
		 * 对于小顶堆,如果父节点的值大于该子节点,那么交换该节点和父节点,然后继续比较该节点与父节点...
		 * 直到该节点是根节点。
		 * 右移(>>)相当于除以2,参考 http://www.jiajiajia.club/blog/artical/200
		 * 
		 * 用数组表示二叉树,有如下性质
		 * 子节点的下标位置>>2等于父节点的下标位置。适用于左节点和右节点。
		 */
	    while (k > 1 && compare(k>>1, k)) {
	    	exchange(k, k>>1);
	        k = k>>1;
	    }
	}
	/**
	 * 下沉操作
	 * @param k 数组下标k
	 */
	private void sink(int k) {
		/**
		 * 从头结点开始,如果当前节点还有子节点,则一直判断是否还可以下沉
		 */
	    while (k<<1 <= n) {
	        int j = k<<1;//
	        /**
	         * j当前节点的左节点,compare(j, j+1)是为了比较做节点大,还是右节点大
	         * 从而可以判断父节点是向左节点下沉,还是右节点下沉,如果是向右节点下沉则j++
	         */
	        if (j < n && compare(j, j+1)) 
	        	j++;
	        /**
	         * 如果父节点小于子节点的最小值,则下沉结束。
	         */
	        if (!compare(k, j)) 
	        	break;
	        /**
	         * 交换子节点和父节点,当前节点变为子节点,继续判断是否还可以下沉
	         */
	        exchange(k, j);
	        k = j;
	    }
	}
	
	/**
	 * 判断是否满足堆得性质
	 * @return
	 */
	private boolean isPriorityQueue() {
		return isPriorityQueue(1);
	}
	/**
	 * 递归遍历判断该堆是否满足堆得性质
	 * @param k
	 * @return
	 */
	private boolean isPriorityQueue(int k) {
		if (k > n) 
			return true;
		/**
		 * 左子树下标位置
		 */
		int left = k<<1;
		/**
		 * 右子树下标位置
		 */
		int right = k<<1 + 1;
		if (left <= n && compare(k, left)) 
			return false;
		if (right <= n && compare(k, right)) 
			return false;
		/**
		 * 递归遍历左右子树
		 */
		return isPriorityQueue(left) && isPriorityQueue(right);
	}
	
}
package com.itdragon.controller;

import org.junit.Test;
/**
 * @author JIA_JIAJIA
 * @website http://www.jiajiajia.club
 * @da2019年5月11日
 */
public class TestMain2 {
	
	@Test
	public void test() {
		PriorityQueue pq=new PriorityQueue(10);
		pq.insert(9);
		pq.insert(8);
		pq.insert(7);
		pq.insert(6);
		pq.insert(5);
		pq.insert(4);
		System.out.println(pq.getDate());
		pq.printf();
	}
}


留言(0)
加载更多
猜你喜欢