从零开始手撕一个数据结构(3)——基于数组实现有界队列

前言

这个系列咕了好久啦
今天没事做看了一下王道考研的数据结构,看着看着才想起来我的数据结构系列拖了好久了,上来水一篇比较简单而巧妙的数组实现队列吧~

源码

看完代码有收获的话,别忘了点个Star哦~

有界队列的实现

我们上次用链表实现过队列,用链表可以实现有界或者无界的队列,那么可不可以用数组来实现队列呢,答案是肯定的

思考

在链表中,我们可以使用头尾指针 Object head,tail;来指向链表的头部和尾部,辅助实现队列的出队和入队

而在数组中,我们同样可以使用头尾索引 int head,tail;,在数组elements中,使用elements[head]表示队头,elements[tail]表示队尾

可是,数组的大小是有限的,如果我们用数组实现队列,在我们出队和入队的时候,都需要让head++tail++,这样自增下去,是会数组越界的,怎么办呢?

取模的作用

这时候我们就要发挥取模的作用了,已知任意整数a、b,当a%b时,得到的结果区间只能是 [0, b) 的半闭半开的区间,我们将这个数学知识用在数组上,就可以让headtail两个索引做到循环使用了~

  • 例:当尾索引++tail==capacity,在用elements[tail]的话已经会造成数组越界了(capacity == elements.length;),但是,如果我们将入队操作对尾索引的操作替换为: tail = (tail+1)%capacity;时 ,tail就会重新归为0
  • 总结:在入队时将tail++;操作更换为tail = (tail+1)%capacity;,可在索引即将越界的时候归零,其它时候为正常自增

实现

一、基本字段 & 构造方法

public class ArrayQueue<T> {

    /**
     * 存放队列元素的数组
     */
    Object[] elements;

    /**
     * 头、尾指针
     */
    private int head, tail;


    /**
     * 队列容量与队列当前大小
     */
    private int capacity, size;

    /**
     * 有界队列构造方法
     *
     * @param capacity
     */
    public ArrayQueue(int capacity) {
        this.capacity = capacity;
        this.elements = new Object[capacity];
    }
    
	// ...未完

这里做了详细的注释,不多说了吧

二、出队 & 入队

    /**
     * 出队操作,队列为空时抛出异常
     *
     * @return 队头
     * @throws Exception
     */
    public T pop() throws Exception {
        if (size == 0) {
            throw new Exception("队列为空!");
        }
        // 元素出队时,size记得自减
        --size;
        T res = (T) elements[head];
        elements[head] = null;
        // head+1,并与容量取模
        // 例:capacity为10,head为9+1=10,即将数组越界
        // 将当前头指针索引10与容量10取模,得head=10%10=0(头指针循环)
        head = (head + 1) % capacity;
        return res;
    }

    /**
     * 入队操作,队列满时返回false,入队成功时返回true
     *
     * @param t
     * @return
     */
    public boolean offer(T t) {
        if (size == capacity) {
            return false;
        }
        // 元素入队时,size记得自增
        ++size;
        elements[tail] = t;
        // 尾指针的取模与头指针同理(尾指针循环)
        tail = (tail + 1) % capacity;
        return true;
    }
    // ...未完

这里在入队出队时,对头索引head和尾索引tail的操作用到了之前讲的取模操作,保证了两个索引的循环使用

三、其它方法

    /**
     * 队列为空时返回true
     *
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public String toString() {
        return Arrays.toString(elements);
    }
}

四、测试用main方法

    /**
     * main方法,测试用
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        ArrayQueue<Integer> queue = new ArrayQueue<>(5);
        for (int i = 1; i <= 6; ++i) {
            System.out.println("offer:" + i + "," + queue.offer(i));
        }
        System.out.println(queue.toString());

        for (int i = 1; i <= 3; ++i) {
            System.out.println("pop:" + queue.pop());
        }
        System.out.println(queue.toString());
        for (int i = 1; i <= 2; ++i) {
            System.out.println("offer:" + i + "," + queue.offer(i));
        }
        System.out.println(queue.toString());
        for (int i = 1; i <= 3; ++i) {
            System.out.println("pop:" + queue.pop());
        }
        System.out.println(queue.toString());
        for (int i = 1; i <= 2; ++i) {
            System.out.println("offer:" + i + "," + queue.offer(i));
        }
        System.out.println(queue.toString());

        while (!queue.isEmpty()) {
            System.out.println(queue.pop());
        }
        queue.pop();
    }

输出:

offer:1,true
offer:2,true
offer:3,true
offer:4,true
offer:5,true
offer:6,false [ 队列满,入队时返回false ]
[1, 2, 3, 4, 5]
pop:1
pop:2
pop:3
[null, null, null, 4, 5]
offer:1,true
offer:2,true
[1, 2, null, 4, 5]
pop:4
pop:5
pop:1
[null, 2, null, null, null]
offer:1,true
offer:2,true
[null, 2, 1, 2, null]
2
1
2
Exception in thread “main” java.lang.Exception: 队列为空![ 队列为空,出队时抛出异常 ]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值