如何从左到右,从上到下排序的二维数组中搜索数字?

时间:2010-03-16 20:18:01

标签: algorithm search multidimensional-array

我最近接受了这个面试问题,我很好奇它是一个很好的解决方案。

  

说我给了一个二维数组所有的   阵列中的数字正在增加   从左到右,从上到下   底部。

     

搜索和搜索的最佳方式是什么?   确定目标号码是否在   阵列

现在,我的第一个倾向是利用二进制搜索,因为我的数据已经排序。我可以确定O(log N)时间内的数字是否在一行中。然而,这是让我失望的两个方向。

我认为可能有用的另一个解决方案是从中间的某个地方开始。如果中间值小于我的目标,那么我可以确定它在中间的矩阵的左方形部分。然后我对角移动并再次检查,减小目标可能存在的方格的大小,直到我在目标数字上磨练。

有没有人对解决这个问题有任何好的想法?

示例数组:

从左到右,从上到下排序。

1  2  4  5  6  
2  3  5  7  8  
4  6  8  9  10  
5  8  9  10 11  

20 个答案:

答案 0 :(得分:105)

这是一个简单的方法:

  1. 从左下角开始。
  2. 如果目标小于该值,则必须高于我们,因此向上移动
  3. 否则我们知道目标不在该列中,因此向右移动
  4. 转到2。
  5. 对于NxM数组,它在O(N+M)中运行。我认为要做得更好很难。 :)


    编辑:很多讨论很好。我在谈论上面的一般情况;很明显,如果NM很小,您可以使用二分搜索方法在接近对数时间的情况下执行此操作。

    以下是一些细节,对于那些好奇的人:

    历史

    这个简单的算法称为Saddleback Search。它已经存在了一段时间,并且在N == M时是最佳的。一些参考文献:

    然而,当N < M时,直觉表明二进制搜索应该能够比O(N+M)做得更好:例如,当N == 1时,纯二进制搜索将以对数运行而不是线性时间。

    最坏情况绑定

    理查德·伯德(Richard Bird)研究了这种直觉,二元搜索可以在2006年的论文中改进Saddleback算法:

    使用一种相当不寻常的会话技巧,Bird告诉我们,对于N <= M,此问题的下限为Ω(N * log(M/N))。这个界限是有道理的,因为它给我们N == M时的线性表现和N == 1时的对数表现。

    矩形阵列的算法

    使用逐行二进制搜索的一种方法如下所示:

    1. N < M所在的矩形阵列开始。假设N是行,M是列。
    2. value的中间行进行二进制搜索。如果我们找到它,我们就完成了。
    3. 否则,我们发现了一对相邻的数字sg,其中s < value < g
    4. s上方和左侧的数字矩形小于value,因此我们可以将其消除。
    5. g下方和右侧的矩形大于value,因此我们可以将其消除。
    6. 对剩余的两个矩形中的每一个都转到步骤(2)。
    7. 就最坏情况的复杂性而言,该算法确实log(M)可以消除一半可能的解决方案,然后在两个较小的问题上递归调用两次。我们必须为每一行重复log(M)工作的较小版本,但如果行数与列数相比较小,则能够在对数时间内消除所有这些列开始变得有价值

      这为算法提供了T(N,M) = log(M) + 2 * T(M/2, N/2)的复杂性,Bird显示为O(N * log(M/N))

      Another approach posted by Craig Gidney描述了一种与上述方法类似的算法:它使用步长M/N一次检查一行。他的分析表明,这也会带来O(N * log(M/N))的表现。

      性能比较

      Big-O分析一切都很好,但这些方法在实践中的效果如何?下面的图表检查了越来越“方形”数组的四种算法:

      algorithm performance vs squareness

      (“天真”算法只搜索数组的每个元素。“递归”算法如上所述。“混合”算法是Gidney's algorithm的实现。对于每个数组大小,性能通过在固定的1,000,000个随机生成的数组上对每个算法进行计时。)

      一些值得注意的要点:

      • 正如所料,“二分搜索”算法在矩形阵列上提供最佳性能,而Saddleback算法在方阵上效果最佳。
      • Saddleback算法比1-d阵列的“天真”算法表现更差,可能是因为它对每个项目进行了多次比较。
      • “二分搜索”算法对方阵的影响可能是由于运行重复二进制搜索的开销所致。

      摘要

      巧妙地使用二进制搜索可以为矩形和方形数组提供O(N * log(M/N)性能。 O(N + M)“saddleback”算法要简单得多,但随着阵列变得越来越矩形,性能会下降。

答案 1 :(得分:32)

此问题需要Θ(b lg(t))时间,b = min(w,h)t=b/max(w,h)。我在this blog post中讨论了解决方案。

下限

攻击者可以通过将自身限制在主对角线来强制算法进行Ω(b lg(t))查询:

Adversary using main diagonal

图例:白色单元格较小,灰色单元格较大,黄色单元格较小或相等,橙色单元格较大或相等。攻击者强制解决方案为算法查询的黄色或橙色单元格。

请注意,b个独立排序列表的大小为t,需要Ω(b lg(t))个查询才能完全消除。

<强>算法

  1. (假设不失一般性w >= h
  2. 将目标项目与有效区域右上角左侧的单元格t进行比较
    • 如果单元格的项目匹配,则返回当前位置。
    • 如果单元格的项目小于目标项目,则使用二进制搜索消除行中剩余的t单元格。如果在执行此操作时找到匹配的项目,请返回其位置。
    • 否则,单元格的项目超过目标项目,消除了t短列。
  3. 如果没有剩余有效区域,则返回失败
  4. 转到第2步
  5. 寻找项目:

    Finding an item

    确定项目不存在:

    Determining an item doesn't exist

    图例:白色单元格较小,灰色单元格较大,绿色单元格相同。

    <强>分析

    要消除b*t个短列。要消除b个长行。消除长行需要花费O(lg(t))时间。消除t短列需要花费O(1)时间。

    在最糟糕的情况下,我们必须消除每一列和每一行,花费时间O(lg(t)*b + b*t*1/t) = O(b lg(t))

    请注意,我假设lg钳位到高于1的结果(即lg(x) = log_2(max(2,x)))。这就是为什么当w=h,即t=1时,我们得到O(b lg(1)) = O(b) = O(w+h)的预期界限。

    <强>代码

    public static Tuple<int, int> TryFindItemInSortedMatrix<T>(this IReadOnlyList<IReadOnlyList<T>> grid, T item, IComparer<T> comparer = null) {
        if (grid == null) throw new ArgumentNullException("grid");
        comparer = comparer ?? Comparer<T>.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;
    }
    

答案 2 :(得分:17)

我会对这个问题使用分而治之的策略,类似于你的建议,但细节有点不同。

这将是对矩阵子范围的递归搜索。

在每个步骤中,选择范围中间的元素。如果找到的值是您正在寻找的,那么您就完成了。

否则,如果找到的值小于您要搜索的值,则表示它不在您当前位置的上方和左侧的象限中。因此,递归搜索两个子范围:当前位置下方的所有内容(专有),以及位于当前位置或以上位置的所有内容(仅限于此)。

否则,(找到的值大于您要搜索的值)您知道它不在您当前位置下方和右侧的象限中。因此,递归搜索两个子范围:当前位置左侧的所有内容(排他性),以及当前列上当前位置或右侧列上的所有内容(仅排他地)。

ba-da-bing,你找到了它。

请注意,每个递归调用仅处理当前子范围,而不是(例如)当前位置上方的所有行。只是当前子范围内的那些。

这里有一些伪代码:

bool numberSearch(int[][] arr, int value, int minX, int maxX, int minY, int maxY)

if (minX == maxX and minY == maxY and arr[minX,minY] != value)
    return false
if (arr[minX,minY] > value) return false;  // Early exits if the value can't be in 
if (arr[maxX,maxY] < value) return false;  // this subrange at all.
int nextX = (minX + maxX) / 2
int nextY = (minY + maxY) / 2
if (arr[nextX,nextY] == value)
{
    print nextX,nextY
    return true
}
else if (arr[nextX,nextY] < value)
{
    if (numberSearch(arr, value, minX, maxX, nextY + 1, maxY))
        return true
    return numberSearch(arr, value, nextX + 1, maxX, minY, nextY)
}
else
{
    if (numberSearch(arr, value, minX, nextX - 1, minY, maxY))
        return true
    reutrn numberSearch(arr, value, nextX, maxX, minY, nextY)
}

答案 3 :(得分:6)

到目前为止,两个主要答案似乎是可论证的O(log N)“ZigZag方法”和O(N+M)二元搜索方法。我以为我会做一些测试,比较两种方法和一些不同的设置。以下是详细信息:

在每次测试中,数组都是N x N平方,N从125到8000不等(我的JVM堆最大可以处理)。对于每个数组大小,我在数组中选择一个随机位置以放置一个2。然后我将3放在任何地方(在2的右侧和下方),然后用1填充数组的其余部分。一些早期的评论者似乎认为这种类型的设置会产生两种算法的最坏情况运行时间。对于每个阵列大小,我为2(搜索目标)选择了100个不同的随机位置并运行测试。我记录了每种算法的平均运行时间和最差情况运行时间。因为它发生得太快而无法在Java中获得良好的ms读数,并且因为我不相信Java的nanoTime(),所以我重复每次测试1000次,只是为了一直添加一个统一的偏差因子。结果如下:

enter image description here

ZigZag在平均和最差情况下的每次测试中均胜过二进制,然而,它们或多或少都在一个数量级内。

这是Java代码:

public class SearchSortedArray2D {

    static boolean findZigZag(int[][] a, int t) {
        int i = 0;
        int j = a.length - 1;
        while (i <= a.length - 1 && j >= 0) {
            if (a[i][j] == t) return true;
            else if (a[i][j] < t) i++;
            else j--;
        }
        return false;
    }

    static boolean findBinarySearch(int[][] a, int t) {
        return findBinarySearch(a, t, 0, 0, a.length - 1, a.length - 1);
    }

    static boolean findBinarySearch(int[][] a, int t,
            int r1, int c1, int r2, int c2) {
        if (r1 > r2 || c1 > c2) return false; 
        if (r1 == r2 && c1 == c2 && a[r1][c1] != t) return false;
        if (a[r1][c1] > t) return false;
        if (a[r2][c2] < t) return false;

        int rm = (r1 + r2) / 2;
        int cm = (c1 + c2) / 2;
        if (a[rm][cm] == t) return true;
        else if (a[rm][cm] > t) {
            boolean b1 = findBinarySearch(a, t, r1, c1, r2, cm - 1);
            boolean b2 = findBinarySearch(a, t, r1, cm, rm - 1, c2);
            return (b1 || b2);
        } else {
            boolean b1 = findBinarySearch(a, t, r1, cm + 1, rm, c2);
            boolean b2 = findBinarySearch(a, t, rm + 1, c1, r2, c2);
            return (b1 || b2);
        }
    }

    static void randomizeArray(int[][] a, int N) {
        int ri = (int) (Math.random() * N);
        int rj = (int) (Math.random() * N);
        a[ri][rj] = 2;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (i == ri && j == rj) continue;
                else if (i > ri || j > rj) a[i][j] = 3;
                else a[i][j] = 1;
            }
        }
    }

    public static void main(String[] args) {

        int N = 8000;
        int[][] a = new int[N][N];
        int randoms = 100;
        int repeats = 1000;

        long start, end, duration;
        long zigMin = Integer.MAX_VALUE, zigMax = Integer.MIN_VALUE;
        long binMin = Integer.MAX_VALUE, binMax = Integer.MIN_VALUE;
        long zigSum = 0, zigAvg;
        long binSum = 0, binAvg;

        for (int k = 0; k < randoms; k++) {
            randomizeArray(a, N);

            start = System.currentTimeMillis();
            for (int i = 0; i < repeats; i++) findZigZag(a, 2);
            end = System.currentTimeMillis();
            duration = end - start;
            zigSum += duration;
            zigMin = Math.min(zigMin, duration);
            zigMax = Math.max(zigMax, duration);

            start = System.currentTimeMillis();
            for (int i = 0; i < repeats; i++) findBinarySearch(a, 2);
            end = System.currentTimeMillis();
            duration = end - start;
            binSum += duration;
            binMin = Math.min(binMin, duration);
            binMax = Math.max(binMax, duration);
        }
        zigAvg = zigSum / randoms;
        binAvg = binSum / randoms;

        System.out.println(findZigZag(a, 2) ?
                "Found via zigzag method. " : "ERROR. ");
        //System.out.println("min search time: " + zigMin + "ms");
        System.out.println("max search time: " + zigMax + "ms");
        System.out.println("avg search time: " + zigAvg + "ms");

        System.out.println();

        System.out.println(findBinarySearch(a, 2) ?
                "Found via binary search method. " : "ERROR. ");
        //System.out.println("min search time: " + binMin + "ms");
        System.out.println("max search time: " + binMax + "ms");
        System.out.println("avg search time: " + binAvg + "ms");
    }
}

答案 4 :(得分:5)

这是对问题下限的简短证明。

你不能比线性时间(在数组维度方面,而不是元素数量方面)做得更好。在下面的数组中,标记为*的每个元素可以是5或6(独立于其他元素)。因此,如果您的目标值是6(或5),则算法需要检查所有这些值。

1 2 3 4 *
2 3 4 * 7
3 4 * 7 8
4 * 7 8 9
* 7 8 9 10

当然,这也扩展到更大的阵列。这意味着this answer是最佳的。

更新:正如Jeffrey L Whitledge所指出的那样,它只是最优的运行时间与输入数据大小的渐近下限(作为单个变量处理)。可以改进在两个阵列维度上被视为双变量函数的运行时间。

答案 5 :(得分:4)

我认为这是答案,它适用于任何类型的排序矩阵

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));
    }
}

