数据结构与算法——Java实现稀疏数组和队列

news2024/11/26 2:46:35

目录

一、基本介绍

 1.1 线性结构

 1.2 非线性顺序结构

二、稀疏数组

2.1 基本介绍

 2.1.1 应用场景

 2.1.2 实现思路

2.2 代码实现

  2.2.1 原始数组

  2.2.2 原始数组转化为稀疏数组

  2.2.3 稀疏数组转化为原始数组

三、队列的应用场景和介绍

3.1 数组模拟队列

   3.1.1数组模拟队列的分析

   3.1.2 代码实现数组模拟队列

3.2 环型队列思路分析图

  3.2.1 环型队列实现


一、基本介绍

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


1.1 线性结构

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

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

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

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

 1.2 非线性顺序结构

   非线性结构包括:二维数组、多维数组、广义表、树结构、图结构(这两种引伸出来了很多算法)

二、稀疏数组

2.1 基本介绍

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

处理方法:

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

我们看看下面的图,看看稀疏数组到底是怎么回事

如果我们使用左侧的的二维数组直接记录,我们会有6*7=42个数据。

我们看一下右侧的稀疏数组,一打眼就没有42个数据。变成了九行三列 9*3=27个数据

 稀疏数组是怎么表示的呢?

  • 表头是行、列、值(利用行、列就可以确定具体的位置,初中的平面坐标系)
  • 第一行记录多少总行数,总列数,有多少个不同的值
  • 下面便开始记录数据,比如第二行,表示第零行(数组是从0开始的,实际是第一行),第三列(左图第四列),此值是22
  •  依次表示出来,只记录非0值的行列值

   

 2.1.1 应用场景

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

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

    整体思路分析

2.1.2 实现思路

二维数组 转 稀疏数组的思路

1. 遍历  原始的二维数组,得到有效数据的个数 sum

2. 根据sum 就可以创建 稀疏数组 sparseArr   int[sum + 1] [3]

3. 二维数组有效数据数据存入到 稀疏数组

稀疏数组转原始的二维数组的思路

1. 先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如上面的  chessArr2 = int [11][11]

2. 在读取稀疏数组后几行的数据,并赋给 原始的二维数组 即可.

2.2 代码实现

 2.2.1 原始数组

创建二维数组实现上面的那个图,1代表黑子,2表示蓝子

//        0表示无子,1表示黑子,2表示蓝子
        int chessArr1[][] = new int[11][11];
        chessArr1[1][2] = 1;
        chessArr1[2][3]=2;

//       输出原始二维数组
        for(int[] row:chessArr1){
            for(int data:row){
//                %d +制表符
                System.out.printf("%d\t",data);
            }
            System.out.println();
        }

 2.2.2 原始数组转化为稀疏数组


//       转化成稀疏数组
//       遍历二维数组,找到非零数据的个数     chessArr1[0].length列数,chessArr1.length行数
        int sum = 0;
       for (int i=0;i<11;i++){    //行
           for (int j=0;j<11;j++){   //列
             if(chessArr1[i][j]!=0){
                 sum++;
             }
           }
       }
//      创建稀疏数组   sum+1表示列数
        int sparseArr[][] = new int[sum+1][3];
        sparseArr[0][0] =11;   //行
        sparseArr[0][1]=11;    //列
        sparseArr[0][2]=sum;   //不同值的个数
//      遍历二维数组,将二维数组中的有效值存放到稀松数组中
        int count =0;
        for (int i=0;i<11;i++){
            for (int j=0;j<11;j++){
                if(chessArr1[i][j]!=0){
                    count++;
                    sparseArr[count][0]=i;
                    sparseArr[count][1]=j;
                    sparseArr[count][2]=chessArr1[i][j];
                }
            }
        }

//        输出稀疏数组的形式
        for (int i=0;i<sum+1;i++){
            for (int j=0;j<3;j++){
                System.out.print(sparseArr[i][j]+"\t");
            }
            System.out.println();
        }

2.2.3 稀疏数组转化为原始数组

        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];
        }
        for(int[] row:chessArr2){
            for(int data:row){
//                %d +制表符
                System.out.printf("%d\t",data);
            }
            System.out.println();
        }

三、队列的应用场景和介绍

有序列表,可以用数组或者链表来实现

遵循先入先出的原则。即:先存入队列的数据先要取出,后存入的要后取出(队首取数据、队尾存数据)

