一.队列

数据结构中程序基本可以分为两大类:线性结构非线性结构

  • 线性结构代表:数组、队列、链表和栈;
  • 非线性结构代表:多维数组(包括二维,但是归到这里有些牵强)、树、图、广义表;

(说把数组归到非线性结构有些不妥,但也仅仅只是“有些”;从本质上来说计算机中没有“多维数组”概念 “多维数组”可以理解为数组的数组,但在内存中他们的存储连续的一条“线”;但是从理解的层面来看,“多维数组”与之对应着“二维表、三维表…”,将他们理解成非线性结构也不是不可)

好了,继续聊队列~

队列的特点是先进先出,这个很重要;举一个很恰当的例子,在实际生活中,银行的 “叫号系统”便是队列很好的诠释者 —— —— 按照到银行的顺序依次排号,先来人的优先服务 (出队)

有了思路以后事情便开始简单,在进入实际代码前,不妨先将思路整理为一个简单的图(先默认利用数组实现):

  • ①MaxSize为数组的长度,MaxSize-1为最大可存储的单元;
  • ②front为队列的头元素所在的前一个位置,rear表示尾元素所在的当前位置
  • ③当front = = rear表示队空;当MaxSize-1 = = rear为队满;

    好了,接下来可以写代码了;
import java.util.Scanner;

public class Queue {
    public static void main(String[] args){
      ArrayQueue arrayQueue=new ArrayQueue(5);//4.方便测试所以没有定义太大

      char key=' ';//5.键盘交互,接收用户输入
        Scanner scanner=new Scanner(System.in);

        boolean loop=true;
        while(loop){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):向队列添加元素");
            System.out.println("g(get):向队列取出元素");
            System.out.println("h(head):查看队列的头数据");

            key=scanner.next().charAt(0);
            if (key<97) key+=32; //实现大小写都可判断

            switch (key){
                case 's' :
                    arrayQueue.showQueue();
                    break;

                case 'a':
                   System.out.println("请输入一个数字");
                   arrayQueue.addQueue(scanner.nextInt());
                    break;

                case 'g' :
                    try {
                        System.out.println("您取到的数据为:"+ arrayQueue.getQueue());

                    } catch (Exception e){
                        System.out.println(e.getMessage());
                    }break;


                case 'h' :
                     try {
                         System.out.println("当前的头数据为:"+ arrayQueue.headQueue());

                     } catch (Exception e){
                      System.out.println(e.getMessage());
                    }break;

                case 'e' :
                    scanner.close();//关闭输入
                    loop=false;
                    break;
            }
        }
        System.out.println("**********程序已退出*********");
    }
}

class ArrayQueue{//1.创建一个实现队列的类

    private int MaxSize;    //队列的最大存储容量
    private int [] array;   //要存储队列的数组

    private int front;   //取出时移动的指针,指向队列前的一个位置;
    private int rear;    //存储时移动的指针,指向队列当前存储到的位置(最后一个数据);

    //创建队列
    public ArrayQueue(int arrMaxSize){//2.设置构造器
        MaxSize=arrMaxSize;
        rear=-1;front=-1;
        array=new int[MaxSize];
    }

    //判断队列是否空
    public boolean isEmpty(){//3.一些必要的方法
        return rear==front;
    }

    //判断队列是否满
    public boolean isFull(){
        return rear==MaxSize-1;
    }

    //添加数据
    public void addQueue(int q){
        if(isFull()) System.out.println("队列已满,不可添加数据!");
        else {
            array[++rear]=q;//java可以这么写
            System.out.println("添加数据成功");
        }
    }

    //取出数据
    public int getQueue(){
        if (!isEmpty()){
            return array[++front];
        }
        throw new RuntimeException("队列为空");//没有合适的返回值,用异常

    }

    //显示当前队列所有数据
    public void showQueue(){
        if (isEmpty()) {
            System.out.println("队列为空,无可打印数据");
            return;
        }
        for (int i=0;i<array.length;i++){
            System.out.printf("%d ",array[i]);
        }
        System.out.println();
    }

    //返回当前队列的头数据
    public int headQueue(){
        if (isEmpty()) throw new NumberFormatException("队列为空,无头数据");
        return array[front+1];

    }
}

运行过程来起来也很正常

java 线性计算 java线性和非线性的区别_java 线性计算


java 线性计算 java线性和非线性的区别_System_02

但!

“this.笔者是一个粗笨男生,不明白这“但”字的可怕:许多坏事固然幸亏有了他才变好,许多好事却也因为有了他都弄糟。”

出现了一个问题,继添加和取出后如果继续操作添加,则会出现这样的效果

java 线性计算 java线性和非线性的区别_环形队列_03


很明显,上面的代码并没有将存入的值真正的取走,即使指向它的指针rear移动了,但数据还是在那里;

或许有人会说:“那在取出的时候加一个删除或置零的操作不就行了吗?”

可以这么做,但是没用;即使这样做了,已经被使用的空间依旧不能再次被利用

java 线性计算 java线性和非线性的区别_环形队列_04


java 线性计算 java线性和非线性的区别_java 线性计算_05

所以,为了解决这样的“空间浪费问题”,科学家在原有的基础上想到了改进的方法,这也有了下面的结果—— ——循环队列

二、循环队列

环形队列产生的原因很简单——实现队列的可重复利用性
而实现它的关键的则更简单——取模计算(%)