答案 6 :(得分:1)

有趣的问题。考虑这个想法 - 创建一个边界,其中所有数字都大于您的目标,另一个边界所有数字都小于您的目标。如果在两者之间留下任何东西,那就是你的目标。

如果我在你的例子中寻找3,我会读到第一行,直到我达到4,然后寻找最大的相邻数字(包括对角线)大于3:

1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11

现在我对那些小于3的数字做同样的事情:

1 2 4 5 6
2 3 5 7 8
4 6 8 9 10
5 8 9 10 11

现在我问,这两个界限内是什么?如果是的话,它必须是3.如果不是,则没有3.间接的排序,因为我实际上没有找到该数字,我只是推断它必须在那里。这有额外的好处,计算所有3的。

我在一些例子上尝试了这个,似乎工作正常。

答案 7 :(得分:1)

通过阵列对角线的二进制搜索是最佳选择。 我们可以找出该元素是否小于或等于对角线中的元素。

答案 8 :(得分:0)

十年来的大部分时间里,我一直在采访中问这个问题,我认为只有一个人能够提出最佳算法。

我的解决方案一直是:

  1. 二进制搜索中间的对角线,该对角线是向右下延伸的对角线,其中包含(rows.count/2, columns.count/2)处的项。

  2. 如果找到目标号码,则返回true。

  3. 否则,将找到两个数字(uv),使得u小于目标值,v大于目标值,而vu的一右一右。

  4. 以递归方式搜索u右侧和v顶部的子矩阵,以及u底部和v左侧的子矩阵。

