数据结构与算法:稀疏数组和队列详解(附LeetCode相关热题)


前言

笔记整理于:【尚硅谷】数据结构与算法(Java数据结构与算法)

数据结构与算法概述

数据结构和算法的关系

  • 数据 data 结构(structure)是一门研究组织数据方式的学科,有了编程语言也就有了数据结构.学好数据结构可以 编写出更加漂亮,更加有效率的代码。
  • 要学习好数据结构就要多多考虑如何将生活中遇到的问题,用程序去实现解决.
  • 程序 = 数据结构 + 算法
  • 数据结构是算法的基础, 换言之,想要学好算法,需要把数据结构学到位。

几个实际编程中遇到的问题

问题一:字符串替换问题

1674225902672

小结:需要使用到单链表数据结构

问题二:五子棋程序问题

1674226019082

如何判断游戏的输赢,并可以完成存盘退出和继续上局的功能

  • 棋盘 二维数组=>(稀疏数组)-> 写入文件【存档功能】

  • 读取文件->稀疏数组->二维数组 -> 棋盘 【接上局】

问题三:约瑟夫(Josephu)问题(丢手帕问题)

  1. Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数 到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止, 由此产生一个出队编号的序列。

  2. 提示:用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表(单向环形链 表),然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点 又从 1 开始计数,直到最后一个结点从链表中删除算法结束。

  3. 小结:完成约瑟夫问题,需要使用到单向环形链表 这个数据结构

其他常见算法问题

1674226271409

  • 修路问题 => 最小生成树(加权值)【数据结构】+ 普利姆算法

  • 最短路径问题 => 图+弗洛伊德算法

  • 汉诺塔 => 分支算法

  • 八皇后问题 => 回溯法

线性结构和非线性结构

数据结构包括:线性结构和非线性结构。

线性结构

  • 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系

  • 线性结构有两种不同的存储结构,即顺序存储结构(数组)和链式存储结构(链表)。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续

  • 链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息

  • 线性结构常见的有:数组、队列、链表和栈

非线性结构

非线性结构包括:二维数组,多维数组,广义表,树结构,图结构

稀疏数组和队列

稀疏 sparsearray 数组

看一个实际需求

编写的五子棋程序中,有存盘退出和续上盘的功能。

分析问题:

1674399037591

因为该二维数组的很多值是默认值 0, 因此记录了很多没有意义的数据.->稀疏数组

基本介绍

当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。

稀疏数组的处理方法是:

  • 记录数组一共有几行几列,有多少个不同的值

  • 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模

稀疏数组举例说明

1674399181984

应用实例

  1. 使用稀疏数组,来保留类似前面的二维数组(棋盘、地图等等)

  2. 把稀疏数组存盘,并且可以从新恢复原来的二维数组数

  3. 整体思路分析

    1674399302273

  4. 代码实现

    package com.atguigu.sparsearray;
    
    @SuppressWarnings("all")
    public class SparseArray {
          
          
    
        public static void main(String[] args){
          
          
            //创建一个原始的二维数组
            //0:表示没有棋子  1 表示 黑子 ,2 表示 蓝子
            int chessArr[][] = new int[11][11];
            chessArr[1][2] = 1;
            chessArr[2][3] = 2;
            System.out.println("原始的二维数组:");
            for (int[] row : chessArr) {
          
          
                for (int data : row) {
          
          
                    System.out.printf("%d\t",data);
                }
                System.out.println();
            }
    
            //将二维数组转为稀疏数组
            //1. 先遍历二维数组,得到非0数据的个数
            int sum = 0;
            int length = chessArr.length;
            for (int i = 0; i <length; i++) {
          
          
                for (int j = 0; j < chessArr[i].length; j++) {
          
          
                    if (chessArr[i][j] != 0){
          
          
                        sum++;
                    }
                }
            }
            //System.out.println("sum = "+sum);
    
            //2. 创建对应的稀疏数组
            int sparseArr[][] = new int[sum+1][3];
            //给稀疏数组赋值
            sparseArr[0][0] = 11;
            sparseArr[0][1] = 11;
            sparseArr[0][2] = sum;
    
            //count 用于记录是第几个非 0 数据
            int count = 0;
            for (int i = 0; i < chessArr.length; i++) {
          
          
                for (int j = 0; j < chessArr[i].length; j++) {
          
          
                    if (chessArr[i][j] != 0){
          
          
                        count++;
                        sparseArr[count][0] = i;
                        sparseArr[count][1] = j;
                        sparseArr[count][2] = chessArr[i][j];
    //                    sparseArr[?][0] = i;
    //                    sparseArr[?][1] = j;
    //                    sparseArr[?][2] = chessArr[i][j];
                    }
                }
            }
            //3. 输出稀疏数组
            System.out.println();
            System.out.println("得到的稀疏数组为:");
            for (int i = 0; i < sparseArr.length; i++) {
          
          
                System.out.printf("%d\t%d\t%d\t\n",sparseArr[i][0],sparseArr[i][1],sparseArr[i][2]);
            }
    
            int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
            for (int i = 1; i < sparseArr.length; i++) {
          
          
                chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
            }
    
            System.out.println();
            System.out.println("恢复后的二维数组:");
    //        for (int i = 0; i < chessArr2.length; i++) {
          
          
    //            for (int j = 0; j < chessArr2[i].length; j++) {
          
          
    //                System.out.printf("%d\t",chessArr2[i][j]);
    //            }
    //            System.out.println();
    //        }
            for (int[] row :chessArr2) {
          
          
                for (int dtat : row) {
          
          
                    System.out.printf("%d\t",dtat);
                }
                System.out.println();
            }
    
        }
    }
    

