数组模拟循环列表的一种实现方法

数组模拟环形队列

前提条件:

front rear是角标值,从零开始,front=0,代表指向第一个数据。在一开始为空的时候,都指向同一个位置,都是0;

maxSzie = 真实数据长度 + 预留的一个空间。

最大容量顾名思义,

当到达满的时候,maxSzie指向 预留空间位置。

一.思路

1.分析

1.1

和上一篇内容不一样的是,front和rear的 指向内容需要做调整。

front 指向队列的__第一个元素_,不再是队列第一个元素之前的那个。front的初始值设置为:0

rear指向队列的最后一个元素的后一位,不再是指向队列的最后一个元素。rear的初始值设置为0

1.2 问题思考

队列满条件:(rear+1)%maxSize==front

队列空条件:front == rear

解析:队列满的条件为什么是这个?

rear +1 代表的是 队列放入的数据数量。

rear+1 可以这么理解: 一开始rear 和front的初始值为零,然后自增1 ,就代表队列里有一个数据。然后假设我一直往里面加数据,,(rear+1)%maxSize 代表我往里面加的数据是不是 maxSize的倍数,当你第一次 模的值为零,就代表这个队列满了。

再考虑我取数据的情况,我取一个数据,那么肯定front = front+1;等等,我取数据又不会导致队列满,只会释放空间,导致队列空 。极端点,一个已经满的 容量为 3 的队列,连续取了三次,我再往里面加,那么这个时候就是 front=3,rear=3;此时已经空了。

时刻记住front和rear指向的是哪里。

考虑一波这样的情况,容量maxSxize为 3 的队列,加两个数据,取一个数据,此时肯定是空,看看它是否逻辑自洽。 1 2 3

​ front == rear==0 是否为空? 是否满?

第一次加数据:0 1 否 否 1+1%3 !=0 发现公式没问题

第二次加数据 0 2 否 否 3%3 ==0 根据公式,此时应该已经满了?

预留了一个数据?

所以三个空间位置 ,我放两个就称之为 满了?

暂时只能这么理解了。

所以就是 ,为了让 (rear +1) %maxSize 这个东西是正确的多列满公式,rear的最大值其实是maxSize -1,这个队列的最大值其实是maxSize-1 .

1. 解析:队列空的条件为什么这么写

如果rear!= font,则 必然有空间存数据,就不是空的了,所以相等一定是空。

再在大脑里想象 一个队列。根据上面规定的 font 和rear 的指向位置,如果一开始不指向同一个位置,那肯定就是队列里有数据了,rear +1 了。

2. 解析:为什么 初始值都是 0?不再是-1?

不知道,目前的理解是 可以是-1,但是为了方便理解,采用 0

这样 0 == 0,也满足一开始队列为空的情况。

2.解析 为什么要预留一位?

答案:rear一直是自增,所以rear是有可能大于maxSize,%rear可以获得rear在环形队列上的真实位置。假设maxSize ==5,但是rear自增到10,此时rear 的实际位置是0

rear+1是预留一个位置,不牺牲这个空间会导致无法判断队列空还是满。

3.解析 为什么有效个数的公式是?

(rear-font+maxSize )%maxSize

这个其实是因为环形队列中,rear是有可能小于 font的,所以其实上面的公式类似与**|rear-font|%maxSize

原公式可以确保是一个正数模别人。

在这里插入图片描述

rear = maxSize -1

rear +1= maxiSize

这样就可以解释为什么 rear 是指向队列的最后一个元素的后一位,因为队列默认留一位来区分空还是满。

package com.lzp.datastructure.study.Day2Queue;

import java.util.Scanner;

/**
 * @author  lzp
 * 循环队列的实现
 */
public class XunHuanArrayQueue {
    public static void main(String[] args) {
        System.out.println("测试数组模拟环形队列");
        CircleArray arrayQueue = new CircleArray(4);
        char key = ' ';//接受用户输入
        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);//接受一个字符
            switch (key){
                case 's':
                    arrayQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("请输入一个数字");
                    int  value = scanner.nextInt();
                    arrayQueue.addQueen(value);
                    arrayQueue.showQueue();
                    break;
                case 'g' ://去除数据
                    try {
                        int res = arrayQueue.getQueen();
                        System.out.printf("取出的数据是%d\n",res);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int res = arrayQueue.hearQueue();
                        System.out.printf("队列头的数据是%d\n",res);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e'://退出程序
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("成功退出程序");
        //根据测试,发现预留空间的角标是变化的。
    }
}

class  CircleArray{
    private int maxSize; //循环队列最大值
    private int front;    //指向队列第一个元素,默认值零
    private int rear;     //指向队列最后一个元素的最后一位,默认为零。
    private int[] arr;  //该数组 用于存放数据,模拟队列

    public CircleArray(int arrMaxSize){
        maxSize =arrMaxSize;
        arr = new int[maxSize];
    }

    //判断队列是否满
    public boolean isFull(){
       return  (rear+1)%maxSize == front;
    }

    //判断队列是否为空
    public boolean isEmpty(){
        return front==rear;
    }

    //添加数据
    public void addQueen(int n){// n是你传入的一个 int型数据。
        //判断队列是否满
        if(isFull()){
            System.out.println("队列满了,不可以加入数据");
            return;
        }
        //rear指向最后一位的后一位,所以可以直接往里面放,不用arr【rear+1】
         arr[rear] = n;
        //这里的rear肯定是要自增1的,两种情况,第一种rear自增后小于maxSize,第二种自增后大于等于maxSize
        //因而需要取模 取模是为了循环数组下标
        rear = (rear +1)%maxSize;   //自增后的队列 rear=0,   (0+1)% 8 = 1,自增后有效数据为1.
    }

    //取出数据
    public int getQueen(){
        //判断队列是否为空
        if(isEmpty()){
            //抛出异常
            throw new RuntimeException("队列已经空,无法取到数据");//执行到这行,throw 就会return,throw下面的代码就已经无法执行了。
        }
       //1.取数据需要front自增,一般来说 取 arr【front】,但是你front后面还要自增,自增又需要考虑你这个front是不是指向数组最后一个元素
        //2.因为front需要取模,所以直接先把front的值赋给一个变量返回出去。
        int index = front;
        front= (front +1 )%maxSize; //% 代表某种意义上的循环 类比上面的 rear的自增。 取模是为了防止角标越界
        return arr[index];
    }

    //显示队列的所有数据
    public void showQueue(){
        //遍历
        if(isEmpty()){
            System.out.println("队列空的,没有数据");
            return;
        }
        System.out.println("输出队列");
      //从front开始遍历,遍历多少元素 ?遍历 有效 个元素\
        // int c =  (rear - front + maxSize) % maxSize;
        for (int i = front; i < front + realSize(); i++) {
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
        }
    }

    //求出当前数据有效值个数
    public int realSize(){
        return  (rear - front + maxSize) % maxSize;
    }


    // 显示队列的头数据  不是取数据  因为这是队列,所以头数据有特殊含义,因为队列取数据永远都是从取头数据的。
    public int hearQueue(){
        //判断
        if(isEmpty()){
            throw new  RuntimeException("队列为空");
        }
        return arr[front];
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值