线性表——队列

队列

目录

  • 队列
    • 概念
    • 顺序队列
      • 基本操作
        • 假溢出
        • 循环队列解决队空队满同条件问题
          • 1.少用一个存储单元
          • 2.使用一个tag变量
          • 3.使用一个计数器
        • 入队
        • 出队
        • 完整代码
    • 链队列
      • 基本操作
        • 入队
        • 出队
        • 完整代码
    • 栈与队列的比较
      • 相同点
      • 不同点

概念

队列也是一种特殊的线性表,它只能从表尾插入数据元素,在表头删除数据元素,所以队列的操纵和栈一样操作受限,队列具有先进先出(FIFO(First In First Out))的特性,就如同在生活中的排队一般。
允许进行删除的一端称为队首(front),允许插入的一端称为队尾(rear)

顺序队列

基本操作

初始化队列时,令front = rear = 0;入队时,直接将新的数据元素存入rear所指向的存储单元中,然后将rear的值增加1;出队时,直接取出front所指向的存储单元中的数据元素得值,然后front++。

假溢出

因为队列的机制为队首出队队尾入队,所以当我们多次进行入队出队操作之后,队首指针front和队尾指针rear会后移,当rear移动到顺序表最大长度时,便不能进行入队操作,但是队首指针所指结点之前的位置为空,但是计算机任然会认为此表满,这种情况我们称为假溢出,防止假溢出的方法为:将队列做成循环队列,这样rear指针在队尾跳转时就不会显示队满,而会跳转至队首。

循环队列解决队空队满同条件问题

当我们用循环队列解决假溢出问题时,我们在对队列进行入队出队操作时又会发现新的问题,那就是队空队满同条件,通常我们采用以下三种方法:

1.少用一个存储单元

当顺序存储空间的最大容量为maxSize时,我们允许存放的最大的元素个数为maxSize-1 此时,队空的判断条件为:front == rear 而队满的判断条件为:front == (rear + 1) % maxSize

这里采用的是一种取余运算,此种算法在后期运用特别多
本种方法下标不固定,front指针与rear指针位置会随着入队出队的变化而变化

//判断是否为空
public boolean isEmpty(){
     
        return front == rear;        //少用一个存储单元
}
//获取长度
public int length(){
     
        return (rear - front + queueElem.length) % queueElem.length; 
}
2.使用一个tag变量

在设计程序的时候引进一个变量flag,其初始值为0,每当有入队操作时,就将其置为flag == 1,每当有出队操作时, 就将其置为flag == 0;队空的判断条件为front == rear && flag == 0;此时队满的判断条件为front == rear && flag == 1

//判断是否为空
    public boolean isEmpty(){
     
        return front == rear && flag == 0;      //设置一个标志变量
    }
    //获取长度
    public int length(){
     
        return (rear - front + queueElem.length) % queueElem.length; 
    }
3.使用一个计数器

在设计程序的时候引入一个变量num;设置其初始值为0;每当有入队操作时,就将其num值加一,出队操作成功之后就将num值减一,此时队空的判断条件为num == 0而队满时的判断条件为num > 0 && front == rear

//判断是否为空
    public boolean isEmpty(){
     
         return num == 0;                //使用计数器
    }
    //获取长度
    public int length(){
     
        return (rear - front + queueElem.length) % queueElem.length; 
    }

入队

当前代码提供三种方法得入队操作,分别对应上述解决队空队满同条件得三种方法

//入队
    public void offer(Object x) throws Exception{
     
        // if((rear + 1) % queueElem.length == front)       //少用一个存储单元
        if(front == rear && flag == 1)                      //设置一个标志变量flag
        // if(num > 0 && front == rear)                        //计数器
            throw new Exception("队列已满");
        else{
         
            queueElem[rear] = x;
            rear = (rear + 1) % queueElem.length;
            flag = 1;                                      //标志量flag
            // num++;                                           //计数器
        }
    }

出队

当前代码提供三种方法得出队操作,分别对应上述解决队空队满同条件得三种方法

