it-swarm.com.de

Wie suche ich nach einer Zahl in einem 2d-Array, das von links nach rechts und von oben nach unten sortiert ist?

Ich habe vor kurzem diese Interviewfrage gestellt und bin gespannt, was eine gute Lösung dafür wäre.

Angenommen, ich bekomme ein 2d-Array, in dem alle Zahlen im Array werden aufsteigend Reihenfolge von links nach rechts und oben zu Unterseite.

Was ist der beste Weg zu suchen und Ermitteln Sie, ob sich eine Zielnummer in der .__ befindet. Array?

Nun möchte ich zunächst eine binäre Suche verwenden, da meine Daten sortiert sind. Ich kann feststellen, ob eine Zahl in der Zeit O (log N) in einer einzelnen Zeile steht. Es sind jedoch die zwei Richtungen, die mich abwerfen.

Eine andere Lösung, von der ich dachte, dass sie funktionieren könnte, ist irgendwo in der Mitte zu beginnen. Wenn der mittlere Wert kleiner als mein Ziel ist, kann ich sicher sein, dass er sich von der Mitte aus im linken quadratischen Bereich der Matrix befindet. Ich bewege mich dann diagonal und überprüfe erneut, wobei ich die Größe des Quadrats verkleinere, in dem sich das Ziel befinden könnte, bis ich die Zielnummer erreicht habe.

Hat jemand gute Ideen, um dieses Problem zu lösen?

Beispiel Array: 

Von links nach rechts und von oben nach unten sortiert.

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

Hier ist ein einfacher Ansatz: 

  1. Beginnen Sie an der linken unteren Ecke. 
  2. Wenn das Ziel kleiner als dieser Wert ist, muss es über uns liegen, also einen Schritt nach oben.
  3. Andernfalls wissen wir, dass sich das Ziel nicht in dieser Spalte befinden kann, also nach rechts verschieben.
  4. Gehe zu 2.

Bei einem NxM-Array wird dies in O(N+M) ausgeführt. Ich denke, es wäre schwierig, es besser zu machen. :)


Edit: Viele gute Diskussionen. Ich habe über den allgemeinen Fall oben gesprochen. Wenn N oder M klein sind, können Sie dies mit einer binären Suchmethode in einer logarithmischen Zeit tun. 

Hier sind einige Details für Neugierige:

Geschichte

Dieser einfache Algorithmus wird als Saddleback Search bezeichnet. Es ist schon eine Weile her und es ist optimal, wenn N == M. Einige Referenzen:

Bei N < M schlägt Intuition jedoch vor, dass die binäre Suche besser als O(N+M) funktionieren sollte: Wenn beispielsweise N == 1 ausgeführt wird, wird eine reine binäre Suche logarithmisch statt linear ausgeführt.

Worst-Case gebunden

Richard Bird untersuchte diese Intuition, dass die binäre Suche den Saddleback-Algorithmus in einem Papier von 2006 verbessern könnte:

Bird zeigt uns mit einer eher ungewöhnlichen Konversationstechnik, dass dieses Problem für N <= M eine untere Grenze von Ω(N * log(M/N)) hat. Diese Schranke ist sinnvoll, da sie lineare Leistung bei N == M und logarithmische Leistung bei N == 1 liefert.

Algorithmen für rechteckige Arrays

Ein Ansatz, der eine zeilenweise binäre Suche verwendet, sieht folgendermaßen aus:

  1. Beginnen Sie mit einem rechteckigen Array mit N < M. Angenommen, N ist Zeilen und M Spalten.
  2. Führen Sie in der mittleren Zeile eine binäre Suche nach value durch. Wenn wir es finden, sind wir fertig.
  3. Ansonsten haben wir ein benachbartes Zahlenpaar s und g gefunden, wobei s < value < g.
  4. Das Rechteck der Zahlen oberhalb und links von s ist kleiner als value, sodass wir es entfernen können.
  5. Das Rechteck unterhalb und rechts von g ist größer als value, sodass wir es entfernen können.
  6. Fahren Sie mit Schritt (2) für jedes der zwei verbleibenden Rechtecke fort.

In Bezug auf die Komplexität des schlimmsten Falls beseitigt dieser Algorithmus log(M), um die Hälfte der möglichen Lösungen zu beseitigen, und ruft sich bei zwei kleineren Problemen rekursiv auf. Wir müssen eine kleinere Version dieser log(M)-Arbeit für jede Zeile wiederholen, aber wenn die Anzahl der Zeilen im Vergleich zu der Anzahl der Spalten klein ist), lohnt es sich, alle diese Spalten in der logarithmischen Zeit zu löschen.

