数据结构Java语言描述之循环队列的两种实现方式

1、队列的描述

队列是一种先进先出的存储数据的结构。如我们现实生活中的排队就是一个典型的例子。

2、循环队列及其优点

  2.1、循环队列是队列的扩展,就是队列首尾连接,形成一个闭环的圈子。

  2.2、优点

  充分利用存储空间。

3、队列的实现方式

  3.1、队列与栈的实现方式一样,一般分为两种:线性队列与链式队列。

  3.2、线性队列的描述

 线性队列又可以分为普通队列与循环队列两种。

  3.2.1、普通队列

  以int值1,2,3,4,5分别进行put队列操作如下图所示

                 图一(普通队列)

此时,数字1,2,3,4已经将队列给占用满了,若要再put 数字5的话,需要将里面的数字全部get出来,然后再put数字5进去,如下图所示:

        图二(普通队列)

3.2.2、循环队列

  同样以int值1,2,3,4,5为例,分别进行put队列操作如下图所示:

 

        图三(循环队列)

  此时存储1,2,3,4是与图一的普通队列没有区别,区别在于当数据大于队列长度本身的时候,

如下所示:

                   图四(循环队列)

  此时put数字5时与图二不一样,不需要将数据全部将数据取出,由于队列先进先出的性质,

只需get出数字1,腾出一个空间,就可以将数字5 put 队列之中了。

当然,以上的例子只是介绍什么是队列与循环队列的存储结构,

至于为什么说循环队列可以充分利用存储空间,比普通队列节省,请往下看。

3.3、线性循环队列的优势展现

  对于3.2,3.3的描述,有读者会说,可以对队列的存储空间进行扩容,反正数组的索引都是固定的,操作起来效率也是蛮快的,不影响啥的。

  但是无论我们将存储空间扩展的有多么的大,都是有限的,毕竟整个计算机都是有限的东西组装成的。再者研究数据结构与算法就是为了将空间与时间怎样进行到最优化,以此来节省有限的资源(包含时间)。

所以在此假设队列的长度为固定值4,不允许扩容的情况下,同样以1,2,3,4,5数字为例,如下所示:

当讲1,2,3,4数字分别依次put到队列,并get出1,2,3数字的情况下,如下图所示:

                 图五(普通队列)

此时,若我们这时需要将5 put到队列时对于普通队列来说只能进行扩容,但是这样就造成对原来队列空间(数字1,2,3所腾出来)的浪费。

 

                    图六(循环队列)

同样的情况,我们的循环队列可以在不扩容的情况下将数字5 put 到队列中,从而避免空间的浪费。

 

3.4、链式队列

链式队列也可以分为普通链式队列(单链表结构)和循环链式队列(循环单链表结构)。

3.4.1、两种方式的描述

此处是将含数字1,2,3,4,5的节点,依次以队列的方式形成链表,如下所示。

                                    图七(普通链式队列)

与普通链式队列不同的是尾节点(tail)永远指向头节点(header),而不是NULL。

                                                              图八(循环链式队列)

优势对比,由于链式队列是对链表的尾节点(tail)进行插入操作,头节点(header)进行删除处理,所以都是O(1)的时间效率,至于空间上,这两者对于空间的占用也几乎没有什么区别,因此,整体上来说实现哪一种都差不多。

4、代码的实现

4.1、线性队列的实现(循环队列)

package com.queue;

/**
 * 用数组实现队列的操作(循环队列)
 */
public class ArrayQueue {
    //队列的最大长度
    private static final int SIZE = 10;

    //队列的当前长度
    private int length;

    //当前的队列头的下标
    private int header;

    //队列数组
    private int queue[];

    /**
     * 初始化队列数组
     * 队列的当前长度都为-1用来判断队列是否为空!
     * 当前的队列头的下标的下标为0
     */
    public ArrayQueue() {
        queue = new int[SIZE];
        length = -1;
        header = 0;
    }

