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

后端 未结 20 945
暖寄归人
暖寄归人 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:25

    This problem takes Θ(b lg(t)) time, where b = min(w,h) and t=b/max(w,h). I discuss the solution in this blog post.

    Lower bound

    An adversary can force an algorithm to make Ω(b lg(t)) queries, by restricting itself to the main diagonal:

    Adversary using main diagonal

    Legend: white cells are smaller items, gray cells are larger items, yellow cells are smaller-or-equal items and orange cells are larger-or-equal items. The adversary forces the solution to be whichever yellow or orange cell the algorithm queries last.

    Notice that there are b independent sorted lists of size t, requiring Ω(b lg(t)) queries to completely eliminate.

    Algorithm

    1. (Assume without loss of generality that w >= h)
    2. Compare the target item against the cell t to the left of the top right corner of the valid area
      • If the cell's item matches, return the current position.
      • If the cell's item is less than the target item, eliminate the remaining t cells in the row with a binary search. If a matching item is found while doing this, return with its position.
      • Otherwise the cell's item is more than the target item, eliminating t short columns.
    3. If there's no valid area left, return failure
    4. Goto step 2

    Finding an item:

    Finding an item

    Determining an item doesn't exist:

    Determining an item doesn't exist

    Legend: white cells are smaller items, gray cells are larger items, and the green cell is an equal item.

    Analysis

    There are b*t short columns to eliminate. There are b long rows to eliminate. Eliminating a long row costs O(lg(t)) time. Eliminating t short columns costs O(1) time.

    In the worst case we'll have to eliminate every column and every row, taking time O(lg(t)*b + b*t*1/t) = O(b lg(t)).

    Note that I'm assuming lg clamps to a result above 1 (i.e. lg(x) = log_2(max(2,x))). That's why when w=h, meaning t=1, we get the expected bound of O(b lg(1)) = O(b) = O(w+h).

    Code

    public static Tuple TryFindItemInSortedMatrix(this IReadOnlyList> grid, T item, IComparer comparer = null) {
        if (grid == null) throw new ArgumentNullException("grid");
        comparer = comparer ?? Comparer.Default;
    
        // check size
        var width = grid.Count;
        if (width == 0) return null;
        var height = grid[0].Count;
        if (height < width) {
            var result = grid.LazyTranspose().TryFindItemInSortedMatrix(item, comparer);
            if (result == null) return null;
            return Tuple.Create(result.Item2, result.Item1);
        }
    
        // search
        var minCol = 0;
        var maxRow = height - 1;
        var t = height / width;
        while (minCol < width && maxRow >= 0) {
            // query the item in the minimum column, t above the maximum row
            var luckyRow = Math.Max(maxRow - t, 0);
            var cmpItemVsLucky = comparer.Compare(item, grid[minCol][luckyRow]);
            if (cmpItemVsLucky == 0) return Tuple.Create(minCol, luckyRow);
    
            // did we eliminate t rows from the bottom?
            if (cmpItemVsLucky < 0) {
                maxRow = luckyRow - 1;
                continue;
            }
    
            // we eliminated most of the current minimum column
            // spend lg(t) time eliminating rest of column
            var minRowInCol = luckyRow + 1;
            var maxRowInCol = maxRow;
            while (minRowInCol <= maxRowInCol) {
                var mid = minRowInCol + (maxRowInCol - minRowInCol + 1) / 2;
                var cmpItemVsMid = comparer.Compare(item, grid[minCol][mid]);
                if (cmpItemVsMid == 0) return Tuple.Create(minCol, mid);
                if (cmpItemVsMid > 0) {
                    minRowInCol = mid + 1;
                } else {
                    maxRowInCol = mid - 1;
                    maxRow = mid - 1;
                }
            }
    
            minCol += 1;
        }
    
        return null;
    }
    

提交回复
热议问题