我认为,这是对Nate here给出的算法的严格改进,因为搜索对角线通常可以减少一半以上的搜索空间(如果矩阵接近于正方形),而搜索行或列始终会消除一半。

这是Swift中的代码(可能不是Swifty):

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)
        }
    }
}

答案 9 :(得分:0)

一个。在目标号码所在的那些行上进行二进制搜索。

B中。使它成为一个图形:通过始终使用最小的未访问的邻居节点来查找数字,并在找到太大的数字时进行回溯

答案 10 :(得分:0)

如果 O(M log(N))解决方案适用于MxN阵列 -

template <size_t n>
struct MN * get(int a[][n], int k, int M, int N){
  struct MN *result = new MN;
  result->m = -1;
  result->n = -1;

  /* Do a binary search on each row since rows (and columns too) are sorted. */
  for(int i = 0; i < M; i++){
    int lo = 0; int hi = N - 1;
    while(lo <= hi){
      int mid = lo + (hi-lo)/2;
      if(k < a[i][mid]) hi = mid - 1;
      else if (k > a[i][mid]) lo = mid + 1;
      else{
        result->m = i;
        result->n = mid;
        return result;
      }
    }
  }
  return result;
}

Working C++ demo.

如果这不起作用或者有错误,请告诉我。

答案 11 :(得分:0)

