【问题标题】:How to find the largest rectangle in a 2D array formed by four identical corners?如何在由四个相同角形成的二维数组中找到最大的矩形?
【发布时间】:2018-09-17 09:41:49
【问题描述】:

考虑这个数组:

[
  ["B", "C", "C", "C", "C", "B", "B", "C", "A", "A"],
  ["B", "A", "C", "B", "B", "A", "B", "B", "A", "A"],
  ["B", "C", "B", "C", "A", "A", "A", "B", "C", "B"],
  ["B", "B", "B", "A", "C", "B", "A", "C", "B", "A"],
  ["A", "A", "A", "C", "A", "C", "C", "B", "A", "C"],
  ["A", "B", "B", "A", "A", "C", "B", "C", "C", "C"],
  ["C", "B", "A", "A", "C", "B", "B", "C", "A", "A"]
]

我正在尝试获取此二维数组中面积最大的矩形的宽度和高度。答案应该是 8 * 4 = 32(坐标 (1, 1), (1, 8), (4, 1) 和 (4, 8)),因为它具有相同角的最大面积"A"

【问题讨论】:

  • 我看不到你的解决方案的矩形
  • 矩形是什么意思?
  • 边缘应该是相同的值(例如([1,1]="A", [1,8]="A", [4,1]="A"[4,8 ]="A")
  • 你应该解释A、B、C是颜色并且你想找到4个同色的角
  • 还定义什么是矩形:最大面积周长

标签: javascript algorithm multidimensional-array


【解决方案1】:

只是因为很有趣:

var l = [ 
["B", "C", "C", "C", "C", "B", "B", "C", "A", "A"],
["B", "A", "C", "B", "B", "A", "B", "B", "A", "A"],
["B", "C", "B", "C", "A", "A", "A", "B", "C", "B"],
["B", "B", "B", "A", "C", "B", "A", "C", "B", "A"],
["A", "A", "A", "C", "A", "C", "C", "B", "A", "C"],
["A", "B", "B", "A", "A", "C", "B", "C", "C", "C"],
["C", "B", "A", "A", "C", "B", "B", "C", "A", "A"]];

var squares = [];
//for each position
for (var column=0; column< l[0].length; column++) 
   for (var row=0; row< l.length ; row++ ) 
    //look for all scuares from this position:
    for (var next_column = column+1; next_column < l[0].length; next_column++ )
        if ( l[row][next_column] == l[row][column] )
            for (var next_row = row+1; next_row < l.length; next_row++)
               //if it is a square
               if (l[next_row][column] == l[row][column] && 
                   l[next_row][next_column] == l[row][column]) 
                    //annotate square
                    squares.push( [ column, row, next_column, next_row  ] );

//get areas from squares
var area_l = squares.map(function(an_square) { 
    return (an_square[2]-an_square[0]+1)*(an_square[3]-an_square[1]+1);
  });

//search for big area
var max_area_index = area_l.indexOf(Math.max(  ...area_l  ));

//here it is
console.log( squares[max_area_index] );

结果: Array(4) [1, 1, 8, 4]

【讨论】:

  • 只考虑时间复杂度,这是 O(m²n²),对吧?我认为,一种方法可以测试每个可能的矩形,从最大的开始,然后减小大小,可以产生 O(n³) 解决方案。
  • 嗨@Xufox,感谢cmets :) 我没有优化它,只是一口气写了。我喜欢你的问题。可能有人可以回答它,也有人可以通过一个很好的优化或更优雅的方法来解决这个问题。我不是 JS 开发者,这是显而易见的。
  • 其实,没关系……我在考虑每一个可能的方格。每个可能的矩形都可能具有更差的时间复杂度,尽管关于从最大开始然后减小大小的部分可能值得考虑。
  • @Xufox,启发式规则可能是“不要搜索小于找到的最大矩形”。
  • @Xufox:我有这样的解决方案,等待解锁OP
【解决方案2】:

如果我们认为:

  1. 小矩形与大矩形有 4 个相同角的概率相同

  2. 一个点可以是多个矩形的角与任何其他点一样无论其位置如何 (N = (width-1)*(height-1))

所以我们确认了直观的算法:我们要先寻找更大的矩形,这些矩形更有可能在边上而不是在中心有角,当我们找到它们时迅速停止.

方法一:按面积严格排序矩形

这个解决方案计算大矩形的形状以便寻找它们。

困难的部分是按面积排序矩形不像缩小正方形或圆形那么容易。区域 12 的矩形可以具有 [1,12]、[2,6]、[3,4]、[4,3]、[6,2] 或 [12,1] 的形状,而区域 11 的矩形只能形状为 [1,11] 或 [11,1]。

function getGreatestRectangleStrict () {
    let hMax = tab.length - 1,
        wMax = tab[0].length - 1;
    // Search valid rectangle by decreasing area
    for (let area = (hMax+1)*(wMax+1); area >= 0; --area) {
        // Compute rectangle shapes that fit in tab with given area
        let kMax = Math.min(area, hMax + 1);
        for (let k = 1; k <= kMax ; ++k)
            if (area % k === 0) {
                let height = k - 1,
                    width = area / k - 1;
                if ( width <= wMax ) {
                    // New fitting shape found, test rectangles
                    for (let top = 0; top <= hMax - height; ++top)
                        for (let left = 0; left <= wMax - width; ++left) {
                            let bottom = top + height,
                                right = left + width,
                                a = tab[top][left];
                            if ( a === tab[bottom][left] &&
                                 a === tab[bottom][right] &&
                                 a === tab[top][right])
                                // Valid rectangle: stop search
                                return [top, left, bottom, right];
                        }
                }
            }
    }
}

