计算二维子数组中的真数
Count the number of trues in a 2D subarray
给定布尔值的二维数组:
{{false, false, true, false, true}
{true, false, true, false, false}
{false, true, false, false, false}
{false, false, false, false, false}
{true, true, true, true, true}}
并且不能直接访问数组,必须通过一个现成的方法hasTrue
,取入子数组的起点和终点上面的二维数组和 return 一个布尔值,如果这个子数组至少有 1 true
.
boolean hasTrue(int startX, int startY, int endX, int endY)
例如,如果我们要检查从索引 (0,0)
到 (1,1)
的区域,我们将调用 hasTrue(0,0,1,1)
并且它将 return true
因为索引 (1,0)
的值为 true
。
我们可以给它一个与起点相同的端点。例如,hasOnes(0,0,0,0)
这将仅检查数组的单个索引,该索引包含值 false
并且将 return false
.
我需要实现一种方法来计算给定子数组中的真数,我 必须使用 hasTrue
方法。
int countTrues(int startX, int startY, int endX, int endY)
一种解决方案是从开始索引到结束进行暴力破解,并计算具有 true
的索引的数量。但在最好的情况下,复杂度将是 n*m
.
我正在考虑的另一个解决方案是实现一个递归方法,将整个子数组一次传递给 hasOnes()
,如果整个子数组 returns false
那么我不会需要遍历所有索引我会 return 0
这将是最好的情况 O(1)
.
如果returns true
我会拆分数组,每半检查一次,继续这样做,统计正确的个数。
如何实施第二种解决方案?
我会写 C++ 代码(抱歉)忘记了 Java,但仍然可以帮助您。当然,转换为 Java 并不困难。实现了你递归分成两半的想法,实际上是分成了4个almost-equal象限(sub-rectangles).
int countTrues(int xb, int yb, int xe, int ye) { // x/y begins/ends
if (xb > xe || yb > ye) // zero-size (empty) array
return 0;
bool h = hasTrue(xb, yb, xe, ye);
if (!h || (xb == xe && yb == ye)) // all-false or single-element array
return (h ? 1 : 0);
int xm = (xb + xe) / 2, ym = (yb + ye) / 2; // middle (center) point
return ( // sum counts of 4 almost-equal quadrants (sub-rectangles)
countTrues(xb, yb, xm, ym) + // top-left
countTrues(xm + 1, yb, xe, ym) + // top-right
countTrues(xb, ym + 1, xm, ye) + // bottom-left
countTrues(xm + 1, ym + 1, xe, ye) // bottom-right
);
}
countTrues
函数可以用递归来实现,基本上是先把数组横向一分为二,当剩下的不超过一行时,再纵向一分为二,简单易懂:
int countTrues(int startX, int startY, int endX, int endY) {
if (!hasTrue(startX, startY, endX, endY)) return 0;
if (startX < endX) {
//split horizontally
return countTrues(startX, startY, (startX + endX) / 2, endY) +
countTrues((startX + endX) / 2 + 1, startY, endX, endY);
} else if (startY < endY) {
//split vertically
return countTrues(startX, startY, endX, (startY + endY) / 2) +
countTrues(startX, (startY + endY) / 2 + 1, endX, endY);
}
//only one value left and is true
return 1;
}
这是一个完整的解决方案:
public class Main {
static boolean array[][] = {
{false, false, true, false, true},
{true, false, true, false, false},
{false, true, false, false, false},
{false, false, false, false, false},
{true, true, true, true, true}};
public static void main(String[] args) {
int rowLen = array.length, colLen = array[0].length;
int res = countTrues(0, 0, rowLen - 1, colLen - 1);
System.out.println("number of Trues: " + res);
}
static boolean hasTrue(int startX, int startY, int endX, int endY) {
while (startX <= endX) {
int indexY = startY;
while (indexY <= endY) {
if (array[startX][indexY]) return true;
indexY++;
}
startX++;
}
return false;
}
static int countTrues(int startX, int startY, int endX, int endY) {
if (!hasTrue(startX, startY, endX, endY)) return 0;
if (startX < endX) {
//split horizontally
return countTrues(startX, startY, (startX + endX) / 2, endY) +
countTrues((startX + endX) / 2 + 1, startY, endX, endY);
} else if (startY < endY) {
//split vertically
return countTrues(startX, startY, endX, (startY + endY) / 2) +
countTrues(startX, (startY + endY) / 2 + 1, endX, endY);
}
//only one value left and is true
return 1;
}
}
为了以最有效的方式解决这个问题,我们必须考虑当hasTrue
returns为真时,我们不知道有多少true
;然而,当它 returns 为 false 时,我们知道我们可以丢弃整个区域,因为它的计数(以及任何子区域的计数)将始终为 0。因此我们应该从最大区域的 hasTrue
开始,并且如果 returns 为真,则划分并继续;如果 returns false,我们可以丢弃整个区域。这个的递归实现:
int countTrues(int startX, int startY, int endX, int endY) {
int count = 0;
if (endX >= startX && endY >= startY) {
int midX = (endX + startX) / 2;
int midY = (endY + startY) / 2;
// top-left
if (hasTrue(startX, startY, midX, midY)) {
count += countTrues(startX, startY, midX, midY);
}
// top-right
if (hasTrue(midX + 1, startY, endX, midY)) {
count += countTrues(midX + 1, startY, endX, midY);
}
// bottom-left
if (hasTrue(startX, midY + 1, midX, endY)) {
count += countTrues(startX, midY + 1, midX, endY);
}
// bottom-right
if (hasTrue(midX + 1, midY + 1, endX, endY)) {
count += countTrues(midX + 1, midY + 1, endX, endY);
}
}
return count;
}
此实现中的重要元素是只有当特定分区上的 hasTrue returns 为真时,才会递归调用 countTrues
。没有这个,递归实现并不比 brute-force 迭代实现更好。
由于你不能直接访问数组,只能通过hasTrue
方法,你可以使用[=20构建递归countTrues
方法=]半除法方法。您可以使用 IntStream
进行迭代:
static boolean[][] arr = {
{false, false, true, false, true},
{true, false, true, false, false},
{false, true, false, false, false},
{false, false, false, false, false},
{true, true, true, true, true}};
static boolean hasTrue(int startX, int startY, int endX, int endY) {
// invalid syntax
if (startX > endX || startY > endY)
return false;
return IntStream
// iterate over specified
// range of the rows
.rangeClosed(startX, endX)
// get row by index
// Stream<boolean[]>
.mapToObj(i -> arr[i])
// at least one true in any row
.anyMatch(row -> IntStream
// iterate over specified
// range of the elements
.rangeClosed(startY, endY)
// at least one true in the row
.anyMatch(j -> row[j]));
}
static int countTrues(int startX, int startY, int endX, int endY) {
// no trues on this iteration
if (!hasTrue(startX, startY, endX, endY))
return 0;
// recursive calls
int lengthX = endX - startX;
int lengthY = endY - startY;
int middleX = lengthX / 2;
int middleY = lengthY / 2;
if (lengthX > 0 || lengthY > 0)
return IntStream
// if 'lengthX > 0' then two iterations X:
// from beginning to middle and from
// middle to end, otherwise one iteration
.rangeClosed(0, lengthX > 0 ? 1 : 0)
.map(i -> i == 0 ? 0 : middleX + 1)
.map(i -> IntStream
// if 'lengthY > 0' then two iterations Y:
// from beginning to middle and from
// middle to end, otherwise one iteration
.rangeClosed(0, lengthY > 0 ? 1 : 0)
.map(j -> j == 0 ? 0 : middleY + 1)
// recursive calls, if it is necessary for
// top-left, top-right, bottom-left, bottom-right
.map(j -> countTrues(startX + i, startY + j,
i == 0 ? startX + middleX : endX,
j == 0 ? startY + middleY : endY))
.sum())
.sum();
// if it reached this point, this point is true
return 1;
}
public static void main(String[] args) {
System.out.println(hasTrue(1, 1, 3, 3)); // true
System.out.println(countTrues(1, 1, 3, 3)); // 2
}
给定布尔值的二维数组:
{{false, false, true, false, true}
{true, false, true, false, false}
{false, true, false, false, false}
{false, false, false, false, false}
{true, true, true, true, true}}
并且不能直接访问数组,必须通过一个现成的方法hasTrue
,取入子数组的起点和终点上面的二维数组和 return 一个布尔值,如果这个子数组至少有 1 true
.
boolean hasTrue(int startX, int startY, int endX, int endY)
例如,如果我们要检查从索引 (0,0)
到 (1,1)
的区域,我们将调用 hasTrue(0,0,1,1)
并且它将 return true
因为索引 (1,0)
的值为 true
。
我们可以给它一个与起点相同的端点。例如,hasOnes(0,0,0,0)
这将仅检查数组的单个索引,该索引包含值 false
并且将 return false
.
我需要实现一种方法来计算给定子数组中的真数,我 必须使用 hasTrue
方法。
int countTrues(int startX, int startY, int endX, int endY)
一种解决方案是从开始索引到结束进行暴力破解,并计算具有 true
的索引的数量。但在最好的情况下,复杂度将是 n*m
.
我正在考虑的另一个解决方案是实现一个递归方法,将整个子数组一次传递给 hasOnes()
,如果整个子数组 returns false
那么我不会需要遍历所有索引我会 return 0
这将是最好的情况 O(1)
.
如果returns true
我会拆分数组,每半检查一次,继续这样做,统计正确的个数。
如何实施第二种解决方案?
我会写 C++ 代码(抱歉)忘记了 Java,但仍然可以帮助您。当然,转换为 Java 并不困难。实现了你递归分成两半的想法,实际上是分成了4个almost-equal象限(sub-rectangles).
int countTrues(int xb, int yb, int xe, int ye) { // x/y begins/ends
if (xb > xe || yb > ye) // zero-size (empty) array
return 0;
bool h = hasTrue(xb, yb, xe, ye);
if (!h || (xb == xe && yb == ye)) // all-false or single-element array
return (h ? 1 : 0);
int xm = (xb + xe) / 2, ym = (yb + ye) / 2; // middle (center) point
return ( // sum counts of 4 almost-equal quadrants (sub-rectangles)
countTrues(xb, yb, xm, ym) + // top-left
countTrues(xm + 1, yb, xe, ym) + // top-right
countTrues(xb, ym + 1, xm, ye) + // bottom-left
countTrues(xm + 1, ym + 1, xe, ye) // bottom-right
);
}
countTrues
函数可以用递归来实现,基本上是先把数组横向一分为二,当剩下的不超过一行时,再纵向一分为二,简单易懂:
int countTrues(int startX, int startY, int endX, int endY) {
if (!hasTrue(startX, startY, endX, endY)) return 0;
if (startX < endX) {
//split horizontally
return countTrues(startX, startY, (startX + endX) / 2, endY) +
countTrues((startX + endX) / 2 + 1, startY, endX, endY);
} else if (startY < endY) {
//split vertically
return countTrues(startX, startY, endX, (startY + endY) / 2) +
countTrues(startX, (startY + endY) / 2 + 1, endX, endY);
}
//only one value left and is true
return 1;
}
这是一个完整的解决方案:
public class Main {
static boolean array[][] = {
{false, false, true, false, true},
{true, false, true, false, false},
{false, true, false, false, false},
{false, false, false, false, false},
{true, true, true, true, true}};
public static void main(String[] args) {
int rowLen = array.length, colLen = array[0].length;
int res = countTrues(0, 0, rowLen - 1, colLen - 1);
System.out.println("number of Trues: " + res);
}
static boolean hasTrue(int startX, int startY, int endX, int endY) {
while (startX <= endX) {
int indexY = startY;
while (indexY <= endY) {
if (array[startX][indexY]) return true;
indexY++;
}
startX++;
}
return false;
}
static int countTrues(int startX, int startY, int endX, int endY) {
if (!hasTrue(startX, startY, endX, endY)) return 0;
if (startX < endX) {
//split horizontally
return countTrues(startX, startY, (startX + endX) / 2, endY) +
countTrues((startX + endX) / 2 + 1, startY, endX, endY);
} else if (startY < endY) {
//split vertically
return countTrues(startX, startY, endX, (startY + endY) / 2) +
countTrues(startX, (startY + endY) / 2 + 1, endX, endY);
}
//only one value left and is true
return 1;
}
}
为了以最有效的方式解决这个问题,我们必须考虑当hasTrue
returns为真时,我们不知道有多少true
;然而,当它 returns 为 false 时,我们知道我们可以丢弃整个区域,因为它的计数(以及任何子区域的计数)将始终为 0。因此我们应该从最大区域的 hasTrue
开始,并且如果 returns 为真,则划分并继续;如果 returns false,我们可以丢弃整个区域。这个的递归实现:
int countTrues(int startX, int startY, int endX, int endY) {
int count = 0;
if (endX >= startX && endY >= startY) {
int midX = (endX + startX) / 2;
int midY = (endY + startY) / 2;
// top-left
if (hasTrue(startX, startY, midX, midY)) {
count += countTrues(startX, startY, midX, midY);
}
// top-right
if (hasTrue(midX + 1, startY, endX, midY)) {
count += countTrues(midX + 1, startY, endX, midY);
}
// bottom-left
if (hasTrue(startX, midY + 1, midX, endY)) {
count += countTrues(startX, midY + 1, midX, endY);
}
// bottom-right
if (hasTrue(midX + 1, midY + 1, endX, endY)) {
count += countTrues(midX + 1, midY + 1, endX, endY);
}
}
return count;
}
此实现中的重要元素是只有当特定分区上的 hasTrue returns 为真时,才会递归调用 countTrues
。没有这个,递归实现并不比 brute-force 迭代实现更好。
由于你不能直接访问数组,只能通过hasTrue
方法,你可以使用[=20构建递归countTrues
方法=]半除法方法。您可以使用 IntStream
进行迭代:
static boolean[][] arr = {
{false, false, true, false, true},
{true, false, true, false, false},
{false, true, false, false, false},
{false, false, false, false, false},
{true, true, true, true, true}};
static boolean hasTrue(int startX, int startY, int endX, int endY) {
// invalid syntax
if (startX > endX || startY > endY)
return false;
return IntStream
// iterate over specified
// range of the rows
.rangeClosed(startX, endX)
// get row by index
// Stream<boolean[]>
.mapToObj(i -> arr[i])
// at least one true in any row
.anyMatch(row -> IntStream
// iterate over specified
// range of the elements
.rangeClosed(startY, endY)
// at least one true in the row
.anyMatch(j -> row[j]));
}
static int countTrues(int startX, int startY, int endX, int endY) {
// no trues on this iteration
if (!hasTrue(startX, startY, endX, endY))
return 0;
// recursive calls
int lengthX = endX - startX;
int lengthY = endY - startY;
int middleX = lengthX / 2;
int middleY = lengthY / 2;
if (lengthX > 0 || lengthY > 0)
return IntStream
// if 'lengthX > 0' then two iterations X:
// from beginning to middle and from
// middle to end, otherwise one iteration
.rangeClosed(0, lengthX > 0 ? 1 : 0)
.map(i -> i == 0 ? 0 : middleX + 1)
.map(i -> IntStream
// if 'lengthY > 0' then two iterations Y:
// from beginning to middle and from
// middle to end, otherwise one iteration
.rangeClosed(0, lengthY > 0 ? 1 : 0)
.map(j -> j == 0 ? 0 : middleY + 1)
// recursive calls, if it is necessary for
// top-left, top-right, bottom-left, bottom-right
.map(j -> countTrues(startX + i, startY + j,
i == 0 ? startX + middleX : endX,
j == 0 ? startY + middleY : endY))
.sum())
.sum();
// if it reached this point, this point is true
return 1;
}
public static void main(String[] args) {
System.out.println(hasTrue(1, 1, 3, 3)); // true
System.out.println(countTrues(1, 1, 3, 3)); // 2
}