动态数组3队列的顺序存储结构(Queue接口,ArrayQueue实现,ArrayQueueLoop优化)

    1.队列的定义。

      队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

      队头:Rear  队尾:Front

    入队操作 O(1)      出队操作O(n)

    2.队列接口Queue的定义。

    代码如下: 

package com.oupeng.p3队列;

public interface Queue<E> {

	/**
	 * 获取元素的总数
	 * @return 返回size
	 * */
	public int getSize();
	/**
	 * 判断队列是否为空
	 * @return 返回false 或者true
	 * */
	public boolean isEmpty();
	/**
	 * 进入队列一个元素
	 * */
	public void enqueue();
	/**
	 * 出队列一个元素
	 * */
	public E dequeue();
	/**
	 * 获取队头的元素
	 * @return 将该元素返回
	 * */
	public E getFront();
	/**
	 * 获取队尾的元素
	 * @return 将该元素返回
	 * */
	public E getRear();
	/**
	 * 清空队列
	 * */
	public E clear();
}

     Queue分析:

int getSize()        获取队列元素的总和
boolean isEmpey()    判断队列元素是否为空
void enqueue(E e)    进入队列一个元素
E dequeue()          出队列一个元素   
E getFront()         获取队列头指针的一个元素
E getRear()          获取队列尾指针的一个元素
E clear()            清空队列的所有元素

    3.队列的顺序存储结构ArrayQueue的定义。

代码如下:

package com.oupeng.p3队列;

import com.oupeng.p1线性表.ArrayList;

public class ArrayQueue<E> implements Queue<E> {

	private ArrayList<E> list;
	public ArrayQueue(){
		list=new ArrayList<E>();
	}
	public ArrayQueue(int capacity){
		list=new ArrayList<E>(capacity);
	}
	@Override
	public int getSize() {
		return list.getSize();
	}

	@Override
	public boolean isEmpty() {
		return list.isEmpty();
	}

	@Override
	public void enqueue(E e) {
		list.addLast(e);;
	}

	@Override
	public E dequeue() {
		return list.removeFirst();
	}

	@Override
	public E getFront() {
		return list.getFirst();
	}

	@Override
	public E getRear() {
		return list.getLast();
	}

	@Override
	public void clear() {
		list.clear();
	}
	public String toString(){
		StringBuilder sb=new StringBuilder();
		sb.append("ArrayQu: size="+getSize()+",capacity="+list.getCapacity()+"\n");
		if(isEmpty()){
			sb.append("[]");
		}else{
			sb.append('[');
			for(int i=0;i<getSize();i++){
				sb.append(list.get(i));
				if(i==getSize()-1){
					sb.append(']');
				}else{
					sb.append(',');
				}
			}
		}
		return sb.toString();
	}
	public boolean equals(Object obj){
		if(obj==null){
			return false;
		}
		if(obj==this.list){
			return true;
		}
		if(obj instanceof ArrayQueue){
			ArrayList<E> l=(ArrayList<E>) obj;
			if(l.getSize()==getSize()){
				for(int i=0;i<getSize();i++){
					if(l.get(i)!=list.get(i)){
						return false;
					}
				}
				return true;
			}
		}
		return false;
	}
}
  • ArrayQueue分析

    

int getSize()        获取队列元素的总和, 相当于返回线性表的list.getSize();
boolean isEmpey()    判断队列元素是否为空,相当于返回线性表的list.isEmpty();
void enqueue(E e)    进入队列一个元素,相当于在线性表的addLast()加入一个数
E dequeue()          出队列一个元素,相当于在线性表removeFirst()删除一个元素
E getFront()         获取队列头指针的一个元素,相当于返回线性表的list.getFirst();
E getRear()          获取队列尾指针的一个元素,相当于返回线性表的list.getLast();
E clear()            清空队列的所有元素,相当于清空线性表的list.clear();
String toString()    
                     1.这里使用StringBuileder  :方便线程不安全
                       不使用StringBuffer      :不方便线程安全
                     2.先判断线性表是否为空  
                       空打印   []
                       非空 sb.append('[');  起步
                       开始遍历线性表  i为最后一个元素时 sb.append(']');
                                     i非最后一个元素时 sb.append(',');
                       最后返回sb.toString();
