数据结构与算法(第二章 稀疏数组和队列)

3.1稀疏sparsearray数组
3.1.1关于稀疏数组的实际需求
数据结构与算法(第二章 稀疏数组和队列)_第1张图片
3.1.2基本介绍
1.当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。

2.稀疏数组的处理方式是:
(1)记录数组一共有几行几列,有多少个不同的值
(2)把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模

3.稀疏数组举例说明
数据结构与算法(第二章 稀疏数组和队列)_第2张图片

3.1.3应用实例
(1)使用稀疏数组,来保存类似前面的二维数组(棋盘、地图等等)
(2)把稀疏数组存盘,并且可以从新恢复原来的二维数组数
(3)整体思路分析
数据结构与算法(第二章 稀疏数组和队列)_第3张图片

二维数组 转 稀疏数组的思路:
(1)遍历原始的二维数组,得到有效的数据的个数sum
(2)根据sum就可以创建稀疏数组sparseArr int[sum + 1] [3]
(3)将二维数组的有效数据存入到稀疏数组

稀疏数组转原始的二维数组的思路
(1)先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如上面的 chessArr2=int [11] [11]
(2)在读取稀疏数组后几行的数据,并赋值给原始的二维数组即可。

(4)代码演示

public class SparseArray {
    public static void main(String[] args) {
        //一、创建原始数组
        //1表示黑子,2表示蓝子
        int chessArray[][]=new int[11][11];
        chessArray[1][2]=1;
        chessArray[2][3]=2;
        //打印原始数组
        System.out.println("输出原始数组");
        for (int[] array: chessArray){
            for (int data: array){
                System.out.printf("%d\t",data);
            }
            System.out.println();
        }
        System.out.println();

        //二、将原始数组转为稀疏sparseArray数组
        //1.遍历原始数组有几个有效数字
        int sum=0;    //使用sum作为标识
        for (int[] array: chessArray){
            for (int data: array){
                if (data!=0){
                    sum++;
                }
            }
        }
        //2.创建稀疏数组
        int sparseArray[][]=new int[sum+1][3];
        sparseArray[0][0]=chessArray.length;
        sparseArray[0][1]=chessArray[0].length;
        sparseArray[0][2]=sum;
        //3.遍历原始数组并给稀疏数组赋值
        int dataSum=0;   //标识当前是第几个值
        for (int i=0;i<chessArray.length;i++){
            for (int j=0;j<chessArray[i].length;j++){
                if (chessArray[i][j]!=0){
                    dataSum++;
                    sparseArray[dataSum][0]=i;
                    sparseArray[dataSum][1]=j;
                    sparseArray[dataSum][2]=chessArray[i][j];
                }
            }
        }
        //4.打印稀疏数组
        System.out.println("稀疏数组为:");
        for (int i=0;i<sparseArray.length;i++){
            System.out.printf("%d\t%d\t%d\t\n",sparseArray[i][0],sparseArray[i][1],sparseArray[i][2]);
            System.out.println();
        }

        //三、将稀疏数组转为原始数组
        //1.先读取第一行创建chessArray2数组
        int chessArray2[][]=new int[sparseArray[0][0]][sparseArray[0][1]];
        //2.从第二行开始读取信息
        for (int i=1;i<sparseArray.length;i++){
            chessArray2[sparseArray[i][0]][sparseArray[i][1]]=sparseArray[i][2];
        }
        //打印稀疏数组转换后的数组
        System.out.println("稀疏数组---> 原始数组");
        for (int[] array2: chessArray2){
            for (int data2: array2){
                System.out.printf("%d\t",data2);
            }
            System.out.println();
        }
    }
}

(5)结果演示(结果下面没截全)
数据结构与算法(第二章 稀疏数组和队列)_第4张图片
3.2 队列
3.2.1队列的一个使用场景
银行排队的案例:
数据结构与算法(第二章 稀疏数组和队列)_第5张图片
3.2.2 队列的介绍
(1)队列是一个有序列表,可以用数组或是链表来实现
(2)遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出。
(3)用数组模拟队列的示意图如下:
数据结构与算法(第二章 稀疏数组和队列)_第6张图片
3.2.3数组模拟队列的思路
(1)队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量
(2)因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变,如图所示:
数据结构与算法(第二章 稀疏数组和队列)_第7张图片
(3)当我们将数据存入队列时称为“addQueue”,addQueue的处理需要有两个步骤:思路分析
1.将尾部指针往后移:rear+1,当front==rear【空】
2.若尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear == maxSize - 1【队列满】

代码如下:

