数据结构与算法——Java实现 19.队列

目录

一、概述

二、链表实现队列

接口定义

 接口实现类

测试类 

三、环形数组实现队列

优点

下标计算

判满和判空

判满

判空

辅助变量size判空和判满

方法1

 接口定义

接口实现类

测试类 

方式2

接口定义

接口实现类

测试类

方法3

 接口定义

接口实现类

测试类 


生活鲜少给人留下退路

                        —— 24.9.27

一、概述

计算机科学中,queue 是以顺序的方式维护的一组数据集合,在一端添加数据,从另一端移除数据。习惯来说,添加的一端称为尾,移除的一端称为头,就如同生活中的排队买商品

二、链表实现队列

下面以单向环形带哨兵链表方式来实现队列

接口定义

offer:向队列尾部插入值

poll:从队列头部获取值,并移除

peek:从队列头获取值,不移除

isEmpty:检查队列是否为空

isFull:检查队列是否为满

public interface Queue<E> {
    /*
        向队列尾部插入值
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    boolean offer(E e);

    /*
        从队列头获取值,并移除
        Returns:如果队列非空返回头部值,否则返回null
     */
    E poll();

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    E peek();

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
     */
    boolean isEmpty();

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
     */
    boolean isFull();
}

 接口实现类

import java.util.Iterator;

// 用泛型好处:1.用null代表特殊值   2.代表更多类型
public class LinkedListQueue<E>
        implements Queue<E>,Iterable<E> {

    // 静态内部结点类
    private static class Node<E>{
        E value;
        Node<E> next;

        public Node(E value, Node<E> next){
            this.value = value;
            this.next = next;
        }
    }

    // 定义队列的头结点和尾节点
    Node<E> head = new Node<>(null,null);
    Node<E> tail = head;
    private int size = 0; // 当前节点数
    private int capacity = 10; // 队列最大容量

    public LinkedListQueue(int capacity) {
        this.capacity = capacity;
        tail.next = head;
    }

    public LinkedListQueue() {
        tail.next = head;
    }

    /*
        队列插入方法,在尾部插入
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    @Override
    public boolean offer(E e) {
        if(isFull()){
            return false;
        }
        Node<E> newNode = new Node<>(e,head);
        tail.next = newNode;
        tail = newNode;
        size++;
        return true;
    }

    /*
        从队头获取值,并移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    @Override
    public E poll() {
        if (isEmpty()){
            return null;
        }
        Node<E> first = head.next;
        head.next = first.next;
        if (first == tail){
            tail = head;
        }
        size--;
        return first.value;
    }

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
    */
    @Override
    public E peek() {
        if(isEmpty()){
            return null;
        }
        return head.next.value;
    }

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
    */
    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
    */
    @Override
    public boolean isFull() {
        return size == capacity;
    }

    // 队列遍历方法 迭代器
    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            Node<E> current = head.next;
            @Override
            public boolean hasNext() {
                return current != head;
            }

            @Override
            public E next() {
                E value = current.value;
                current = current.next;
                return value;
            }
        };
    }
}

测试类 

import org.junit.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

public class TestLinkedListQueue {
    // 向队列尾部插入值
    @Test
    public void offer() {
        LinkedListQueue<Integer> queue = new LinkedListQueue<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(queue,List.of(1,2,3,4,5));
    }

    // 从队列头获取值,不移除
    @Test
    public void peek(){
        LinkedListQueue<Integer> queue = new LinkedListQueue<>();
        assertNull(queue.peek());
        queue.offer(1);
        assertEquals(1,queue.peek());
        queue.offer(2);
        assertEquals(1,queue.peek());
        queue.offer(3);
        assertEquals(1,queue.peek());
    }

    // 从队头获取值,并移除
    @Test
    public void poll() {
        LinkedListQueue<Integer> queue = new LinkedListQueue<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);

