赞
踩
队列是数据结构中最常见的一种,与此对应的还有栈。我前面写过一篇使用java实现栈的,有兴趣的可以点击查看。学习的时候,应该大多数读者都是使用c语言去实现,在数据结构书中一般也都是使用c语言去实现栈这种数据结构。确实,因为c语言有指针能够更好地操作内存,而且运行速度快很适合作为写数据结构地语言。但是因为学习了java,所以今天也来尝试一下使用java来实现队列。同时简单介绍一下什么是队列。
队列是一种很长见的数据结构,他的模型也很简单:像一个单行道的隧道,先进去的车先出来,“先进先出”就是他的特点。我们知道数据结构中的物理结构有顺序和链式两种,所以对应的就有顺序队列和链式队列。这里的顺序队列我只讲循环队列,首尾相接。其他类型的循环队列有兴趣的读者可自行去了解。
顺序队列,使用数组去实现的。这里简单讲一下循环队列。
这里的循环队列的实现方式使用首尾相接的模式,每当出队的时候,队头的标记位置+1,每当入队的时候队尾的标记位置+1.但现在就有一个问题了,假如我现在是一个长度为10的数组,然后我的队头标记位置是8,队尾是9,那当再次入队的时候岂不是就溢出了?而且前面还有好多空间没用,所以这个时候就要把整个数组首尾相接,队尾从9过渡到0 可以看图片理解一下:
然后再讲一下如何通过队头和队尾的位置来判断队列是否已经满了呢?这里涉及到一个简单的算法,因为他是循环的,假如maxsize是最大长度,front是队头位置,rear是队尾位置,那么(rear+1)%maxsize == front的时候,队列满了。可以通过上图来理解。
这样应该既可以理解这个循环队列的意思了。
要建立一种数据结构,首先要定义接口:
package Stucture; public interface QueueApi<T> { //初始化队列 boolean initQueue(int maxSize); //新元素入队 boolean pushQueue(T t); //队头元素出队 T popQueue(); //获取队列队头元素 T getTop(); //获取队列的元素数目 int getElemNum(); //判断队列是否为空 boolean isQueueEmpty(); //清空队列 void clearQueue(); }
这里一共定义了7个方法,使用了泛型以便可以对应不同的数据类型。
定义了接口之后,那么就得来写队列这个类了。上面的接口规定了队列拥有的方法,然后还需要5个属性:队列中的数组,队列的头位置,队列的尾位置,队列的长度,队列的元素数目。因为java不允许建立泛型数据数组,关于为什么可以看我的另一篇文章学习笔记之java为什么不能新建泛型类型数据数组?,有兴趣的读者可以了解一下。所以我们必须先给他指定数据类型,我这里定义了一个Student类:
package entity; public class Student { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
接下来看看完整的队列类,然后我们再一个个来解析下,先看看代码:
package Stucture; import entity.Student; public class StudentSequeueQueue implements QueueApi<Student> { private Student[] studentQueue ; private int front = 0; private int rear = 0; private int num = 0; private int size; //初始化队列 @Override public boolean initQueue(int maxSize) { // TODO Auto-generated method stub if (studentQueue == null) { studentQueue = new Student[maxSize]; if(studentQueue == null) return false; size = maxSize; return true; }else return false; } //元素入队 @Override public boolean pushQueue(Student t) { // TODO Auto-generated method stub if(studentQueue!=null&&(rear+1)%size != front) { studentQueue[rear] = t; num++; if(rear+1 == size) rear = 0; else rear++; return true; }else { return false; } } //队头元素出队 @Override public Student popQueue() { // TODO Auto-generated method stub if(num!=0) { int position = front; if(front+1!=size) front++; else front = 0; num--; return studentQueue[position]; } return null; } //获取队头元素 @Override public Student getTop() { // TODO Auto-generated method stub if(num!=0) return studentQueue[front]; else return null; } //获取队列中的元素数目 @Override public int getElemNum() { // TODO Auto-generated method stub return num; } //判断队列是否为空 @Override public boolean isQueueEmpty() { // TODO Auto-generated method stub if(num==0) return true; else return false; } //清空队列 @Override public void clearQueue() { // TODO Auto-generated method stub front = 0; rear = 0; num = 0; } }
代码看起来挺多,不慌,一个一个来看:
先看看代码:
import Stucture.LinearQueue; import Stucture.LinearStack; import Stucture.StudentSequeneStack; import Stucture.StudentSequeueQueue; import entity.Student; public class Main { public static void main(String[] args) { Student student1 = new Student(); student1.setAge(12); student1.setName("mike"); Student student2 = new Student(); student2.setAge(13); student2.setName("sali"); StudentSequeueQueue queue = new StudentSequeueQueue(); queue.initQueue(10); queue.pushQueue(student1); queue.pushQueue(student2); System.out.println(queue.getTop().getName()); System.out.println(queue.getElemNum()); queue.popQueue(); System.out.println(queue.getTop().getName()); queue.popQueue(); for(int i = 0;i<=7;i++) queue.pushQueue(student1); queue.pushQueue(student2); System.out.println(queue.getElemNum()); System.out.println(queue.getTop().getName()); for(int i = 0;i<=7;i++) queue.popQueue(); System.out.println(queue.getTop().getName()); queue.clearQueue(); System.out.println(queue.getElemNum()); if (queue.getTop()==null) { System.out.println("清空成功"); }
上面对我们刚刚写的队列进行了简单的测试,看看测试结果:
可以看到顺序队列成功了。
链队列相比顺序队列而言不用考虑循环的问题,只要通过链表把每个元素串起来,链表头出队,入队的时候接在链表尾。就可以实现链队列了。
接口在顺序队列已经写好了,就不用再写一次了
和顺序队列不同的是,链队列的每个节点都需要一个指向下一个元素的引用,每一个节点都需要两个域:数据域和指针域。说的玄乎,但其实就是上面我说的一个是Student这个对象的引用一个是节点的引用。看代码:
package Stucture;
//节点
public class Node<T> {
T t;
Node<T> elem;
}
这里我用了泛型,这样的话,对于不同的数据类型可以对应不同的泛型。例如我们上面的Student类,那么我们就可以指定泛型为Student。为什么这里不像c语言那样把具体的数据放进去,例如把name和age这两个数据放进去而是放了一个Student引用呢?这其实和结构体是一样的,外部只需要关注每个节点的数据是什么而不需要关注什么引用指针。外部只需要传入Student这种对象就ok了,不用去关注其他的,这也是一种封装的思想。那么定好了节点类,接下来看看队列类怎么写,先看看代码在再一个个来解析
队列类的属性需要队列头的引用,队列尾的引用,队列的长度。实现队列接口。先看看整体代码,再来解析:
package Stucture; public class LinearQueue<T> implements QueueApi<T> { private Node<T> frontNode = null; private Node<T> rearNode = null; private int num = 0; @Override public void initQueue(int maxSize) { // TODO Auto-generated method stub } //元素入队 @Override public boolean pushQueue(T t) { // TODO Auto-generated method stub Node<T> node = new Node<T>(); node.elem = t; node.next = null; if(rearNode == null) rearNode = node; else { rearNode.next = node; rearNode = node; } if(frontNode == null) frontNode = node; num++; return true; } //元素出队 @Override public T popQueue() { // TODO Auto-generated method stub if(num!=0) { Node<T> node; node = frontNode; frontNode = frontNode.next; num--; return node.elem; }else { return null; } } //获取队头元素 @Override public T getTop() { // TODO Auto-generated method stub if(num!=0) return frontNode.elem; else return null; } //获取队列中的元素数目 @Override public int getElemNum() { // TODO Auto-generated method stub return num; } //判断队列是否是空的 @Override public boolean isQueueEmpty() { // TODO Auto-generated method stub if(num == 0)return true; else return false; } //清空队列 @Override public void clearQueue() { // TODO Auto-generated method stub if(num != 0) { Node<T> node; while(frontNode!=null) { node = frontNode; frontNode = frontNode.next; node.next = null; } rearNode = null; } } }
首先看看代码:
import Stucture.LinearQueue; import entity.Student; public class Main { public static void main(String[] args) { Student student1 = new Student(); student1.setAge(12); student1.setName("mike"); Student student2 = new Student(); student2.setAge(13); student2.setName("sali"); LinearQueue<Student> linearQueue = new LinearQueue<Student>(); linearQueue.pushQueue(student1); linearQueue.pushQueue(student2); linearQueue.pushQueue(student1); linearQueue.pushQueue(student1); for(int i=0;i<3;i++) { System.out.println(linearQueue.getTop().getName()); linearQueue.popQueue(); } System.out.println(linearQueue.getElemNum()); linearQueue.clearQueue(); if(!linearQueue.isQueueEmpty()) System.out.println("清空成功"); } }
然后来看看测试结果:
这样我们的链队列就成功实现了。
队列是数据结构中比较常见而且相对简单的一种数据结构。在学习的时候看起来好像很简单,但其实实际写起来的话一开始还是有一点难以下手。特别是习惯了c语言的写法,再转思想用java写可能会比较困难。但是当我们亲手写出来之后肯定印象会更加深的,也会更加了解这两种语言之间的差别。上面实现的是简单的队列模型,有哪些地方不足读者可以在评论区留言。
·
·
·
《数据结构》吴伟民
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。