【数据结构与算法】数据结构介绍与稀疏数组相关操作

news2024/11/16 23:30:01

文章目录

  • 一 数据结构介绍
    • 1 数据结构和算法的重要性
    • 2 数据结构和算法的关系
    • 3 线性结构和非线性结构
      • (1)线性结构
      • (2)非线性结构
  • 二 稀疏数组
    • 1 应用场景
    • 2 基本概念
      • (1)基本介绍
      • (2)压缩策略
        • a 压缩存储
        • b 链式存储
    • 3 需求实现
      • (1)需求分析
      • (2)思路分析
        • a 二维数组转稀疏数组的思路
        • b 稀疏数组转原始的二维数组的思路
      • (3)代码实现
        • a 普通数组 --> 稀疏数组
        • b 稀疏数组 --> 普通数组
    • 4 完整代码

一 数据结构介绍

1 数据结构和算法的重要性

  • 算法是程序的灵魂,优秀的程序可以在海量数据计算时,依然保持高速计算。
  • 一般来讲程序会使用了内存计算框架 ( 比如Spark ) 和缓存技术 ( 比如Redis等 ) 来优化程序,再深入的思考一下,这些计算框架和缓存技术, 它的核心功能是哪个部分呢?
  • 拿实际工作经历来说, 在Unix下开发服务器程序,功能是要支持上千万人同时在线, 在上线前,做内测,一切OK。可上线后,服务器就支撑不住了,公司的CTO对代码进行优化,再次上线,坚如磐石。就能感受到程序是有灵魂的,就是算法。
  • 目前程序员面试的门槛越来越高,很多一线IT公司(大厂),都会有数据结构和算法面试题。
  • 如果不想永远都是代码工人,那就花时间来研究下数据结构和算法。

2 数据结构和算法的关系

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

3 线性结构和非线性结构

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

(1)线性结构

  • 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。
  • 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。
    • 顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的。
    • 链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。
  • 线性结构常见的有:数组、队列、链表和栈。

(2)非线性结构

  • 其特点是数据元素之间存在一对多的非线性关系。
  • 非线性结构包括:二维数组,多维数组,广义表,树结构,图结构。

二 稀疏数组

1 应用场景

假如想要开发一个五子棋游戏,如果使用二维数组记录棋盘,将黑棋置为1,白棋置为2,会发现这样一个数组存在很多没有意义的默认值0,浪费很多空间,这时就可以使用稀疏数组。

  • 使用稀疏数组,来保留类似棋盘,地图等二维数组。
  • 把稀疏数组存盘,并且可以从新恢复原来的二维数组数。

2 基本概念

(1)基本介绍

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

非零元素远远少于矩阵元素的个数。

(2)压缩策略

稀疏数组的处理方法(压缩策略)是:

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

如下图是一个稀疏矩阵的示例:

在这里插入图片描述

a 压缩存储

针对上图稀疏数组,使用压缩存储策略:顺序存储——三元组 <行,列,值>。

存储形式如下图:

在这里插入图片描述

注意存储的行列值与数组的下标相差一。

b 链式存储

使用链式存储策略存储上图稀疏数组,存储成下图:其中行列值的下面分别存在两个指针,左指针和右指针,具体含义如图:

在这里插入图片描述

3 需求实现

(1)需求分析

使用普通数组实现上图中的数组,再将普通数组转化成上图的稀疏数组,最后从稀疏数组恢复成普通数组。

(2)思路分析

