栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

队列及优先队列(Java实现)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

队列及优先队列(Java实现)

引入

在我们的日常生活中,有许多排队的情形,像在食堂排队打饭,在购票窗口排队购票等等,像这种满足成员元素先进先出特性的问题可以抽象成队列问题(不考虑队尾或者队伍中间的人由于种种因素中途离开的情形),那么什么是队列呢?

队列(Queue)

队列也是线性表的一种,和栈一样是一种特殊的线性表,只不过队列满足的特点是先进先出(FIFO或者FCFS–>First Come First Service),而后者的特点是先进后出(FILO)。

ADT

作为一个队列,它要实现的基本功能如下:置空,判空,获取队列长度,获取队首元素,入队,出队并返回队首元素,输出。其相应的接口定义如下

public interface IQueue {
	public void clear();//置空
	public boolean isEmpty();//判空
	public int length();//获取队列长度
	public Object peek();//获取队首元素
	public void offer(Object x)throws Exception;//入队
	public Object pop();//出队并返回队首元素
	public void display();
}
实现

队列的实现同样有顺序存储结构和链式存储结构两种,顺序存储结构也同样是用数组实现,但是常规的队列使用顺序存储会存在一个很大的问题,首先,我们假设只定义常规的队列并使用顺序存储,而定义的入队和出队的方法如下:
入队:

//入队
public void offer(Object x) throws Exception {
		if(rear==ElemQueue.length) {
			throw new Exception("队列已满");
		}else {
			ElemQueue[rear]=x;
			rear=rear+1;
		}
	}
	//出队
	public Object poll() {
		if(!isEmpty()) {
			Object x=ElemQueue[front];
			front++;
			return x;
		}
		return null;
	}

伴随着不断的入队出队操作,front指针和rear指针都会向后移,而当到了rear与ElemQueue.length相等时,队列满,但事实上这个数组空间可能还有很多空间是没有使用的(front之前的),这就是“假溢出”,此时数组空间没满但是程序将队列判断为满,新的元素入队抛出溢出错误。所以为了解决这个问题我们引入了循环队列
循环队列
空出一个数组元素的空间,不存放元素,调整代码的逻辑结构,rear指向队尾元素的下一个元素,当队列满的条件修改为(rear+1)%ElemQueue.length等于0,并且在其他的函数中同样作出相应的修改(相较于常规的顺序表的函数),最终实现循环队列的代码如下:

package circleQueue;

import iQueue.IQueue;

public class CircleQueue implements IQueue{
	public int front,rear;
	public Object[] ElemQueue;
	public CircleQueue(int maxSize) {
		front=rear=0;
		ElemQueue=new Object[maxSize];
	}
	@Override
	public void clear() {
		front=rear=0;
	}

	@Override
	public boolean isEmpty() {
		return front==rear;
	}

	@Override
	public int length() {
		return (rear-front+ElemQueue.length)%ElemQueue.length;
	}

	@Override
	public Object peek() {
		if(!isEmpty()) {
			return ElemQueue[front];
		}
		return null;
	}

	@Override
	public void offer(Object x) throws Exception {
		if((rear+1)%ElemQueue.length==0) {
			throw new Exception("队列已满");
		}else {
			ElemQueue[rear]=x;
			rear=(rear+1)%ElemQueue.length;
		}
	}

	@Override
	public Object poll() {
		if(!isEmpty()) {
			Object x=ElemQueue[front];
			front=(front+1)%ElemQueue.length;
			return x;
		}
		return null;
	}
	public void display() {
		if(!isEmpty()) {
			for(int i=front;i!=rear;i=(i+1)%ElemQueue.length) {
				System.out.print(ElemQueue[i].toString()+" ");
			}
			System.out.println();
		}
		else {
			System.out.println("队列为空");
		}
	}
}

这样就避免了假溢出的问题,虽然浪费了一个数组元素的空间,但是我们在实现其他函数的时候方便了很多,当然最主要的还是解决了假溢出的问题
链队列
队列的链式实现,就和单链表差不多,只不过由于先进先出的特点,在入队函数和出队函数相比有点特殊而已,具体代码实现如下:

package linkQueue;
import iQueue.IQueue;
public class linkQueue implements IQueue{
	Node front,rear;
	public linkQueue() {
		front=rear=null;
	}
	@Override
	public void clear() {
		front=rear=null;
	}

