数据结构之简单队列的实现

来源:互联网 发布:linux ant 安装 编辑:程序博客网 时间:2024/06/15 05:27

数据结构中有一种存储数据的形式,我们把它叫做队列(Queue).主要探讨

       一.队列的特点以及表现形式

       二.写一个环形队列

       三.编写过程中遇到的坑及设计模式


一.队列的特点以及表现形式

1.首先,队列存储数据的方式和栈(stack是有区别的)。栈对于数据的管理是先入后出的(FILO),而队列

  对于数据的管理则是先入先出的(FIFO).

                2.队列分为普通队列和环形队列

1)普通队列

   这样子的队列管理数据的方式有两种,当head的数据被删除时,要么后面的位置往前移动变成头元素,要么队列管理者将head后一位的元素当做head来处理,但是这么做都是有问题的。

           如果是head数据被删除后,队列管理者把head+1当做头索引的话,那么此时那个head索引处的位置就会被废弃掉,对应到计算机中就是内存被浪费掉了

           如果是head数据被删除后,队列自动全部往前补一位的话, 那么内存是不会被浪费掉,但是所有的位置都要进行前移,浪费了时间。

           因此上述两者处理数据的方式都有缺陷,因此,就产生了环形队列。

 2)环形队列

可以完美的解决上述两个问题,即当head被删除时,head索引就像前移动一位,但此时head的位置没有不能用,就被空下来,可以重新插入。


二.队列的实现

首先要明确实现一个队列需要什么方法,以及每个方法的注意点

1.需要的方法

1)初始化队列

2)清空队列

3)销毁队列

4)插入元素

5)删除元素

6)判空

7)判满

8)迭代

9)队列长度

据此可以得到代码如下:

IQueueOp.java

/** * 队列行为的接口,主要负责进行下面的事情 * @author szxgg * 0.初始化队列----由队列自身来实现,也可以它来实现 * 1.插入一个元素 * 2.删除一个元素 * 3.销毁队列 * 4.清空队列 * 5.判空 * 6.判满 * 7.队列长度 * 8.循环遍历队列 * */public interface IQueueOp<T> {/** * 初始化队列 * @param capacity 容量值,有几个元素值 */public void initQueue(int capacity);/** * 插入一个元素,每次都是插入到队尾的索引处 * @param t 元素值 * @return */public boolean addElement(T t);/** * 删除一个元素 * 删除的都是队列头部的元素 * @return */public boolean removeElement();/** * 销毁一个队列 * 初始化队列的逆向,并且释放内存 * @return */public void destroyQueue();/** * 清空一个队列信息 * 但是不用释放内存 * @return */public void clearQueue();/** * 队列是否为空的判断 * @return */public boolean isEmpty();/** * 队列是否为满的判断 * @return */public boolean isFull();/** * 队列长度 * @return */public int getQueueLength();/** * 对队列中的元素进行迭代 */public void iteartorQueue();}
我们使用接口来封装这些操作,让具体的队列类实现这些接口,或者持有这些接口的引用(策略模式),来完成队列的数据操作。

设计一个抽象的队列基类,这里是实现了这个接口,其实可以尝试持有接口的引用的。

这个基类有以下几个属性

  1)队列的容量,初始化时设置的。

   2)队列的长度

   3)队列尾的索引值,插入元素时插入到这个索引处

           4)队列头的索引值,删除时删除这个索引处的值

           5)数组,用来维护队列内部的元素数据

MyAbsQueue.java