        assertEquals(1,queue.poll());
        assertEquals(2,queue.poll());
        assertEquals(3,queue.poll());
        assertEquals(4,queue.poll());
        assertEquals(5,queue.poll());
        assertNull(queue.poll());
    }

    // 向有限队列头部加入元素
    @Test
    public void offerLimit(){
        LinkedListQueue<Integer> queue = new LinkedListQueue<>(3);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(queue,List.of(1,2,3));
    }

}

三、环形数组实现队列

环形数组:

        大小固定,首尾相连的数组

        环形数组是一种逻辑上形成环形结构的数组,它在物理上通常是一个普通的线性数组,但在访问和操作时采用特定的逻辑来处理边界条件,使得元素可以从数组的末尾“循环”到开头,或者从开头“循环”到末尾。这种结构常用于实现循环队列、滑动窗口和约瑟夫环等问题,因为它避免了传统数组的越界问题,并且具有空间效率高、适合FIFO(先进先出)操作等优点。

环形数组的特点

① 逻辑环形:环形数组在逻辑上形成一个闭环,数组的最后一个元素与第一个元素相连。

② 无边界问题:由于索引是循环的,因此不存在传统数组的越界问题。

③ 空间效率高:环形数组可以充分利用数组空间,避免不必要的空间浪费。

④ 适合特定操作:如循环队列、滑动窗口等,这些操作在环形数组上实现起来更加高效。

环形数组的应用

① 循环队列:队列是一种先进先出(FIFO)的数据结构,而循环队列则是使用环形数组来实现的一种队列。在循环队列中,当队列的尾部达到数组的末尾时,下一个元素将插入到数组的开头,从而形成一个循环。

② 滑动窗口:滑动窗口是一种常用于数组/字符串处理的算法技巧,它可以在线性时间内解决一些看似需要嵌套循环的问题。环形数组可以用于实现滑动窗口的某些变体,特别是当窗口大小固定且需要循环移动时。

③ 约瑟夫环问题:约瑟夫环问题是一个著名的数学问题,描述的是n个人围成一圈,从第一个人开始报数,报到m的人出圈,然后从下一个人重新开始报数,如此循环直到所有人出圈。环形数组可以很好地模拟这个问题中的环形结构。

优点

        1.对比普通数组,起点和终点更为自由,不用考虑数据移动

        2.“环“意味着不会存在【越界】问题

        3.数组性能更佳

        4.环形数组比较适合实现有界队列、RingBuffer 等

下标计算

索引位置 =  (cur + step) % length

cur:当前指针位置

step:前进步数

length:数组长度

判满和判空

判满

(尾指针+1)% 数组长度 = 头指针

判空

头指针 = 尾指针

辅助变量size判空和判满

方法1

使用头尾指针判断队列空和满的情况

 接口定义

public interface Queue<E> {
    /*
        向队列尾部插入值
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    boolean offer(E e);

    /*
        从队列头获取值,并移除
        Returns:如果队列非空返回头部值,否则返回null
     */
    E poll();

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    E peek();

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
     */
    boolean isEmpty();

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
     */
    boolean isFull();
}

接口实现类

import java.util.Iterator;

public class ArrayQueue1<E> implements Queue<E>,Iterable<E> {
    private final E[] array;
    private int head = 0;
    private int tail = 0;

    public ArrayQueue1(int capacity) {
        array = (E[])new Object[capacity+1];
    }

    // 从队列尾部加入元素
    @Override
    public boolean offer(E e) {
        if (isFull()){
            return false;
        }
        array[tail] = e;
        tail = (tail + 1) % array.length;
        return true;
    }

    // 从队列头部移除元素
    @Override
    public E poll() {
        if (isEmpty()){
            return null;
        }
        E value = array[head];
        head = (head + 1) % array.length;
        return value;
    }