我们可以仔细看一下,下面的这个图,front代表队列首(头)的前一个位置(初始时指向第一个元素之前,所以是-1),rear代表队列尾,初始时都是-1

当数据进来的时候,我们发现front还是-1,rear跟着新加入的元素移动,指向新元素

当数据出去的时候,此时我们的front开始移动了,并且执行现如今最早加入的元素

简言之:front随着数据输出而改变,rear随着数据的增加而改变

 

3.1 数组模拟队列

  3.1.1数组模拟队列的分析

    我们将数据存入队列时称为“addQueue”,其中处理需要两个步骤

  • 将尾指针往后移动:rear+1,当front == rear  ,此时队列为空
  •  若尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据。 rear==maxSize-1 时,队列存满

   3.1.2 代码实现数组模拟队列

虽然我们这个地方模拟成功了,但是这个数组只能用一次,我们后面会讲环型数组,就真正的实现了队列


class ArrayQueue{
    private int maxSize;  //数组最大容量
    private int front;    //队列头
    private int rear;     //队列尾
    private int[] arr;    //该数据用于存放数据,模拟队列

    public ArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        this.front=-1;    //初始时指向队列头部的前一个位置
        this.rear=-1;     //执行队列的尾部,即就是队列的最后一个数据
        arr = new int[maxSize];
    }

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

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

//   添加数据到队列
    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++;  //后移动

        return  arr[front];
    }

//    展示队列的所有数据
    public void showQueue(){
//        遍历
        if(isEmpty()){
            System.out.println("队列空的,无法遍历");
            return;
        }
       for(int i =0;i<arr.length;i++){
           System.out.println(arr[i]);
       }
    }

//    显示队列的头数据,不是取出数据
    public int headQueue(){
        if (isEmpty()){
            System.out.println("队列空,没有数据");
            throw new RuntimeException("队列空,没有数据");
        }
        return arr[front+1];
    }


}

测试程序

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("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.addQueue(value);
                   break;
               case 'g':
//                  取数据
                   try{
                     int res =  arrayQueue.getQueue();
                       System.out.println("取出的数据是:"+res);
                   }catch (Exception e){
                       System.out.println(e.getMessage());
                   }
                   break;
               case 'h':
                   try{
                    int res =  arrayQueue.headQueue();
                       System.out.println("队列头的数据是"+res);
                   }catch (Exception e){
                       System.out.println(e.getMessage());
                   }
                   break;
               case 'e':
                   scanner.close();
                   loop=false;
                   break;

               default:
                   break;
           }
          
       }
        System.out.println("程序退出");
    }
}

3.2 环型队列思路分析图

刚刚我们用数组实现的队列只能使用一次,没有达到复用的效果,下面我们将这个数组使用算法,改进成一个环型的队列

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

  • rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定(这个地方一定要读明白,有些算法可以不预留,看个人习惯)rear 的初始值 = 0

  • 当队列满时,条件是  (rear  + 1) % maxSize == front   【

  • 对队列为空的条件, rear == front

  • 当我们这样分析, 队列中有效的数据的个数   (rear + maxSize - front) % maxSize   // rear = 1 front = 0

先解释一下上面队列满的时候的条件(rear  + 1) % maxSize == front   【

   我们直接把环型队列想想成一个环状,看下面的分析

 

  按照上面进行修改,就可以得到一个环型队列

  3.2.1 环型队列实现

通过取模的方式实现


class  CircleArray{
    private int maxSize;  //数组最大容量
    private int front ;    //队列头  执行第一个元素,初始值0
    private int rear=0;     //队列尾  执行最后一个元素的下一个位置,初始值0
    private int[] arr;    //该数据用于存放数据,模拟队列

    public CircleArray(int maxSize) {
        this.maxSize = maxSize;
        this.front=0;    //初始时指向队列头部的前一个位置
        this.rear=0;     //执行队列的尾部,即就是队列的最后一个数据
        arr = new int[maxSize];
    }

//  判断队列是否满
    public boolean isFull(){
//           相等true就是满了
        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指向的元素是队列的第一个元素
//           把front对应的值保存到一个临时变量
//           将front后移动
//           将临时变量返回
        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.println(arr[i%maxSize]);
        }
    }

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

