寻找最佳解决方案

时间:2016-11-22 19:10:33

标签: algorithm data-structures

这是亚马逊的面试问题之一。 给定0和1和1的2D数组,我们需要找到最大尺寸的模式。 Patters如下:

size = 1:

   1
 1 1 1 
   1

size = 2:

   1
   1 
 1 1 1 
   1 
   1

朴素解决方案:遍历MxN矩阵的每个元素,并搜索值为1的索引,并检查是否为左和右。将条目输入为1并记下索引上方和下方的最大长度1。

寻找更好的解决方案。如果有人有线索请发帖。

3 个答案:

答案 0 :(得分:1)

我假设围绕这种模式的任何1个值都不会破坏它,所以这也会有1:

1 1 1 1
1 1 1 1
1 1 0 1
1 1 1 1

在这种情况下,我会建议一种算法,对于每列,您执行以下操作:

  1. 尺寸初始化为0
  2. 对于此列中的每个单元格:
    • 将当前 size 推入堆栈;它表示从该单元格开始向上方向的1个值的数量。
    • 如果此单元格中的值为1,则增加 size ,否则将其设置为0
  3. 尺寸初始化为0
  4. 对于此列中的每个单元格,但顺序相反:
    • 弹出堆栈中的最后一个值
    • 调用 thisSize 最少的弹出值和 size 的值。
    • 如果 thisSize 大于目前为止找到的最佳模式且当前单元格两侧的值均为1,则认为这是最佳模式。
    • 如果此单元格中的值为1,则增加 size ,否则将其设置为0
  5. 作为进一步优化,只要当前单元格与网格顶部之间的距离小于我们之前已找到的最大图案的大小,您就可以退出第二个循环。

    以下是JavaScript中的实现:

    function findPattern(a) {
        var rows = a.length,
            cols = a[0].length,
            maxSize = -1,
            stack = [],
            row, col, pos, thisSize;
            
        for (col = 1; col < cols-1; col++) {
            stack = [];
            // Downward counting to store the number of 1s in upward direction
            size = 0;
            for (row = 0; row < rows; row++) {
                stack.push(size);
                size = a[row][col] == 1 ? size + 1 : 0;
            }
            // Reverse, but only as far as still useful given the size we already found
            size = 0;
            for (row = rows - 1; row > maxSize; row--) {
                thisSize = Math.min(size, stack.pop());
                if (thisSize >= maxSize && a[row][col-1] == 1 && a[row][col+1] == 1) {
                    maxSize = thisSize;
                    pos = [row, col];
                }
                size = a[row][col] == 1 ? size + 1 : 0;
            }
        }
        return [maxSize, pos];
    }
    
    // Sample data:
    var a = [
        [0, 0, 1, 0, 0, 1, 0],
        [0, 0, 1, 1, 0, 1, 0],
        [1, 1, 1, 0, 0, 1, 1],
        [1, 0, 1, 0, 1, 1, 0],
        [1, 1, 1, 1, 0, 1, 0],
        [0, 1, 1, 1, 1, 1, 1],
        [0, 0, 1, 0, 0, 1, 0]];
    
    var [size, pos] = findPattern(a);
    
    console.log('Size ' + size + ' with center at row ' + (pos[0]+1) 
                + ' and column ' + (pos[1]+1) + ' (first row/col is numbered 1)');

答案 1 :(得分:1)