    // 获取队列头部元素值
    @Override
    public E peek() {
        if (isEmpty()){
            return null;
        }
        return array[head];
    }

    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    @Override
    public boolean isFull() {
        return (tail+1) % array.length == head;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int cursor = head;

            @Override
            public boolean hasNext() {
                return cursor != tail;
            }

            @Override
            public E next() {
                E value = array[cursor];
                cursor = (cursor + 1) % array.length;
                return value;
            }
        };
    }
}

测试类 

import org.junit.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;

public class TestArrayQueue1 {
    // 向队列尾部插入值
    @Test
    public void offer() {
        ArrayQueue1<Integer> queue = new ArrayQueue1<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(queue, List.of(1,2,3,4,5));
    }

    // 从队列头获取值,不移除
    @Test
    public void peek(){
        ArrayQueue1<Integer> queue = new ArrayQueue1<>(5);
        assertNull(queue.peek());
        queue.offer(1);
        assertEquals(1,queue.peek());
        queue.offer(2);
        assertEquals(1,queue.peek());
        queue.offer(3);
        assertEquals(1,queue.peek());
    }

    // 从队头获取值,并移除
    @Test
    public void poll() {
        ArrayQueue1<Integer> queue = new ArrayQueue1<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);

        assertEquals(1,queue.poll());
        assertEquals(2,queue.poll());
        assertEquals(3,queue.poll());
        assertEquals(4,queue.poll());
        assertEquals(5,queue.poll());
        assertNull(queue.poll());
    }

    // 向有限队列头部加入元素
    @Test
    public void offerLimit(){
        ArrayQueue1<Integer> queue = new ArrayQueue1<>(3);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(queue,List.of(1,2,3));
    }
}

方式2

引入一个辅助变量size,记录队列中的元素个数,直接通过用数组长度和size变量比较,判断队列空还是满。

接口定义

public interface Queue<E> {
    /*
        向队列尾部插入值
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    boolean offer(E e);

    /*
        从队列头获取值,并移除
        Returns:如果队列非空返回头部值,否则返回null
     */
    E poll();

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    E peek();

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
     */
    boolean isEmpty();

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
     */
    boolean isFull();
}

接口实现类

import java.util.Iterator;

public class ArrayQueue2<E> implements Queue<E>,Iterable<E> {
    private final E[] array;
    private int head = 0;
    private int tail = 0;
    private int size = 0;

    public ArrayQueue2(int capacity) {
        array = (E[]) new Object[capacity];
    }

    // 从队列尾部加入元素
    @Override
    public boolean offer(E e) {
        if (isFull()){
            return false;
        }
        array[tail] = e;
        tail = (tail + 1) % array.length;
        size++;
        return true;
    }

    // 从队列头部移除元素
    @Override
    public E poll() {
        if (isEmpty()){
            return null;
        }
        E value = array[head];
        head = (head + 1) % array.length;
        size--;
        return value;
    }

    // 获取队列头部元素值
    @Override
    public E peek() {
        if (isEmpty()){
            return null;
        }
        return array[head];
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean isFull() {
        return size == array.length;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int cursor = head;
            int count = 0;

            @Override
            public boolean hasNext() {
                return count < size;
            }

            @Override
            public E next() {
                E value = array[cursor];
                cursor = (cursor + 1) % array.length;
                count++;
                return value;
            }
        };
    }
}

测试类

import org.junit.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;

public class TestArrayQueue2 {
    // 向队列尾部插入值
    @Test
    public void offer() {
        ArrayQueue2<Integer> queue = new ArrayQueue2<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(queue, List.of(1,2,3,4,5));
    }

    // 从队列头获取值,不移除
    @Test
    public void peek(){
        ArrayQueue2<Integer> queue = new ArrayQueue2<>(5);
        assertNull(queue.peek());
        queue.offer(1);
        assertEquals(1,queue.peek());
        queue.offer(2);
        assertEquals(1,queue.peek());
        queue.offer(3);
        assertEquals(1,queue.peek());
    }

