线性表--07---队列

Posted 高高for 循环

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表--07---队列相关的知识,希望对你有一定的参考价值。

队列

定义:

队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。

队列的存储结构

  • 因为队列也是一种特殊的线性表,所以其存储结构有顺序结构链式结构两种,其中链式结构又可以分为单向链表和双向链表。

数组实现

顺序队列:

1)队头不动,出队列时队头后的所有元素向前移动

缺陷:操作是如果出队列比较多,要搬移大量元素。

public class MyOrderQueue<T>  implements Iterable<T>{

    private T[] element;
    private static final int initSize = 10; //数组初始大小
    private int size; //记录有效个数

    public MyOrderQueue(){
        this(initSize);
    }

    public MyOrderQueue(int capacity){
        this.element = (T[])new Object[capacity];
    }

    //判满操作
    private boolean isFull(){
        return size == element.length;
    }

    //判空操作
    public boolean isEmpty(){
        return size == 0;
    }

    //扩容操作
    private void dilatation(){
        element = Arrays.copyOf(element,element.length << 1);
    }

    //获取有效个数
    public int getSize(){
        return size;
    }

   //入队操作(尾插头删方法)
   public void add(T value){
       if(isFull()){
           dilatation();
       }
       element[size++] = value;
   }

   //出队操作(尾插头删方法)
   public T remove(){
       if(isEmpty()){
           throw new RuntimeException("队列空,不能取数据");
       }
       //记录移除的元素
       T oldFirst=element[0];
       //出队列时队头后的所有元素向前移动
       for(int i = 0;i < size-1;i++){
           element[i] = element[i+1];
       }
       element[--size] = null;
       return oldFirst;
   }

   //获取队头元素(尾插头删方法)
   public T peek(){
       if(isEmpty()){
           throw new EmptyStackException();
       }
       return element[0];
   }


    @Override
    public Iterator<T> iterator() {
        return new QIterator();
    }

    private class QIterator implements Iterator{
        private int cusor;

        public QIterator(){
            this.cusor=-1;
        }
        @Override
        public boolean hasNext() {
            return cusor<size-1;
        }

        @Override
        public Object next() {
           return element[++cusor];
        }
    }
}

测试:

public class QueueTest01 {
    public static void main(String[] args) {
        MyOrderQueue<String> queue = new MyOrderQueue<>();
        queue.add("a");
        queue.add("b");
        queue.add("c");
        queue.add("d");
        for (String str : queue) {
            System.out.print(str+" ");
        }
        System.out.println("-----------------------------");
        String result = queue.remove();
        System.out.println("出列了元素:"+result);

        System.out.println(queue.getSize());
        for (String str : queue) {
            System.out.print(str+" ");
        }
    }
}

2)队头移动,出队列时队头向后移动一个位置

如果还有新元素进行入队列容易造成假溢出。

  • 假溢出:
    顺序队列因多次入队列和出队列操作后出现的尚有存储空间但不能进行入队列操作的溢出。
  • 真溢出:
    顺序队列的最大存储空间已经存满二又要求进行入队列操作所引起的溢出。
import java.util.Iterator;

public class ArrayQueue<T> implements Iterable<T>{

    private static final int initSize = 10; //数组初始大小
    private int front; // 队列头
    private int rear; // 指向队列尾的数据的后一个位置
    private T[] arr; // 该数据用于存放数据, 模拟队列


    public ArrayQueue(){
        this(initSize);
    }

    // 创建队列的构造器
    public ArrayQueue(int arrMaxSize) {
        arr = (T[])new Object[arrMaxSize];
        front = 0; // 指向队列头部,分析出front是指向队列头位置.(即就是队列第一个数据)
        rear = 0; // 指向队列尾的数据的后一个位置
    }

    // 判断队列是否满
    public boolean isFull() {
        return rear == arr.length;
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }

    //获取有效个数
    public int getSize(){
        // 判断队列是否空
        if (isEmpty()) {
            // 通过抛出异常
            throw new RuntimeException("队列空");
        }
        return rear-front;
    }


    // 添加数据到队列
    public void add(T n) {
        // 判断队列是否满
        if (isFull()) {
            System.out.println("队列满,不能加入数据~");
            return;
        }
        arr[rear++] = n;
    }

    // 获取队列的数据, 出队列
    public T remove() {
        // 判断队列是否空
        if (isEmpty()) {
            // 通过抛出异常
            throw new RuntimeException("队列空,不能取数据");
        }
        return arr[front++];
    }


    // 显示队列的头数据, 注意不是取出数据
    public T peek() {
        // 判断
        if (isEmpty()) {
            throw new RuntimeException("队列空的,没有数据~~");
        }
        return arr[front];
    }


    @Override
    public Iterator<T> iterator() {
        return new ArrayQueue.QIterator();
    }

    private class QIterator implements Iterator {
        private int cusor;

        public QIterator(){
            this.cusor=front;
        }
        
        @Override
        public boolean hasNext() {
            return cusor<rear;
        }

        @Override
        public Object next() {
            return arr[cusor++];
        }
    }

}

测试:

