views:

141

answers:

3

M is a 2D matrix of integers (nXm) they are sorted in both row and column Write a function search(int s) that return the exact location of the number or Null. What would be the most efficient way to do so?

+1  A: 

Say we have

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

Now we search for 6. You can see that there is a "strip" going from top right (5 7) to bottom left (5 6 7) where the values smaller than 6 flip to values bigger than 6 ("strip" marked with *):

1 2 5*7
2 4*7 9
3 5*8 9
5*6*9 9

So an algorithm would be:

  • check if top left is bigger as your number -> return null
  • check if bottom right is smaller as your number -> return null
  • go the diagonal from top left down to bottom right until you find "the strip"
  • follow the strip in both directions, until you find the number.
Landei
isn't it O(n^2)?
I'd rather say that it is O(sqrt(n)). First, when we're talking about sorting a bunnch of numbers, our n is obviously the number of numbers (and not the side length of the square). Finding the strip takes sqrt(n) in the worst case, and the strip itself has a maximum length of 2*sqrt(n). Of course the method gets worse if the array is not square but a flat rectangle.
Landei
so I guess it's not the most efficient algorithm, since I can just iterate the Matrix....There must be a better solution, no?
It's definitely better than iterating through the matrix, but there are some clever solutions in the link KennyTM pointed out ( http://stackoverflow.com/questions/2457792/given-a-2d-array-sorted-in-increasing-order-from-left-to-right-and-top-to-bottom ), which may be better.
Landei
It has the same worst case complexity as any other possible algorithm. Using divide and conquer, we can improve the typical and best case.
mdma
+4  A: 

init: m[1..n(rows),1....m(columns)]

i=n,j=1

Start at (i,j):

STEP 1) if the value equals m(i,j) return (i,j)
STEP 2) if the value > m(i,j) go to step 1 with j+=1
STEP 3) else go to step 1 with i-=1

at each step if j or i is out of bound return no-solution.

The complexity of this solution is O(n+m) in case n=m the complexity is O(n)

I wonder if there is a log(n*m) solution like in binary search

EDIT another possible solution:

STEP 1)take the middle (n/2,m/2) of the matrix and compare it to the value
STEP 2) if the value equals m(i,j) return (i,j)
STEP 3) if the value is higher you can get rid from the first quarter  
STEP 4) if the value is lower you can get rid from the forth quarter
STEP 5) split the 3 other quarters to 2, a rectangle and a box, 
        send those both items to step 1 in a recursive manner

I am not sure about the efficiency of this solution: if R = N*M then T(R) = T(R/2) + T(R/4) + O(1)

MrOhad
A professor gave me this one for fun; I came up with your recursive divide-and-conquer algorithm pretty quickly (which, recursing on squares and setting T(R) = 3T(R/4), is `O(R^(log_{4}(3))) = O(R^0.8)` by Master's Theorem), but couldn't figure out the O(n+m) algorithm. When I finally gave up, as soon as he said "walk the opposite diagonal" I slapped my head as it *immediately* came to me!
BlueRaja - Danny Pflughoeft
A: 

I just opened up notepad and wrote a bit, but I think this will do it in O(x) time, where x is the larger index between n and m. The worst case for this algorithm would be for the search term to be equal to the largest term in the array, for which this method will loop through n or m (whichever is larger) times. If that's going to be common, one can just start from the bottom right instead of the top left, and decrement indicies instead of incrementing them.

int[] SearchArray(int s, int[][] array)
{
    int i = 0;
    int j = 0;
    int n = array.GetLength(0);
    int m = array.GetLength(1);
    if (s > array[n][m])
            return null;
    while (i < n && j < m)
    {
        if (array[i][j] > s)
            return null;
        if (array[i][j] == s)
            return new int[]{i,j};
        try
        {
            if (array[i+1][j+1] < s)
            {
                    i++;
                    j++;
            }
            else if (array[i+1][j] > array[i][j+1])
                if (array[i+1][j] < s)
                    i++;
                else
                    j++;
            else if (array[i][j+1] > array[i+1][j])
                if (array[i][j+1] < s)
                    j++;
                else
                    i++;
            else if (array[i+1][j] == array[i][j+1])
                if (n < m)
                    i++;
                else
                    j++;
        }
        catch(IndexOutOfRangeException e)
        {
            if (i == n-1)
                j++;
            if (k == m-1)
                i++;
        }
    }
}

Edited for optimization and formatting

T.K.