boolean equals()     先对传入的元素进行判断,看是否为空,空则返回false
                     再对该元素是否直接等于this,是返回true
                     最后再比较里边内容,当出现内容元素不等时返回一个false,比完相等则返回true

 

 

    存在弊端:

    1、 队列的顺序存储结构本身是由ArrayList实现的。
    2、在数据元素入队的时候,相当于在ArrayList表尾添加元素,addLast();
    3、在数据元素出队的时候,相当于在ArrayList表头 删除元素,removeFirst();
    4、入队时间复杂度O(1),从下标最后一个入队。出队的时间复杂度O(n),因为下标为0进行出列,后边所有的元素往前移动。
    5、线性表增删数据元素时间复杂符都是O(n)(平均计算的)。
    6、队列的出队时间复杂度O(n),可不是按平均算的,因为每次出队都是O(n)。

    4.对ArrayQqueue进行优化。

    优化1、想方法减少出队的时间复杂度,可以这么思考。

    元素出队后,不将队头Front下标后边的元素遍历往前移动,而是将队头Front的位置往后移动一位。

    这样便解决了时间复杂度的问题,出队和入队都是O(1)。但是却产生了空间的浪费,当队尾Rear指针到达末点时无法在进行移     动了。

    优化2、所以在这里便出现第二种思路优化。

    当队尾Rear指针到达末端时,当再次进行元素入队操作,可以将队尾Rear指针位置移动到表头(也就是数组下标0处)。

    所以在这里可以想到,优化1是一条线,优化2便成了一个圆环。

    现在就需要判断什么时候队列已满?什么时候队列为空?

    当队头Front 队尾Rear按正常逻辑对应位置存在元素时。队列满:(Rear+1)%n==Front  队列空: (Rear+1)%n==Front

    这时候出现队满和队空的判断条件一样,这时候也就无法进行判断了。

    优化3、对队满队空判断条件的优化。

    队头Front指针,队尾Rear指针。让队尾Rear指针指向一个空元素位置,队头Front指针指向存元素位置。

    这时候便出现,队满时:(Rear+1)%n==Front   队空时:Rear==Front

    5.优化后的ArrayQueue的实现。

    代码如下:

package com.oupeng.p3队列;

public class ArrayQueueLoop<E> implements Queue<E>{
	private E[] data;
	private int front;
	private int rear;
	private int size;
	private static int DEFAULT_SIZE=10;
	
	public ArrayQueueLoop() {
		this(DEFAULT_SIZE);
	}
	public ArrayQueueLoop(int capacity){
		data=(E[]) new Object[capacity+1];
		front=0;
		rear=0;
		size=0;
	}

	@Override
	public int getSize() {
		return size;
	}

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

	@Override
	public void clear() {
		size=0;
		front=0;
		rear=0;
		//与其缩容清空 不如重新创建数组
	}

	@Override
	public void enqueue(E e) {
		if((rear+1)%data.length==front){
			//【扩容】
			resize(data.length*2-1);
		}
		data[rear]=e;
		rear=(rear+1)%data.length;
		size++;
	}

	private void resize(int newLen) {
		E[] newData=(E[]) new Object[newLen];
		int index=0;//表示新数组角标
		for(int i=front;i!=rear;i=(i+1)%data.length){
			newData[index++]=data[i];
		}
		front=0;
		rear=index;
		data=newData;
	}
	@Override
	public E dequeue() {
		if(isEmpty()){
			throw new NullPointerException("队列为空!");
		}
		E e=data[front];
		front=(front+1)%data.length;
		size--;
		if(size<=data.length/4&&data.length>DEFAULT_SIZE){
			resize(data.length/2+1);
		}
		return e;
	}

	@Override
	public E getFront() {
		return data[front];
	}
	
	@Override
	public E getRear() {
		return data[(data.length+rear-1)%data.length];
	}
	
	@Override
	public String toString() {
		
		StringBuilder sb=new StringBuilder();
		sb.append("ArrayQueueLoop: size="+getSize()+",capacity="+(data.length-1)+"\n");
		if(isEmpty()){
			sb.append("[]");
		}else{
			sb.append('[');
			for(int i=front;i!=rear;i=(i+1)%data.length){
				sb.append(data[i]);
				if((i+1)%data.length==rear){
					sb.append(']');
				}else{
					sb.append(',');
				}
			}
		}
		return sb.toString();
	}
}

    这段优化主要需要注意队满和队空的判断,便容易理解。

    ArrayLoop分析:

    

无参无返回值构造函数:  默认队列容量初始值
有参无返回值构造函数:  队列容量初始值为传入的值
                     定义头指针Front初始值为0,尾指针初始值为0,有效元素size初始值为0
int getSize()        获取队列元素的总和, 相当于返回size
boolean isEmpey()    判断队列元素是否为空,相当于判断front==rear&&size==0
void enqueue(E e)    进入队列一个元素,尾指针的圆环移动,如果元素满则进行判断扩容
E dequeue()          出队列一个元素,相当于在头指针的圆环移动,如果元素大量减少,进行判断缩容
E getFront()         获取队列头指针的一个元素,相当于返回队列头指针Front对应的元素;
E getRear()          获取队列尾指针的一个元素,相当于返回队列(尾指针Rear+队列长度-1)对队列长度取余;
E clear()            清空队列的所有元素,相当于相当于初始化size=0;
		                                           front=0;
		                                           rear=0;
String toString()    
                     1.这里使用StringBuileder  :方便线程不安全
                       不使用StringBuffer      :不方便线程安全
                     2.先判断线性表是否为空  
                       空打印   []
                       非空 sb.append('[');  起步
                       开始遍历线性表  i为最后一个元素时 sb.append(']');
                                     i非最后一个元素时 sb.append(',');
                       最后返回sb.toString();
					   这里的toString,初始值未优化0对应优化front
					                         未优化data.length对应rear

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值