此处:gist.github.com/...是一个通用的 Java 实现,可在任意大小的2D矩阵中找到最大的加号(+)模式。

  1. 我们的想法是首先在矩阵的中心元素(初始窗口)周围找到最大可能的加号(+)模式。对于窗口中的每个元素,找到以该元素为中心的max plus size。

  2. 如果找到最大值,则返回最大尺寸。

  3. 如果可能性最大&#39; +&#39;找不到,存储小于找到的大小的大小,并在前一个窗口周围的下一个外窗口中重复步骤#1搜索1个较小的&#39; +&#39;图案;迭代地搜索&#39; +&#39;在洋葱层图案&#39;从内到外。

  4. 选择初始中心窗口,使得矩阵的边缘在此窗口的所有边上都相等。

    • 示例1 - 对于大小为{4x3}的矩阵,最小的中心窗口位于 从(1,1)到(2,1)
    • 示例2 - 对于大小为{3x9}的矩阵,最小 中心窗口位于(1,1)到(1,7)

      int rows = arr.length;
      int cols = arr[0].length;
      int min = rows < cols ? rows : cols;
      int diff = rows > cols ? rows - cols : cols - rows;
      
      // Initializing initial window params. The center most smallest window possible
      int first_r, first_c, last_r, last_c;
      first_r = (min - 1) / 2;
      first_c = (min - 1) / 2;
      last_r = rows < cols ? (rows / 2) : (cols / 2) + diff;
      last_c = rows > cols ? (cols / 2) : (rows / 2) + diff;
      

    完整的Java代码:

    public class PlusPattern {
    
        /**
         * Utility method to verify matrix dimensions
         *
         * @param a matrix to be verified
         * @return true if matrix size is greater than 0;
         */
        private static boolean isValid(int[][] a) {
            return a.length > 0 && a[0].length > 0;
        }
    
        /**
         * Finds the size of largest plus(+) pattern of given 'symbol' integer in an integer 2D matrix .
         *
         * The idea is to find for the biggest possible plus(+) pattern first around the central elements
         * of the matrix. If largest is found return the largest size. If largest possible + is not
         * found, store the size of whatever smaller than that was found and repeat search for 1 size
         * smaller + in the next outer window around the previous window.
         *
         * @param arr    matrix to be searched
         * @param symbol whose + patter is sought
         * @return the radius of largest + found in the matrix.
         */
        static int findLargestPlusPattern(int[][] arr, int symbol) {
            if (!isValid(arr)) {
                throw new IllegalArgumentException("Cannot perform search on empty array");
            }
            int maxPlusRadius = 0;
    
            int rows = arr.length;
            int cols = arr[0].length;
            int min = rows < cols ? rows : cols;
            int diff = rows > cols ? rows - cols : cols - rows;
    
            // Initializing initial window params. The center most smallest window possible
            // Example - For matrix of size {4x3}, smallest central window lies from [1][1] to [2][1]
            // Example - For matrix of size {3x9}, smallest central window lies from [1][1] to [1][7]
            int first_r, first_c, last_r, last_c;
            first_r = (min - 1) / 2;
            first_c = (min - 1) / 2;
            last_r = rows < cols ? (rows / 2) : (cols / 2) + diff;
            last_c = rows > cols ? (cols / 2) : (rows / 2) + diff;
    
            // Initializing with biggest possible search radius in the matrix
            int searchRadius = (min - 1) / 2;
    
            int r, c;
            int found;
    
            // Iteratively searching for + in an 'onion layer pattern' from inside to outside
            while (searchRadius > maxPlusRadius) {     // no need to find smaller + patterns than the one already found
                // initializing r and c cursor for this window iterations.
                r = first_r;
                c = first_c;
    
                // Search each of the 4 sides of the current window in a clockwise manner
                // 1# Scan the top line of window
                do { // do-while used to search inside initial window with width==1
                    found = findLargestPlusAt(r, c, arr, symbol, searchRadius);
                    if (found == searchRadius) {
                        return searchRadius;      // cannot find a bigger plus(+) than this in remaining matrix
                    } else if (found > maxPlusRadius) {
                        maxPlusRadius = found;
                    }
                    c++;
                } while (c < last_c);
                if (c > last_c)
                    c--; // for initial window with width==1. Otherwise #3 condition will be true for invalid c-index
    
                // 2# Scan the right line of window
                do {  // do-while used to search inside initial window with height==1
                    found = findLargestPlusAt(r, c, arr, symbol, searchRadius);
                    if (found == searchRadius) {
                        return searchRadius;
                    } else if (found > maxPlusRadius) {
                        maxPlusRadius = found;
                    }
                    r++;
                } while (r < last_r);
                if (r > last_r)
                    r--; // for initial window with height==1. Otherwise #4 condition will be true for invalid r-index
    
                // 3# Scan the bottom line of window
                while (c > first_c) {
                    found = findLargestPlusAt(r, c, arr, symbol, searchRadius);
                    if (found == searchRadius) {
                        return searchRadius;
                    } else if (found > maxPlusRadius) {
                        maxPlusRadius = found;
                    }
                    c--;
                }
    
                // 4# Scan the left line of window
                while (r > first_r) {
                    found = findLargestPlusAt(r, c, arr, symbol, searchRadius);
                    if (found == searchRadius) {
                        return searchRadius;
                    } else if (found > maxPlusRadius) {
                        maxPlusRadius = found;
                    }
                    r--;
                }
                // r and c comes back at first_r and first_c.
    
                // increasing window on all sides by 1.
                first_r--;
                first_c--;
                last_r++;
                last_c++;
    
                // reducing search radius to avoid out of bounds error on next window.
                searchRadius--;
            }
            return maxPlusRadius;
        }
    
        /**
         * Finds, if exist, the size of largest plus around a given point a[r][c]. It grows radius
         * greedily to maximise the search for + pattern returns 0 if is the point is the only symbol.
         *
         * @param r          row coordinate of search center
         * @param c          column coordinate of search center
         * @param a          matrix
         * @param symbol     search symbol
         * @param max_radius around the center to be searched for + pattern
         * @return returns -1 if the point itself is not the symbol.
         * returns n if all the next elements in E W N S directions within radius n are the symbol elements.
         */
        static int findLargestPlusAt(int r, int c, int[][] a, int symbol, int max_radius) {
            int largest = -1;
    
            if (a[r][c] != symbol) {   // If center coordinate itself is not the symbol
                return largest;
            } else {
                largest = 0;
            }
            for (int rad = 1; rad <= max_radius; rad++) {
                if (a[r + rad][c] == symbol && a[r][c + rad] == symbol && a[r - rad][c] == symbol && a[r][c - rad] == symbol) {
                    largest = rad;   // At least a '+' of radius 'rad' is present.
                } else {
                    break;
                }
            }
            return largest;
        }
    
        public static void main(String[] args) {
            int mat[][];
            mat = new int[][]{      // max + = 3
                    {1, 1, 0, 1, 1, 0, 1,},
                    {1, 1, 0, 1, 1, 0, 1,},
                    {1, 1, 0, 1, 1, 0, 1,},
                    {1, 1, 1, 1, 1, 1, 1,},
                    {1, 1, 0, 1, 1, 0, 1,},
                    {1, 1, 0, 1, 1, 0, 1,},
                    {1, 1, 0, 1, 1, 0, 1,},
            };
            int find = findLargestPlusPattern(mat, 1);
            System.out.println("# Max + size radius is : " + find);
            mat = new int[][]{  // max + = 2
                    {1, 1, 9, 1, 1, 9, 1,},
                    {1, 1, 9, 1, 1, 9, 1,},
                    {7, 1, 1, 1, 1, 1, 1,},
                    {1, 1, 9, 1, 1, 9, 1,},
                    {1, 1, 9, 1, 1, 9, 1,},
            };
            find = findLargestPlusPattern(mat, 1);
            System.out.println("# Max + size radius is : " + find);
    
            mat = new int[][]{      // max + = 1
                    {1, 1, 0, 1, 1},
                    {1, 1, 0, 1, 1},
                    {1, 1, 0, 1, 1},
                    {1, 1, 1, 6, 1},
                    {1, 1, 0, 1, 1},
                    {1, 1, 0, 1, 1},
            };
            find = findLargestPlusPattern(mat, 1);
            System.out.println("# Max + size radius is : " + find);
        }
    }
    

