数据结构与算法--------队列的两种实现(顺序表、链表)

(一)什么是队列?

队列又叫先进先出表,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和另一端进行取数据操作,插入数据的一端是队尾,取数据的一端是队头

(二)队列取、添加数据操作的抽象形式

在这里插入图片描述

为了形象的表达队列,使用了管道进行表示(因为队列只能在队头和队尾进行操作,而它刚好有两端的出口)(在实际队列中是不存在的),在管道中存储的数据是有序的,它按照添加数据的先后来进行排序,并且管道中的操作只能在两端进行,因此说明队列是无法进行中间插入操作,它只能在队尾中进行添加

(三)队列的结构
1、顺序表表示(空队列:head == tail )

在这里插入图片描述

2、单链表表示(head = node tail = node.next)

在这里插入图片描述

(四)队列的方法

在这里插入图片描述
add(Object o):添加一个元素,返回boolean类型的值,如果队列已满,则抛出一个IllegalStateException异常,如果添加的元素为nulll,则抛出NullPointerException异常
offer(Object o):添加一个元素,如果添加成功则返回true,否则返回false,如果添加的元素为nulll,则抛出NullPointerException异常
remove():移除元素(队头),返回的是一个Object类型的数据(移除的数据),如果队列为空,则抛出NoSuchElementException异常
poll():移除元素(队头),返回的是一个Objcet类型的数据(移除的数据),如果队列为空,则返回null
element():返回队头的元素,但是没有移除它,如果队列为空,则抛出NoSuchElementException异常
peek():返回队头的元素,但是没有移除它,如果队列为空,则返回null

(五)使用顺序表实现队列
/**
 * 队列:
 * 增加:在队尾进行
 *查询:根据下标查询
 * 删除:在对头中进行,不能对对头以外的
 * 队列中没有改操作
 */
public class myQueue<T> {
    //使用顺序表
    private Object[] elements;
    private int size;
    private int head;
    private int tail;

    public myQueue(int space){
       elements = new Object[space+1];
       size = 0;
       head = 0;
       tail = 0;
    }
    //添加元素
    public boolean add(T data){
        checkPositionTail(tail);
        //队尾不能小于队头
        checkQueue();
        //队列已满,抛出IllegalStateException异常
        if (tail == elements.length-1) throw new IllegalStateException("队列已满"+tail);
        //如果添加的元素为null,则抛出NullPointerException异常
        if (data == null) throw new NullPointerException("队列中不允许出现null元素");
        //向数组中添加元素
        elements[tail] = data;
        tail++;
        size++;
        return true;
    }

    public boolean offer(T data){
        //队尾不能小于队头
        checkQueue();
        //如果添加的元素为null,则抛出NullPointerException异常
        if (data == null) throw new NullPointerException("队列中不允许出现null元素");
        //如果下标不符合则返回false(添加不成功),添加成功返回true
        if (tail < 0 || tail >= elements.length-1){
            return false;
        }else{
            elements[tail] = data;
            tail++;
            size++;
            return true;
        }
    }

    //判断是否溢出
    private void checkPositionTail(int tail) {
        if (tail < 0 || tail>= elements.length) throw new IndexOutOfBoundsException("溢出");
    }
    //队头和队尾的判断  (这句有没有都没关系,因为如果队列为空时抛出异常结束了任务)
    private void checkQueue(){
        //队尾不能小于队头
        if (tail < head){
            throw new IndexOutOfBoundsException("不符合队列规则");
        }
    }
    //移除元素
    public T remove(){
        checkQueue();
        checkPositionTail(tail);
        //如果队列为空,则抛出NoSuchElementException异常
        if (head == tail) throw new NoSuchElementException("队列为空");
        T data = (T) elements[head];  //得到队头的数据
        elements[head] = null;
        head++;  //改变队头
        size--;
        return data;
    }

    public T poll(){
        checkQueue();
        checkPositionTail(tail);
        //如果队列为空,则返回null
        if (head == tail){
            return null;
        }else{
            T data = (T) elements[head];  //得到队头的数据
            elements[head] = null;
            head++;  //改变队头
            size--;
            return data;
        }

    }

