数据结构Java实现07----队列:顺序队列&顺序循环队列、链式队列、顺序优先队列
一、队列的概念:
队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。
队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。
下图是一个依次向队列中插入数据元素a0,a1,...,an-1后的示意图:
上图中,a0是当前 队头数据元素,an-1是当前 队尾数据元素。
为了避免当只有一个元素时,对头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样的话,当front指针等于rear时,此队列不是还剩一个元素,而是空队列。
二、队列的抽象数据类型:
数据集合:
队列的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类型。
操作集合:
(1)入队列append(obj):把数据元素obj插入队尾。
(2)出队列delete():把队头数据元素删除并由函数返回。
(3)取队头数据元素getFront():取队头数据元素并由函数返回。
(4)非空否isEmpty():非空否。若队列非空,则函数返回false,否则函数返回true。
三、循环顺序队列:
线性表有顺序存储和链式存储,队列是一种特殊的线性表,同样也存在这两种存储方式。我们先来看一下队列的顺序存储。
1、顺序队列的“假溢出”:
上图中,front指针指向队头元素,rear指针指向队尾元素的下一个位置。图(d)中b、c、d出队后,front指针指向元素e,rear指针在数组外面。假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经被占用,再向后加就会产生数组越界的错误,可实际上队列在下标为0、1、2、3、4的地方还是空闲的,我们把这种现象叫做“假溢出”。
2、循环顺序队列:
所以解决假溢出的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种逻辑上首尾相连的顺序存储结构称为循环队列。
如何判断循环队列究竟是空的还是满的:
现在问题又来了,我们之前说,空队列时,front指针等于rear指针,那么现在循环队列满的时候,也是front等于rear,那么如何判断循环队列究竟是空的还是满的?有如下办法:
- 办法1:设置一个标志位flag。初始时置flag=0;每当入队列操作成功就置flag=1;每当出队列操作成功就置flag=0。则队列空的判断条件为:rear == front && tag==0;队列满的判断条件为:rear = = front && tag= =1。
- 办法2:保留一个元素的存储空间。此时,队列满时的判断条件为 (rear + 1) % maxSize == front;队列空的判断条件还是front == rear。
- 办法3:设计一个计数器count,统计队列中的元素个数。此时,队列满的判断条件为:count > 0 && rear == front ;队列空的判断条件为count == 0。
我们在接下来的代码中采用方法3来实现。
3、代码实现:(循环顺序队列的创建)
(1)Queue.java:(队列接口)
//队列接口
public interface Queue { //入队
public void append(Object obj) throws Exception; //出队
public Object delete() throws Exception; //获得队头元素
public Object getFront() throws Exception; //判断对列是否为空
public boolean isEmpty();
}
(2)CircleSequenceQueue.java:(循环顺序队列)
//循环顺序队列
public class CircleSequenceQueue implements Queue { static final int defaultSize = 10; //默认队列的长度
int front; //队头
int rear; //队尾
int count; //统计元素个数的计数器
int maxSize; //队的最大长度
Object[] queue; //队列 public CircleSequenceQueue() {
init(defaultSize);
} public CircleSequenceQueue(int size) {
init(size);
} public void init(int size) {
maxSize = size;
front = rear = 0;
count = 0;
queue = new Object[size];
} @Override
public void append(Object obj) throws Exception {
// TODO Auto-generated method stub
if (count > 0 && front == rear) {
throw new Exception("队列已满!");
}
queue[rear] = obj;
rear = (rear + 1) % maxSize;
count++;
} @Override
public Object delete() throws Exception {
// TODO Auto-generated method stub
if (isEmpty()) {
throw new Exception("队列为空!");
}
Object obj = queue[front];
front = (front + 1) % maxSize;
count--;
return obj;
} @Override
public Object getFront() throws Exception {
// TODO Auto-generated method stub
if (!isEmpty()) {
return queue[front];
} else {
return null;
}
} @Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return count == 0;
} }
(3)Test.java:
public class Test {
public static void main(String[] args) throws Exception { CircleSequenceQueue queue = new CircleSequenceQueue();
queue.append("a");
queue.append("b");
queue.append("c");
queue.append("d");
queue.append("e");
queue.append("f"); queue.delete();
queue.delete(); queue.append("g"); while (!queue.isEmpty()) {
System.out.println(queue.delete());
}
}
}
运行效果:
4、循环队列应用:
使用顺序循环队列和多线程实现一个排队买票的例子。而且,我们只允许这个队伍中同时排队的只有10个人,那就需要用到队列了。
生产者(等候买票)
消费者 (买票离开)
这里面我们需要用到上面的Queue.java类和CircleSequenceQueue.java类。
代码结构:
(3)WindowQueue.java:
//卖票窗口
public class WindowQueue { //卖票的队列
int maxSize = 10;
CircleSequenceQueue queue = new CircleSequenceQueue(maxSize);
int num = 0; //统计卖票的数量,一天最多卖100张票。
boolean isAlive = true; //判断是否继续卖票。 //排队买票
public synchronized void producer() throws Exception {
if (queue.count < maxSize) {
queue.append(num++); //等待买票的数量加1
System.out.println("第" + num + "个客户排队等待买票!");
this.notifyAll();//唤醒卖票的线程
} else {
try {
System.out.println("队列已满...请等待!");
this.wait();//队列满时,排队买票线程等待。
} catch (Exception ex) {
ex.printStackTrace();
}
}
} //卖票
public synchronized void consumer() throws Exception {
if (queue.count > 0) {
Object obj = queue.delete();
int temp = Integer.parseInt(obj.toString());
System.out.println("第" + (temp + 1) + "个客户买到票离开队列!");
//如果当前队列为空,并且卖出票的数量大于等于100,说明卖票结束
if (queue.isEmpty() && this.num >= 100) {
this.isAlive = false;
}
this.notifyAll(); //唤醒排队买票的线程。
} else {
try {
System.out.println("队列已空...请等待!");
this.wait();//队列空时,卖票线程等待。
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
(4)Producer.java:
//买票者
public class Producer implements Runnable { WindowQueue queue; public Producer(WindowQueue queue) {
this.queue = queue;
} @Override
public void run() {
// TODO Auto-generated method stub
while (queue.num < 100) {
try {
queue.producer();
} catch (Exception ex) {
ex.printStackTrace();
}
}
} }
(5)Consumer.java:
//卖票者
public class Consumer implements Runnable { WindowQueue queue; public Consumer(WindowQueue queue) {
this.queue = queue;
} @Override
public void run() {
// TODO Auto-generated method stub
while (queue.isAlive) {
try {
queue.consumer();
} catch (Exception ex) {
ex.printStackTrace();
}
}
} }
(6)test.java:
public class Test { public static void main(String[] args) throws Exception { WindowQueue queue = new WindowQueue(); Producer p = new Producer(queue);//注意一定要传同一个窗口对象
Consumer c = new Consumer(queue); //排队买票线程
Thread pThread = new Thread(p);
//卖票线程
Thread cThread = new Thread(c); pThread.start(); //开始排队买票
cThread.start(); //开始卖票
} }
注意第07行的注释。
运行效果:
四、链式队列:
链式队列其实就是特殊的单链表,只不过它只能尾进头出而已。链式队列的存储结构如下图所示:
1、链式队列的实现:
(1)Node.java:结点类
//结点类
public class Node { Object element; //数据域
Node next; //指针域 //头结点的构造方法
public Node(Node nextval) {
this.next = nextval;
} //非头结点的构造方法
public Node(Object obj, Node nextval) {
this.element = obj;
this.next = nextval;
} //获得当前结点的后继结点
public Node getNext() {
return this.next;
} //获得当前的数据域的值
public Object getElement() {
return this.element;
} //设置当前结点的指针域
public void setNext(Node nextval) {
this.next = nextval;
} //设置当前结点的数据域
public void setElement(Object obj) {
this.element = obj;
} public String toString() {
return this.element.toString();
}
}
(2)Queue.java:
//队列接口
public interface Queue { //入队
public void append(Object obj) throws Exception; //出队
public Object delete() throws Exception; //获得队头元素
public Object getFront() throws Exception; //判断对列是否为空
public boolean isEmpty();
}
(3)LinkQueue.java:
public class LinkQueue implements Queue { Node front; //队头
Node rear; //队尾
int count; //计数器 public LinkQueue() {
init();
} public void init() {
front = rear = null;
count = 0;
} @Override
public void append(Object obj) throws Exception {
// TODO Auto-generated method stub
Node node = new Node(obj, null); //如果当前队列不为空。
if (rear != null) {
rear.next = node; //队尾结点指向新结点
} rear = node; //设置队尾结点为新结点 //说明要插入的结点是队列的第一个结点
if (front == null) {
front = node;
}
count++;
} @Override
public Object delete() throws Exception {
// TODO Auto-generated method stub
if (isEmpty()) {
new Exception("队列已空!");
}
Node node = front;
front = front.next;
count--;
return node.getElement();
} @Override
public Object getFront() throws Exception {
// TODO Auto-generated method stub
if (!isEmpty()) {
return front.getElement();
} else {
return null;
}
} @Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return count == 0;
} }
(4)Test.java:
public class Test { public static void main(String[] args) throws Exception { LinkQueue queue = new LinkQueue();
queue.append("a");
queue.append("b");
queue.append("c");
queue.append("d");
queue.append("e");
queue.append("f"); queue.delete();
queue.delete(); queue.append("g"); while (!queue.isEmpty()) {
System.out.println(queue.delete());
}
}
}
运行效果:
2、链式队列的应用:
题目:
编写一个判断一个字符串是否是回文的算法。
思路:
设字符数组str中存放了要判断的字符串。把字符数组中的字符逐个分别存入一个队列和栈中,然后逐个出队和出栈比较出队的字符与出栈的字符是否相同,若全部相等则该字符串为回文。
代码实现:
这里面需要用到上面一段中的LinkQueue类。代码结构如下:
(4)Stack.java:栈接口
//栈接口
public interface Stack { //入栈
public void push(Object obj) throws Exception; //出栈
public Object pop() throws Exception; //获得栈顶元素
public Object getTop() throws Exception; //判断栈是否为空
public boolean isEmpty();
}
(5)LinkStack.java:
public class LinkStack implements Stack { Node head; //栈顶指针
int size; //结点的个数 public LinkStack() {
head = null;
size = 0;
} @Override
public Object getTop() throws Exception {
// TODO Auto-generated method stub
return head.getElement();
} @Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return head == null;
} @Override
public Object pop() throws Exception {
// TODO Auto-generated method stub
if (isEmpty()) {
throw new Exception("栈为空!");
}
Object obj = head.getElement();
head = head.getNext();
size--;
return obj; } @Override
public void push(Object obj) throws Exception {
// TODO Auto-generated method stub
head = new Node(obj, head);
size++;
} }
(6)Test.java:测试类
public class Test { public static void main(String[] args) throws Exception { String str1 = "ABCDCBA"; //是回文
String str2 = "ABCDECAB"; //不是回文 try {
if (Test.isHuiWen(str1)) {
System.out.println(str2 + ":是回文!");
} else {
System.out.println(str2 + ":不是回文!");
}
} catch (Exception ex) {
ex.printStackTrace();
}
} //方法:判断字符串是否回文
public static boolean isHuiWen(String str) throws Exception {
int n = str.length();
LinkStack stack = new LinkStack();//创建堆栈
LinkQueue queue = new LinkQueue();//创建队列
for (int i = 0; i < n; i++) {
stack.push(str.subSequence(i, i + 1)); //把字符串每个字符压进堆栈
queue.append(str.subSequence(i, i + 1));//把字符串每个字符压入队列
}
while (!queue.isEmpty() && !stack.isEmpty()) {
if (!queue.delete().equals(stack.pop())) { //出队列,出栈,同时判断是否相同
return false;
}
} return true;
} }
3、循环队列和链式队列的比较:
(1)从时间上看,它们的基本操作都是常数时间,即O(1)的。不过循环队列是事先申请好空间,使用期间不释放;而链式队列,每次申请和释放结点也会存在一定的时间开销,如果入栈和出栈比较频繁,则两者还是有细微的差别。
(2)从空间上看,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链式队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链式队列更加灵活。
总结:总的来说,在可以确定队列长度的最大值的情况下,建议用循环队列,如果你无法估计队列的长度,那就用链式队列。
五、优先级队列:
优先级队列是带有优先级的队列。
用顺序存储结构实现的优先级队列称作顺序优先级队列。
用链式存储结构存储的优先级队列称作链式优先级队列。
顺序优先级队列和顺序循环队列相比主要有两点不同:
(1)对于顺序优先级队列来说,出队列操作不是把队头数据元素出队列,而是把队列中优先级最高的数据元素出队列。(入队操作没区别)
(2)对于顺序优先级队列来说,数据元素由两部分组成,一部分是原先意义上的数据元素,另一部分是优先级。通常设计优先级为int类型的数值,并规定数值越小优先级越高。
1、顺序优先队列的实现:
设计顺序优先级队列分为两个类:
数据元素类
优先级队列类
代码实现:
(1)Element.java:
//优先级队列元素类
public class Element { private Object element; // 数据
private int priority; // 优先级 public Element(Object obj, int priority) {
this.element = obj;
this.priority = priority;
} public Object getElement() {
return element;
} public void setElement(Object element) {
this.element = element;
} public int getPriority() {
return priority;
} public void setPriority(int priority) {
this.priority = priority;
} }
(2)Queue.java:
//队列接口
public interface Queue { //入队
public void append(Object obj) throws Exception; //出队
public Object delete() throws Exception; //获得队头元素
public Object getFront() throws Exception; //判断对列是否为空
public boolean isEmpty();
}
(3)PrioritySequenceQueue.java:
//优先级队列
public class PrioritySequenceQueue implements Queue { static final int defaultSize = 10; //默认队列长度
int front; //队头
int rear; //队尾
int count; //计数器
int maxSize; //队列最大长度
Element[] queue; //队列 public PrioritySequenceQueue() {
init(defaultSize);
} public PrioritySequenceQueue(int size) {
init(size);
} public void init(int size) {
maxSize = size;
front = rear = 0;
count = 0;
queue = new Element[size];
} @Override
public void append(Object obj) throws Exception {
// TODO Auto-generated method stub
//如果队列已满
if (count >= maxSize) {
throw new Exception("队列已满!");
}
queue[rear] = (Element) obj;
rear++;
count++;
} @Override
public Object delete() throws Exception {
// TODO Auto-generated method stub
if (isEmpty()) {
throw new Exception("队列为空!");
}
//默认第一个元素为优先级最高的。
Element min = queue[0];
int minIndex = 0;
for (int i = 0; i < count; i++) {
if (queue[i].getPriority() < min.getPriority()) {
min = queue[i];
minIndex = i;
}
} //找的优先级别最高的元素后,把该元素后面的元素向前移动。
for (int i = minIndex + 1; i < count; i++) {
queue[i - 1] = queue[i]; //移动元素
}
rear--;
count--;
return min;
} @Override
public Object getFront() throws Exception {
// TODO Auto-generated method stub
if (isEmpty()) {
throw new Exception("队列为空!");
}
//默认第一个元素为优先级最高的。
Element min = queue[0];
int minIndex = 0;
for (int i = 0; i < count; i++) {
if (queue[i].getPriority() < min.getPriority()) {
min = queue[i];
minIndex = i;
}
}
return min;
} @Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return count == 0;
} }
2、代码测试:
设计一个程序模仿操作系统的进程管理问题。进程服务按优先级高的先服务,优先级相同的先到先服务的原则管理。
模仿数据包含两个部分:进程编号和优先级。如下有五个进程:
1 30
2 20
3 40
4 20
5 0 ----------优先级最高,先服务
(4)Test.java:
public class Test { public static void main(String[] args) throws Exception { PrioritySequenceQueue queue = new PrioritySequenceQueue();
Element temp; //五个进程入队
queue.append(new Element(1, 30));
queue.append(new Element(2, 20));
queue.append(new Element(3, 40));
queue.append(new Element(4, 20));
queue.append(new Element(5, 0)); //按照优先级出队。
System.out.println("编号 优先级");
while (!queue.isEmpty()) {
temp = (Element) queue.delete();
System.out.println(temp.getElement() + " " + temp.getPriority());
}
}
}
运行效果:
数据结构Java实现07----队列:顺序队列&顺序循环队列、链式队列、顺序优先队列的更多相关文章
- 数据结构----队列:顺序队列&顺序循环队列、链式队列、顺序优先队列
一.队列的概念: 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其 ...
- c++实验5 顺序/链式队列
链式队列及循环队列 1.循环队列的实现(请采用模板类及模板函数实现) [实现提示] 同时可参见教材p65-p67页的ADT描述及算法实现及ppt)函数.类名称等可自定义,部分变量请加上学号后3位.也可 ...
- 顺序队列与链式队列--C语言实现
关于队列,因为我自己在平时使用不多,所以在这里直接将队列的两种存储方式放在一起,作为一篇随笔,这两份代码均可直接运行,亲测.注释写的应该也算比较详细了,就不过多的解释了 顺序队列 #include&l ...
- 利用链式队列(带头节点)解决银行业务队列简单模拟问题(c++)-- 数据结构
题目: 7-1 银行业务队列简单模拟 (30 分) 设某银行有A.B两个业务窗口,且处理业务的速度不一样,其中A窗口处理速度是B窗口的2倍 —— 即当A窗口每处理完2个顾客时,B窗口处理完1个顾客 ...
- java实现链式队列
java实现链式队列...比较简单 package datastruct; public class QueueLink implements Queue { // 定义一个节点内部类 class N ...
- C语言数据结构-链式队列的实现-初始化、销毁、清空、长度、队列头元素、插入、删除、显示操作
1.数据结构-链式队列的实现-C语言 typedef struct QNode { int data; struct QNode *next; }QNode,*QueuePtr; typedef st ...
- 数据结构-链式队列-C++
用链表搭建的栈与队列相对简单,队列的特点是先进先出,不啰嗦了,由于代码比较简单,相信光顾的人不会太多,下面直接贴代码. 头文件 #ifndef QUEUELI_H #define QUEUELI_H ...
- C语言实现链式队列
链式队列,简称"链队列",即使用链表实现的队列存储结构. 链式队列的实现思想同顺序队列类似,只需创建两个指针(命名为 top 和 rear)分别指向链表中队列的队头元素和队尾元素, ...
- 队列(链式队列)----C语言
链式队列----用链表实现,链式队列就是一个操作受限的单向链表,如果读者了解单向链表的建立过程,那理解链式队列就很容易了,先回顾一下单向链表的建立过程 (不熟悉单向链表的可以先看看另一片随笔,再回来看 ...
随机推荐
- Ahjesus Nodejs02 使用集成开发环境
下载最新版webstorm, 选择此集成开发环境是因为支持性较好,在vs下也有插件支持,不过感觉有些牵强 附vs插件 NTVS 详细介绍 安装好以后就需要配置npm NPM 国内高速镜像 source ...
- 推荐轻量友好的.NET测试断言工具Shouldly
Shouldly是一个轻量的断言(Assertion)框架,用于补充.NET框架下的测试工具.Shouldly将焦点放在当断言失败时如何简单精准的给出很好的错误信息. Shouldly在GitHub的 ...
- css清除浮动定位造成的异常
清除浮动是为了解决高度塌陷的问题:内层有好几个div有宽有高,并且选择了浮动定位,但是外层的div却并没有设置宽高.在非IE浏览器(如Firefox)下,当容器的高度为auto,且容器的内容中有浮动( ...
- JavaScript中的null与nudefined
null和undefined 概述 null与undefined都可以表示"没有",含义非常相似.将一个变量赋值为undefined或null,老实说,语法效果几乎没区别. var ...
- MapGIS转Shp文件的单位问题
在MapGIS浏览查看一下数据,各种不习惯:用mapgis自带的转shp功能,属性表会出错:利用名为map2shp的试用版软件可将mapgis格式的数据较为良好转成shp格式.但经常会遇到一个单位问题 ...
- Powershell Remove "Limited Access" - 金大昊(jindahao)
对于有多级web获取getlist会报错:Exception calling “GetList” with “1” argument $SPWeb = Get-SPWeb -Identity http ...
- Bit-Coin收入的一分钱
好吧,这是我在Slush's pool上对Bit-coin收入的第一分钱. 回想起来,2013年平安夜开始到今天,将近3个月没日没夜窝在这个矿里挖矿 从最开始的集成显卡挖,买了显卡挖,加了显卡挖,使用 ...
- android ButterKnife 解决重复findViewById
简介: 程序员都是懒惰的,不想写一大堆像下面这样的代码 class ExampleActivity extends Activity { TextView title; TextView subtit ...
- Lucene总体架构
Lucene总的来说是:• 一个高效的,可扩展的,全文检索库.• 全部用Java实现,无须配置.• 仅支持纯文本文件的索引(Indexing)和搜索(Search).• 不负责由其他格式的文件抽取纯文 ...
- UIWebView的简单使用
在iOS9.0之后需要在ifo.plist文件中添加一个配置文件,不然只能识别https开头的网址,http开头的不识别 主要有三个步骤 // 1.获取URL NSURL *url = [NSURL ...