【数据结构】Java实现队列与循环队列

目录

1. 概念

 2. 队列的使用  

3. 自己动手实现队列

3.1 MyQueue接口

3.2 LinkedQueue类

3.3 入队列

3.4 出队列

3.5 获取队头元素

3.6 获取队列中有效元素个数与检测队列是否为空

3.7 toString方法

4. 整体实现

4.1 LinkedQueue类

4.2 Test类

4.3 测试结果

5. 循环队列

 6. 实现循环队列

 6.1 LoopQueue类

6.2 判断队列是否为满与是否为空&获取队列中有效元素的个数

6.3 入队列

6.4 出队列

6.5 获取队头元素

6.6 toString方法

7. 整体实现循环队列

7.1 LoopQueue类

7.2 Test类

 7.3 测试结果

8. 双端队列 (Deque)

8.1 双端队列的线性实现

8.2 双端队列的链式实现


1. 概念

队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)。

入队列:进行插入操作的一端称为队尾(Tail/Rear)

出队列:进行删除操作的一端称为队头(Head/Front)

 2. 队列的使用  

在Java中,Queue是个接口,底层是通过链表实现的

【注意】Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

public static void main(String[] args) {
        Queue<Integer> q = new LinkedList<>();
        q.offer(1);
        q.offer(2);
        q.offer(3);
        q.offer(4);
        q.offer(5); // 从队尾入队列
        System.out.println(q.size());
        System.out.println(q.peek()); // 获取队头元素
        q.poll();
        System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
        if(q.isEmpty()){
            System.out.println("队列空");
        }else{
            System.out.println(q.size());
        }
    }

3. 自己动手实现队列

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 和 链式结构。思考下:队列的实现使用顺序结构还是链式结构好

3.1 MyQueue接口

public interface MyQueue<E> {
    void offer( E val);
    E pop();
    E peek();
    boolean isEmpy();
    int size();
}

3.2 LinkedQueue类

public class LinkedQueue<E> implements MyQueue<E> {
    private Node head;//头节点
    private Node tail;//尾节点

    private int size; // 车厢节点个数,保存的元素个数

    //车厢类的定义,车厢作为火车的内部类,对外部完全隐藏
    private class Node {
        E val;//保存的元素
        Node next;//下节车厢的位置
        Node(E val) {
            this.val = val;
        }
    }
}

3.3 入队列

public void offer(E val) {
        Node node = new Node(val);
        if (size == 0){
            head = node;
            tail = node;
        }
        tail.next = node;
        tail = node;
        size ++;
    }

3.4 出队列

public E pop() {
        if(size == 0){
            throw new IllegalArgumentException("Queue is empty,cannot pop!");
        }
        Node node = head;
        head = head.next;
        size --;
        return node.val;
    }

3.5 获取队头元素

public E peek() {
        if(size == 0){
            throw new IllegalArgumentException("jjj");
        }
        return head.val;
    }

3.6 获取队列中有效元素个数与检测队列是否为空

public boolean isEmpy() {
        return size == 0;
    }

    public int size() {
        return size;
    }

3.7 toString方法

public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("head [");
        for (Node x = head; x != null; x = x.next) {
            sb.append(x.val);
            if(x.next != null){
                sb.append("->");
            }
        }
        sb.append("] tail");
        return sb.toString();
    }

4. 整体实现

4.1 LinkedQueue类

import MyQueue;
public class LinkedQueue<E> implements MyQueue<E> {
    private Node head;//头节点
    private Node tail;

    private int size; // 车厢节点个数,保存的元素个数

    //车厢类的定义,车厢作为火车的内部类,对外部完全隐藏
    private class Node {
        E val;//保存的元素
        Node next;//下节车厢的位置
        Node(E val) {
            this.val = val;
        }
    }

    @Override
    public void offer(E val) {
        Node node = new Node(val);
        if (size == 0){
            head = node;
            tail = node;

        }
        tail.next = node;
        tail = node;
        size ++;

    }

    @Override
    public E pop() {
        if(size == 0){
            throw new IllegalArgumentException("jjj");
        }
        Node node = head;
        head = head.next;
        size --;
        return node.val;
    }

    @Override
    public E peek() {
        if(size == 0){
            throw new IllegalArgumentException("jjj");
        }
        return head.val;
    }

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

    @Override
    public int size() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("head [");
        for (Node x = head; x != null; x = x.next) {
            sb.append(x.val);
            if(x.next != null){
                sb.append("->");
            }
        }
        sb.append("] tail");
        return sb.toString();
    }
}

4.2 Test类

public class LinkedQueueTest {
    public static void main(String[] args) {
        LinkedQueue<Integer> queue = new LinkedQueue<>();
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        System.out.println(queue);
        System.out.println("出队列");
        queue.pop();
        System.out.println(queue);
        System.out.println("查看队头元素");
        System.out.println(queue.peek());
        System.out.println("获取队长以及判断队列是否为空");
        System.out.println(queue.size() + "  " + queue.isEmpy());

    }
}