我建议将所有字符存储在2D list中。然后找到所需元素的索引(如果它存在于列表中)。

如果不存在则打印相应的消息,否则打印行和列为:

row = (index/total_columns)column = (index%total_columns -1)

这将仅在列表中产生二进制搜索时间。

请提出任何更正建议。 :)

答案 12 :(得分:0)

最佳解决方案是从左上角开始,它具有最小的价值。向右对角线向右移动,直到您点击其值> gt =给定元素的值的元素。如果元素的值等于给定元素的值,则返回find为true。

否则,从这里我们可以通过两种方式进行。

策略1:

  1. 在列中向上移动并搜索给定元素,直到我们到达结尾。如果找到,则返回发现为真
  2. 在行中向左移动并搜索给定元素,直到我们到达结尾。如果找到,则返回发现为真
  3. 返回发现为假
  4. 策略2: 让我表示行索引,j表示我们已经停止的对角元素的列索引。 (这里,我们有i = j,BTW)。设k = 1。

    • 重复以下步骤,直到i-k> = 0
      1. 搜索[i-k] [j]是否等于给定元素。如果是,则返回发现为真。
      2. 搜索a [i] [j-k]是否等于给定元素。如果是,则返回发现为真。
      3. 增量k

    1 2 4 5 6
    2 3 5 7 8
    4 6 8 9 10
    5 8 9 10 11

