How do I search for a number in a 2d array sorted left to right and top to bottom?

后端 未结 20 736
暖寄归人
暖寄归人 2020-11-22 13:03

I was recently given this interview question and I\'m curious what a good solution to it would be.

Say I\'m given a 2d array where all the numbers i

相关标签:
20条回答
  • 2020-11-22 13:08

    Well, to begin with, let us assume we are using a square.

    1 2 3
    2 3 4
    3 4 5
    

    1. Searching a square

    I would use a binary search on the diagonal. The goal is the locate the smaller number that is not strictly lower than the target number.

    Say I am looking for 4 for example, then I would end up locating 5 at (2,2).

    Then, I am assured that if 4 is in the table, it is at a position either (x,2) or (2,x) with x in [0,2]. Well, that's just 2 binary searches.

    The complexity is not daunting: O(log(N)) (3 binary searches on ranges of length N)

    2. Searching a rectangle, naive approach

    Of course, it gets a bit more complicated when N and M differ (with a rectangle), consider this degenerate case:

    1  2  3  4  5  6  7  8
    2  3  4  5  6  7  8  9
    10 11 12 13 14 15 16 17
    

    And let's say I am looking for 9... The diagonal approach is still good, but the definition of diagonal changes. Here my diagonal is [1, (5 or 6), 17]. Let's say I picked up [1,5,17], then I know that if 9 is in the table it is either in the subpart:

                5  6  7  8
                6  7  8  9
    10 11 12 13 14 15 16
    

    This gives us 2 rectangles:

    5 6 7 8    10 11 12 13 14 15 16
    6 7 8 9
    

    So we can recurse! probably beginning by the one with less elements (though in this case it kills us).

    I should point that if one of the dimensions is less than 3, we cannot apply the diagonal methods and must use a binary search. Here it would mean:

    • Apply binary search on 10 11 12 13 14 15 16, not found
    • Apply binary search on 5 6 7 8, not found
    • Apply binary search on 6 7 8 9, not found

    It's tricky because to get good performance you might want to differentiate between several cases, depending on the general shape....

    3. Searching a rectangle, brutal approach

    It would be much easier if we dealt with a square... so let's just square things up.

    1  2  3  4  5  6  7  8
    2  3  4  5  6  7  8  9
    10 11 12 13 14 15 16 17
    17 .  .  .  .  .  .  17
    .                    .
    .                    .
    .                    .
    17 .  .  .  .  .  .  17
    

    We now have a square.

    Of course, we will probably NOT actually create those rows, we could simply emulate them.

    def get(x,y):
      if x < N and y < M: return table[x][y]
      else: return table[N-1][M-1]            # the max
    

    so it behaves like a square without occupying more memory (at the cost of speed, probably, depending on cache... oh well :p)

    0 讨论(0)
  • 2020-11-22 13:08

    I've been asking this question in interviews for the better part of a decade and I think there's only been one person who has been able to come up with an optimal algorithm.

    My solution has always been:

    1. Binary search the middle diagonal, which is the diagonal running down and right, containing the item at (rows.count/2, columns.count/2).

    2. If the target number is found, return true.

    3. Otherwise, two numbers (u and v) will have been found such that u is smaller than the target, v is larger than the target, and v is one right and one down from u.

    4. Recursively search the sub-matrix to the right of u and top of v and the one to the bottom of u and left of v.

    I believe this is a strict improvement over the algorithm given by Nate here, since searching the diagonal often allows a reduction of over half the search space (if the matrix is close to square), whereas searching a row or column always results in an elimination of exactly half.

    Here's the code in (probably not terribly Swifty) Swift:

    import Cocoa
    
    class Solution {
        func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool {
            if (matrix.isEmpty || matrix[0].isEmpty) {
                return false
            }
    
            return _searchMatrix(matrix, 0..<matrix.count, 0..<matrix[0].count, target)
        }
    
        func _searchMatrix(_ matrix: [[Int]], _ rows: Range<Int>, _ columns: Range<Int>, _ target: Int) -> Bool {
            if (rows.count == 0 || columns.count == 0) {
                return false
            }
            if (rows.count == 1) {
                return _binarySearch(matrix, rows.lowerBound, columns, target, true)
            }
            if (columns.count == 1) {
                return _binarySearch(matrix, columns.lowerBound, rows, target, false)
            }
    
            var lowerInflection = (-1, -1)
            var upperInflection = (Int.max, Int.max)
            var currentRows = rows
            var currentColumns = columns
            while (currentRows.count > 0 && currentColumns.count > 0 && upperInflection.0 > lowerInflection.0+1) {
                let rowMidpoint = (currentRows.upperBound + currentRows.lowerBound) / 2
                let columnMidpoint = (currentColumns.upperBound + currentColumns.lowerBound) / 2
                let value = matrix[rowMidpoint][columnMidpoint]
                if (value == target) {
                    return true
                }
    
                if (value > target) {
                    upperInflection = (rowMidpoint, columnMidpoint)
                    currentRows = currentRows.lowerBound..<rowMidpoint
                    currentColumns = currentColumns.lowerBound..<columnMidpoint
                } else {
                    lowerInflection = (rowMidpoint, columnMidpoint)
                    currentRows = rowMidpoint+1..<currentRows.upperBound
                    currentColumns = columnMidpoint+1..<currentColumns.upperBound
                }
            }
            if (lowerInflection.0 == -1) {
                lowerInflection = (upperInflection.0-1, upperInflection.1-1)
            } else if (upperInflection.0 == Int.max) {
                upperInflection = (lowerInflection.0+1, lowerInflection.1+1)
            }
    
            return _searchMatrix(matrix, rows.lowerBound..<lowerInflection.0+1, upperInflection.1..<columns.upperBound, target) || _searchMatrix(matrix, upperInflection.0..<rows.upperBound, columns.lowerBound..<lowerInflection.1+1, target)
        }
    
        func _binarySearch(_ matrix: [[Int]], _ rowOrColumn: Int, _ range: Range<Int>, _ target: Int, _ searchRow : Bool) -> Bool {
            if (range.isEmpty) {
                return false
            }
    
            let midpoint = (range.upperBound + range.lowerBound) / 2
            let value = (searchRow ? matrix[rowOrColumn][midpoint] : matrix[midpoint][rowOrColumn])
            if (value == target) {
                return true
            }
    
            if (value > target) {
                return _binarySearch(matrix, rowOrColumn, range.lowerBound..<midpoint, target, searchRow)
            } else {
                return _binarySearch(matrix, rowOrColumn, midpoint+1..<range.upperBound, target, searchRow)
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 13:11

    As this is an interview question, it would seem to lead towards a discussion of Parallel programming and Map-reduce algorithms.

    See http://code.google.com/intl/de/edu/parallel/mapreduce-tutorial.html

    0 讨论(0)
  • 2020-11-22 13:15

    I think Here is the answer and it works for any kind of sorted matrix

    bool findNum(int arr[][ARR_MAX],int xmin, int xmax, int ymin,int ymax,int key)
    {
        if (xmin > xmax || ymin > ymax || xmax < xmin || ymax < ymin) return false;
        if ((xmin == xmax) && (ymin == ymax) && (arr[xmin][ymin] != key)) return false;
        if (arr[xmin][ymin] > key || arr[xmax][ymax] < key) return false;
        if (arr[xmin][ymin] == key || arr[xmax][ymax] == key) return true;
    
        int xnew = (xmin + xmax)/2;
        int ynew = (ymin + ymax)/2;
    
        if (arr[xnew][ynew] == key) return true;
        if (arr[xnew][ynew] < key)
        {
            if (findNum(arr,xnew+1,xmax,ymin,ymax,key))
                return true;
            return (findNum(arr,xmin,xmax,ynew+1,ymax,key));
        } else {
            if (findNum(arr,xmin,xnew-1,ymin,ymax,key))
                return true;
            return (findNum(arr,xmin,xmax,ymin,ynew-1,key));
        }
    }
    
    0 讨论(0)
  • 2020-11-22 13:16

    Binary search would be the best approach, imo. Starting at 1/2 x, 1/2 y will cut it in half. IE a 5x5 square would be something like x == 2 / y == 3 . I rounded one value down and one value up to better zone in on the direction of the targeted value.

    For clarity the next iteration would give you something like x == 1 / y == 2 OR x == 3 / y == 5

    0 讨论(0)
  • 2020-11-22 13:17

    Given a square matrix as follows:

    [ a b c ]
    [ d e f ]
    [ i j k ]
    

    We know that a < c, d < f, i < k. What we don't know is whether d < c or d > c, etc. We have guarantees only in 1-dimension.

    Looking at the end elements (c,f,k), we can do a sort of filter: is N < c ? search() : next(). Thus, we have n iterations over the rows, with each row taking either O( log( n ) ) for binary search or O( 1 ) if filtered out.

    Let me given an EXAMPLE where N = j,

    1) Check row 1. j < c? (no, go next)

    2) Check row 2. j < f? (yes, bin search gets nothing)

    3) Check row 3. j < k? (yes, bin search finds it)

    Try again with N = q,

    1) Check row 1. q < c? (no, go next)

    2) Check row 2. q < f? (no, go next)

    3) Check row 3. q < k? (no, go next)

    There is probably a better solution out there but this is easy to explain.. :)

    0 讨论(0)
提交回复
热议问题