在此之前,先修改一下之前的设定(当然不修改也可以,算法有很多)

  • front: 指向队列的第一个元素;
  • rear: 指向队列最后一个元素的后一个位置
  • java 线性计算 java线性和非线性的区别_java 线性计算_06

  • 队满的条件:(rear+1)%maxsize == front
  • java 线性计算 java线性和非线性的区别_数据_07

  • 新建方法:
    当前队列中已经使用的空间个数:(rear+maxsize-front)%maxsize;
  • java 线性计算 java线性和非线性的区别_java 线性计算_08

当然,随着front和rear的设定更改,有些细节的地方也要随着对应更改;

  • ①可以看出,随着设定的更改,数组里面保留了一个空间作为约定,这样一来之前设置的5个数的存储额只能存4个,所以,我们再给它加一个:
//创建队列
    public ArrayQueue(int arrMaxSize){//设置构造器
 	  //front与rear默认都是零,不做修改;
  	 MaxSize=arrMaxSize+1;//修改:因为留了一个空间做约定,所以多加一个空间;
  	 array=new int[MaxSize];
   }
  • ②队满的判断条件
//判断队列是否满
	public boolean isFull(){
    return (rear+1)%MaxSize==front;//环形队列核心(一);
}
  • ③添加数据
public void addQueue(int q){
    if(isFull()) System.out.println("队列已满,不可添加数据!");
    else {
        array[rear]=q;//修改这里
        System.out.println("添加数据成功");

        rear=(rear+1)%MaxSize;//环形队列核心(二);
    }
}
  • ④取出数据
public int getQueue(){
    if (!isEmpty()){
        int value=array[front];//如果直接返回会被终止,找一个临时变量存放;
        front=(front+1)%MaxSize;//环形队列核心(三);
        return value;//返回存放;
    }
    throw new RuntimeException("队列为空");//没有合适的返回值,用异常

}
  • ⑤显示队列
public void showQueue(){
    if (isEmpty()) {
        System.out.println("队列为空,无可打印数据");
        return;
    }
    for (int i=front;i<front+SumQueue();i++){//环形队列核心(四);
        System.out.printf("%d ",array[i%MaxSize]);//环形队列核心(五);
    }
    System.out.println();
}

完整代码:

import java.util.Scanner;

public class Queue {
    public static void main(String[] args){
      ArrayQueue arrayQueue=new ArrayQueue(5);//4.方便测试所以没有定义太大

      char key=' ';//5.键盘交互,接收用户输入
        Scanner scanner=new Scanner(System.in);

        boolean loop=true;
        while(loop){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):向队列添加元素");
            System.out.println("g(get):向队列取出元素");
            System.out.println("h(head):查看队列的头数据");

            key=scanner.next().charAt(0);
            if (key<97) key+=32; //实现大小写都可判断

            switch (key){
                case 's' :
                    arrayQueue.showQueue();
                    break;

                case 'a':
                   System.out.println("请输入一个数字");
                   arrayQueue.addQueue(scanner.nextInt());
                    break;

                case 'g' :
                    try {
                        System.out.println("您取到的数据为:"+ arrayQueue.getQueue());

                    } catch (Exception e){
                        System.out.println(e.getMessage());
                    }break;


                case 'h' :
                     try {
                         System.out.println("当前的头数据为:"+ arrayQueue.headQueue());

                     } catch (Exception e){
                      System.out.println(e.getMessage());
                    }break;

                case 'e' :
                    scanner.close();//关闭输入
                    loop=false;
                    break;
            }
        }
        System.out.println("**********程序已退出*********");
    }
}

class ArrayQueue{//1.创建一个实现队列的类

    private int MaxSize;    //队列的最大存储容量
    private int [] array;   //要存储队列的数组

    private int front;   //取出时移动的指针,指向队列当前首元素的位置;
    private int rear;    //存储时移动的指针,指向队列最后一个数据的后一个位置;

    //创建队列
    public ArrayQueue(int arrMaxSize){//2.设置构造器
        //front与rear默认都是零,不做修改;
        MaxSize=arrMaxSize+1;//修改:因为留了一个空间做约定,所以多加一个空间;
        array=new int[MaxSize];
    }

    //判断队列是否空
    public boolean isEmpty(){//3.一些必要的方法
        return rear==front;
    }
    //判断队列是否满
    public boolean isFull(){
        return (rear+1)%MaxSize==front;//环形队列核心(一);
    }

    //添加数据
    public void addQueue(int q){
        if(isFull()) System.out.println("队列已满,不可添加数据!");
        else {
            array[rear]=q;//修改这里
            System.out.println("添加数据成功");

            rear=(rear+1)%MaxSize;//环形队列核心(二);
        }
    }

    //取出数据
    public int getQueue(){
        if (!isEmpty()){
            int value=array[front];//如果直接返回会被终止,找一个临时变量存放;
            front=(front+1)%MaxSize;//环形队列核心(三);
            return value;//返回存放;
        }
        throw new RuntimeException("队列为空");//没有合适的返回值,用异常

    }

    //显示当前队列所有数据
    public void showQueue(){
        if (isEmpty()) {
            System.out.println("队列为空,无可打印数据");
            return;
        }
        for (int i=front;i<front+SumQueue();i++){//环形队列核心(四);
            System.out.printf("%d ",array[i%MaxSize]);//环形队列核心(五);
        }
        System.out.println();
    }

    //返回当前队列的头数据
    public int headQueue(){
        if (isEmpty()) throw new NumberFormatException("队列为空,无头数据");
        return array[front];

    }

    //新加方法:求出当前队列有效数据个数;
    public int SumQueue(){
        return (rear+MaxSize-front)%MaxSize;
    }

}

好了,这样一来一个队列就写好了,测试一下,可以随便玩啦!

java 线性计算 java线性和非线性的区别_java 线性计算_09