1. 概述
在Java中,处理二维数组(2D数组)非常常见,尤其是在涉及矩阵运算的任务中,其中一项任务就是计算二维数组中对角线值的总和。
在本教程中,我们将探索对二维数组中主对角线和次对角线的值求和的不同方法。
2. 问题介绍
首先,我们来快速了解一下问题。
二维数组构成一个矩阵,由于我们需要对对角线上的元素求和,我们假设矩阵是n x n,例如,一个4 x 4的二维数组:
static final int[][] MATRIX = new int[][] {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 100 }
};
接下来,让我们明确一下主对角线和次对角线的含义:
- 主对角线:对角线从矩阵的左上角延伸到右下角;例如,在上面的例子中,主对角线上的元素是1、6、11和100
- 次对角线:对角线从右上角延伸至左下角,在同一个例子中,4、7、10和13属于次对角线。
两个对角线值的总和如下:
static final int SUM_MAIN_DIAGONAL = 118; // 1+6+11+100
static final int SUM_SECONDARY_DIAGONAL = 34; // 4+7+10+13
由于我们想要创建方法来覆盖两种对角线类型,因此让我们为它们创建一个枚举:
enum DiagonalType {
Main, Secondary
}
稍后,我们可以将DiagonalType传递给我们的解决方案来获得相应的结果。
3. 识别对角线上的元素
要计算对角线值的和,我们必须首先确定对角线上的元素。在主对角线上,这非常简单,当元素的行索引(rowIdx)和列索引(colIdx)相等时,该元素位于主对角线上,例如MATRIX[0][0] = 1、MATRIX[1][1] = 6和MATRIX[3][3] = 100。
另一方面,给定一个n x n矩阵,如果元素位于次对角线上,则rowIdx + colIdx = n – 1。例如,在我们的4 x 4矩阵示例中,MATRIX[0][3] = 4(0 + 3 = 4 - 1),MATRIX[1][2] = 7(1 + 2 = 4 - 1),MATRIX[3][0] = 13(3 + 0 = 4 - 1)。因此,colIdx = n – rowIdx – 1。
现在我们了解了对角线元素的规则,让我们创建计算总和的方法。
4. 循环方法
一种直接的方法是循环遍历行索引,根据所需的对角线类型对元素求和:
int diagonalSumBySingleLoop(int[][] matrix, DiagonalType diagonalType) {
int sum = 0;
int n = matrix.length;
for (int rowIdx = 0; rowIdx < n; row++) {
int colIdx = diagonalType == Main ? rowIdx : n - rowIdx - 1;
sum += matrix[rowIdx][colIdx];
}
return sum;
}
正如我们在上面的实现中看到的,我们根据给定的diagonalType计算所需的colIdx,然后将rowIdx和colIdx上的元素相加到sum变量中。
接下来我们来测试一下这个解决方案是否能达到预期的效果:
assertEquals(SUM_MAIN_DIAGONAL, diagonalSumBySingleLoop(MATRIX, Main));
assertEquals(SUM_SECONDARY_DIAGONAL, diagonalSumBySingleLoop(MATRIX, Secondary));
事实证明,这种方法可以对两种对角线类型求和正确的值。
5. 使用IntBinaryOperator对象的DiagonalType
基于循环的解决方案很简单,但是,在每个循环步骤中,我们必须检查diagonalType实例来确定colIdx,尽管diagonalType是一个在循环期间不会改变的参数。
接下来我们看看是否可以稍微改进一下。
一个想法是为每个DiagonalType实例分配一个IntBinaryOperator对象,这样我们就可以计算colIdx而无需检查我们拥有哪种对角线类型:
enum DiagonalType {
Main((rowIdx, len) -> rowIdx),
Secondary((rowIdx, len) -> (len - rowIdx - 1));
public final IntBinaryOperator colIdxOp;
DiagonalType(IntBinaryOperator colIdxOp) {
this.colIdxOp = colIdxOp;
}
}
如上代码所示,我们为DiagonalType枚举添加了一个IntBinaryOperator属性,IntBinaryOperation是一个函数式接口,它接收两个int参数并返回一个int值。在此示例中,我们使用两个Lambda表达式作为枚举实例的IntBinaryOperator对象。
现在,我们可以删除for循环中的对角线类型检查的三元运算:
int diagonalSumFunctional(int[][] matrix, DiagonalType diagonalType) {
int sum = 0;
int n = matrix.length;
for (int rowIdx = 0; rowIdx < n; row++) {
sum += matrix[rowIdx][diagonalType.colIdxOp.applyAsInt(rowIdx, n)];
}
return sum;
}
可以看到,我们可以通过调用applyAsInt()直接调用diagonalType的colIdxOp函数来获取所需的colIdx。
当然,测试仍然通过:
assertEquals(SUM_MAIN_DIAGONAL, diagonalSumFunctional(MATRIX, Main));
assertEquals(SUM_SECONDARY_DIAGONAL, diagonalSumFunctional(MATRIX, Secondary));
6. 使用Stream API
除了函数式接口,Java 8带来的另一个重要特性是Stream API。接下来,让我们使用Java 8的这两个特性来解决这个问题:
public int diagonalSumFunctionalByStream(int[][] matrix, DiagonalType diagonalType) {
int n = matrix.length;
return IntStream.range(0, n)
.map(i -> MATRIX[i][diagonalType.colIdxOp.applyAsInt(i, n)])
.sum();
}
在此示例中,我们用IntStream.range()替换了for循环。此外,map()负责将每个索引(i)转换为对角线上所需的元素,然后,sum()生成结果。
最后,这个解决方案也通过了测试:
assertEquals(SUM_MAIN_DIAGONAL, diagonalSumFunctionalByStream(MATRIX, Main));
assertEquals(SUM_SECONDARY_DIAGONAL, diagonalSumFunctionalByStream(MATRIX, Secondary));
与最初的基于循环的解决方案相比,这种方法更流式且更易于阅读。
7. 总结
在本文中,我们探讨了计算二维Java数组中对角线值之和的不同方法,理解主对角线和次对角线的索引是解决问题的关键。
Post Directory