答案 13 :(得分:0)

public boolean searchSortedMatrix(int arr[][] , int key , int minX , int maxX , int minY , int maxY){

    // base case for recursion
    if(minX > maxX || minY > maxY)
        return false ;
    // early fails
    // array not properly intialized
    if(arr==null || arr.length==0)
        return false ;
    // arr[0][0]> key return false
    if(arr[minX][minY]>key)
        return false ;
    // arr[maxX][maxY]<key return false
    if(arr[maxX][maxY]<key)
        return false ;
    //int temp1 = minX ;
    //int temp2 = minY ;
    int midX = (minX+maxX)/2 ;
    //if(temp1==midX){midX+=1 ;}
    int midY = (minY+maxY)/2 ;
    //if(temp2==midY){midY+=1 ;}


    // arr[midX][midY] = key ? then value found
    if(arr[midX][midY] == key)
        return true ;
    // alas ! i have to keep looking

    // arr[midX][midY] < key ? search right quad and bottom matrix ;
    if(arr[midX][midY] < key){
        if( searchSortedMatrix(arr ,key , minX,maxX , midY+1 , maxY))
            return true ;
        // search bottom half of matrix
        if( searchSortedMatrix(arr ,key , midX+1,maxX , minY , maxY))
            return true ;
    }
    // arr[midX][midY] > key ? search left quad matrix ;
    else {
         return(searchSortedMatrix(arr , key , minX,midX-1,minY,midY-1));
    }
    return false ;

}

答案 14 :(得分:0)

我有一个递归的Divide&amp;征服解决方案。 一步的基本思路是:我们知道左上(LU)最小而右下(RB)是最大的数字,因此给定的否(N)必须:N&gt; = LU和N <= RB

如果N == LU和N == RB :::: Element Found和Abort返回位置/索引 如果N> = LU并且N <= RB = FALSE,则不存在并且中止。 如果N> = LU并且N <= RB = TRUE,则以逻辑方式将2D阵列分成4个相等的2D阵列部分。     然后将相同的算法步骤应用于所有四个子阵列。