答案 2 :(得分:0)

以下使用与他的答案中由trincot提供的逻辑基本相同的逻辑,但是只要连续1中断,就会进行反向扫描。这消除了构建显式堆栈的需要。

运行时间应大致相同。我的方法的唯一优点是该算法使用O(1)额外空间,而trincot使用O(rowCount)额外空间用于堆栈。

额外的堆栈使得代码更短,更易读。

代码在C#中:

class FindPatternResult
{
    public int Size { get; set; }
    public int Col { get; set; }
    public int Row { get; set; }
}

private FindPatternResult FindPattern(int[,] a)
{
    var numCols = a.GetUpperBound(0)+1;
    var numRows = a.GetUpperBound(1)+1;
    var maxSize = -1;
    var maxCol = -1;
    var maxRow = -1;

    // anonymous function for checking matches when there is
    // a break in consecutive 1's.
    var checkForMatch = new Action<int, int, int>((height, bottomRow, col) =>
    {
        var topRow = bottomRow - height + 1;
        for (int row = bottomRow-1; row > topRow; --row)
        {
            // There's a potential optimization opportunity here.
            // If we get beyond the midpoint and size is decreasing,
            // then if size < maxSize, we can early-out.
            // For example, if maxSize is 3 and tow-topRow < 3,
            // then there can't possibly be a longer match in this column.
            // I didn't add that optimization because it doesn't
            // really change the algorithm. But if the number of rows
            // is very large, it could be meaningful.
            if (a[row, col - 1] == 1 && a[row, col + 1] == 1)
            {
                var size = Math.Min(bottomRow-row, row-topRow);
                if (size > maxSize)
                {
                    maxSize = size;
                    maxCol = col;
                    maxRow = row;
                }
            }
        }
    });
    for (int col = 1; col < numCols - 1; ++col)
    {
        var size = 0;
        for (int row = 0; row < numRows; ++row)
        {
            if (a[row,col] == 1)
            {
                ++size;
            }
            else
            {
                // If size >= 3, then go back and check for a match
                if (size >= 3)
                {
                    checkForMatch(size, row, col);
                }
                size = 0;
            }
        }
        // If we end the loop with size >= 3, then check from the bottom row.
        if (size >= 3)
        {
            checkForMatch(size, numRows - 1, col);
        }
    }

测试:

private void DoIt()
{
    var rslt = FindPattern(_sampleData);
    Console.WriteLine($"Result: {rslt.Size}, [{rslt.Row}, {rslt.Col}]");
}

private readonly int[,] _sampleData =
{
    {0, 0, 1, 0, 0, 1, 0},
    {0, 0, 1, 1, 0, 1, 0},
    {1, 1, 1, 0, 0, 1, 1},
    {1, 0, 1, 0, 1, 1, 0},
    {1, 1, 1, 1, 0, 1, 0},
    {0, 1, 1, 1, 1, 1, 1},
    {0, 0, 1, 0, 0, 1, 0}
};