public class ArrayQueueDemo {
    public static void main(String[] args) {
        //测试数组实现的队列
        ArrayQueue queue = new ArrayQueue(3);
        char ch = ' ';   //存放用户的输入字符
        boolean loop=true;
        Scanner sc = new Scanner(System.in);
        //创建菜单测试队列
        while (loop){
            System.out.println("s(show):展示队列数据");
            System.out.println("a(add):向队列中添加信息");
            System.out.println("d(delete):弹出队列的数据");
            System.out.println("h(head):展示队列的头部信息");
            System.out.println("e(exit):退出程序");
            ch= sc.next().charAt(0);  //接收控制台的字符
            switch (ch){
                case 's':    //展示队列元素
                    queue.showQueue();
                    break;
                case 'a':    //添加元素
                    System.out.println("请输入要添加的值");
                    int i = sc.nextInt();
                    queue.addQueue(i);
                    break;
                case 'd':     //弹出元素
                    try {
                        int i1 = queue.deleteQueue();
                        System.out.printf("弹出元素为:%d\n",i1);
                        break;
                    }catch (Exception e){
                        e.getMessage();
                    }
                case 'h':     //查看队列头元素
                    try {
                        int i2 = queue.headQueue();
                        System.out.printf("头部元素为:%d\n",i2);
                        break;
                    }catch (Exception e){
                        e.getMessage();
                    }
                case 'e':
                    sc.close();    //关闭资源
                    loop =false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("退出菜单~~~");
    }
}

//使用数组来实现队列
class ArrayQueue{
    private int maxSize;  //队列的最大容量
    private int front;    //指向队列的头部的前一个位置(不包括头部的第一个数据)
    private int rear;     //指向队列的尾部数据(包括尾部的数据)
    private int[] arr;    //使用此数组实现队列

    //使用构造方法初始化成员变量,创建此队列时需传入队列容量的大小
    public ArrayQueue(int arrMaxSize){
        maxSize = arrMaxSize;
        front = -1;      //初始化front,指向队列头部的前一个位置
        rear = -1;      //初始化rear,指向队列头部前一个位置
        arr = new int[arrMaxSize];  //初始化数组
    }

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

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

    //向队列中添加元素
    public void addQueue(int data){
        if (isFull()){
            System.out.println("队列已满,添加数据失败!");
            return;   //return;的作用相当于break;用于中断循环的作用(常用于返回值为void中)
        }
        rear++;    //队列从尾部添加数据,所以rear后移
        arr[rear]=data;
    }

    //向队列中取出元素
    public int deleteQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列没有数据,取出数据失败");  //抛出异常
        }
        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]);
        }
    }

    //展示队列的头部元素(注意是展示,不是取出)
    public int headQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列没有数据,展示数据失败");
        }
        return arr[++front];
    }
}

效果展示:
数据结构与算法(第二章 稀疏数组和队列)_第8张图片
@问题分析并优化
(1)目前数组使用一次就不能用,没有达到复用的效果
(2)将这个数组使用算法,改进成一个环形的队列 取模: %

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

@分析说明:
(1)尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意(rear+1)%maxSize == front【满】
(2)rear = = front【空】
(3)分析示意图:
数据结构与算法(第二章 稀疏数组和队列)_第9张图片
代码如下:

public class CircleQueueDemo {
    public static void main(String[] args) {
        //测试数组实现的队列
        CircleQueue queue = new CircleQueue(4); //容量为4,但此时是环形队列,所以有效数为3个
        char ch = ' ';   //存放用户的输入字符
        boolean loop=true;
        Scanner sc = new Scanner(System.in);
        //创建菜单测试队列
        while (loop){
            System.out.println("s(show):展示队列数据");
            System.out.println("a(add):向队列中添加信息");
            System.out.println("d(delete):弹出队列的数据");
            System.out.println("h(head):展示队列的头部信息");
            System.out.println("e(exit):退出程序");
            ch= sc.next().charAt(0);  //接收控制台的字符
            switch (ch){
                case 's':    //展示队列元素
                    queue.showQueue();
                    break;
                case 'a':    //添加元素
                    System.out.println("请输入要添加的值");
                    int i = sc.nextInt();
                    queue.addQueue(i);
                    break;
                case 'd':     //弹出元素
                    try {
                        int i1 = queue.deleteQueue();
                        System.out.printf("弹出元素为:%d\n",i1);
                        break;
                    }catch (Exception e){
                        e.getMessage();
                    }
                case 'h':     //查看队列头元素
                    try {
                        int i2 = queue.headQueue();
                        System.out.printf("头部元素为:%d\n",i2);
                        break;
                    }catch (Exception e){
                        e.getMessage();
                    }
                case 'e':
                    sc.close();    //关闭资源
                    loop =false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("退出菜单~~~");
    }
}

//创建环形队列(解决上述代码的bug)
class CircleQueue{
    private int maxSize;  //队列的最大容量
    private int front;    //front指向队列的第一个元素,也就是说arr【front】就是队列的第一个元素
                          //front的初始值=0
    private int rear;     //rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定
                          //rear的初始值=0
    private int[] arr;    //使用此数组实现队列

    //使用构造方法初始化成员变量,创建此队列时需传入队列容量的大小
    public CircleQueue(int arrMaxSize){
        maxSize = arrMaxSize;
        arr = new int[arrMaxSize];  //初始化数组
    }

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

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

    //向队列中添加元素
    public void addQueue(int data){
        if (isFull()){
            System.out.println("队列已满,添加数据失败!");
            return;   //return;的作用相当于break;用于中断循环的作用(常用于返回值为void中)
        }
        //直接在尾部添加数据即可
        arr[rear]=data;
        rear=(rear+1)%maxSize;    //取模,防止越界
    }

    //向队列中取出元素
    public int deleteQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列没有数据,取出数据失败");  //抛出异常
        }
        //1.首先用临时变量将需取出元素存起来
        //2.移动front的位置 ,需要取模
        //3.返回临时变量
        int variable=arr[front];
        front=(front+1)%maxSize;
        return variable;
    }

    //展示队列中的元素
    public void showQueue(){
        if (isEmpty()){
            System.out.println("队列没有数据,展示数据失败");
            return;
        }
        //此时使用环形队列,不可对整个数组进行遍历
        for (int i=front;i<front+(rear+maxSize-front)%maxSize;i++){
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
        }
    }

    //展示队列的头部元素(注意是展示,不是取出)
    public int headQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列没有数据,展示数据失败");
        }
        return arr[front];
    }
}

运行结果:
数据结构与算法(第二章 稀疏数组和队列)_第10张图片

你可能感兴趣的:(java,数据结构,java,算法)