数据结构与算法---顺序存储结构(队)

ArrayQueue(队列)
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表,对于队的操作也是基于线性表来进行操作,所以针对面向对象思想,我们在对队进行操作时就可以直接把队ArrayList的某些操作直接拿来用。

package DS01.动态数组;

public interface Queue<E> extends Iterable<E>{
    //获取队列中元素的个数
    int getSize();
    //判断队列是否为空
    boolean isEmpty();
    //入队一个元素
    void enqueue(E e);
    //出队一个元素
    E dequeue();
    //获取队头
    E getFront();
    //获取队尾
    E getRear();
    //清空队列
    void clear();
}

package DS01.动态数组;

import java.util.Iterator;
/*
* 线性队列也是基于线性表的
* */
public class ArrayQueue<E> implements Queue<E> {
    private  ArrayList<E> list;
    public ArrayQueue(){
        list=new ArrayList<>();
    }
    public ArrayQueue(int capacity){
        list=new ArrayList<>(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();
    }

    @Override
    public Iterator<E> iterator() {
        return list.iterator();
    }
    public String toString(){
        StringBuilder sb=new StringBuilder();
        sb.append(String.format("ArrayQueue:%d/%d\n",getSize(),list.getCapacity()));
        sb.append("[");
        if(isEmpty()){
            sb.append("]");
        }else{
            for(int i=0;i<getSize();i++){
                sb.append(list.get(i));
                if(i==getSize()-1){
                    sb.append("]");
                }else {
                    sb.append(",");
                }
            }
        }
        return sb.toString();
    }
}

ArrayQueue队列顺序存储结构的弊端

队列的顺序存储结构本身就是由ArrayList实现的
在数据元素入队的时候,相当于在ArrayList表尾添加元素
在数据元素出对的时候,相当于在ArrayList表头删除元素
很明显,入队的时间复杂度O(1),出队的时间复杂度O(n),因为出队只在表头进行,每出队一次,后面的元素就要全部向前移一位,时间复杂度较高。所以就要进行优化。

ArrayQueue的优化
第一步:让队头指针和队尾指针一样随着数据元素的变化而移动。
在这里插入图片描述
就是队头是动态的,每出队一个元素,队头Front就向后移动一次。出队复杂度O(1)。

第二步:第一步会使复杂度降低,但是队尾指针Rear移动到最后就不能移动了,每出队一次,表前面的空间就会空出来,会造成空间的浪费,所以可以当Front或者Rear到达尾部时,如果需要后移可重新指向表头。
在这里插入图片描述
第三步:第二步优化节省了空间,但是队列判空条件与队列判满条件一样,无法判断,所以我们就要始终预留一个空位置,令Rear指向空位置。
在这里插入图片描述
队列满的条件:(Rear+1)%n==Front

队列空的条件:Rear==Front

优化过后就变为ArrayQueueLoop

ArrayQueueLoop(循环队列)

package DS01.动态数组;

import java.util.Iterator;
//循环队列不基于顺序表,有自己独特的想法
/*
* 优化复杂度,出队时队头动态的
* 优化浪费的空间,使其变为循环的
* 预留一个空间来判满,尾指针总是指向,队尾的后一个空位
* */
/*
* 判空条件:front=rear;
* 判满条件:(rear+1)%data.length=front
*
* */
public class ArrayQueueLoop<E> implements Queue<E> {
    private E[] data;
    private int front;
    private  int rear;
    private  int size;//为了存储该循环队列的有效元素
    public ArrayQueueLoop(){
        data=(E[])(new Object[11]);//多创建一个空间,有一个预留空位
        front=0;
        rear=0;
        size=0;
    }
    @Override
    public int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size==0&&front==rear;
    }
	//入队
    @Override
    public void enqueue(E e) {
        if((rear+1)%data.length==front){
            resize(data.length*2);
        }
        data[rear]=e;
        rear=(rear+1)%data.length;
        size++;
    }
	//出队
    @Override
    public E dequeue() {
        if(isEmpty()){
            throw new IllegalArgumentException("队列为空");
        }
        if(size<=(data.length-1)/4&&(data.length-1)/2>10){
            resize(data.length/2+1);
        }
        E ret=data[front];
        front=(front+1)%data.length;
        size--;
        return ret;
    }
    //扩容
    public void resize(int newLen){
        E[] newData=(E[])(new Object[newLen]);
        int p=front;//重新设一个变量,从队头开始
        int i=0;
        while (true){
            newData[i]=data[p];
            p=(p+1)%data.length;//移动队头
            i++;
            if(p==rear){//移动完毕
                break;
            }
        }
        data=newData;
    }
    //返回队头
    @Override
    public E getFront() {
        if(isEmpty()){
            throw new IllegalArgumentException("队列为空");
        }
        return data[front];
    }
	//返回队尾
    @Override
    public E getRear() {
        if(isEmpty()){
            throw new IllegalArgumentException("队列为空");
        }
        return data[(rear+1)%data.length];
    }
	//清空队列
    @Override
    public void clear() {
        data=(E[])(new Object[11]);//多创建一个空间,有一个预留空位
        front=0;
        rear=0;
        size=0;
    }
    public String toString(){
        StringBuilder sb=new StringBuilder();
        sb.append(String.format("ArrayQueueLoop:,%d/%d\n",size,data.length-1));
        sb.append("[");
        if(isEmpty()){
            sb.append("]");
        }else{
            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();
    }
    //仅仅只是为了遍历,支持froeach语句
    @Override
    public Iterator<E> iterator() {
        return new ArrayQueueLoopItertor();
    }
    private class ArrayQueueLoopItertor implements Iterator{
        int p=front;
        @Override
        public boolean hasNext() {//该元素是否有下一个
            return p!=rear;
        }

        @Override
        public E next() {//有直接返回下一个
            E ret=data[p];
            p=(p+1)%data.length;
            return ret;
        }
    }
}

发布了49 篇原创文章 · 获赞 4 · 访问量 903

猜你喜欢

转载自blog.csdn.net/weixin_45404784/article/details/103526476