它在小型阵列上表现得非常好,但在大型阵列上却不行。我认为它需要一种启发式方法来快速找到形状并在找到解决方案时仍然停止。

方法二:混合高效搜索

此解决方案查找矩形,并在找到一些大矩形时加快速度。

function getGreatestRectangleMixed () {
    let greatestArea = 0;
    let greatestRectangle = [];

    // Get horizontal pairs of corners of same color
    // and their vertical positions (y)
    let pairs = {};
    for (let k = 0; k < tab.length; ++k) {
        // Heuristic: alternately search top and bottom
        let y = ( !(k % 2) ? k/2 : tab.length - (k+1)/2);

        let line = tab[y];
        if ( line.length * Math.max(tab.length-y,y+1) < greatestArea )
            break; // Heuristic: height too small

        for (let i = 0; i < line.length - 1; ++i) {
            let color = line[i];
            for (let j = line.length - 1; j > i; --j) {
                if ( (j-i+1) * tab.length < greatestArea )
                    break; // Heuristic: width too small
                if (line[i] === line[j]) {
                    // Pair of corners found
                    let pairKey = i+" "+j;
                    let cornerPair = {
                        corners: [i,j],
                        width: j-i+1,
                        y: y
                    };
                    if (! (color in pairs) )
                        pairs[color] = {};
                    if (! (pairKey in pairs[color]) )
                        // New pair of corners found, keep only first one
                        pairs[color][pairKey] = cornerPair;
                    else {
                        // Rectangle found, check area
                        let isCurrentBottom = pairs[color][pairKey].y < cornerPair.y;
                        let top = (isCurrentBottom ? pairs[color][pairKey] : cornerPair);
                        let bottom = (isCurrentBottom ? cornerPair : pairs[color][pairKey]);
                        let area = top.width * (bottom.y - top.y + 1);
                        if (area > greatestArea) {
                            greatestArea = area;
                            greatestRectangle = [
                                [top.corners[0], top.y],
                                [top.corners[1], top.y],
                                [bottom.corners[0], bottom.y],
                                [bottom.corners[1], bottom.y]
                            ];
                        }
                    }
                }
            }
        }
    }

    return greatestRectangle;
}

这个解决方案在 OP 大小的矩形上比之前的解决方案要慢一些,但是对于更大的矩形更稳定。

【讨论】:

  • 方法 2 对于 n 行和 m 列的时间复杂度为 O(n²m),对吗?它的复杂性相当于将每个垂直角对散列到它们出现在 () 的列中,并为每一列 (m) 构建和/或查找该散列map 以查找匹配对,计算找到的矩形,并仅保留最大的矩形。我试过散列很多不同的东西,比如行到角,角到行,行-行匹配到​​列,矩形部分到其他部分;我只是无法避免最终“检查每个排列” something 以降低到 O(nm) 时间......
  • 我的意思是……除了预先解决每个数组,并在恒定时间内查找哈希。
  • @SebastianSimon:据我记得,我的第二个解决方案实现了一种记忆,说“我找到了这个区域的一个矩形,所以我不再寻找更小的矩形”。最坏的情况就像你说的那样。但平均而言,它应该表现更好。我相信我们可以做得更好,如果这个话题很活跃,我可能会花更多时间改天。
【解决方案3】:

您可以首先获取相关数组中所有相同的字母位置,然后迭代数组以找到最右边和最底部的位置。然后检查另外两个点是否有想要的值。

如果是这样,将找到的矩形推送到临时结果集。稍后对该数组进行排序或直接选择最大区域。

var array = [["B", "C", "C", "C", "C", "B", "B", "C", "A", "A"], ["B", "A", "C", "B", "B", "A", "B", "B", "A", "A"], ["B", "C", "B", "C", "A", "A", "A", "B", "C", "B"], ["B", "B", "B", "A", "C", "B", "A", "C", "B", "A"], ["A", "A", "A", "C", "A", "C", "C", "B", "A", "C"], ["A", "B", "B", "A", "A", "C", "B", "C", "C", "C"], ["C", "B", "A", "A", "C", "B", "B", "C", "A", "A"]],
    points = {},
    rectangles = [],
    count=0;

array.forEach((a, i) => a.forEach((b, j) => (points[b] = points[b] || []).push({ j, i })));

Object
    .keys(points)
    .forEach(k => {
        points[k].slice(0,-1).forEach((p, m) => {
            var n = points[k].length,
                q;

            while (n--) {
                q = points[k][n];
                if (p.i < q.i && p.j < q.j && k === array[p.i][q.j] && k === array[q.i][p.j]) {
                    rectangles.push({ key: k, top: p.i, left: p.j, bottom: q.i, right: q.j, size: (q.i - p.i + 1) * (q.j - p.j + 1) });
                }
            }
        });
    });

rectangles.sort((a, b) => b.size - a.size);

console.log(rectangles);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-04
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多