    //查询
    public T element(){
        checkQueue();
        checkPositionTail(tail);
        //如果队列为空,则抛出NoSuchElementException异常
        if (tail == head) throw new NoSuchElementException("队列为空");
        return (T) elements[head];
    }

    public T peek(){
        checkQueue();
        checkPositionTail(tail);
        if (tail == head) return null;
        else
            return (T) elements[head];
    }

    public static void main(String[] args) {
        myQueue q = new myQueue(5);
        for (int i = 0; i < 5; i++) {
            q.offer(i);
            //q.add(i);
        }
        //q.add(6);
        //System.out.println(q.offer(6));
        // q.remove();
        q.poll();
        System.out.println("队头元素"+q.peek());
        int size = q.size; //不能直接使用q.size放到for循环中判断,因为每次判断时都调用q.size,而q.size是动态变化的(poll()中size--)
        for (int i = 0; i < size; i++) {
            q.poll();
        }
        System.out.println("队头元素"+q.element());
    }
}

运行结果截图:
在这里插入图片描述

(六)使用链式表实现队列
import java.util.NoSuchElementException;

//使用单链表实现队列
public class myLinkedQueue<T> {
    private int size;
    private Node headNode;
    private Node head;
    private Node tail;
    public myLinkedQueue(){
        int size = 0;
    }
    //添加数据
    public boolean offer(T data){
        //如果队列为空,则抛出NoSuchElementException异常
        if (data == null) throw new NoSuchElementException("添加的元素不能为null");
        Node newNode = new Node();  //创建新的节点
        newNode.data = data;
        newNode.next = tail;
        if (size == 0){
            head = newNode;
        }else{
            headNode.next = newNode;
        }
        headNode = newNode;  //记录添加的新节点
        size++;
        return true;
    }

    //移除元素
    public T poll(){
        if (head == tail){
            return null;
        }else{
            Node target = head;
            head = head.next;
            T data = target.data;
            target.next = null;
            target.data = null;  //gc
            size--;
            return data;
        }
    }

    //获取头元素
    public T element(){
      //如果队列为空,则抛出NoSuchElementException异常
      if (head == tail) throw new NoSuchElementException("队列为空");
      return head.data;
    }

    //返回节点的数目
    public int size(){
        return size;
    }

    //节点类
    class Node{
        private Node next;
        private T data;
    }

    public static void main(String[] args) {
        myLinkedQueue q = new myLinkedQueue();
        for (int i = 0; i < 5; i++) {
            q.offer(i);
        }
        System.out.println(q.element());
        while (q.head!=q.tail){
            q.poll();
        }
        System.out.println(q.element());
    }
}

运行结果截图:
在这里插入图片描述

问题小答:

Q:在使用顺序表实现队列中为什么初始化数组大小的时候要加1?
R:在使用队列中,我们传入的int参数表示我们希望在队列中存储的个数,而队列中队尾始终是指向一个空的数组空间,如果我们没有加1的话,我们储存数据的空间就等于传入的参数减1
Q:为什么队尾要始终指向null?
R:我们知道判断一个队列是否为空的语句是head ?= tail,如果队尾没有指向null的话,等到head ==tail理论上我们的队列为空了,但是实际上我们还存储一个元素,所以队尾要始终指向null
Q:在使用顺序表实现队列时,初始化的空间大小是5,开始时队列填满了,我们移除2个元素,理论上我们还可以继续添加2个元素,那么为什么抛出IndexOutOfBoundsException异常?
R:通过上面顺序表实现队列的代码我们可以知道,队列中我们移除元素的操作实际是将队头的指向往下移,队尾始终是不变的(在这个始终指向队列最后的空间),如果再添加元素时,那么队尾将指向下一个空间,可是这时它已经指向的是这个数组空间的最大值了,如果再下移的话那么就会抛出IndexOutOfBoundsException异常,这种现象称为"假溢出"
Q:我们有什么方法解决这种"假溢出"的现象吗?
R:我们可以使用循环队列,什么是循环队列?请看下一次分析
Q:在上面使用单链表实现的队列中会出现"假溢出"这种现象吗?
R:不会,因为"假溢出"出现的原因是存储空间的不足的假现,而在链表中存储空间是动态,它根据队列的元素变化而变化

以上是对单向队列的分析及实现,如果对你有帮助的话点个赞👍哦,谢谢(*^_^*)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值