    /**
     * 添加队列数据
     * @param data 数据
     */
    public void addQueue(int data) throws Exception {

        if(length % SIZE == header) {
            throw new Exception("该队列已满!");
        }

        if(length == -1) {
            length = header;
        }

        //将数据放入队列数组中
        queue[length++] = data;

        if(length == SIZE) {
            length = 0;
        }
    }

    /**
     * 获取队列数据
     */
    public int getQueue() throws Exception {

        if(length == -1) {
            throw new Exception("该栈为已空!");
        }

        //将数据放入队列数组中
        int data = queue[header++];

        if(header == SIZE) {
            header = 0;
        }
        if(header % SIZE == length) {
            length = -1;
        }
        return data;
    }

    /**
     * 清空队列
     */
    public void clearQueue() {
        length = -1;
    }


    /**
     * 判断队列是否为空!
     */
    public boolean isEmpty() {
        return length == -1? true : false;
    }

    /**
     * 判断队列是否已满!
     */
    public boolean isFull() {
        return length % SIZE == header? true : false;
    }

    /**
     * 打印队列
     */
    public void printQueue() {
        if(length == -1) {
            System.out.println("该队列为空!");
        }

        for(int i = header; (i % SIZE)< length; i++) {
            System.out.print(queue[i] + " ");
        }
        System.out.println();
    }
}

4.2、链式队列(也是循环列表为例)

4.2.1、链表节点的实现

package com.node;

/**
 * 单链表节点
 *
 */
public class Node {
    //数据
    public int data;

    //指向下一个节点的引用
    public Node next;

    /**
     * 初始化节点数据
     * @param data 数据
     */
    public Node(int data) {
        this.data = data;
    }
}

4.2.2、链式队列的实现

package com.queue;

import com.node.Node;

/**
 * 使用链式实现队列的操作(循环队列)
 */
public class LinkQueue {

    //队列的头节点
    private Node header = null;

    //队列的尾节点
    private Node tail = null;

    public void putQueue(Node node) {
        if(header == null) {//队列不存在时(头节点就是尾节点,对尾节点进行插入操作)
            header = node;
            tail = node;
            tail.next = header;
        } else {//队列存在时(对尾节点进行插入操作)
            tail.next = node;
            tail = tail.next;
            tail.next = header;
        }
    }

    public int getQueue() throws Exception {
        if(header == null) {
            throw new Exception("该队列为已空!");
        }

        int data = header.data;//取出头节点的值
        if(header.next != header) {//删除头节点
            header = header.next;
            tail.next = header;
        } else {
            //若头节点与尾节点同指向一个节点时,
            // 此时取出该节点的值,并将头结点与尾节点置为空!
            header = null;
            tail = null;
        }
        return data;
    }

    /**
     * 清空队列
     */
    public void clearQueue() {
        header = null;
        tail = null;
    }

    /**
     * 判断队列是否为空!
     */
    public boolean isEmpty() {
        return header == null? true : false;
    }
}

5、测试

5.1、线性队列测试的代码实现

package com.test;

import com.queue.ArrayQueue;

/**
 * 线性循环队列的测试
 */
public class ArrayQueueTest {

    public static void main(String[] args) throws Exception{
        ArrayQueue arrayQueue = new ArrayQueue();

        //put值
        for (int i = 0; i < 10; i++) {
            arrayQueue.addQueue(i);
        }

        //get值
        for (int i = 0; i < 10; i++) {
            System.out.print(arrayQueue.getQueue() + " ");
        }

        System.out.println();
    }
}

测试结果

5.2、链式队列测试的代码实现

package com.test;

import com.node.Node;
import com.queue.LinkQueue;

/**
 * 链式循环队列的测试
 */
public class LinkQueueTest {

    public static void main(String[] args) throws Exception {

        LinkQueue linkQueue = new LinkQueue();

        //put含数字1,2的节点
        linkQueue.putQueue(new Node(1));
        linkQueue.putQueue(new Node(2));

        //get节点中的值
        System.out.println(linkQueue.getQueue());
        System.out.println(linkQueue.getQueue());
//        System.out.println(linkQueue.getQueue());
    }
}

测试结果

6、结束

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值