public class QueueTest01 {
    public static void main(String[] args) {
        ArrayQueue<String> queue = new ArrayQueue<>();
        queue.add("a");
        queue.add("b");
        queue.add("c");
        queue.add("d");
        for (String str : queue) {
            System.out.print(str+" ");
        }
        System.out.println("-----------------------------");
        String result = queue.remove();
        System.out.println("出列了元素:"+result);

        System.out.println(queue.getSize());
        for (String str : queue) {
            System.out.print(str+" ");
        }
    }
}

循环队列:

循环队列概念:

  • 即不管入队列还是出队列,不进行数据移动,当数组后面满了,而前面空着时候,更新下标,继续从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。

对此我们引入了两个指针:

  • front 指针指向队头元素
  • rear 指针指向队尾元素的下一个位置,即下次元素入队的位置。

    在这里,我们又可以思考一个问题,当队列为空时,我们可以看到 front 等于 rear,此时该表达式可以作为我们队列判空的条件。但是,我们思考,当队列满的时候,是不是 front 依然等于 rear ,那么此时队列到底是空队列还是满队列,就不好说了。为了解决这种问题,我们有两种解决方法.

方法1: 设置一个标记flag;

  • 初始值 flag = 0;入队列:flag = 1; 出队列:flag = 0;
  • 队列为空时:(front == rear && flag == 0)
  • 队列为满时:(front == rear && flag == 1)

方法2:

是可以将队列中最后一个位置浪费掉,作为标记 ,以达到我们想要的目标.

此时

空队列的判断条件依然是: front == rear

队列判满的条件是: (rear + maxSize - front) % maxSize

当前队列有效数据的个数: (rear + maxSize - front) % maxSize

maxSize: 表示数组的最大容量

循环队列代码实现 :

public class CircleArray<T> implements Iterable<T> {

    private int maxSize ; //表示数组的最大容量  默认为10
    private int front; // 队列头  默认从0开始
    private int rear; // 指向队列尾的数据的后一个位置  默认从0开始
    private T[] arr; // 该数据用于存放数据, 模拟队列


    public CircleArray(){
        this(10);//默认数组初始化为10
    }


    // 创建队列的构造器
    public CircleArray(int arrMaxSize) {
        this.maxSize=arrMaxSize;
        arr = (T[])new Object[maxSize];
    }

    // 判断队列是否满
    public boolean isFull() {
        return (rear  + 1) % maxSize == front;
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }


    // 求出当前队列有效数据的个数
    public int getSize() {
        // rear = 2
        // front = 1
        // maxSize = 3
        return (rear + maxSize - front) % maxSize;
    }


    // 添加数据到队列
    public void add(T n) {
        // 判断队列是否满
        if (isFull()) {
            System.out.println("队列满,不能加入数据~");
            return;
        }
        //直接将数据加入
        arr[rear] = n;
        //将 rear 后移, 这里必须考虑取模
        rear = (rear + 1) % maxSize;
    }

    // 获取队列的数据, 出队列
    public T remove() {
        // 判断队列是否空
        if (isEmpty()) {
            // 通过抛出异常
            throw new RuntimeException("队列空,不能取数据");
        }
        // 这里需要分析出 front是指向队列的第一个元素
        // 1. 先把 front 对应的值保留到一个临时变量
        // 2. 将 front 后移, 考虑取模
        // 3. 将临时保存的变量返回
        T oldFirst = arr[front];
        front = (front + 1) % maxSize;
        return oldFirst;

    }
    // 显示队列的所有数据
    public void showQueue() {
        // 遍历
        if (isEmpty()) {
            System.out.println("队列空的,没有数据~~");
            return;
        }
        for (int i = front; i < front + getSize() ; i++) {
            System.out.printf("arr[%d]=%d\\n", i % maxSize, arr[i % maxSize]);
        }
    }


    // 显示队列的头数据, 注意不是取出数据
    public T peek() {
        // 判断
        if (isEmpty()) {
            throw new RuntimeException("队列空的,没有数据~~");
        }
        return arr[front];
    }


    @Override
    public Iterator<T> iterator() {
        return new CircleArray.QIterator();
    }

    private class QIterator implements Iterator{

        private int cusor;

        public QIterator(){
            this.cusor=front;
        }

        @Override
        public boolean hasNext() {
            return cusor<front + getSize();
        }

        @Override
        public Object next() {
            return arr[(cusor++)% maxSize];
        }
    }
}

测试:

public class QueueTest01 {
    public static void main(String[] args) {
        CircleArray<String> queue = new CircleArray<>(5);
        queue.add("a");
        queue.add("b");
        queue.add("c");
        queue.add("d");
        for (String str : queue) {
            System.out.print(str+" ");
        }
        System.out.println();
        System.out.println("-----------------------------");

        String result = queue.remove();
        System.out.println("出列了元素:"+result);

        System.out.println(queue.getSize());
        for以上是关于线性表--07---队列的主要内容,如果未能解决你的问题,请参考以下文章

初级--04---链表反转----链表实现栈队列双端队列

《线性表的插入和删除算法实现》以及《栈和队列的插入和删除算法实现》的c语言代码

(源代码见大话数据结构)线性表—循环队列的顺序存储结构

线性表 链队列

数据结构(C语言版)严蔚敏(线性表队列栈数组树图等数据结构参考代码,持续更新中。。。)

数据结构(C语言版)严蔚敏(线性表队列栈串树图等数据结构参考代码,持续更新中。。。)