数组结构

转载请注明来源:http://shicaid.github.io/2016/06/01/%E6%95%B0%E7%BB%84%E7%BB%93%E6%9E%84/

此篇文章主要以矩阵的相关知识来介绍数组结构,至于数组的定义,这篇文章 解释的挺好的,在此就不赘述了。我们直接步入正题吧。

矩阵相加

  什么是矩阵呢?简单说,矩阵是用来描述二维数组的最好方式,也就是说,一个矩阵就相当于一个二维数组。
例如: $$A_{m*n}+B_{m*n}=C_{m*n}$$
即:
$$\begin{bmatrix}1 & 3 & 5\\7 & 9 & 11\\13&15&17\end{bmatrix} \qquad+\qquad \begin{bmatrix}2 & 4 & 6\\8 & 10 & 12\\14&16&18\end{bmatrix} \qquad=\qquad \begin{bmatrix}3 & 7 & 11\\15 & 19 & 23\\27&31&35\end{bmatrix} $$
实现起来很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 矩阵相加
* arrA[][]、arrB[][]、arrResult[][]大小必须相同
* @param arrA m*n(m行n列,以下形如m*n皆为此)
* @param arrB m*n
* @param arrResult 相加结果 m*n
*/
public static void matrixAdd(int arrA[][],int arrB[][],int arrResult[][]){
if (arrA == null || arrB == null || arrResult == null){
throw new NullPointerException("传递非法参数");
}
final int row = arrA.length;
final int col = arrA[0].length;
if (row != arrB.length || row != arrResult.length ||
col != arrB[0].length || col != arrResult[0].length){
throw new IllegalArgumentException("矩阵非法相加");
}
for (int i = 0; i < row; i++) { //行
for (int j = 0; j < col; j++) { //列
arrResult[i][j] = arrA[i][j] + arrB[i][j];
}
}
}

我们传入以下数据

int arrA[][] = {{1,3,5},{7,9,11},{13,15,17}};
int arrB[][] = {{2,4,6},{8,10,12},{14,16,18}};
int arrResult[][] = new int[3][3];

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
矩阵A:
1 3 5
7 9 11
13 15 17
矩阵B:
2 4 6
8 10 12
14 16 18
结果:
3 7 11
15 19 23
27 31 35

矩阵相乘

  在我看来,矩阵的相关运算乘法是最为难理解的,我们先看看数学中矩阵是怎样相乘的。
  假设两个矩阵A和B相乘,必须满足一个条件:A为一个mn的矩阵,B必须为一个np的矩阵。那么AB的结果是一个mp的矩阵C(m,n,p相互独立且都>0)。运算过程如下:
$$\begin{bmatrix}a_{11} & ...... & a_{1n}\\... & ... & ...\\a_{m1}&......&a_{mn}\end{bmatrix}_{m*n} \qquad*\qquad \begin{bmatrix}b_{11} & ...... & b_{1p}\\... & ... & ...\\b_{n1}&......&b_{np}\end{bmatrix}_{n*p} \qquad=\qquad \begin{bmatrix}c_{11} & ...... & c_{1p}\\... & ... & ...\\c_{m1}&......&c_{mp}\end{bmatrix}_{m*p} $$
$c_{11}$的结果是取矩阵A的第1行元素依次和矩阵B的第1列元素相乘之和,$c_{ij}$的坐标是(取A的第i行)(取B的第j列)所得
$c_{11} = a_{11}*b_{11}+a_{12}*b_{21}+a_{13}*b_{31}+...+a_{1n}*b_{n1}$
$\vdots $
$c_{ij} = a_{i1}*b_{1j}+a_{i2}*b_{2j}+a_{i3}*b_{3j}+...+a_{in}*b_{nj}$
$\vdots $
$c_{mp} = a_{m1}*b_{1p}+a_{m2}*b_{2p}+a_{m3}*b_{3p}+...+a_{mn}*b_{np}$
多练习几遍就能理解了,下面我们用代码来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 矩阵相乘
* @param arrA m*n
* @param arrB n*p
* @param arrResult m*p
*/
public static void matrixPuls(int arrA[][],int arrB[][],int arrResult[][]){
if (arrA == null || arrB == null || arrResult == null){
throw new NullPointerException("传递非法参数");
}
final int m = arrA.length;//arrA行数也是arrResult行数
final int n = arrB.length;//arrB行数也是arrA列数
final int p = arrB[0].length;//arrB列数也是arrResult列数
if (m != arrResult.length || n != arrA[0].length || p != arrResult[0].length){
throw new IllegalArgumentException("矩阵非法相乘");
}
//临时储存计算后的每一行的数据
int resultRow[] = new int[n];
for (int i = 0; i < m; i++) { //arrA的行
for (int j = 0; j < n; j++) { // 初始化resultRow的值
resultRow[j] = 0;
}
for (int k = 0; k < p; k++) { //arrB的列
for (int j = 0; j < n; j++) { //arrA 的列
resultRow[k] += arrA[i][j] * arrB[j][k];//arrB[j][k]这里要为k不能是i,因为与arrA[i]行数组相乘的arrB列数组要右移
}
System.out.println("in matrixPuls() : resultRow["+k+"] = "+resultRow[k]);
}
//完成resultRow的数据填充
//将resultRow的数据加进arrResult[i]行中
for (int j = 0; j < p; j++) { //arrResult列
arrResult[i][j] = resultRow[j];
}
}
}

同样,我们把以下数据传入:

int arrA[][] = {{1,3,5},{7,9,11},{13,15,17}};
int arrB[][] = {{2,4,6},{8,10,12},{14,16,18}};
int arrResult[][] = new int[3][3];

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
矩阵A:
1 3 5
7 9 11
13 15 17
矩阵B:
2 4 6
8 10 12
14 16 18
结果:
96 114 132
240 294 348
384 474 564

转置矩阵

  这个就简单了,就是$a_{ij}$和$a_{ji}$交换位置,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 矩阵倒置
* @param previewArr 倒置前 m*n
* @param invertedArr 倒置后 n*m
*/
public static void matrixInvert(int previewArr[][],int invertedArr[][]){
if (previewArr == null || invertedArr == null){
throw new NullPointerException("传递非法参数");
}
final int m = previewArr.length;
final int n = previewArr[0].length;
if (n != invertedArr.length || m != invertedArr[0].length){
throw new IllegalArgumentException("矩阵非法倒置");
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
invertedArr[j][i] = previewArr[i][j];
}
}
}

将arrA[][] = {{1,3,5},{7,9,11},{13,15,17}}带入的

1
2
3
4
5
6
7
8
矩阵A:
1 3 5
7 9 11
13 15 17
结果:
1 7 13
3 9 15
5 11 17

压缩稀疏矩阵

  所谓稀疏矩阵,就是一个矩阵中大部分的元素都为0。如果我们直接使用这个稀疏矩阵,将造成内存的浪费,而这些浪费我们是可以避免的,对,就是用压缩稀疏矩阵的方法。
即将矩阵$\begin{bmatrix}1&0&0&3&0&0&0\\0&0&9&0&0& 11&0\\13&0&0&0&0&0&15\\0&0&0&0&0&0&0\\0&0&7&0&0&0&0\end{bmatrix}_{5*7}$压缩为形如$\begin{bmatrix}5&7&7\\1&1&1\\1&4&3\\2&3&9\\2&6&11\\3&1&13\\3&7&15\\5&3&7\end{bmatrix}_{8*3}$的矩阵。
我们可以用表格来形容压缩后的矩阵,第一行的元素从左到右的含义分别是原矩阵的行数、原矩阵的列数、原矩阵不为0元素的总个数;从第二行开始以后,每一行的含义是:原矩阵的第i行第j列的元素为k。

5 7 7
i1 j1 k1
i2 j2 k2

实现起来也不难:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 压缩稀疏矩阵
* 将大型的sparseArr矩阵压缩为小型的p*3矩阵compressArr
* @param sparseArr m*n
* @return
* 压缩后的矩阵ompressArr
*/
public static int[][] matrixSparse(int sparseArr[][]) {
if (sparseArr == null){
throw new NullPointerException("传递非法参数");
}
final int row = sparseArr.length;
final int col = sparseArr[0].length;
int temp[][] = new int[row*col][3];//row*col以防不够
int compressArr[][] = null;
int sum = 0;
//遍历sparseArr并压缩至temp
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
//空出temp第一行来存放压缩前矩阵的信息
int value = sparseArr[i][j];
if (value != 0){
++sum;
temp[sum][0] = i+1;
temp[sum][1] = j+1;
temp[sum][2] = value;
}
}
}
temp[0][0] = row;
temp[0][1] = col;
temp[0][2] = sum;
compressArr = new int[sum+1][3];
for (int i = 0; i <= sum; i++) {
compressArr[i][0] = temp[i][0];
compressArr[i][1] = temp[i][1];
compressArr[i][2] = temp[i][2];
}
return compressArr;
}

传入矩阵

final int sparseArr[][] = {{0,0,0,0,0,3,0,0,5,0,0},
            {0,0,0,1,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,8,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0},
            {0,9,0,0,0,0,0,0,5,0,0},
            {0,0,0,0,2,0,0,0,0,7,0}};

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sparseArr:
0 0 0 0 0 3 0 0 5 0 0
0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 8 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 9 0 0 0 0 0 0 5 0 0
0 0 0 0 2 0 0 0 0 7 0
result:
6 11 8
1 6 3
1 9 5
2 4 1
3 7 8
5 2 9
5 9 5
6 5 2
6 10 7

除此之外,还有上/下三角矩阵

  上/下三角矩阵有左上三角矩阵、右上三角矩阵、左下三角矩阵、右下三角矩阵,在此就以右上三角矩阵为例进行分析。
  右上三角矩阵,也就是大小为n*n矩阵A的对角线(0,0)–(n,n)下方的元素全为0。也就是:
$\begin{bmatrix}a_{11}&a_{12}&\cdots&a_{n1}\\0&a_{22}&\cdots&a_{n2}\\\vdots&\ddots&\ddots&\vdots\\0&\cdots&0&a_{nn}\end{bmatrix}_{n*n}$

  • A(i,j)$\left\{\begin{matrix}A(i,j)=0&i>j\\A(i,j)=a_{ij}&i<=j\end{matrix}\right.$
  • 共有1+2+…+n=$\frac{n*(n+1)}{2}$个非零元素

  此时我们可以用压缩稀疏矩阵的方法或直接用二维数组保存这矩阵数据,但是总感觉有点“杀猪用牛刀”,这里我用的是一个一维数组来保存。先保存矩阵第一行数据,接着保存第二行…直到保存完全。这样我们会得到一个数字B[$\frac{n*(n+1)}{2}$]。
  我们假设$a_{ij}$在数组B的k位置,即B[k]中。然后我们很容易发现:k=n*(i-1)-$\frac{i*(i-1)}{2}$+j。当然,如果你是一列一列保存进数组B中的话,k=$\frac{j*(j-1)}{2}$+i。所以对于任意一个$a_{ij}$,我们都可以找到对应的B[k]。相关代码比较简单,就略了。

热评文章