Dies gibt dem Algorithmus eine Komplexität von T(N,M) = log(M) + 2 * T(M/2, N/2), die von Bird als O(N * log(M/N)) bezeichnet wird.

Ein anderer von Craig Gidney veröffentlichter Ansatz beschreibt einen Algorithmus, der dem oben beschriebenen Ansatz ähnlich ist: Er untersucht jeweils eine Zeile unter Verwendung einer Schrittgröße von M/N. Seine Analyse zeigt, dass dies auch zu O(N * log(M/N)) Leistung führt.

Leistungsvergleich

Big-O-Analyse ist gut und gut, aber wie funktionieren diese Ansätze in der Praxis? Die folgende Tabelle untersucht vier Algorithmen für zunehmend "quadratische" Arrays:

algorithm performance vs squareness

(Der "naive" Algorithmus durchsucht einfach jedes Element des Arrays. Der "rekursive" Algorithmus ist oben beschrieben. Der "Hybrid" -Algorithmus ist eine Implementierung von Gidneys Algorithmus . Für jede Arraygröße wurde die Leistung durch Timing gemessen Algorithmus über einen festen Satz von 1.000.000 zufällig generierten Arrays.)

Einige bemerkenswerte Punkte:

  • Wie erwartet bieten die "Binärsuche" -Algorithmen die beste Leistung bei rechteckigen Arrays, und der Saddleback-Algorithmus funktioniert am besten bei quadratischen Arrays.
  • Der Saddleback-Algorithmus ist schlechter als der "naive" Algorithmus für 1-D-Arrays, vermutlich weil er für jedes Element mehrere Vergleiche vornimmt.
  • Der Performance-Hit, den die Algorithmen der "binären Suche" bei quadratischen Arrays annehmen, ist vermutlich auf den Mehraufwand beim Ausführen wiederholter binärer Suchen zurückzuführen.

Zusammenfassung

Die clevere Verwendung der binären Suche kann O(N * log(M/N) für rechteckige und quadratische Arrays liefern. Der O(N + M)-Algorithmus "saddleback" ist viel einfacher, leidet jedoch an Leistungseinbußen, da Arrays immer rechteckiger werden.

104
Nate Kohl

Dieses Problem benötigt Θ(b lg(t)) Zeit, wobei b = min(w,h) und t=b/max(w,h). Die Lösung bespreche ich in diesem Blogbeitrag .

Untergrenze

Ein Gegner kann einen Algorithmus zwingen, Ω(b lg(t))-Abfragen durchzuführen, indem er sich auf die Hauptdiagonale beschränkt:

Adversary using main diagonal

Legende: weiße Zellen sind kleinere Elemente, graue Zellen sind größere Elemente, gelbe Zellen sind kleinere oder gleiche Elemente und orangefarbene Zellen sind größere oder gleiche Elemente. Der Gegner zwingt die Lösung zu der gelben oder orangefarbenen Zelle der Zelle Algorithmusabfragen zuletzt.

Beachten Sie, dass es b unabhängige sortierte Listen der Größe t gibt, für die Ω(b lg(t))-Abfragen vollständig entfernt werden müssen.

Algorithmus

  1. (Nehmen Sie ohne Verlust der Allgemeinheit an, dass w >= h)
  2. Vergleichen Sie das Zielelement mit der Zelle t links von der oberen rechten Ecke des gültigen Bereichs
    • Wenn das Element der Zelle übereinstimmt, geben Sie die aktuelle Position zurück.
    • Wenn das Element der Zelle kleiner als das Zielelement ist, beseitigen Sie die verbleibenden t-Zellen in der Zeile mit einer binären Suche. Wenn dabei ein übereinstimmender Artikel gefunden wird, kehren Sie mit seiner Position zurück.
    • Ansonsten ist das Element der Zelle mehr als das Zielelement, wodurch t kurze Spalten entfernt werden.
  3. Wenn kein gültiger Bereich übrig ist, wird ein Fehler zurückgegeben
  4. Weiter zu Schritt 2

Einen Artikel finden:

Finding an item

Bestimmen eines Artikels ist nicht vorhanden:

Determining an item doesn't exist

Legende: weiße Zellen sind kleinere Elemente, graue Zellen sind größere Elemente und die grüne Zelle ist ein gleiches Element.

Analyse

Es gibt b*t kurze Spalten, die entfernt werden sollen. Es gibt b lange Zeilen, die entfernt werden sollen. Das Beseitigen einer langen Reihe kostet O(lg(t)) Zeit. Die Beseitigung t kurzer Spalten kostet O(1) Zeit.

Im schlimmsten Fall müssen wir jede Spalte und jede Zeile eliminieren und nehmen uns die Zeit O(lg(t)*b + b*t*1/t) = O(b lg(t)).

Beachten Sie, dass ich davon ausgehe, dass lg ein Ergebnis über 1 festlegt (d. H. lg(x) = log_2(max(2,x))). Deshalb erhalten wir bei w=h, also t=1, die erwartete Grenze von O(b lg(1)) = O(b) = O(w+h).

Code

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;
}
32
Craig Gidney