	@Override
	public boolean isEmpty() {
		return front==null;
	}

	@Override
	public int length() {
		if(!isEmpty()) {
			Node p=front;
			int len=0;
			while(p!=null) {
				++len;
				p=p.next;
			}
			return len;
		}
		return 0;
	}

	@Override
	public Object peek() {
		return front.data;
	}

	@Override
	public void offer(Object x) throws Exception {
		Node s=new Node(x);
		if(!isEmpty()) {
			rear.next=s;
			rear=s;
		}else
			front=rear=s;
	}

	@Override
	public Object poll() {
		if(!isEmpty()) {
			if(front==rear) {
				front=null;
			}
			Node p=front;
			front=front.next;
			return p.data;
		}
		return null;
	}

	@Override
	public void display() {
		if(!isEmpty()) {
			Node p=front;
			while(p!=null) {
				System.out.print(p.data.toString()+" ");
				p=p.next;
			}
		}else {
			System.out.println("队列为空");
		}
	}
}
优先队列

但是在实际情况中,经常会出现并不完全按照先到先服务的情形(先进先出),比如程序的进程问题:正常情况下按照程序启动的先后顺序执行程序,但是在特殊情况下并不会按照先启动就先运行,比如现在有一个管理系统负荷超载的程序(此时程序的负荷超过了计算机系统的承受范围,需要执行该程序,防止系统崩溃),那么系统肯定会优先执行这个程序,因为它的优先级比其他的程序要高。由此我们引入了优先级队列,这是应用程度比其他队列要高的一种数据结构。通常使用链式存储实现,由于优先级的存在,笔者采用的是重新定义了结点Node类(定义后的结点为优先级结点PriNode)的方法
PriNode:

package linkQueue;
//带有优先级的结点
public class PriNode {
	public Object data;
	public int prior;
	public PriNode next;
	public PriNode() {
		data=null;
		prior=-1;
	}
	public PriNode(Object data,int prior) {
		this.data=data;
		this.prior=prior;
	}
}

而优先队列的特殊也只在于入队操作,需要进行一个优先级的比较,并不是像其他队列那样简单的从队尾插入。具体实现代码如下:

package linkQueue;
import iQueue.IQueue;
//优先级队列
public class PriQueue implements IQueue{
	PriNode front,rear;
	public PriQueue() {
		front=rear=null;
	}
	@Override
	public void clear() {
		front=rear=null;
	}
	@Override
	public boolean isEmpty() {
		return front==null;
	}
	@Override
	public int length() {
		if(!isEmpty()) {
			PriNode p=front;
			int len=0;
			while(p!=null) {
				++len;
				p=p.next;
			}
			return len;
		}
		return 0;
	}
	@Override
	public Object peek() {
		if(!isEmpty()) {
			return front.data;
		}
		return null;
	}
	@Override
	public void offer(Object x) throws Exception {
		//默认传入的实参为PriNode类型所以可以强转
		PriNode s=(PriNode)x;
		if(!isEmpty()) {
			PriNode p=front,q=p;
			while(p!=null && p.prior<=s.prior) {
				q=p;
				p=p.next;
			}
			if(p==null) {
				rear.next=s;
				rear=s;
			}
			else if(p==front){
				s.next=front;
				front=s;
			}else {
				s.next=q.next;
				q.next=s;
			}
		}
		else front=rear=s;
		
	}
	@Override
	public Object pop() {
		if(!isEmpty()) {
			if(front==rear) {
				front=null;
			}
			PriNode p=front;
			front=front.next;
			return p.data;
		}
		return null;
	}
	@Override
	public void display() {
		if(!isEmpty()) {
			PriNode p=front;
			while(p!=null) {
				System.out.print(p.data.toString()+" ");
				p=p.next;
			}
		}else {
			System.out.println("队列为空");
		}			
	}
		
}
总结

以上就是队列(主要是循环队列和链队列)和优先队列的一个实现,笔者在优先队列的入队函数可能不太完善,传的参数其实是Object类型,但是笔者考虑时默认传的是PriNode类型,将问题简单化了,如果有更好的处理方法欢迎各位大佬指出。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/346001.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号