我的Algo是正确的我已经在我的朋友PC上实现了。 复杂性:在最坏的情况下,每4个比较可以用来将元素的总数减少到四分之一。 所以我的复杂性是1 + 4 x lg(n)+ 4 但真的希望这可以用于O(n)

我认为在计算复杂性时某些地方出了问题,如果是这样,请更正..

答案 15 :(得分:0)

编辑:

我误解了这个问题。正如评论所指出的那样,这只适用于更受限制的情况。

在C语言中以行主要顺序存储数据,只需将其视为大小为n * m的一维数组,并使用二进制搜索。

答案 16 :(得分:0)

嗯,首先,让我们假设我们正在使用正方形。

1 2 3
2 3 4
3 4 5

<强> 1。搜索正方形

我会在对角线上使用二进制搜索。目标是找到不严格低于目标数量的较小数字。

我说我正在寻找4,然后我最终会在5找到(2,2)

然后,我确信如果表格中有4,则(x,2)位于(2,x)x [0,2]。好吧,这只是2个二进制搜索。

复杂性并不令人生畏:O(log(N))(长度范围为N的3次二进制搜索)

<强> 2。搜索矩形,天真的方法

当然,当NM不同(带有矩形)时会有点复杂,请考虑这种退化情况:

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

让我们说我正在寻找9 ...对角线方法仍然很好,但对角线的定义发生变化。这里我的对角线是[1, (5 or 6), 17]。假设我选择了[1,5,17],那么我知道如果表中有9,则它位于子部分中:

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

这给了我们2个矩形:

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

所以我们可以递归!可能从较少元素开始(虽然在这种情况下它会杀死我们)。

我应该指出,如果其中一个维度小于3,我们就无法应用对角线方法,必须使用二进制搜索。这意味着:

  • 10 11 12 13 14 15 16上应用二进制搜索,找不到
  • 5 6 7 8上应用二进制搜索,找不到
  • 6 7 8 9上应用二进制搜索,找不到

这很棘手,因为为了获得良好的性能,您可能需要区分几种情况,具体取决于一般形状....

第3。搜索矩形,残酷的方法

如果我们处理一个正方形会更容易......所以让我们把事情放在一边。

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

我们现在有一个广场。

当然,我们可能不会实际创建这些行,我们可以简单地模拟它们。

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

所以它的行为就像一个正方形而没有占用更多的内存(以速度为代价,可能,取决于缓存......哦,好吧:p)

答案 17 :(得分:0)

二进制搜索将是最好的方法,imo。从1/2 x开始,1/2 y将其切成两半。 IE 5x5平方就像x == 2 / y == 3。我将一个值向下舍入,将一个值向上舍入到目标值方向的更好区域。

为清楚起见,下一次迭代会给你一些像x == 1 / y == 2 OR x == 3 / y == 5

答案 18 :(得分:-1)

给出如下的方阵:

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

我们知道&lt; c,d&lt; f,i&lt; ķ。我们不知道的是d&lt; c或d> c等我们只有一维保证。

查看结束元素(c,f,k),我们可以做一种过滤:N&lt; C ? search():next()。因此,我们对行进行了n次迭代,每行采用O(log(n))进行二进制搜索,如果滤出则采用O(1)。

让我给出一个例子,其中N = j,

  

1)检查第1行.j&lt; C? (不,下一步)

     

2)检查第2行.j&lt; F? (是的,bin搜索什么都没有)

     

3)检查第3行.j&lt; K + (是的,bin搜索找到它)

再次尝试使用N = q,

  

1)检查第1行.q&lt; C? (不,下一步)

     

2)检查第2行.q&lt; F? (不,下一步)

     

3)检查第3行.q&lt; K + (不,下一步)

可能有更好的解决方案,但这很容易解释.. :)

答案 19 :(得分:-4)

由于这是一个采访问题,它似乎会导致讨论并行编程和Map-reduce 算法。

请参阅http://code.google.com/intl/de/edu/parallel/mapreduce-tutorial.html