Ich würde die Divide-and-Conquer-Strategie für dieses Problem verwenden, ähnlich wie Sie es vorgeschlagen haben, aber die Details sind etwas anders.

Dies wird eine rekursive Suche in Teilbereichen der Matrix sein.

Wählen Sie bei jedem Schritt ein Element in der Mitte des Bereichs. Wenn der gefundene Wert das ist, was Sie suchen, sind Sie fertig.

Wenn der gefundene Wert unter dem gesuchten Wert liegt, wissen Sie, dass er sich nicht im Quadranten oberhalb und links von Ihrer aktuellen Position befindet. Suchen Sie also rekursiv die beiden Unterbereiche: alles (ausschließlich) unter der aktuellen Position und alles (ausschließlich) rechts, das sich an oder über der aktuellen Position befindet.

Andernfalls wissen Sie (der gefundene Wert ist größer als der von Ihnen gesuchte Wert), dass er sich nicht im Quadranten unterhalb und rechts von Ihrer aktuellen Position befindet. Suchen Sie also rekursiv die beiden Unterbereiche: alles (ausschließlich) links von der aktuellen Position und alles (ausschließlich) oberhalb der aktuellen Position, die sich in der aktuellen Spalte oder in einer rechten Spalte befindet.

Und ba-da-bing, du hast es gefunden.

Beachten Sie, dass jeder rekursive Aufruf nur den aktuellen Unterbereich behandelt, nicht jedoch ALLE Zeilen über der aktuellen Position. Nur die im aktuellen Unterbereich.

Hier ist ein Pseudocode für Sie:

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

Die beiden wichtigsten Antworten scheinen bisher die wohl O(log N) "ZigZag-Methode" und die O(N+M) Binary Search-Methode zu sein. Ich dachte, ich würde ein paar Tests durchführen und die beiden Methoden mit verschiedenen Setups vergleichen. Hier sind die Details:

Das Array ist in jedem Test ein N × N-Quadrat, wobei N zwischen 125 und 8000 variiert (der größte, den meine JVM-Heap verarbeiten kann). Für jede Arraygröße habe ich einen zufälligen Platz im Array ausgewählt, um einen einzelnen 2 zu setzen. Ich habe dann einen 3 überall (rechts und unterhalb der 2) eingefügt und dann den Rest des Arrays mit 1 gefüllt. Einige der früheren Kommentatoren schienen zu glauben, dass diese Art von Setup die Laufzeit für beide Algorithmen im schlechtesten Fall ergeben würde. Für jede Arraygröße habe ich 100 verschiedene zufällige Positionen für die 2 (Suchziel) ausgewählt und den Test ausgeführt. Ich habe für jeden Algorithmus die durchschnittliche Laufzeit und die Laufzeit im schlechtesten Fall aufgezeichnet. Da es zu schnell geschah, um gute ms-Messwerte in Java zu erhalten, und weil ich Javas nanoTime () nicht vertraue, habe ich jeden Test 1000-mal wiederholt, nur um einen einheitlichen Bias-Faktor für alle Zeiten hinzuzufügen. Hier sind die Ergebnisse:

enter image description here

ZigZag schlagen binär in jedem Test sowohl für die durchschnittliche als auch für die ungünstigste Zeit, jedoch liegen sie alle in einer Größenordnung von mehr oder weniger.

Hier ist der Java-Code:

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");
    }
}
6
The111

Dies ist ein kurzer Beweis für die untere Grenze des Problems.

Sie können es nicht besser machen als die lineare Zeit (in Bezug auf die Feldabmessungen, nicht die Anzahl der Elemente). In dem unten stehenden Array kann jedes der als * markierten Elemente entweder 5 oder 6 sein (unabhängig von anderen). Wenn Ihr Zielwert also 6 (oder 5) ist, muss der Algorithmus alle Werte untersuchen. 

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

Natürlich erweitert sich dies auch auf größere Arrays. Dies bedeutet, dass diese Antwort optimal ist.