如果使用二维数组存储棋盘会浪费很多空间,所以采用稀疏数组进行优化。

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

  • 遍历原始的二维数组,得到有效数据的个数 sum
  • 根据sum可以创建稀疏数组``sparseArr,数组大小为int[sum] [3]`(三列分别为压缩存储中对应的【行row、列col、值val】。
  • 将二维数组的有效数据数据存入到稀疏数组。

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

  • 先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组。
  • 在读取稀疏数组后几行的数据时,赋给原始的二维数组。

(3)代码实现

a 普通数组 --> 稀疏数组

// 创建原始的二维数据
int orginalArr[][] = new int[5][6];
orginalArr[0][2] = 4;
orginalArr[0][5] = 5;
orginalArr[1][1] = 3;
orginalArr[1][3] = 9;
orginalArr[2][4] = 7;
orginalArr[3][1] = 2;
// 输出原始二维数组
System.out.println("==================输出原始二维数组==================");
for(int[] row : orginalArr){
    for(int date : row){
        System.out.print(date + " ");
    }
    System.out.println();
}

// 将二维数组转化成稀疏数组
// 遍历二维数组,得到非0数据的个数
int sum = 0;
for(int[] row : orginalArr){
    for(int date : row){
        if(date != 0){
            sum++;
        }
    }
}
// 创建对应的稀疏数组
int sparseArray[][] = new int[sum][3];
// 记录当前是第几个非0数据
int count = 0;
for(int i = 0; i < orginalArr.length; i++){
    for (int j = 0; j < orginalArr[0].length; j++){
        if(orginalArr[i][j] != 0){
            sparseArray[count][0] = i + 1;
            sparseArray[count][1] = j + 1;
            sparseArray[count][2] = orginalArr[i][j];
            count++;
        }
    }
}
// 输出稀疏数组
System.out.println("==================输出对应的稀疏数组==================");
System.out.println("i" + " " + "j" + " " + "v");
for(int i = 0; i < sparseArray.length; i++){
    System.out.print(sparseArray[i][0] + " ");
    System.out.print(sparseArray[i][1] + " ");
    System.out.print(sparseArray[i][2] + " ");
    System.out.println();
}

结果如下:

在这里插入图片描述

b 稀疏数组 --> 普通数组

    // 将稀疏数组恢复成原始数组
    System.out.println("==================输出恢复后的数组==================");
    int row = 0;
    int col = 0;
    int maxRowIndex = 0;
    int maxColIndex = 0;
    for(int i = 0; i < sparseArray.length;i++){
        if(sparseArray[i][0] >= maxRowIndex){
            maxRowIndex = sparseArray[i][0];
            row = maxRowIndex;
        }
        if(sparseArray[i][1] >= maxColIndex){
            maxColIndex = sparseArray[i][1];
            col = maxColIndex;
        }
    }
    // 创建数组
    int newOriginArr[][] = new int[row][col];
    // 为新数组赋值
    for(int i = 0; i < sparseArray.length; i++){
        newOriginArr[sparseArray[i][0] - 1][sparseArray[i][1] - 1] = sparseArray[i][2];
    }
    // 遍历输出
    for(int i = 0; i < newOriginArr.length; i++){
        for(int j = 0; j < newOriginArr[0].length; j++){
            System.out.print(newOriginArr[i][j] + " ");
        }
        System.out.println();
    }

结果如下:

在这里插入图片描述

注意:最后一行全为0的行没有被恢复。

4 完整代码

public class SparseArray {
    public static void main(String[] args) {
        // 创建原始的二维数据
        int orginalArr[][] = new int[5][6];
        orginalArr[0][2] = 4;
        orginalArr[0][5] = 5;
        orginalArr[1][1] = 3;
        orginalArr[1][3] = 9;
        orginalArr[2][4] = 7;
        orginalArr[3][1] = 2;
        // 输出原始二维数组
        System.out.println("==================输出原始二维数组==================");
        for(int[] row : orginalArr){
            for(int date : row){
                System.out.print(date + " ");
            }
            System.out.println();
        }

        // 将二维数组转化成稀疏数组
        // 遍历二维数组,得到非0数据的个数
        int sum = 0;
        for(int[] row : orginalArr){
            for(int date : row){
                if(date != 0){
                    sum++;
                }
            }
        }
        // 创建对应的稀疏数组
        int sparseArray[][] = new int[sum][3];
        // 记录当前是第几个非0数据
        int count = 0;
        for(int i = 0; i < orginalArr.length; i++){
            for (int j = 0; j < orginalArr[0].length; j++){
                if(orginalArr[i][j] != 0){
                    sparseArray[count][0] = i + 1;
                    sparseArray[count][1] = j + 1;
                    sparseArray[count][2] = orginalArr[i][j];
                    count++;
                }
            }
        }
        // 输出稀疏数组
        System.out.println("==================输出对应的稀疏数组==================");
        System.out.println("i" + " " + "j" + " " + "v");
        for(int i = 0; i < sparseArray.length; i++){
            System.out.print(sparseArray[i][0] + " ");
            System.out.print(sparseArray[i][1] + " ");
            System.out.print(sparseArray[i][2] + " ");
            System.out.println();
        }

        // 将稀疏数组恢复成原始数组
        System.out.println("==================输出恢复后的数组==================");
        int row = 0;
        int col = 0;
        int maxRowIndex = 0;
        int maxColIndex = 0;
        for(int i = 0; i < sparseArray.length;i++){
            if(sparseArray[i][0] >= maxRowIndex){
                maxRowIndex = sparseArray[i][0];
                row = maxRowIndex;
            }
            if(sparseArray[i][1] >= maxColIndex){
                maxColIndex = sparseArray[i][1];
                col = maxColIndex;
            }
        }
        // 创建数组
        int newOriginArr[][] = new int[row][col];
        // 为新数组赋值
        for(int i = 0; i < sparseArray.length; i++){
            if(sparseArray[i][2] != 0){
                newOriginArr[sparseArray[i][0] - 1][sparseArray[i][1] - 1] = sparseArray[i][2];
            }
        }
        // 遍历输出
        for(int i = 0; i < newOriginArr.length; i++){
            for(int j = 0; j < newOriginArr[0].length; j++){
                System.out.print(newOriginArr[i][j] + " ");
            }
            System.out.println();
        }
    }
}

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

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

相关文章

并发编程——1.进程与线程

目录1.进程与线程1.1.概述1.2.并行与并发本文笔记整理来自黑马视频https://www.bilibili.com/video/BV16J411h7Rd/?p1&#xff0c;相关资料可在视频评论区进行获取。 1.进程与线程 1.1.概述 &#xff08;1&#xff09;进程 程序由指令和数据组成&#xff0c;但这些指令要运…

2022 年度作品优秀大赏 | 开发者说·DTalk

岁聿云暮之际&#xff0c;回首 2022&#xff0c;开发者们一直在迈着坚定的步伐向顶峰攀登&#xff0c;我们也竭尽所能不断为大家提供帮助——操作系统 Android 13 完成 Beta 版到正式版的蜕变&#xff0c;开发工具 Flutter 3.3 全力支持您的跨平台之旅&#xff0c;应用平台 Goo…

并行、并发、同步、异步、阻塞、非阻塞

并行 是指两个或者多个事件在同一时刻发生。并行是在不同实体上的多个事件。 并行针对多核 CPU 而言&#xff0c;它指的是多个核心同时执行多个任务的能力 单核 CPU 无法并行&#xff0c;并行只可能发生在多核 CPU 中。 并发 是指两个或多个事件在同一时间间隔发生。并发是…

【观察】让行业AI“触手可及”,NVIDIA创新与实践“从未止步”

毫无疑问&#xff0c;今天AI正与产业结合得越来越紧密&#xff0c;从自动驾驶&#xff0c;到智慧医疗&#xff0c;智慧金融、智慧城市等&#xff0c;AI已经开始渗透到我们生活的方方面面。事实上&#xff0c;即便目前来自传统行业用户的AI转型需求尚未完全激活爆发&#xff0c;…

第一篇 AlexNet——论文翻译

文章目录摘要1 简介2 数据集3 架构 3.1 ReLU非线性3.2 多GPU训练3.3 局部响应归一化3.4 重叠池化3.5 整体架构4 减少过拟合4.1 数据增强4.2 失活(Dropout)5 学习细节6 结果6.1 定性评估7 探讨摘要 论文链接&#xff1a;http://www.cs.toronto.edu/~fritz/absps/imagenet.pdf …

VTK--交互方式vtkInteractorStyleTrackballCamera

前言&#xff1a;本博文主要介绍vtk中基于Camera的交互方式vtkInteractorStyleTrackballCamera 及其子类&#xff0c;小伙伴可以根据需求自定义交互方式。 目录 vtkInteractorStyleTrackballCamera vtkGeoInteractorStyle vtkInteractorStyleImage vtkInteractorStyleMult…

LeetCode刷题复盘笔记—一文搞懂动态规划之152. 乘积最大子数组问题(动态规划系列第三十六篇)

今日主要总结一下动态规划的一道题目&#xff0c;152. 乘积最大子数组 题目&#xff1a;152. 乘积最大子数组 Leetcode题目地址 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&…

怎样学习线性代数?

最近在看《线性代数的几何意义》这本书&#xff0c;刚好也借用书里的总结&#xff0c;分享一下。 &#xff08;注&#xff1a;本文是一篇我国代数名家丘维声教授在电大讲授线性代数课程时关于如何学好线性代数的综合论述&#xff0c;超牛&#xff01;&#xff09; 初学线性代数…

社区10款年度优秀插件框架盘点!

Cocos 社区年度插件框架 TOP 10以下游戏资源排名不分先后&#xff0c;Cocos Store & Cocos微店 年终元旦限时优惠进行中......作者&#xff1a;Chuan——张川介绍&#xff1a;一款节点树实时预览插件&#xff0c;支持 Cocos Creator 2.x\3.x 引擎版本&#xff0c;除节点预览…

云信小课堂 | 如何管理音视频的通话状态?

2022.12业务背景 用户在实际使用云信音视频通话 2.0 服务中&#xff0c;常常会遇到弱网&#xff0c;掉线等情况&#xff0c;对于这类情况&#xff0c;云信 SDK 提供了自动重连的相关策略&#xff0c;业务层不需要单独处理重连&#xff0c;只需要监听并做好相应的 UI 逻辑。所以…

【OpenCV】使用 Python 的铅笔素描图像

目录&#xff1a;使用 Python 的铅笔素描图像一、前言二、代码实战2.1 导包2.2 读取照片2.3 使用 OpenCV 显示图像2.4 灰度图像2.5 反转图像2.6 模糊图像2.7 减淡和融合三、结果展示五、源代码一、前言 图片在 Python 中表示为一组数字。所以我们可以进行各种矩阵操作来得到令…

跨域问题(三种解决方法)

跨域就是一个域名的网页去请求另一个域名的资源&#xff0c;比如你刚刚在A网站输入了自己的账号密码&#xff0c;然后访问B网站&#xff0c;B网站无法获取账号密码 两个请求的协议&#xff08;比如http&#xff09;&#xff0c;域名&#xff08;比如说localhost或者192.168.0.…

python 时间

目录标题python的时间模块1、时间戳2、时间元组3、获取格式化的时间可以自定义输出格式日期格式化的符号4、显示某月的日历5、sleep模块python的时间模块 1、时间戳 时间戳&#xff0c;以1970为时间基准&#xff0c;但是太过于遥远的时间就不可以了&#xff0c;windows最源支持…

基于springboot实验室管理系统(程序+数据库)

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

C进阶:征服指针之指针笔试题强化(3)

目录 ​​​​​​​ &#x1f432;T1. ​​​​​​​ ​​​​​​​ &#x1f916; T2. ​​​​​​​ ​​​​​​​ &#x1f42c; T3. ​​​​​​​ ​​​​​​​ &#x1f433; T4. ​​​​​​​ ​​​…

并发编程——5.共享模型之无锁

目录5.共享模型之无锁5.1.问题提出5.1.1.案例5.1.2.解决思路——锁5.1.3.解决思路——无锁5.2.CAS 与 volatile5.2.1.CAS5.2.2.volatile5.2.3.为什么无锁效率高5.2.4.CAS 的特点5.3.原子整数5.4.原子引用5.4.1.概述5.4.2.案例5.4.3.ABA 问题及解决5.5.原子数组5.6.字段更新器5.…

数位统计DP

目录 算法简介 算法讲解 数字计数 数位统计DP的递推实现 数位统计DP的记忆化搜索实现 算法实践 一 Windy数 二 手机号码 附录&#xff1a; 算法简介 数位统计 DP 用于数字的数位统计,是一种比较简单的 DP 套路题。 一个数字的数位有个位、十位、百位,等等,如果题目和…

Python3入门基础(03)数据结构

Python3 数据结构 Python3 中有四种标准的数据结构&#xff1a; List&#xff08;列表&#xff09;Tuple&#xff08;元组&#xff09;Set&#xff08;集合&#xff09;Dictionary&#xff08;字典&#xff09; Python 3 的六个标准数据类型中&#xff1a; 不可变数据&…

MATLAB算法实战应用案例精讲-【人工智能】语义分割

前言 语义分割是一种典型的计算机视觉问题,其涉及将一些原始数据(例如,平面图像)作为输入并将它们转换为具有突出显示的感兴趣区域的掩模。许多人使用术语全像素语义分割(full-pixel semantic segmentation),其中图像中的每个像素根据其所属的感兴趣对象被分配类别ID。…

跟领导提离职了,现在后悔,想留下来,怎么办?

提完离职后悔了&#xff0c;怎么办&#xff1f;是坚持不变&#xff0c;继续离开&#xff1f;还是厚着脸皮留下来&#xff1f;有些网友支持楼主留下&#xff0c;让他舔回去。有人说&#xff0c;等领导挽留&#xff0c;哪怕是做个样子&#xff0c;就同意留下来&#xff0c;如果完…