课后练习

要求

  • 在前面的基础上,将稀疏数组保存到磁盘上,比如 map.data

  • 恢复原来的数组时,读取 map.data 进行恢复

分析

只需要在原来的基础上增加两个方法:

  • saveSparseArrToFile(sparseArr, filePath):把稀疏数组保存到本地文件中
  • getSparseArrFromFile(filePath):读取文件,将读取到的稀疏数组转化为二维数组

通用方法

代码实现

    public static void saveSparseArrToFile(int[][] sparse, String filePath) throws IOException {
    
    
        //写入文件
        FileOutputStream fos = null;
        DataOutputStream dos = null;
        try {
    
    
            fos = new FileOutputStream(new File(filePath));
            dos = new DataOutputStream(fos);
            for (int[] row : sparse) {
    
    
                for (int data : row) {
    
    
                    dos.writeInt(data);
                }
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if (dos != null) {
    
    
                dos.close();
            }

            if (fos != null) {
    
    
                fos.close();
            }
        }
    }

    public static int[][] getSparseArrFromFile(String filePath) throws IOException {
    
    
        int[][] arr = {
    
    };

        FileInputStream fis = null;
        DataInputStream dis = null;

        try {
    
    
            fis = new FileInputStream(new File(filePath));
            dis = new DataInputStream(fis);

            //读取数据
            int row = dis.readInt();
            int column = dis.readInt();
            int count = dis.readInt();
            //稀疏数组第一行有三个数字 分别代表原数组的 有几行  有几列  有多少个非空数据
            arr = new int[row][column];
            //根据count 给 arr赋值
            for (int i = 0; i < count; i++) {
    
    
                arr[dis.readInt()][dis.readInt()] = dis.readInt();
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //关闭流
            if (dis != null) {
    
    
                dis.close();
            }
            if (fis != null) {
    
    
                fis.close();
            }
        }
        return arr;
    }

推荐阅读文章Java IO流之DataInputStream和DataOutputStream分析

只针对稀疏数组

代码实现

public static void saveSparseArrToFile(int[][] sparse, String filePath) throws IOException {
    
    
        //写入文件
        FileOutputStream fos = null;

        try {
    
    
            fos = new FileOutputStream(filePath);
            for (int i = 0; i < sparse.length; i++) {
    
    
                StringBuffer stringBuffer = new StringBuffer();
                stringBuffer.append(sparse[i][0] + "\t" + sparse[i][1] + "\t" + sparse[i][2] + "\n");
                //System.out.println(stringBuffer);
                fos.write(stringBuffer.toString().getBytes("utf-8"));
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            fos.close();
        }
    }

    public static int[][] getSparseArrFromFile(String filePath) throws IOException {
    
    

        int[][] arr = null;
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;

        try {
    
    
            fileReader = new FileReader(filePath);
            bufferedReader = new BufferedReader(fileReader);
            String line = null;
            int countSparse = 0;
            int number = 0;

            while ((line = bufferedReader.readLine()) != null) {
    
    
                //System.out.println(line);
                // 将每一行转为数组
                String[] split = line.split("\t");
                Integer row = Integer.valueOf(split[0].trim());
                Integer col = Integer.valueOf(split[1].trim());
                Integer value = Integer.valueOf(split[2].trim());
                if (countSparse == 0) {
    
    
                    arr = new int[row][col];
                } else {
    
    
                    number++;
                    arr[row][col] = value;
                }
                countSparse++;
            }

        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            bufferedReader.close();
            fileReader.close();
        }
        return arr;
    }
}

基本代码如下(上述两种方法二选一)

package com.atguigu.sparsearray;

import java.io.*;

@SuppressWarnings("all")
public class SparseArrayIO {
    
    

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

        //1. 创建一个原始的二维数组
        int chessArr[][] = new int[11][11];
        chessArr[1][2] = 1;
        chessArr[2][3] = 2;
        System.out.println("原始的二维数组:");
        for (int[] ints : chessArr) {
    
    
            for (int i : ints) {
    
    
                System.out.printf("%d\t", i);
            }
            System.out.println();
        }

        //2. 遍历二维数组,得到二维数组非 0 数据的个数
        int count = nums(chessArr);

        //3. 将二维数组转化为稀疏数组
        int index = 1;
        int sparseArr[][] = new int[count + 1][3];

        //稀疏数组 第 1 行
        sparseArr[0][0] = chessArr.length;
        sparseArr[0][1] = 11;
        sparseArr[0][2] = count;

        //稀疏数组 第二行及以后
        for (int i = 0; i < chessArr.length; i++) {
    
    
            for (int j = 0; j < chessArr[i].length; j++) {
    
    
                if (chessArr[i][j] != 0) {
    
    
                    sparseArr[index][0] = i;
                    sparseArr[index][1] = j;
                    sparseArr[index][2] = chessArr[i][j];
                    index++;
                }
            }
        }

        //打印稀疏数组
        System.out.println("转换后的稀疏数组:\n----------------------------");
        printNums(sparseArr);
        
        //4.将稀疏数组保存到文件中
        String filePath = "E:\\myTemp\\123.txt";
        saveSparseArrToFile(sparseArr, filePath);

        //5.将文件数据读取到稀疏数组中
        int[][] fileSparseArr = getSparseArrFromFile(filePath);
        //打印读取的稀疏数组
        System.out.println("打印读取转换后的二维数组:\n----------------------------");
        printNums(fileSparseArr);
    }

    public static int nums(int[][] num) {
    
    
        int count = 0;
        for (int i = 0; i < num.length; i++) {
    
    
            for (int j = 0; j < num[i].length; j++) {
    
    
                if (num[i][j] != 0) {
    
    
                    count++;
                }
            }
        }
        return count;
    }


    public static void printNums(int[][] nums) {
    
    
        for (int[] ints : nums) {
    
    
            for (int i : ints) {
    
    
                System.out.printf("%d\t", i);
            }
            System.out.println();
        }
    }

队列

队列的一个使用场景

银行排队的案例:

1674399502538

队列介绍

  1. 队列是一个有序列表,可以用数组或是链表来实现。

  2. 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取

  3. 示意图:(使用数组模拟队列示意图)

1674399592978

数组模拟队列思路

  • 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队 列的最大容量。

  • 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标, front 会随着数据输出而改变,而 rear 则是随着数据输入而改变,如图所示:

    1674399739539

  • 当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析

    • 将尾指针往后移:rear+1 , 当 front == rear 【空】

    • 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。 rear == maxSize - 1[队列满]

  • 代码实现

  package com.atguigu.queue;

import java.util.Scanner;

public class ArrayQueueDemo {
    
    

    public static void main(String[] args) {
    
    

        //测试
        //创建一个队列
        ArrayQueue arrayQueue = new ArrayQueue(3);
        char key = ' '; //接受用户输入
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;

        //输出一个菜单
        while(loop){
    
    
            System.out.println("---------数组模拟队列--------");
            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): 查看队列头的数据");
            System.out.println("--------------------------");

            key = scanner.next().charAt(0);     //接收一个字符
            switch (key){
    
    
                case 's':
                    arrayQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("清输入要添加的数:");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case 'g':
                    try {
    
    
                        int res = arrayQueue.getQueue();
                        System.out.printf("取出的数据是:%d\n",res);
                    } catch (Exception e) {
    
    
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
    
    
                        int head = arrayQueue.headQueue();
                        System.out.printf("队列头为:%d\n",head);
                    }catch(Exception e){
    
    
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }

        }
        System.out.println("---------退出程序----------");

    }

}

// 使用数组模拟队列

//编写一个 ArrayQueue类
class ArrayQueue{
    
    

    //数组最大容量
    private int maxSize;

    //队列头
    private int front;

    //队列尾
    private int rear;

    //该数组用于存放数据,模拟队列
    private int[] arr;

    //创造队列的构造器
    public ArrayQueue(int arrMaxSize){
    
    
        maxSize = arrMaxSize;
        arr = new int[maxSize];
        front = -1;  //指向队列头部,分析书 front 是 指向队列头的前一个位置
        rear = -1;  //指向队列尾,指向队列尾的数据(队列最后一个数据)
    }

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

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

    //添加数据到队列
    public void addQueue(int n){
    
    

        //判断队列是否满
        if (isFull()){
    
    
            System.out.println("队列满,不能添加数据!");
            return;
        }

        rear++;     //让 rear 后移
        arr[rear] = n;
    }

    //获取队列的数据,出队列
    public int getQueue(){
    
    

        //判断队列是否为空
        if (isEmpty()){
    
    
            //通过异常抛出
            throw new RuntimeException("对列空,不能获取数据!");
        }

        front++;        //front后移
        return arr[front];
    }

    //显示队列所有数据
    public void showQueue(){
    
    
        //遍历
        if (isEmpty()){
    
    
            System.out.println("队列空,没有数据!");
            return;
        }
//		这里老师写的有点问题,详见分析
//        for (int i = 0; i < arr.length; i++) {
    
    
//            System.out.printf("arr[%d] = %d\n",i,arr[i]);
//        }
        for (int i = front + 1; i <= rear; i++) {
    
    
            System.out.printf("arr[%d] = %d\n",i,arr[i]);
        }
    }

    //显示队列定头数据
    public int headQueue(){
    
    
        //判断
        if (isEmpty()){
    
    
            throw new RuntimeException("队列空的,没有数据~");
        }
        return arr[front+1];
    }

}
  • 问题分析并优化

  • 目前数组使用一次就不能用, 没有达到复用的效果

    • 将这个数组使用算法,改进成一个环形的队列 取模:%

数组模拟环形队列

对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过取模的方式来实现即可)

分析说明:

  1. 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的 时候需要注意 (rear + 1) % maxSize == front 满]

  2. rear == front [空]

  3. 分析示意图:

    1674400099382

  4. 代码实现

    package com.atguigu.queue;
    
    import java.util.Scanner;
    
    public class CircleArrayQueueDemo {
          
          
        public static void main(String[] args) {
          
          
    
            //测试一把
            System.out.println("测试数组模拟环形队列的案例~~~");
            // 创建一个环形队列
            CircleArray arrayQueue = new CircleArray(4); //说明设置 4, 其队列的有效数据最大是 3
            char key = ' '; // 接收用户输入
            Scanner scanner = new Scanner(System.in);//
            boolean loop = true;
            // 输出一个菜单
            while(loop){
          
          
                System.out.println("-------数组模拟环形队列------");
                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): 查看队列头的数据");
                System.out.println("--------------------------");
    
                key = scanner.next().charAt(0);     //接收一个字符
                switch (key){
          
          
                    case 's':
                        arrayQueue.showQueue();
                        break;
                    case 'a':
                        System.out.println("清输入要添加的数:");
                        int value = scanner.nextInt();
                        arrayQueue.addQueue(value);
                        break;
                    case 'g':
                        try {
          
          
                            int res = arrayQueue.getQueue();
                            System.out.printf("取出的数据是:%d\n",res);
                        } catch (Exception e) {
          
          
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'h':
                        try {
          
          
                            int head = arrayQueue.headQueue();
                            System.out.printf("队列头为:%d\n",head);
                        }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;
    
        //变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素
        //front 的初始值 = 0
        private int front;
    
        //变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定
        //rear 的初始值 = 0
        private int rear;
    
        //该数组用于存放数据,模拟队列
        private int[] arr;
    
        public CircleArray(int arrMaxSize){
          
          
            maxSize = arrMaxSize;
            arr = new int[maxSize];
    //        front = 0;
    //        rear = 0
        }
    
        //判断队列是否满
        public boolean isFull(){
          
          
            return (rear + 1) % maxSize == front;
        }
    
        //判断是否为空
        public boolean isEmpty(){
          
          
            return rear == front;
        }
    
        //添加数据到队列
        public void addQueue(int n){
          
          
            //判断队列是否满
            if (isFull()){
          
          
                System.out.println("队列满,不能加入数据~");
                return;
            }
            //直接将数据加入
            arr[rear] = n;
            //将 rear 后移,这里必须考虑取模
            rear = (rear + 1) % maxSize;
        }
    
        //获取队列的数据,出队列
        public int getQueue(){
          
          
            //判断队列是否为空
            if (isEmpty()){
          
          
                // 通过抛出异常
                throw new RuntimeException("队列空,不能取数据");
            }
            // 这里需要分析出 front 是指向队列的第一个元素
            // 1. 先把 front 对应的值保留到一个临时变量
            // 2. 将 front 后移, 考虑取模
            // 3. 将临时保存的变量返回
            int value = arr[front];
            front = (front + 1)% maxSize;
            return value;
        }
    
        //显示队列的所有数据
        public void showQueue(){
          
          
            //遍历
            if (isEmpty()){
          
          
                System.out.println("队列空的,没有数据~~");
                return;
            }
            // 思路:从 front 开始遍历,遍历多少个元素
            // 动脑筋
            for (int i = front; i < front+size(); i++) {
          
          
                System.out.printf("arr[%d] = %d\n",i%maxSize,arr[i%maxSize]);
            }
        }
    
        //求出当前队列有效数据个数
        public int size(){
          
          
            //rear = 2
            //front = 1
            //maxSize = 3
            return (rear + maxSize - front) % maxSize;
        }
    
        // 显示队列的头数据, 注意不是取出数据
        public int headQueue(){
          
          
            // 判断
            if (isEmpty()) {
          
          
                throw new RuntimeException("队列空的,没有数据~~");
            }
            return arr[front];
        }
    }
    

分析

数组模拟队列

问题 && 解决

  1. 只添加一个数据:10,输入 s 回车后发现一共 3 个元素,只不过其他元素为 0

    1674476135565

  2. 取出数据后,按 s 回车查看队列发现还在,虽然 h 查看队列头数据已改为 20 ,但感觉不妥

    1674476405311

  3. 修改 showQueue() 方法

    首先看定义:

            front = -1;  //指向队列头部,分析书 front 是 指向队列头的前一个位置
            rear = -1;  //指向队列尾,指向队列尾的数据(队列最后一个数据)
    

    这里 for 循环中,i 修改为 front + 1(本质还是 0),长度变为 i <= rear

    rear 就是指向队尾最后一个元素,所以队列数据就是从 front 到 rear(包括自身)

        //显示队列所有数据
        public void showQueue(){
          
          
            //遍历
            if (isEmpty()){
          
          
                System.out.println("队列空,没有数据!");
                return;
            }
    
    //        for (int i = 0; i < arr.length; i++) {
          
          
    //            System.out.printf("arr[%d] = %d\n",i,arr[i]);
    //        }
            for (int i = front + 1; i <= rear; i++) {
          
          
                System.out.printf("arr[%d] = %d\n",i,arr[i]);
            }
        }
    
  4. 测试

    向队列中添加数据后查看:

    1674476872846

    从队列中取数据后查看:

    1674476978137

    测试成功!

数组模拟环形队列

前提条件

首先理解:% 运算符

x % y = z,不管 x 如何变化(递增等等)z 一定是 0到 y-1(循环等等),很神奇,这是算法的精妙所在!!!

    //数组最大容量
    private int maxSize;

    //变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素
    //front 的初始值 = 0
    private int front;

    //变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定
    //rear 的初始值 = 0
    private int rear;

    //该数组用于存放数据,模拟队列
    private int[] arr;

如何判断队列是否满/空?

代码

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

推荐阅读文章循环队列:判断队列空和满的3种方法

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

由rear 变量的含义可知:rear = maxsize - 1,队列满时,其实最后一个位置是空的,并未存储数据,所以 rear + 1 = maxsize,以代码中 maxSize = 4 为例,队列满时,最后一个数据位置为 2 (0,1,2,3),rear = 2+1=3,front = 0,套入公式:(3+1)% 4 == 0

队列空的条件:rear == front

当队列front、rear指针重合时,表明队列里没有数据

如何添加数据到队列?

代码

    public void addQueue(int n){
    
    
        //判断队列是否满
        if (isFull()){
    
    
            System.out.println("队列满,不能加入数据~");
            return;
        }
        //直接将数据加入
        arr[rear] = n;
        //将 rear 后移,这里必须考虑取模
        rear = (rear + 1) % maxSize;
    }

分析

队列未满时,直接将数据赋值给 arr[rear],然后 rear 后移一位即可。rear 后移,不能直接 rear ++。 因为是环形队列,这样会有数组下标越界异常,还是以 maxsize = 4 为例:当队列中 arr[2],arr[3]存放数据,(arr[0],arr[1] 为空时),再次添加数据存放位置为:arr[0]

将参数n赋值给数组arr中rear的位置,由于rear在环形队列中指rear后一位,故无需考虑数组arr[0]的问题,此时的rear的位置将会由(rear+1)%maxSize所赋值过去,由于rear本身自带+1属性,故在表达式中的”1”是给即将添加的数据准备的,而rear本身自带的+1是为腾出一个空间准备的

如何获取队列的数据,出队列?

代码

    public int getQueue(){
    
    
        //判断队列是否为空
        if (isEmpty()){
    
    
            // 通过抛出异常
            throw new RuntimeException("队列空,不能取数据");
        }
        // 这里需要分析出 front 是指向队列的第一个元素
        // 1. 先把 front 对应的值保留到一个临时变量
        // 2. 将 front 后移, 考虑取模
        // 3. 将临时保存的变量返回
        int value = arr[front];
        front = (front + 1)% maxSize;
        return value;
    }

分析

如果不为空,则定义一个变量value作为用户取出的数值,同时将*arr[front]*赋值给value,并将其作为取出的数值。由于front自带+1的属性(定义),故无需考虑数组本身的特性
front后移一位留出一空,符合环形队列的约定,将其用于与数组最大容量maxSize取模,得出此时环形队列front应该在的位置,最终return value

如何显示队列的所有数据?

代码

    public void showQueue(){
    
    
        //遍历
        if (isEmpty()){
    
    
            System.out.println("队列空的,没有数据~~");
            return;
        }
        // 思路:从 front 开始遍历,遍历多少个元素
        // 动脑筋
        for (int i = front; i < front+size(); i++) {
    
    
            System.out.printf("arr[%d] = %d\n",i%maxSize,arr[i%maxSize]);
        }
    }

分析

arr[%d]可以用i自增时候的数值去对该环形队列进行取模,得出该数组的实际位置,同时通过arr数组锁定数值

如何求出当前队列有效数据个数?

代码

    public int size(){
    
    
        //rear = 2
        //front = 1
        //maxSize = 3
        return (rear + maxSize - front) % maxSize;
    }

推荐阅读文章数组实现环形队列-获取有效数据个数

如何显示队列的头数据, 注意不是取出数据?

代码

    public int headQueue(){
    
    
        // 判断
        if (isEmpty()) {
    
    
            throw new RuntimeException("队列空的,没有数据~~");
        }
        return arr[front];
    }

学以致用

LeetCode数组相关题目

LeetCode队列相关题目

数据结构和算法系列文章:持续更新中!!!

猜你喜欢

转载自blog.csdn.net/Htupc/article/details/129250665