//出队
    public Object poll(){
     
        if(isEmpty()){
                            
            return null;
        }else{
     
            Object t = queueElem[front];
            front = (front + 1) % queueElem.length; //front值循环+1
            flag = 0;                           //标志量flag
            // num--;                                  //计数器
            return t;
        }
    }

完整代码

方法类

public class CircleSqQueue {
     
    Object[] queueElem;
    int front;
    int rear;
    int flag = 0;           //设置标志位flag
    int num = 0;            //设置计数器
    //构造函数
    public CircleSqQueue(int maxSize){
     
        front = rear = 0;
        queueElem = new Object[maxSize];
    }
    public CircleSqQueue(int maxSize, Object[] temp){
     
        this(maxSize);
        for (int i = 0; i < temp.length; i++) {
     
            queueElem[rear] = temp[i];
            rear = (rear + 1) % queueElem.length;
            flag = 1;           //设置标志位flag
            // num++;                  //计数器
        }
    }
    //置空
    public void clear(){
     
        front = rear = 0;
        flag = 0;                       //标志量flag
        // num = 0;                           //计数器
    }
    //判断是否为空
    public boolean isEmpty(){
     
        // return front == rear;        //少用一个存储单元
        return front == rear && flag == 0;      //设置一个标志变量
        // return num == 0;                //使用计数器
    }
    //获取长度
    public int length(){
     
        return (rear - front + queueElem.length) % queueElem.length; 
    }
    //取队列首元素
    public Object peek(){
     
        if(isEmpty())
            return null;
        else    
            return queueElem[front];
    }
    //入队
    public void offer(Object x) throws Exception{
     
        // if((rear + 1) % queueElem.length == front)       //少用一个存储单元
        if(front == rear && flag == 1)                      //设置一个标志变量flag
        // if(num > 0 && front == rear)                        //计数器
            throw new Exception("队列已满");
        else{
         
            queueElem[rear] = x;
            rear = (rear + 1) % queueElem.length;
            flag = 1;                                      //标志量flag
            // num++;                                           //计数器
        }
    }
    //出队
    public Object poll(){
     
        if(isEmpty()){
                            
            return null;
        }else{
     
            Object t = queueElem[front];
            front = (front + 1) % queueElem.length; //front值循环+1
            flag = 0;                           //标志量flag
            // num--;                                  //计数器
            return t;
        }
    }
    //打印输出整个队列
    public void display(){
     
        if(!isEmpty()){
     
            // for (int i = front; i != rear; i = (i + 1) % queueElem.length)       //少用一个存储单元
                // System.out.print(queueElem[i].toString() + " ");

            if(front == rear && flag == 1){
     
                for (int i = front, j = queueElem.length; j > 0; i = (i + 1) % queueElem.length,j--)          //设置标志量flag
                    System.out.print(queueElem[i].toString() + " ");
            }else{
     
                for (int i = front; i != rear; i = (i + 1) % queueElem.length)          //设置标志量flag
                    System.out.print(queueElem[i].toString() + " ");
            }
            
            // if(front == rear && num > 0){
     
            //     for(int i = front, j = num; j > 0;  i = (i + 1) % queueElem.length, j--)  //计数器
            //         System.out.print(queueElem[i].toString() + " ");
            // }else{
     
            //     for(int i = front; i != rear && num != 0;  i = (i + 1) % queueElem.length)  //计数器
            //         System.out.print(queueElem[i].toString() + " ");
            // } 

        }else{
     
            System.out.println("此队列为空");
        }
    }
}

链队列

基本操作

队列的链式存储结构可以不用带头结点的单链表来实现。为了便于实现入队和出队操作,需要引进两个指针front和rear来分别指向队首元素和队尾元素的结点。
在链式存储结构中我们也需要使用到前面链表中的结点类Node类

入队

