数组问题:a[i][j] 和 a[j][i] 有什么区别?
本文以一个简单的程序开头——数组赋值:
- int LEN = 10000;
- int[][] arr = new int[LEN][LEN];
- for (int i = 0; i < LEN; i++) {
- for (int j = 0; j < LEN; j++) {
- arr[i][j] = 1;
- }
- }
示例中虽然采用了Java,但是熟悉其他编程语言的同学可以自行脑补成自己熟悉的语言,如C/C++、Go、Python之类的,这里的知识点不限制在语言层级。
我们在使用这种for循环的时候,是否会习惯性地使用arr[i][j]的这种写法? 其实很多开源代码、技术书籍示例中都是如此。你是否还考虑过还有如下这种写法,即将 arr[i][j] 替换成 arr[j][i] :
- int LEN = 10000;
- int[][] arr = new int[LEN][LEN];
- for (int i = 0; i < LEN; i++) {
- for (int j = 0; j < LEN; j++) {
- arr[j][i] = 1;
- }
- }
两段代码功能完全一样,究竟有何区别?两者性能会有数十倍或者数百倍之差。
有些同学看到这里,直接祭出IDE,运行试了一下,发现第一段代码的性能最优,所以很快地得出了a[i][j]最优的结论。不过也会有同学会运行出第二段代码性能更优的结果,即a[j][i]最优。这到底是怎么回事呢?
从语言本身看,这个问题的要点在于语言怎样实现矩阵 arr[M][N] 的存放。有两种最基本的存放方式:一种是第1下标优先存放;另一种是第2下标优先存放。也就是一般所说的行优先(Row-major)和列优先(Column-major)。
举个例子,对于下面的数组:
可以有两种存储方式:左为列优先,右为行优先。
行优先存储,顾名思义,就是一行的数据存放在一起,然后逐行存放。列优先存储,就是每一列的数据是存储在一起的,一列一列地存放在内存中。这两种存储方法,对于编写遍历二维矩阵的循环语句,还是有一定影响的。比如,如果是按行优先存储的,那么在遍历时,一行一行的读取数据,肯定比一列一列地读取整个数组,要方便许多(前者连续访问内存,有利于CPU高速缓存,后者不联系访问内存,会引起频繁更新高速缓存)。
行优先(Row-major)或者列优先(Column-major)没有好坏,但其直接涉及到对内存中数据的最佳存储访问方式。因为在内存使用上,程序访问的内存地址之间连续性越好,程序的访问效率就越高;相应地,程序访问的内存地址之间连续性越差。所以,我们应该尽量在行优先机制的编译器,比如C/C++、Objective-C(for C-style arrays)、Pascal等等上,采用行优先的数据存储方式;在列优先机制的编译器,比如Fortune、Matlab、R等等上,采用列优先的数据存储方式。但这种思想渗透到编程中之后,代码的质量就会提高一个档次。
这里没有提到市场占有率很高的Java语言,那么它们属于行优先还是列优先呢?
答案:两者都不是。
密集数组存储的一个典型替代方案是使用伊利夫向量(Iliffe vector),它通常将元素存储在连续的同一行中(如同Row-major顺序),但不存储行本身。
什么是Iliffe向量?在计算机编程中,Iliffe向量是一种用于实现多维数组的数据结构。n维数组的Iliffe向量(其中n≥2)由指向(n - 1)维数组的指针的向量(或1维数组)组成。它们通常用于避免在对数组元素执行地址计算时执行昂贵的乘法操作。它们还可用于实现交错数组,如三角形数组、三角形矩阵和其他各种不规则形状的数组。数据结构以John K. Iliffe命名。
它们的缺点包括需要多个链接指针来访问一个元素,以及需要额外的工作来确定n维数组中的下一行,以允许优化编译器预取它。在CPU明显快于主存的系统中,这两个问题都是延迟的来源。
在Java、Python(多维列表)、Ruby、Visual Basic . net、Perl、PHP、JavaScript、Objective-C(当使用NSArray时,不是一个Row-magor C-Style的数组)、Swift和Atlas Autocode等语言中的多维数组被实现为Iliffe向量。利用Iliffe向量实现OLAP产品全息的稀疏多维阵列。
Java已测
工具:IDEA;
结果:arr[i][j]较快,大约90ms;arr[j][i]大约2270ms
数组问题:a[i][j] 和 a[j][i] 有什么区别?的更多相关文章
- 无序数组a,求a[i]-a[j]的最大值,且i<j
一道面试题:对于无序数组a,求a[i]-a[j]的最大值,其中i<j package test; import java.util.Arrays; public class FindMax { ...
- 给出一个数组A,找出一对 (i, j)使得A[i] <= A[j] (i < j)并且j-i最大
题目:给出一个数组A,找出一对 (i, j)使得A[i] <= A[j] (i <= j)并且j-i最大 ,若有多个这样的位置对,返回i最小的那一对. 最直接的想法就是对于每一个 i 从数 ...
- 在数组a中,a[i]+a[j]=a[k],求a[k]的最大值,a[k]max——猎八哥fly
在数组a中,a[i]+a[j]=a[k],求a[k]的最大值,a[k]max. 思路:将a中的数组两两相加,组成一个新的数组.并将新的数组和a数组进行sort排序.然后将a数组从大到小与新数组比较,如 ...
- a[i][j] 和 a[j][i] 有什么区别?
本文以一个简单的程序开头--数组赋值: int LEN = 10000;int[][] arr = new int[LEN][LEN]; for (int i = 0; i < LEN; i++ ...
- GCD - Extreme (II) for(i=1;i<N;i++) for(j=i+1;j<=N;j++) { G+=gcd(i,j); } 推导分析+欧拉函数
/** 题目:GCD - Extreme (II) 链接:https://vjudge.net/contest/154246#problem/O 题意: for(i=1;i<N;i++) for ...
- Codeforces Round #191 (Div. 2) A. Flipping Game【*枚举/DP/每次操作可将区间[i,j](1=<i<=j<=n)内牌的状态翻转(即0变1,1变0),求一次翻转操作后,1的个数尽量多】
A. Flipping Game time limit per test 1 second memory limit per test 256 megabytes input standard ...
- 组合数性质求K个数选取i*j个数分成j组的方案数
分析:设方案数为ANS,C代表组合数: ANS=(C[K,I]*C[K-I,I][K-2*I,I]*...*C[K-(J-1)*I,I])/(J!); 也即: ANS=C[K,I*J]*(C[I*J, ...
- 数组的常用方法concat,join,slice和splice的区别,map,foreach,reduce
1.concat()和join() concat()是连对两个或两个数组的方法,直接可以将数组以参数的形式放入 join()是将数组中的所有元素放入一个字符串中,通俗点讲就是可以将数组转换成字符串 2 ...
- Eclipse中Java文件图标由实心J变成空心J的问题
在eclipse中空心J的java文件,表示不被包含在项目中进行编译,而是当做资源存在项目中.例如 当是单个文件为空心J的时候 1.右击该文件 -- >BuildPath -->Inclu ...
随机推荐
- rbd无法map(rbd feature disable)
在jewel版本下默认开启了rbd的一些属性 [root@lab8106 ~]# ceph --show-config|grep rbd|grep features rbd_default_featu ...
- MOOC JAVA笔记
MOOC JAVA笔记 1.基础了解 JDK是开发人员安装的,它提供了开发java程序的必须工具 JRE是普通用户安装的,它提供了java的运行环境 JVM是java虚拟机运行程序的核心 2.程序的移 ...
- unctf2020 部分简单题题解
unctf2020 水一波简单题..菜狗前来报道..大佬轻喷..如果有时间做题就好了呜呜呜 misc 1.baba_is_you 题目告诉我们,了解png文件格式. 下载得到一张png格式的图片. 用 ...
- 如何用FL Studio将乐器组合与分层
有过音乐制作经历的小伙伴应该知道,我们在用以FL Studio20为代表的音乐编曲软件制作音乐时,往往需要在同一节奏点添加多种音效,这样可以使音乐听起来更具层次感.正因如此,我们就需要不断添加音符,就 ...
- python办公入门4:xlrd操作excel行
操作excel行 1 #通过索引获取操作行 2 sheet=data.sheet_by_index(0) 3 #获取当前sheet下的有效行数 4 print(sheet.nrows) 5 #获取某一 ...
- JDBC【3】-- SPI技术使用以及在数据库连接中的使用
目录 1.SPI是什么? 2.如何使用SPI来提供自定义服务? 2.1 DBInterface定义接口 2.2 模拟Mysql实现驱动 2.3 模拟SqlServer实现驱动 2.4 模拟用户使用不同 ...
- JVM类加载机制详解,建议看这一篇就够了,深入浅出总结的十分详细!
类加载机制 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类加载的时机 遇到new(比如n ...
- 01-Python里字符串的常用操作方法--replace()函数
1. replace() 函数 作用: 替换字符串 语法: replace('需要替换的子串', '新的子串', '替换的次数(可不传)') # 当替换次数不传时,默认全部替换 实例一: mystr ...
- 你的Idea还可用吗?不妨试试这个神器!
@ 目录 一.STS安装 1.STS下载 2.STS安装 二.STS使用 1.STS配置JDK 2.STS配置Maven 3.使用STS创建SpringBoot项目 三.优化STS 1.主题美化 2. ...
- 深度优先遍历&广度优先遍历
二叉树的前序遍历,中序遍历,后序遍历 树的遍历: 先根遍历--访问根结点,按照从左至右顺序先根遍历根结点的每一颗子树. 后根遍历--按照从左至右顺序后根遍历根结点的每一颗子树,访问根结点. 先根:AB ...