    // 从队头获取值,并移除
    @Test
    public void poll() {
        ArrayQueue2<Integer> queue = new ArrayQueue2<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);

        assertEquals(1,queue.poll());
        assertEquals(2,queue.poll());
        assertEquals(3,queue.poll());
        assertEquals(4,queue.poll());
        assertEquals(5,queue.poll());
        assertNull(queue.poll());
    }

    // 向有限队列头部加入元素
    @Test
    public void offerLimit(){
        ArrayQueue2<Integer> queue = new ArrayQueue2<>(3);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(queue,List.of(1,2,3));
    }
}

方法3

head和tail直接存储指针值,tail存储最后一个元素的指针值,计算tail存储指针值指向的索引,tail并不直接指向索引不把计算结果存入head和tail中

 接口定义

public interface Queue<E> {
    /*
        向队列尾部插入值
        Params:value-待插入值
        Returns:插入成功返回true,插入失败返回false
     */
    boolean offer(E e);

    /*
        从队列头获取值,并移除
        Returns:如果队列非空返回头部值,否则返回null
     */
    E poll();

    /*
        从队列头获取值,不移除
        Returns:如果队列非空返回队头值,否则返回null
     */
    E peek();

    /*
        检查队列是否为空
        Return:空返回true,否则返回false
     */
    boolean isEmpty();

    /*
        检查队列是否为满
        Return:满返回true,否则返回false
     */
    boolean isFull();
}

接口实现类

import java.util.Iterator;

public class ArrayQueue3<E> implements Queue<E>,Iterable<E> {
    private final E[] array;
    // head tail是两个不断递增的整数
    private int head = 0;
    private int tail = 0;

    @SuppressWarnings("all")
    public ArrayQueue3(int capacity) {
        // 初始化数组
        array = (E[]) new Object[capacity];
    }

    // 从队列尾部加入元素
    @Override
    public boolean offer(E e) {
        // 判满
        if(isFull()){
            return false;
        }
        array[tail % array.length] = e;
        tail ++;
        return true;
    }
    
    // 从队列头部移除元素
    @Override
    public E poll() {
        if (isEmpty()){
            return null;
        }
        // 找到索引位置
        E e = array[head % array.length];
        head ++;
        return e;
    }

    // 获取队列头部元素值
    @Override
    public E peek() {
        if (isEmpty()){
            return null;
        }
        return array[head % array.length];
    }

    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    @Override
    public boolean isFull() {
        return tail - head == array.length;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int cursor = head;
            @Override
            public boolean hasNext() {
                return cursor != tail;
            }

            @Override
            public E next() {
                E e = array[cursor % array.length];
                cursor++;
                return e;
            }
        };
    }
}

测试类 

import org.junit.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;

public class TestArrayQueue3 {
    // 向队列尾部插入值
    @Test
    public void offer() {
        ArrayQueue3<Integer> queue = new ArrayQueue3<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(List.of(1, 2, 3, 4, 5), queue);
    }

    // 从队列头获取值,不移除
    @Test
    public void peek(){
        ArrayQueue3<Integer> queue = new ArrayQueue3<>(5);
        assertNull(queue.peek());
        queue.offer(1);
        assertEquals(1,queue.peek());
        queue.offer(2);
        assertEquals(1,queue.peek());
        queue.offer(3);
        assertEquals(1,queue.peek());
    }

    // 从队头获取值,并移除
    @Test
    public void poll() {
        ArrayQueue3<Integer> queue = new ArrayQueue3<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);

        assertEquals(1,queue.poll());
        assertEquals(2,queue.poll());
        assertEquals(3,queue.poll());
        assertEquals(4,queue.poll());
        assertEquals(5,queue.poll());
        assertNull(queue.poll());
    }

    // 向有限队列头部加入元素
    @Test
    public void offerLimit(){
        ArrayQueue3<Integer> queue = new ArrayQueue3<>(3);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        assertIterableEquals(List.of(1, 2, 3), queue);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值