Update: Wie von Jeffrey L. Whitledge hervorgehoben, ist es nur als asymptotische Untergrenze der Laufzeit gegenüber der Eingangsdatengröße (als eine einzelne Variable behandelt) optimal. Die Laufzeit, die auf beiden Array-Dimensionen als Funktion mit zwei Variablen behandelt wird, kann verbessert werden.

5
Rafał Dowgird

Ich denke, hier ist die Antwort und es funktioniert für jede Art von sortierter 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));
    }
}
4

Interessante Frage. Betrachten Sie diese Idee: Erstellen Sie eine Grenze, bei der alle Zahlen größer als Ihr Ziel sind, und eine andere, bei der alle Zahlen kleiner als Ihr Ziel sind. Wenn irgendetwas zwischen den beiden bleibt, ist dies Ihr Ziel. 

Wenn ich in Ihrem Beispiel nach 3 suche, lese ich die erste Reihe bis zu 4 und suche dann nach der kleinsten benachbarten Zahl (einschließlich Diagonalen), die größer als 3 ist: 

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

Jetzt mache ich dasselbe für diese Zahlen unter 3: 

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

Jetzt frage ich, ist irgendetwas innerhalb der zwei Grenzen? Wenn ja, muss es 3 sein. Wenn nein, dann gibt es keine 3. Indirekt, da ich die Nummer nicht wirklich finde. Dies hat den zusätzlichen Vorteil, dass ALLE 3er gezählt werden.

Ich habe dies an einigen Beispielen ausprobiert und es scheint OK zu funktionieren. 

1
Grembo

Die binäre Suche durch die Diagonale des Arrays ist die beste Option. Wir können herausfinden, ob das Element kleiner oder gleich den Elementen in der Diagonale ist.

1
Nikhil K R

Ich schlage vor, alle Zeichen in einem 2D list zu speichern. Suchen Sie dann den Index des erforderlichen Elements, falls es in der Liste vorhanden ist.

Wenn nicht vorhanden, entsprechende Nachricht drucken, andernfalls Zeile und Spalte drucken als:

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

Dies führt nur zu einer binären Suchzeit in einer Liste.

Bitte schlagen Sie eventuelle Korrekturen vor. :)

0
Abhi31jeet

A. Führen Sie eine binäre Suche in den Zeilen durch, in denen sich die Zielnummer befinden kann.

B. Machen Sie einen Graphen: Suchen Sie nach der Nummer, indem Sie immer den kleinsten nicht besuchten Nachbarknoten nehmen und zurückverfolgen, wenn eine zu große Nummer gefunden wird

0
Tuomas Pelkonen

Binäre Suche wäre der beste Ansatz, imo. Bei 1/2 x beginnend, wird 1/2 y halbiert. IE Ein 5x5-Quadrat wäre so etwas wie x == 2/y == 3. Ich habe einen Wert abgerundet und einen Wert in eine bessere Zone in Richtung des Zielwerts gerundet. 

Zur Verdeutlichung würde die nächste Iteration etwas wie x == 1/y == 2 OR x == 3/y == 5 ergeben

0
Woot4Moo

Wenn O (M log (N)) - Lösung für ein MxN-Array in Ordnung ist - 

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

C++ Demo arbeiten.

Bitte lassen Sie mich wissen, ob dies nicht funktioniert oder ob es einen Fehler gibt.

0
kaushal
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 ;

}
0
gsb

BEARBEITEN:

Ich habe die Frage falsch verstanden. Wie aus den Kommentaren hervorgeht, funktioniert dies nur im eher eingeschränkten Fall.

In einer Sprache wie C, die Daten in Zeilenreihenfolge speichert, behandeln Sie sie einfach als 1D-Array der Größe n * m und verwenden Sie eine binäre Suche.

0
Hugh Brackett

Nun, zunächst einmal, nehmen wir an, wir verwenden ein Quadrat.

1 2 3
2 3 4
3 4 5

1. Suchen eines Quadrats

Ich würde eine binäre Suche in der Diagonale verwenden. Das Ziel ist das Finden der kleineren Zahl, die nicht strikt unter der Zielnummer liegt.

Angenommen, ich suche nach 4 zum Beispiel, dann würde ich 5 am (2,2) suchen.

Dann bin ich versichert, dass sich 4 in der Tabelle entweder (x,2) oder (2,x) mit x in [0,2] befindet. Nun, das sind nur 2 binäre Suchen.

Die Komplexität ist nicht schwer: O(log(N)) (3 binäre Suchen in Längenbereichen N)

2. Suche nach einem Rechteck, naiver Ansatz

Natürlich wird es etwas komplizierter, wenn N und M sich unterscheiden (mit einem Rechteck).

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