//  显示队列的头数据,不是取出数据
    public int headQueue(){
        if (isEmpty()){
            System.out.println("队列空,没有数据");
            throw new RuntimeException("队列空,没有数据");
        }
        return arr[front];
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/83983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Find My资讯|Seinxon推出支持苹果 Find My 防丢卡

在美国&#xff0c;平均每个人每年丢失 3,000 件物品。而在 2021 年&#xff0c;Pixie 数据显示&#xff0c;丢失产品的更换成本超过 25 亿美元。每周超过两次&#xff0c;将近 1/4 的美国人丢失房门钥匙、钱包、宠物、电话、眼镜、耳机、遥控器、手提箱或孩子最喜欢的物品。 …

GIT系列(七)切换ssh连接,上传不再输入账号、密码

文章目录前言操作流程前言 使用HTTP连接方式时&#xff0c;上传代码总是需要登录&#xff0c;键盘都打坏了&#xff0c;切换SSH可以无需密码&#xff0c;直接上传。 操作流程 step 1 确保在git服务器已经部署本机公钥。 没有配置SSH的&#xff0c;戳这里 GIT系列&#xff08;…

k8s教程(18)-pod之DaemonSet(每个node上只调度一个pod)

文章目录01 引言02 DaemonSet2.1 应用场景2.2 举例2.3 注意事项03 文末01 引言 声明&#xff1a;本文为《Kubernetes权威指南&#xff1a;从Docker到Kubernetes实践全接触&#xff08;第5版&#xff09;》的读书笔记 DaemonSet是 Kubernetes1.2 版本新增的一种资源对象&#xf…

事件轮询机制 Event Loop、浏览器更新渲染时机、setTimeout VS setInterval

目录 1. 事件轮询机制&#xff08;Event Loop&#xff09;是什么 1.1 宏任务、微任务 1.2 Event Loop 循环过程 1.3 经典题目分析 1.3.1 第一轮事件循环 1.3.2 第二、三次事件循环 1.3.3 参考文章 2. async、await 在事件轮询中的执行时机 3. 浏览器更新渲染时机、Vue…

线上使用雪花算法生成id重复问题

项目中使用的是hutool工具类库提供的雪花算法生成id方式&#xff0c;版本使用的是5.3.1 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.1</version></dependency>雪花算法生成id…

10分钟数仓实战之kettle整合Hadoop

1.写在前面 很多朋友在做数仓的ETL的动作的时候&#xff0c;还是喜欢比较易上手的kettle 前面章节有介绍过安装kettle&#xff0c;可以参考 ETL工具--安装kettle_老码试途的博客-CSDN博客_spoon.bat 安装 kettle在Windows系统中对数据的转换、表和文件的转换等&#xff0c;…

Blender 3D环境场景创建教程

Blender 3D环境场景创建教程 学习 Blender 3.2&#xff0c;探索几何节点并创建美妙的 3D 环境 课程英文名&#xff1a;Creating 3D Environments in Blender 2.81 by Rob Tuytel (2019) 此视频教程共8.0小时&#xff0c;中英双语字幕&#xff0c;画质清晰无水印&#xff0c;…

腾讯云从业者基础认证完整笔记

腾讯云从业者基础认证完整笔记 就考这些&#xff0c;干就完事儿了&#xff01;不要介意图多哟&#xff0c;ppt能更好的表达意思呀 一、云计算基础 1.1 数据中心 一般企业要么自建数据中心EDC&#xff0c;EDC分层如下&#xff1a; 要么租用或者托管也就是IDC如下&#xff…

ZYNQ之FPGA学习----EEPROM读写测试实验

1 EEPROM简介 EEPROM (Electrically Erasable Progammable Read Only Memory&#xff0c;E2PROM)即电可擦除可编程只读存储器&#xff0c;是一种常用的非易失性存储器(掉电数据不丢失)。ZYNQ开发板上使用的是AT24C64&#xff0c;通过IIC协议实现读写操作。IIC通信协议基础知识…

Oracle 11g---基于CentOS7

Oracle 11g安装教程 以下步骤基于网络配置完成&#xff0c;并且能连接xshell和xftp工具 文章目录Oracle 11g安装教程1.将oracle压缩包拷贝到安装机器&#xff0c;指定目录中2.安装依赖包3.验证依赖包4.创建oracle用户5.创建oradata目录,解压oracle安装6.修改系统配置参数7.创建…

2023年开始当年授权或转让的知识产权申报高新将不再认可。

前段时间&#xff0c;由国家科技部火炬中心组织全国高新技术企业管理机构召开会议&#xff0c;会议宣导要求加强企业知识产权管理&#xff0c;强调对当年授权或转让的专利&#xff0c;用来申报当年高新将不再认可。 、从多省市反馈的消息显示部分省市执行了该政策。虽然广东暂…

Java 2022圣诞树+2023元旦倒计时打包一起领走

2022最后一个月充满了期待&#xff0c;平安夜、圣诞节、元旦节&#xff1b;2023年也是一个早年&#xff0c;因此关于程序方面的浪漫&#xff0c;大家应该趁早准备。下面我将分享一个元旦的倒计时和圣诞树的绘制核心代码。大家可以依据自身的需求&#xff0c;稍微调整即可用。 …

振弦渗压计怎样安装?振弦式渗压计工作原理

振弦渗压计是一种长期测量混凝土或地基内的孔隙(渗透)水压力&#xff0c;并可同步测量埋设点温度。适用于大坝工程安全监测、尾矿库工程安全监测、各类公路、桥梁、隧洞安全监测、土工建筑物基坑安全监测等。    1、设备介绍 通过不断的生产工艺技术的积累&#xff0c;采用…

vscode给docker内部的的ros工程代码打断点

背景 打断点debug虽然不能直观看到变量在时间轴上的整体变化曲线&#xff0c;但是其针对某一帧问题数据&#xff0c;暂停后一步步单步执行监视每个变量的变化&#xff0c;方便直观的判断每一步逻辑的正确性&#xff0c;即使这个变量结构再复杂也能直接监视&#xff0c;可以准确…

推荐5款压箱底的小工具软件

今天要给大家推荐5款压箱底的宝贝软件了&#xff0c;百度搜索一下就能找到下载链接了。 1.阅读笔记——BookxNote BookxNote 是一款 PDF 和 EPUB 阅读笔记软件&#xff0c;集阅读、笔记、批注、思维导图、划词翻译等于一体&#xff0c;可以边读边记。它的标注功能非常全&…

惊 GitHub首次开源,在国内外都被称为分布式理论+实践的巅峰之作

前言 蓦然回首自己做开发已经十年了&#xff0c;这十年中我获得了很多&#xff0c;技术能力、培训、出国、大公司的经历&#xff0c;还有很多很好的朋友。但再仔细一想&#xff0c;这十年中我至少浪费了五年时间&#xff0c;这五年可以足够让自己成长为一个优秀的程序员&#…

自有服务器(2台)被 kthreaddk木马挖矿解决过程(实操)不重启服务器

第一台服务器&#xff1a; #查看进程和CPU使用情况 top #查找相关联的进程 systemctl status 326858 #查看下所的 端口号和进程&#xff0c;发现有异常端口和进程 netstat -ntpl #杀死关联进程&#xff08;异常进程&#xff09;&#xff0c; kill -9 2900707 #杀死主进程&a…

Redis高可用之主从复制架构(第一部分)

引言 之前的文章 Redis持久化策略AOF、RDB详解及源码分析&#xff0c;我们介绍了Redis中的数据持久化技术&#xff0c;包括 RDB快照 和 AOF日志以及混合持久化 。有了持久化技术&#xff0c;我们就不用担心因Redis所在服务机器宕机&#xff0c;导致数据丢失。但是&#xff0c;…

四阶龙格库塔法求解一次常微分方程组(python实现)

四阶龙格库塔法求解一次常微分方程组一、前言二、RK4求解方程组的要点1. 将方程组转化为RK4求解要求的标准形式2. 注意区分每个方程的独立性三、python实现RK4求解一次常微分方程组1. 使用的方程组2. python代码3. 运行结果一、前言 之前在博客发布了关于使用四阶龙格库塔方法…

字节测试开发最牛教程,全栈Jmeter_性能测试(总结)

Jmeter_性能测试(4)&#xff1a; 性能测试脚本的优化 以PHP论坛为例&#xff1a;http://47.107.178.45/phpwind/ 根据上一篇的性能测试(3&#xff09;的脚本进行优化&#xff1b;见下图&#xff1a; 如上图中&#xff0c;把发帖和回帖的事务添加到随机控制器中&#xff0c;登…