/** * 队列的抽象基类 *  * @author szxgg * * @param  */public  abstract class MyAbsQueue<T> implements IQueueOp<T>{/** * 头元素的索引 */protected int headIndex;/** * 尾元素的索引 */protected int tailIndex;/** * 当前队列的长度 */protected int currentLength;/** * 队列的容量 */protected int capacity;/** * 数组,维护队列内部的元素排列 */protected T[] arrays;public void initQueue(int capacity) {this.capacity = capacity;arrays        =initQueueElements(this.capacity);}protected abstract T[] initQueueElements(int capacity);public boolean addElement(T t) {if(isFull()){return false;}//在末尾的索引处插入元素arrays[tailIndex] = t;//末尾的索引加1tailIndex++;//防止越界tailIndex = tailIndex%capacity;//长度加1currentLength++;return true;}public boolean removeElement() {//判空if(isEmpty()){return false;}//删除头指针指向的元素arrays[headIndex] = null;//头指针元素+1headIndex++;//防止越界headIndex = headIndex%capacity;//长度减1currentLength--;return true;}public void destroyQueue() {clearQueue();arrays=null;capacity=0;}public void clearQueue() {headIndex=0;tailIndex=0;currentLength=0;}public boolean isEmpty() {return currentLength==0;}public boolean isFull() {return currentLength==capacity;}public int getQueueLength() {return currentLength;}}

下面是队列的实现类,主要负责实现初始化的操作以及迭代的具体操作(因为在泛型中没法执行)

/** * @author lenovo *队列,存放的是整型数组的 */public class IntegerQueue extends MyAbsQueue<Integer>{public IntegerQueue(int capacity) {initQueue(capacity);}@Overrideprotected Integer[] initQueueElements(int capacity) {return new Integer[capacity];}public void iteartorQueue() {for(int i=headIndex;i<headIndex+currentLength;i++){System.out.println(arrays[i%capacity]);}}}
基本上这个队列就好了,下面可以用测试代码进行测试分析

TestActivity.java

import java.util.List;public class TestActivity {public static void main(String[] args){MyAbsQueue<Integer> queue = new IntegerQueue(4);//初始化先打印一下queue.iteartorQueue();//插入元素System.out.println("success add:"+queue.addElement(1));System.out.println("success add:"+queue.addElement(2));System.out.println("success add:"+queue.addElement(3));System.out.println("success add:"+queue.addElement(4));System.out.println("success add:"+queue.addElement(5));queue.iteartorQueue();//删除元素System.out.println("success delete:"+queue.removeElement());queue.iteartorQueue();System.out.println("success delete:"+queue.removeElement());queue.iteartorQueue();System.out.println("success delete:"+queue.removeElement());queue.iteartorQueue();System.out.println("success delete:"+queue.removeElement());queue.iteartorQueue();System.out.println("success delete:"+queue.removeElement());queue.iteartorQueue();//再插入3个System.out.println("success add:"+queue.addElement(10));System.out.println("success add:"+queue.addElement(20));System.out.println("success add:"+queue.addElement(30));queue.iteartorQueue();//清除队列queue.clearQueue();System.out.println("after clear");queue.iteartorQueue();queue.addElement(45);queue.iteartorQueue();//destroyqueue.destroyQueue();System.out.println("after destroy");queue.iteartorQueue();queue.addElement(11);queue.iteartorQueue();}}

输出如下:success add:true
success add:true
success add:true
success add:true
success add:false
1
2
3
4
success delete:true
2
3
4
success delete:true
3
4
success delete:true
4
success delete:true
success delete:false
success add:true
success add:true
success add:true
10
20
30
after clear
45
after destroy


三.下面分析下设计队列时相关方法的坑和注意点

1.插入一个元素:

1) 插入前要判断队列是否满了,如果满了就不插入---判断满了的标志是数组当前的长度==数组的capacity

2)是在tailIndex处插入一个元素,即arrays[tailIndex] = element;然后需要将tailIndex++,并且再和capacity取余,防止多次存取后的数组越界,注意环形特别需要取余操作。

3)插入完成后队列的长度也要+1;

     2.删除一个元素

1)删除一个元素前需要判断这个队列是否为空队列,判断空队列状态的标准是length ==0;

                 2)是从startIndex处进行元素的删除,删除后startIndex++,然后为了防止数组越界再和队列的capacity取余。

                 3)数组的长度自减

3.迭代

for(int i = startIndex;i<startIndex+length;i++){

syso(arrays[i%capacity]);

}

首先不能是从0到length,因为有可能经过多次存取操作后首位置不是0,并且0上没有数据

其次不能是startIndex到length,因为有可能startIndex是3,但是length是4,这时应该迭代4个元素,但是只迭代了一个

所以就是startIndex到startIndex+length。这样可以保证迭代的准确

然后是打印

不能直接打印arrays[i],因为i是从startIndex到startIndex+length,非常有可能就越界了,

越界的话就直接和容量取余。

这块可以参考collection list arraylist的实现,画一个uml图进行分析学习


0 0
原创粉丝点击