4.3 测试结果

5. 循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。循环队列通常使用数组实现。

【数组下标循环的小技巧】 

在循环队列的数组中,下标进行取模操作 :

如何区分空与满

1. 通过添加 size 属性记录

2. 保留一个位置

3. 使用标记

 6. 实现循环队列

 6.1 LoopQueue类

public class LoopQueue<E> implements MyQueue<E> {

    private int head;
    private int tail;
    private int size;
//    size = (head - tail + data.length) % data.length;
    Object[] data;

    public LoopQueue() {
         data = new Object[10];
    }
    public LoopQueue(int size) {
         data = new Object[size + 1];
    }
}

6.2 判断队列是否为满与是否为空&获取队列中有效元素的个数

    private boolean isFull(){
        return (tail + 1) % data.length == head;
//        return size == data.length -1;
    }

    @Override
    public boolean isEmpy() {
//        return size == 0;
        return tail ==head;
    }

    @Override
    public int size() {
        return size;
    }

6.3 入队列

public void offer(E val) {
        if (isFull()){
            throw new NoSuchElementException("LoopQueue is full , cannot offer!!!");
        }
        data[tail] = val;
        tail = (tail + 1) % data.length;
        size++;
    }

6.4 出队列

public E pop() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot pop!!!");
        }
        E val = (E) data[head];
        head = (head + 1) % data.length;
        size --;
        return val;
    }

6.5 获取队头元素

public E peek() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot peek!!!");
        }

        return (E) data[head];
    }

6.6 toString方法

public String toString() {
        StringBuilder sb = new StringBuilder();
        int j = head;
        sb.append("fornt [");
        for (int i = 0; i < size; i++) {
            sb.append(data[j]);
            if(i != size - 1){
                sb.append(", ");
            }
            j = (j + 1) % data.length;
        }

        sb.append("] tail");
        return sb.toString();
    }

7. 整体实现循环队列

7.1 LoopQueue类

import queue.MyQueue;

import java.util.NoSuchElementException;

public class LoopQueue<E> implements MyQueue<E> {

    private int head;
    private int tail;
    private int size;

    Object[] data;

    public LoopQueue() {
         data = new Object[10];
    }
    public LoopQueue(int size) {
         data = new Object[size + 1];
    }

    @Override
    public void offer(E val) {
        if (isFull()){
            throw new NoSuchElementException("LoopQueue is full , cannot offer!!!");
        }
        data[tail] = val;
        tail = (tail + 1) % data.length;
        size++;
    }

    @Override
    public E pop() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot pop!!!");
        }
        E val = (E) data[head];
        head = (head + 1) % data.length;
        size --;
        return val;
    }

    @Override
    public E peek() {
        if (isEmpy()){
            throw new IllegalArgumentException("LoopQueue is empty , cannot peek!!!");
        }

        return (E) data[head];
    }
    private boolean isFull(){
        return (tail + 1) % data.length == head;
//        return size == data.length -1;
    }

    @Override
    public boolean isEmpy() {
//        return size == 0;
        return tail ==head;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        int j = head;
        sb.append("fornt [");
        for (int i = 0; i < size; i++) {
            sb.append(data[j]);
            if(i != size - 1){
                sb.append(", ");
            }
            j = (j + 1) % data.length;
        }

        sb.append("] tail");
        return sb.toString();
    }
}

7.2 Test类

public class LoopQueueTest {
    public static void main(String[] args) {
        LoopQueue<Integer> queue = new LoopQueue<>(5);
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        System.out.println(queue);
        System.out.println("出队列");
        queue.pop();
        System.out.println(queue);
        System.out.println("查看队头元素");
        System.out.println(queue.peek());
        System.out.println("两次出队列");
        queue.pop();
        queue.pop();
        System.out.println("查看队头元素");
        System.out.println(queue.peek());
        System.out.println(queue);
        System.out.println("获取队长以及判断队列是否为空");
        System.out.println(queue.size() + "  " + queue.isEmpy());
    }
}

 7.3 测试结果

8. 双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

【注意】Deque是一个接口,使用时必须创建LinkedList的对象。

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

8.1 双端队列的线性实现

public static void main(String[] args) {
        // 可以使用双端队列作为栈使用,API
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(1);
        stack.push(3);
        stack.push(5);
        // 5
        System.out.println(stack.pop());
        // 3
        System.out.println(stack.peek());

    }

8.2 双端队列的链式实现

public static void main(String[] args) {
        // 可以使用双端队列作为栈使用,API
        Deque<Integer> stack = new LinkedList<>();
        stack.offer(1);
        stack.offer(3);
        stack.offer(5);
        // 1
        System.out.println(stack.poll());
        // 3
        System.out.println(stack.peek());
    }

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值