Und sagen wir, ich suche nach 9... Die diagonale Annäherung ist immer noch gut, aber die Definition der Diagonalen ändert sich. Hier ist meine Diagonale [1, (5 or 6), 17]. Nehmen wir an, ich habe [1,5,17] abgeholt, dann weiß ich, dass 9 in der Tabelle entweder im Unterabschnitt ist:

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

Dies gibt uns 2 Rechtecke:

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

Also können wir wiederkommen! Beginnt wahrscheinlich mit einem Element mit weniger Elementen (obwohl es uns in diesem Fall tötet).

Ich sollte darauf hinweisen, dass wir, wenn eine der Dimensionen kleiner als 3 ist, die Diagonalmethoden nicht anwenden können und eine binäre Suche verwenden müssen. Hier würde es bedeuten:

  • Wenden Sie die binäre Suche nach 10 11 12 13 14 15 16 an, nicht gefunden
  • Wenden Sie die binäre Suche nach 5 6 7 8 an, nicht gefunden
  • Wenden Sie die binäre Suche nach 6 7 8 9 an, nicht gefunden

Es ist schwierig, weil Sie für eine gute Leistung möglicherweise mehrere Fälle unterscheiden möchten, abhängig von der allgemeinen Form.

3. Suche nach einem Rechteck, brutaler Ansatz

Es wäre viel einfacher, wenn wir uns mit einem Quadrat beschäftigen würden.

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

Wir haben jetzt ein Quadrat.

Natürlich werden wir diese Zeilen wahrscheinlich NICHT erstellen, wir könnten sie einfach nachahmen.

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

also verhält es sich wie ein Quadrat ohne mehr Speicher zu belegen (auf Kosten der Geschwindigkeit, wahrscheinlich abhängig vom Cache ... na ja: p)

0
Matthieu M.

Ich habe eine rekursive Divide & Conquer-Lösung. Die Grundidee für einen Schritt lautet: Wir wissen, dass das Left-Upper (LU) am kleinsten ist und das Right-Bottom (RB) die größte Nummer ist, also das Nein ( N) muss: N> = LU und N <= RB

WENN N == LU und N == RB :::: Element gefunden und Abbruch, wobei die Position/Index .__ zurückgegeben wird. Wenn N> = LU und N <= RB = FALSE ist, ist Nein nicht vorhanden und der Abbruch erfolgt . Wenn N> = LU und N <= RB = TRUE, Unterteilen Sie das 2D-Array in jeweils 4 gleiche Teile des 2D-Arrays auf logische Weise .. Wenden Sie dann den gleichen Algo-Schritt auf alle vier Sub-Arrays an.

Mein Algo ist richtig, das ich auf meinem PC mit Freunden implementiert habe ..__ Komplexität: Jeder Vergleich kann verwendet werden, um die Gesamtzahl der Elemente auf ein Viertel im schlimmsten Fall abzuleiten ... Die Komplexität beträgt also 1 + 4 x lg (n) + 4 .__ Wirklich erwartet, dass dies an O (n) arbeitet.

Ich denke, dass irgendwo in meiner Berechnung der Komplexität etwas nicht stimmt. Bitte korrigieren Sie dies, wenn ja.

0
Pervez Alam

Die optimale Lösung ist, an der oberen linken Ecke zu beginnen, die einen minimalen Wert hat. Bewegen Sie sich diagonal nach unten, bis Sie auf ein Element treffen, dessen Wert> = Wert des angegebenen Elements ist. Wenn der Wert des Elements dem Wert des angegebenen Elements entspricht, geben Sie den Wert true als zurück.

Ansonsten können wir von hier aus auf zwei Arten vorgehen.

Strategie 1:

  1. Bewegen Sie sich in der Spalte nach oben und suchen Sie nach dem angegebenen Element, bis wir das Ende erreichen. Wenn gefunden, als wahr zurückkehren 
  2. Bewegen Sie sich in der Zeile nach links und suchen Sie nach dem angegebenen Element, bis wir das Ende erreichen. Wenn gefunden, als wahr zurückkehren 
  3. rückkehr als falsch gefunden

Strategie 2: Ich bezeichne den Zeilenindex und j den Spaltenindex des Diagonalelements, an dem wir angehalten haben. (Hier haben wir i = j, BTW). Sei k = 1.

  • Wiederholen Sie die folgenden Schritte, bis i-k> = 0 .__ ist.
    1. Suche, ob a [i-k] [j] dem angegebenen Element entspricht. wenn ja, als wahr zurückkehren.
    2. Suche, ob a [i] [j-k] dem angegebenen Element entspricht. wenn ja, als wahr zurückkehren.
    3. Inkrement k

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

0
Murali Mohan