对于链队列的入队操作,我们需要先构造一个新的数据域为x的结点,然后判断整个队列是否为空,因为如果为空,则直接让队首指针和队尾指针指向新结点即可,否则就需要找到队尾指针指向结点的指针域,然后将新结点的地址交给它,再让队尾指针后移,否则会报空指针异常NullPointerException

 //入队
    public void offer(Object x){
     
        Node p = new Node(x);       //初始化新结点
        if(!isEmpty()){
                  //判空
            rear.next = p;          //连接新结点
            rear = rear.next;       //尾指针后移
            // rear = p;
        }else
            front = rear = p;       //首结点
    }

出队

对于出队我们也需要对其进行判空,如果队列为空则直接返回null,没有其他操作,否则就需要先保存队首结点,然后队首指针后移,最后返回删除的旧队首结点的数据域。

//出队
    public Object poll(){
     
        if(!isEmpty()){
                  //判空
            Node p = front;         //新指针p指向队首指针指向的结点
            front = front.next;     //首结点后移
            if(p == rear)           //若p指向尾指针
                rear = null;        //则将尾指针指向结点(最后一个结点)出队
            return p.data;          //返回p指向结点的数据域
        }else   
            return null;            //返回空值
    }

完整代码

方法类

public class LinkQueue {
     
    private Node front;     //队首指针
    private Node rear;      //队尾指针

    //链队列的构造函数
    public LinkQueue(){
     
        front = rear = null;        //将队首指针和队尾指针都置空
    }

    //链队列构造完整链表
    public LinkQueue(Object[] temp){
     
        this();
        for (int i = 0; i < temp.length; i++) {
     
            offer(temp[i]);
        }
    }

    //队列置空
    public void clear(){
     
        front = rear = null;         //将队首指针和队尾指针都置空
    }

    //队列判空
    public boolean isEmpty(){
     
        return front == null;           //队首指针为空则空
    }

    //求队列长度
    public int length(){
             
        Node p = front;             //将p指向队首指针指向的位置
        int length = 0;             //设置长度默认值为0
        while(p != null){
            //若p指向位置不为空
            p = p.next;         //指针下移
            ++length;;          //计数器+1
        }
        return length;          //返回长度值
    }

    //取队首元素
    public Object peek(){
     
        if(!isEmpty())             //判空
            return front.data;      //返回首元素数据域、
        else
            return null;            //返回空
    }

    //入队
    public void offer(Object x){
     
        Node p = new Node(x);       //初始化新结点
        if(!isEmpty()){
                  //判空
            rear.next = p;          //连接新结点
            rear = rear.next;       //尾指针后移
            // rear = p;
        }else
            front = rear = p;       //首结点
    }

    //出队
    public Object poll(){
     
        if(!isEmpty()){
                  //判空
            Node p = front;         //新指针p指向队首指针指向的结点
            front = front.next;     //首结点后移
            if(p == rear)           //若p指向尾指针
                rear = null;        //则将尾指针指向结点(最后一个结点)出队
            return p.data;          //返回p指向结点的数据域
        }else   
            return null;            //返回空值
    }

    //输出队列
    public void display() throws Exception {
     
        if(!isEmpty()){
     
            Node p = front;         //新指针指向队首指针指向的结点
            while(p != null){
     
                System.out.print(p.data + " ");
                p = p.next;
            }
        }else{
     
            throw new Exception("此队列为空");
        }
    }
}

Node类

结点类与之前发表博客中的链表中结点类一致

栈与队列的比较

相同点

  1. 都是线性结构,即数据元素之间具有“一对一”的逻辑关系。
  2. 插入操作都是限制在表尾进行
  3. 都可以在顺序存储结构和链式存储结构上实现
  4. 在时间代价上,插入与删除操作都需要常数时间(O(1));在空间代价上,情况也相同
  5. 多链栈和多链队列的管理模式可以相同

不同点

  1. 删除数据元素操作的位置不同。栈的删除操作控制在表尾进行,而队列的删除操作控制在表尾进行
  2. 两者的应用场合不同(FILO与FIFO)
  3. 顺序栈可以实现多栈空间共享,而顺序队列不行

你可能感兴趣的:(笔记,算法与数据结构,队列,java,数据结构,算法,链表)