1. Array数组简介
当我们提到数组时,很多时候都说的是一维数组,一维数组是线性的,但数组本身并不是线性的,它包括二维,多维数组。
数组在内存中的分配是连续的,也就意味着你在创建它时就需要告诉内存它的容量(length)。这里我们用length表示,以和Java中的数组属性一致。length在java的数组中是固有属性,在一般情况下也是所以其他的数组实现是固有属性。
1.1 Array的数学表达
如果你擅长离散,你将会很乐意看见这样的表达方式
数据对象:
数据关系:
在此进行一下上面公式的讲解(并不总会讲解):
数据对象:
我们用n来表示数组的维度,很显然n>0,n=0也意味着该数组是null,每一个个维度都有一个固有属性length,用表示,由此我们就可以定义一个索引来指向数组一个维度中的第 的位置,规定索引从0开始,那么其取值范围应该是 ,那么怎么用索引在找到数据,n维数据需要n个索引来确定一个位置,这个是一个n元组。
数据关系:
那么数据关系又是怎么样的,不擅长离散的人可能比较难理解上面的公式,其实它的关系就是,n个线性数据结构关系的集合。在每一个维度内都是线性的数据结构。更直观一些,确定n-1个不同维的索引值后,即将n维降为1维,呈现的是线性数据结构。
1.2 数组的基本操作
- InitArray(初始化)
- DestoryArray(销毁)
- Value(取值)
- Assgin(赋值)
在这儿仅提一下,因为高级语言(包括C)都有对数组的支持。
当你使用汇编,写单片机,嵌入式的程序时,可能需要考虑这样的问题,这时你将会考虑内存地址,寄存器标志位,串口等问题。你可能会需要开辟一个内存区域去存放一些必要的数据,该内存区域可能就使用到数组结构。
2. 矩阵压缩
矩阵即对应了二维数组,但仍然稍微有些不一样,数组是索引是从0开始的。
我们看两个场景:
-
想一想无平行变的无向图用邻接矩阵的方式表示是什么样的?(随手画了一个)
-
你会怎样保存一个这样的棋局?
2.1 对称矩阵
看第一个问题,我们用邻接矩阵表示一下看看,如下
很容易看出来这是一个对称矩阵,即满足
故我们可以将个元压缩到个元(感兴趣的可以自己算一下)
我们可以进一步将二维数组存储压缩为一维数组存储来存储,与存在一一对应的关系
上述是把矩阵当作矩阵来看,矩阵描述中没有第0行,从第1行开始,在程序往往使用二维数组来存储矩阵,故第一行的索引为0。我们真正使用的索引转换如下:
压缩代码实现:
package xin.ajay.utils;
import javax.xml.crypto.Data;
import java.util.Arrays;
/*
Two-dimensional array
二维数组工具
*/
public class TwoDimArrayUtils {
/**
* 该方法用于 {@code int} 型对称二维数组的压缩.
*
* @param arr 待压缩的对称二维数组
*
* @return 压缩完成的一维数组
*/
public static int[] compressSymmetricArrays(int[][] arr){
//1.获取arr数组长度
int length = arr.length;
//2. 获取压缩后的数组长度
int len =length*(length+1)/2;
//3. 创建一维数组
int[] compressed = new int[len];
//4. 将二维数组压缩为一维数组
for (int i = 0; i < length; i++) {
System.arraycopy(arr[i], 0, compressed, ((i + 1) * i)/ 2, i + 1);
}
return compressed;
}
/**
* 该方法用于 {@code int} 型对称二维数组的解压缩.
*
* @param compressedArr 需要解压为二维对称数组的一维数组
* @return 二维对称数组
*/
public static int[][] decompressSymmetricArrays(int[] compressedArr){
int length = compressedArr.length;
//1. 计算出原数组的n
int len = ((int)Math.sqrt(length * 8 + 1)-1)/2;
//2. 创建原数组
int[][] decompressedArr = new int[len][len];
int i = 0;
//3. 遍历压缩数组解压
for (int k = 0; k < compressedArr.length; k++) {
if(k > i * (i + 1) / 2 + i){
i++;
}
decompressedArr[i][k-i*(i+1)/2] = compressedArr[k];
decompressedArr[k-i*(i+1)/2][i] = compressedArr[k];
}
return decompressedArr;
}
//test
public static void main(String[] args) {
int[][] integers = new int[][]{{0,1,3,6},
{1,2,4,7},
{3,4,5,8},
{6,7,8,9}};
int[] compressedArr = TwoDimArrayUtils.compressSymmetricArrays(integers);
int[][] decompressedArr = TwoDimArrayUtils.decompressSymmetricArrays(compressedArr);
System.out.println("---------压缩前:");
for (int[] i : integers) {
System.out.println(Arrays.toString(i));
}
System.out.println();
System.out.println("--------压缩后:");
System.out.println(Arrays.toString(compressedArr));
System.out.println();
System.out.println("---------解压缩后:");
for (int[] i : decompressedArr) {
System.out.println(Arrays.toString(i));
}
System.out.println();
}
}
输出结果:
---------压缩前:
[0, 1, 3, 6]
[1, 2, 4, 7]
[3, 4, 5, 8]
[6, 7, 8, 9]
--------压缩后:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
---------解压缩后:
[0, 1, 3, 6]
[1, 2, 4, 7]
[3, 4, 5, 8]
[6, 7, 8, 9]
2.2 稀疏矩阵
上图棋盘上的数据如果使用二维数组存储,如下
该数组大部分都是相同的都为0,可以进行稀疏数组的压缩。
原数组含 个单位空间,压缩后,只需要 个单位空间。稀疏数组的第0行存放的分别是原数组的行数、列数和有效数据个数。后存放的是原数组第i行,第j列的值。
代码实现,包含压缩和解压缩:
package xin.ajay.utils;
import java.util.Arrays;
/*
Two-dimensional array
二维数组工具
*/
public class TwoDimArrayUtils {
/**
* 该方法用于 {@code int} 型二维疏数组的压缩.
* 默认 0 为数组中出现的大多少数据。
*
* @param arr 一个二维数组
* @return 压缩后的二维数组
*/
public static int[][] compressSparseArrays(int[][] arr){
return compressSparseArrays(arr,0);
}
/**
* 该方法用于 {@code int} 型二维稀疏数组的压缩.
*
* @param arr 一个二维数组
* @param d 数组中出现的大多数数据,一般为填充的无效数据0
* @return 压缩后的二维数组
*/
public static int[][] compressSparseArrays(int[][] arr,int d){
//非d的数量
int sum =0;
//1. 得到非d的数量
for (int[] a : arr) {
for (int num : a) {
if (num != d) sum++;
}
}
//2. 创建稀疏数组
int[][] sparesArr = new int[sum + 1][3];
sparesArr[0][0] = arr.length;
sparesArr[0][1] = arr[0].length;
sparesArr[0][2] = sum;
int count=0;
//3. 将二维数组中的有效数据存入到稀疏数组中
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
if(arr[i][j]!=d){
count++;
sparesArr[count][0] = i;
sparesArr[count][1] = j;
sparesArr[count][2] = arr[i][j];
}
}
}
return sparesArr;
}
/**
* 解压缩 {@code int} 稀疏数组.
* 默认 0 为原数组中出现的大多少数据。
*
* @param compressedArr 需要解压的稀疏数组
* @return 解压后的二维数组
*/
public static int[][] decompressSparseArrays(int[][] compressedArr){
return decompressSparseArrays(compressedArr,0);
}
/**
* 解压缩 {@code int} 稀疏数组.
*
* @param compressedArr 需要被解压的二维数组
* @param d 原二维数组中的大多数数据,一般为无效的数据0
* @return 解压后的二维数组
*/
public static int[][] decompressSparseArrays(int[][] compressedArr,int d){
int sum = compressedArr[0][2];
//1. 创建二维数组,并初始化
int[][] decompressedArr = new int[compressedArr[0][0]][compressedArr[0][1]];
if(d!=0){
for (int i = 0; i < decompressedArr.length; i++) {
for (int j = 0; j < decompressedArr[i].length; j++) {
decompressedArr[i][j] = d;
}
}
}
//2. 遍历稀疏数组
for (int i = 1; i <= sum; i++) {
decompressedArr[compressedArr[i][0]][compressedArr[i][1]] = compressedArr[i][2];
}
return decompressedArr;
}
//test
public static void main(String[] args) {
int[][] integers = new int[15][15];
integers[6][6] =1;
integers[6][8] =1;
integers[7][7] =2;
integers[8][6] =2;
integers[9][5] =2;
int[][] compressedArr = TwoDimArrayUtils.compressSparseArrays(integers);
int[][] decompressedArr = TwoDimArrayUtils.decompressSparseArrays(compressedArr);
System.out.println("---------压缩前:");
for (int[] i : integers) {
System.out.println(Arrays.toString(i));
}
System.out.println();
System.out.println("--------压缩后:");
for (int[] i : compressedArr) {
System.out.println(Arrays.toString(i));
}
System.out.println();
System.out.println("--------解压后");
for (int[] i : decompressedArr) {
System.out.println(Arrays.toString(i));
}
System.out.println();
}
}
结果显示:
---------压缩前:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
--------压缩后:
[15, 15, 5]
[6, 6, 1]
[6, 8, 1]
[7, 7, 2]
[8, 6, 2]
[9, 5, 2]
--------解压后:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
最后附上以上所有的代码:
package xin.ajay.utils;
import java.util.Arrays;
/**
* Two-dimensional array
* 二维数组工具
*
* 2019/12/9
*
* @author AjayZeng
*/
public class TwoDimArrayUtils {
/**
* 该方法用于对称二维数组的压缩.
* 此处的对称数组使用的是对称矩阵的概念。将对称 n维的二维对称数组压缩为一维数组。
*
* @deprecated 没有实用价值,也未经过测试,不使用
* @param arr 待压缩的对称二维数组
*
* @param <T> 数组存放的被压缩的的对象类型.
* 新生成的一维数组没有对 T 进行克隆,只是将 T 的引用值放在了一个新的数组中
* @return 被压缩成为一维数组
*/
public static <T> T[] compressSymmetricArrays(T[][] arr) {
int length = arr.length;
int len = length * (length + 1) / 2;
@SuppressWarnings("unchecked") T[] compressed = (T[]) new Object[len];
for (int i = 0; i < length; i++) {
System.arraycopy(arr[i], 0, compressed, ((i + 1) * i) / 2, i + 1);
}
return compressed;
}
/**
* 该方法用于 {@code int} 型对称二维数组的压缩.
*
* @param arr 待压缩的对称二维数组
*
* @return 压缩完成的一维数组
*/
public static int[] compressSymmetricArrays(int[][] arr){
//1.获取arr数组长度
int length = arr.length;
//2. 获取压缩后的数组长度
int len =length*(length+1)/2;
//3. 创建一维数组
int[] compressed = new int[len];
//4. 将二维数组压缩为一维数组
for (int i = 0; i < length; i++) {
System.arraycopy(arr[i], 0, compressed, ((i + 1) * i)/ 2, i + 1);
}
return compressed;
}
/**
* 该方法用于 {@code int} 型对称二维数组的解压缩.
*
* @param compressedArr 需要解压为二维对称数组的一维数组
* @return 二维对称数组
*/
public static int[][] decompressSymmetricArrays(int[] compressedArr){
int length = compressedArr.length;
//1. 计算出原数组的n
int len = ((int)Math.sqrt(length * 8 + 1)-1)/2;
//2. 创建原数组
int[][] decompressedArr = new int[len][len];
int i = 0;
//3. 遍历压缩数组解压
for (int k = 0; k < compressedArr.length; k++) {
if(k > i * (i + 1) / 2 + i){
i++;
}
decompressedArr[i][k-i*(i+1)/2] = compressedArr[k];
decompressedArr[k-i*(i+1)/2][i] = compressedArr[k];
}
return decompressedArr;
}
/**
* 该方法用于 {@code int} 型二维疏数组的压缩.
* 默认 0 为数组中出现的大多少数据。
*
* @param arr 一个二维数组
* @return 压缩后的二维数组
*/
public static int[][] compressSparseArrays(int[][] arr){
return compressSparseArrays(arr,0);
}
/**
* 该方法用于 {@code int} 型二维稀疏数组的压缩.
*
* @param arr 一个二维数组
* @param d 数组中出现的大多数数据,一般为填充的无效数据0
* @return 压缩后的二维数组
*/
public static int[][] compressSparseArrays(int[][] arr,int d){
//非d的数量
int sum =0;
//1. 得到非d是数量
for (int[] a : arr) {
for (int num : a) {
if (num != d) sum++;
}
}
//2. 创建稀疏数组
int[][] sparesArr = new int[sum + 1][3];
sparesArr[0][0] = arr.length;
sparesArr[0][1] = arr[0].length;
sparesArr[0][2] = sum;
int count=0;
//3. 将二维数组中的有效数据存入到稀疏数组中
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
if(arr[i][j]!=d){
count++;
sparesArr[count][0] = i;
sparesArr[count][1] = j;
sparesArr[count][2] = arr[i][j];
}
}
}
return sparesArr;
}
/**
* 解压缩 {@code int} 稀疏数组.
* 默认 0 为原数组中出现的大多少数据。
*
* @param compressedArr 需要解压的稀疏数组
* @return 解压后的二维数组
*/
public static int[][] decompressSparseArrays(int[][] compressedArr){
return decompressSparseArrays(compressedArr,0);
}
/**
* 解压缩 {@code int} 稀疏数组.
*
* @param compressedArr 需要被解压的二维数组
* @param d 原二维数组中的大多数数据,一般为无效的数据0
* @return 解压后的二维数组
*/
public static int[][] decompressSparseArrays(int[][] compressedArr,int d){
int sum = compressedArr[0][2];
//1. 创建二维数组,并初始化
int[][] decompressedArr = new int[compressedArr[0][0]][compressedArr[0][1]];
if(d!=0){
for (int i = 0; i < decompressedArr.length; i++) {
for (int j = 0; j < decompressedArr[i].length; j++) {
decompressedArr[i][j] = d;
}
}
}
//2. 遍历稀疏数组
for (int i = 1; i <= sum; i++) {
decompressedArr[compressedArr[i][0]][compressedArr[i][1]] = compressedArr[i][2];
}
return decompressedArr;
}
//测试整型对称数组的压缩
public static void main(String[] args) {
int[][] integers = new int[][]{{0,1,3,6},
{1,2,4,7},
{3,4,5,8},
{6,7,8,9}};
int[] compressedArr = TwoDimArrayUtils.compressSymmetricArrays(integers);
int[][] decompressedArr = TwoDimArrayUtils.decompressSymmetricArrays(compressedArr);
System.out.println("---------压缩前:");
for (int[] i : integers) {
System.out.println(Arrays.toString(i));
}
System.out.println();
System.out.println("--------压缩后:");
System.out.println(Arrays.toString(compressedArr));
System.out.println();
System.out.println("---------解压缩后:");
for (int[] i : decompressedArr) {
System.out.println(Arrays.toString(i));
}
System.out.println();
}
/* //测试整型稀疏数组
public static void main(String[] args) {
int[][] integers = new int[15][15];
integers[6][6] =1;
integers[6][8] =1;
integers[7][7] =2;
integers[8][6] =2;
integers[9][5] =2;
int[][] compressedArr = TwoDimArrayUtils.compressSparseArrays(integers);
int[][] decompressedArr = TwoDimArrayUtils.decompressSparseArrays(compressedArr);
System.out.println("---------压缩前:");
for (int[] i : integers) {
System.out.println(Arrays.toString(i));
}
System.out.println();
System.out.println("--------压缩后:");
for (int[] i : compressedArr) {
System.out.println(Arrays.toString(i));
}
System.out.println();
System.out.println("--------解压后:");
for (int[] i : decompressedArr) {
System.out.println(Arrays.toString(i));
}
System.out.println();
}*/
}
来源:CSDN
作者: Ajay
链接:https://blog.csdn.net/weixin_43741289/article/details/103651390