数组
数组是顺序存储的随机存取结构,是其他数据结构实现顺序存储的基础。
一维数组的逻辑结构是线性表,多维数组是线性表的拓展。下面就让我们以二维数组为例,说明多维数组的逻辑结构、遍历和存储结构。
1.二维数组的逻辑结构
二维数组是一维数组的扩展,二维数组是“元素为一维数组”的一维数组。一个m行n列的二维数组,既可以看成由m个一维数组(行)所组成的线性表,也可以看成n个一维数组(列)所组成的线性表
其中,每个元素aij(-1<i<m,-1<j<n)同时属于两个线性表:第i行的线性表和第j列的线性表。具体分析如下:
(1)a0,0是起点,没有前驱;am-1,n-1是终点,没有后继。
(2)边界元素,a0,j和ai,0(0<i<m,0<j<n)只有一个前驱;am-1,j和ai,n-1(-1<i<m-1,-1<j<n-1)只有一个后继。
(3)ai,j(0<i<m-1,0<j<n-1)有两个前驱(行前驱ai-1,j和列前驱ai,j-1)和两个后继(行后继ai+1,j和列后继ai,j+1)。
同样,三维数组中的每个元素最多可以有三个前驱和三个后继。推广可知,m维数组的元素最多可以有m个前驱和m个后继。
2.二维数组的遍历
对二维数组进行遍历操作,有两种次序:行主序和列主序。
(1)行主序。以行序为主要次序,按行序递增访问数组每行,同一行按列序递增访问数组元素。
(2)列主序。以列序为主要次序,按列序递增访问数组每列,同一列按行序递增访问数组元素。
3.二维数组的存储结构
二维数组的存储结构,是由多个一维数组组合而成,组合方式有以下两种。
(1)二维数组的顺序存储结构
二维数组的顺序存储结构,将数组元素映射成线性关系存储,映射关系有行主列和列主序。存储结构如下图所示。采用一维数组连续存储二维数组的所有元素,将若干连续的存储单元在逻辑上划分成多个行/列。行主序或列主序由程序设计语言确定。
设二维数组有m行n列,Loc(a0,0)为二维数组的首地址,每个元素占c字节,元素ai,j的地址计算公式如下:
Loc(ai,j)=Loc(a0,0)+(i*n+j)*c //行主序
Loc(aj,i)=Loc(a0,0)+(j*m+i)*c //列主序
由此可见,二维数组元素地址是两个下标的线性函数,给定一对下标,无论行主序或列主序,计算地址花费的时间都是O(1),因此,二维数组也是随机存取结构。
(2)二维数组的动态存储结构
二维数组所包含的多个一维数组可以分散存储,存储结构如下图所示。这也是随机存取结构,计算元素地址花费的时间是O(1)。
无论采用哪种存储结构,多维数组都是基于一维数组存储顶点,因此只能进行赋值、取值两种随机存取操作,不能进行插入、删除等。
4.Java语言的二维数组
Java语言的二维数组都是动态的,采用的引用模型如上图所示。
(1)声明和使用二维数组
声明二维数组变量并动态申请空间4行5列,语句如下:
int []element; //声明二维数组变量,不能指定长度
element=new int[4][5] //动态申请二维数组存储空间,元素已经初始化
声明时可为二维数组赋初值,将值用多层花括号括起来,例如:
int [] [] element={{1,2,3},{4,5,6}};
二维数组element由若干一维数组element[0]等组成,所以element和element[0]军均可以使用length属性表示数组长度,但含义不同。例如:
element.length //返回二维数组的长度,即返回行数
element[0].length //返回一维数组的长度,相当于列数
二维数组第i行第j列元素表示为element[i][j],i的取值范围是0~element.length-1,j的取值范围是0-element[i].length-1。
(2)不规则的二维数组
动态数组的存储结构比静态数组更灵活,所包含的多个一维数组可多次分别申请获得,还可以是不等长的。例如,多次申请二维数组存储空间过程如下所示:
(a)声明数组变量 int element[][];
(b)申请第一维的存储空间 element=new int[3][];
(c)申请第二维的存储空间 element[0]=new int[3]; element[1]=new int[4];
(3)二维数组可作为方法的参数和返回值
二维数组可以作为方法的参数和返回值,参数传递规则同赋值,即传递数组引用。例如:
void print(int element[][]) //二维数组作为方法的参数
int [] [] yanghui(int n) //返回二维数组
应用——矩阵类
矩阵是工程设计中常用的数学对象。设A mn=[ai,j]是由mn个元素ai,j(-1<i<m,-1<j<n)组成的矩阵
在此处第一个元素为a0,0
矩阵运算主要有矩阵加、矩阵减、矩阵乘、矩阵转置等。设Bmn=[bi,j],C=[ci,j],Tmn=[ti,j],矩阵加、减、乘、转置运算定义如下:
设Cmn=Amn+Bmn,有ci,j=ai,j+bi,j。
设Cmn=Amn-Bmn,有ci,j=ai,j-bi,j。
设Tmn=Amn的矩阵转置,有ti,j=aj,i。
矩阵乘法参考如下:
由此我们可以声明矩阵类Matrix如下,其中成员变量rows、columns表示矩阵的行数和列数;二维数组element存储矩阵元素,elemeny[i][j]存储ai,j。
代码如下所示:
package Matrix;
public class Matrix {
private int rows,columns; //矩阵行数、列数
private int[][] element; //二维数组,存储矩阵元素
public Matrix(int m,int n) { //构造m*n零矩阵。若m和n为负数,Java抛出负数组长度异常
this.element=new int[m][n]; //数组元素初值为0
this.rows=m;
this.columns=n;
}
public Matrix(int n) {
this(n,n); //构造n*n零方方阵
}
public Matrix(int m,int n,int[][] value) { //构造m*n矩阵,由value[][]提供元素
this(m,n);
for(int i=0;i<value.length&&i<m;i++) { //value元素不足收获补0,忽略多余元素
for(int j=0;j<value[i].length&&j<n;j++) {
this.element[i][j]=value[i][j];
}
}
}
public int getRows() { //返回矩阵行数
return this.rows;
}
public int getColumns() { //返回矩阵列数
return this.columns;
}
public int get(int i,int j) { //返回矩阵第i行第j列元素。若i、j越界,抛出序号越界异常
if(i>=0&&i<this.rows&&j>=0&&j<this.columns)
return this.element[i][j];
throw new IndexOutOfBoundsException("i="+i+",j="+j);
}
public void set(int i,int j,int x) { //设置矩阵第i行j列元素为x
if(i>=0&&i<this.rows&&j>=0&&j<this.columns) {
this.element[i][j]=x;
}else {
throw new IndexOutOfBoundsException("i="+i+",j="+j);
}
}
public String toString() { //返回矩阵元素描述字符串,行主序遍历
String str=" 矩阵"+this.getClass().getName()+"("+this.rows+"x"+this.columns+"):\n";
for(int i=0;i<this.rows;i++) {
for(int j=0;j<this.columns;j++) {
str+=String.format("%6d", this.element[i][j]); //用"%6d"格式表示十进制整数占6列
}
str+="\n";
}
return str;
}
public static void main(String[] args) {
int value[] []= {{1,2,3},{4,5,6,7,8},{9}};
Matrix mata=new Matrix(3,5,value);
System.out.println("A"+mata.toString());
}
}
运行结果如下: