web-dev-qa-db-fra.com

Comment rechercher un nombre dans un tableau 2D trié de gauche à droite et de haut en bas?

On m'a récemment remis cette question d'entrevue et je suis curieux de savoir quelle solution serait la bonne.

Supposons que je reçoive un tableau 2D où tous les les nombres dans le tableau sont en augmentation ordre de gauche à droite et en haut à bas.

Quelle est la meilleure façon de rechercher et déterminer si un numéro cible est dans le tableau?

Maintenant, ma première tendance est d’utiliser une recherche binaire puisque mes données sont triées. Je peux déterminer si un nombre est dans une seule ligne dans le temps O (log N). Cependant, ce sont les 2 directions qui me déconcertent.

Une autre solution qui, à mon avis, pourrait fonctionner est de commencer quelque part au milieu. Si la valeur du milieu est inférieure à ma cible, je peux être sûr que cela se trouve dans la partie carrée de gauche de la matrice à partir du milieu. Je me déplace ensuite en diagonale et vérifie à nouveau, en réduisant la taille du carré dans lequel la cible pourrait potentiellement se trouver jusqu'à ce que je précise le nombre cible.

Quelqu'un a-t-il de bonnes idées pour résoudre ce problème?

Exemple de tableau: 

Trié de gauche à droite, de haut en bas.

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

Voici une approche simple: 

  1. Commencez par le coin inférieur gauche. 
  2. Si la cible est inférieure à cette valeur, elle doit être au-dessus de nous, donc déplacez-vous d'un.
  3. Sinon, nous savons que la cible ne peut pas être dans cette colonne, donc déplace le curseur à droite.
  4. Aller à 2.

Pour un tableau NxM, ceci s’exécute dans O(N+M). Je pense qu'il serait difficile de faire mieux. :)


Edit: Beaucoup de bonnes discussions. Je parlais du cas général ci-dessus; Clairement, si N ou M sont petits, vous pouvez utiliser une approche de recherche binaire pour le faire dans un contexte logarithmique proche. 

Voici quelques détails, pour ceux qui sont curieux:

L'histoire

Cet algorithme simple s'appelle un Saddleback Search . Il existe depuis un moment et il est optimal lorsque N == M. Quelques références:

Cependant, lorsque N < M, l'intuition suggère que la recherche binaire devrait pouvoir faire mieux que O(N+M): Par exemple, lorsque N == 1, une recherche binaire pure s'exécutera en temps logarithmique plutôt qu'en temps linéaire.

Lié au pire des cas

Richard Bird a examiné cette intuition selon laquelle la recherche binaire pourrait améliorer l'algorithme de Saddleback dans un article de 2006:

En utilisant une technique de conversation plutôt inhabituelle, Bird nous montre que pour N <= M, ce problème a une limite inférieure de Ω(N * log(M/N)). Cette borne est logique, car elle nous donne des performances linéaires lorsque N == M et des performances logarithmiques lorsque N == 1.

Algorithmes pour les tableaux rectangulaires

Une approche qui utilise une recherche binaire ligne par ligne ressemble à ceci:

  1. Commencez avec un tableau rectangulaire où N < M. Disons que N est des lignes et M est des colonnes.
  2. Effectuez une recherche binaire sur la rangée du milieu pour value. Si nous le trouvons, nous avons terminé.
  3. Sinon, nous avons trouvé une paire adjacente de nombres s et g, où s < value < g.
  4. Le rectangle de nombres au-dessus et à gauche de s est inférieur à value, nous pouvons donc l'éliminer.
  5. Le rectangle ci-dessous et à droite de g est supérieur à value, afin que nous puissions l'éliminer.
  6. Passez à l'étape (2) pour chacun des deux rectangles restants.

En termes de complexité dans le cas le plus défavorable, cet algorithme log(M) supprime la moitié des solutions possibles, puis s’appelle de manière récursive deux fois sur deux problèmes plus petits. Nous devons répéter une version plus petite de ce travail log(M) pour chaque ligne, mais si le nombre de lignes est faible par rapport au nombre de colonnes, le fait de pouvoir éliminer toutes ces colonnes en temps logarithmique commence à en valoir la peine.

Cela donne à l’algorithme une complexité de T(N,M) = log(M) + 2 * T(M/2, N/2), que Bird montre être O(N * log(M/N)).

Une autre approche publiée par Craig Gidney décrit un algorithme similaire à l’approche ci-dessus: il examine une ligne à la fois en utilisant une taille de pas de M/N. Son analyse montre que cela entraîne également une performance de O(N * log(M/N)).

Comparaison de performance

L'analyse Big-O est une bonne chose, mais dans quelle mesure ces approches fonctionnent-elles dans la pratique? Le tableau ci-dessous examine quatre algorithmes pour des tableaux de plus en plus «carrés»:

algorithm performance vs squareness

(L'algorithme "naïf" recherche simplement chaque élément du tableau. L'algorithme "récursif" est décrit ci-dessus. L'algorithme "hybride" est une implémentation de algorithme de Gidney . Pour chaque taille de tableau, la performance a été mesurée en chronométrant chaque algorithme sur un ensemble fixe de 1 000 000 de tableaux générés aléatoirement.)

Quelques points notables:

  • Comme prévu, les algorithmes de "recherche binaire" offrent les meilleures performances sur les tableaux rectangulaires et l'algorithme Saddleback fonctionne au mieux sur les tableaux carrés.
  • L'algorithme Saddleback est moins performant que l'algorithme "naïf" pour les tableaux 1-d, probablement parce qu'il effectue plusieurs comparaisons sur chaque élément.
  • La performance des algorithmes de "recherche binaire" sur les tableaux carrés est probablement due à la surcharge de l'exécution de recherches binaires répétées.

Résumé

Une utilisation judicieuse de la recherche binaire peut fournir des performances O(N * log(M/N) pour les tableaux rectangulaires et carrés. L'algorithme O(N + M) "saddleback" est beaucoup plus simple, mais souffre de la dégradation des performances car les tableaux deviennent de plus en plus rectangulaires.

104
Nate Kohl

Ce problème prend Θ(b lg(t)) time, où b = min(w,h) et t=b/max(w,h). Je discute de la solution dans cet article de blog .

Borne inférieure

Un adversaire peut forcer un algorithme à faire des requêtes Ω(b lg(t)), en se limitant à la diagonale principale:

Adversary using main diagonal

Légende: les cellules blanches sont des éléments plus petits, les cellules grises sont des éléments plus grands, les cellules jaunes sont des éléments plus petits ou identiques et les cellules orange sont des éléments plus grands ou égaux L'adversaire force la solution à choisir la cellule jaune ou orange dans laquelle l'algorithme pose la dernière question.

Notez qu'il existe b listes triées indépendantes de taille t, nécessitant d'éliminer complètement les requêtes Ω(b lg(t)).

Algorithme

  1. (Supposons sans perte de généralité que w >= h)
  2. Comparez l'élément cible à la cellule t située à gauche du coin supérieur droit de la zone valide
    • Si l'élément de la cellule correspond, renvoie la position actuelle.
    • Si l'élément de la cellule est inférieur à l'élément cible, supprimez les cellules t restantes de la ligne avec une recherche binaire. Si un élément correspondant est trouvé, renvoyez-le avec sa position.
    • Sinon, l'élément de la cellule est supérieur à l'élément cible, ce qui élimine les colonnes t courtes.
  3. Si aucune zone valide n'est laissée, échec de retour
  4. Aller à l'étape 2

Trouver un article:

Finding an item

Déterminer un article n'existe pas:

Determining an item doesn't exist

Légende: les cellules blanches sont des éléments plus petits, les cellules grises sont des éléments plus grands et la cellule verte est un élément identique.

Une analyse

b*t colonnes courtes à éliminer. Il y a b longues lignes à éliminer. L'élimination d'une longue ligne coûte O(lg(t)) time. L'élimination des t colonnes courtes coûte O(1) time.

Dans le pire des cas, nous devrons éliminer chaque colonne et chaque ligne en prenant le temps O(lg(t)*b + b*t*1/t) = O(b lg(t)).

Notez que je suppose que lg se fixe à un résultat supérieur à 1 (c'est-à-dire lg(x) = log_2(max(2,x))). C'est pourquoi, lorsque w=h, ce qui signifie t=1, nous obtenons la limite attendue de 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

J'utiliserais la stratégie diviser pour mieux régner pour ce problème, semblable à ce que vous avez suggéré, mais les détails sont un peu différents.

Ce sera une recherche récursive sur les sous-gammes de la matrice.

À chaque étape, choisissez un élément au milieu de la plage. Si la valeur trouvée correspond à ce que vous recherchez, vous avez terminé.

Sinon, si la valeur trouvée est inférieure à la valeur que vous recherchez, vous savez alors qu'elle ne se trouve pas dans le quadrant situé au-dessus et à gauche de votre position actuelle. Vous devez donc effectuer une recherche récursive dans les deux sous-catégories: tout (exclusivement) en dessous de la position actuelle et tout (exclusivement) vers la droite correspondant ou au-dessus de la position actuelle.

Sinon, (la valeur trouvée est supérieure à la valeur que vous recherchez), vous savez que ce n'est pas dans le quadrant en dessous et à droite de votre position actuelle. Vous devez donc effectuer une recherche récursive dans les deux sous-plages: tout (exclusivement) à gauche de la position actuelle et tout (exclusivement) au-dessus de la position actuelle figurant dans la colonne en cours ou dans une colonne à droite.

Et ba-da-bing, vous l'avez trouvé.

Notez que chaque appel récursif ne traite que de la sous-gamme actuelle, pas (par exemple) TOUTES les lignes au-dessus de la position actuelle. Juste ceux de la sous-gamme actuelle.

Voici quelques pseudocodes pour vous:

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

Jusqu'ici, les deux réponses principales semblent être la méthode O(log N) "Méthode ZigZag" et la méthode O(N+M) Binary Search. Je pensais faire des tests en comparant les deux méthodes avec différentes configurations. Voici les détails:

La matrice est N x N carré dans chaque test, N variant de 125 à 8 000 (la plus grande pile que ma machine virtuelle Java puisse gérer). Pour chaque taille de tableau, j'ai choisi un emplacement aléatoire dans le tableau pour mettre un seul 2. Je mets ensuite un 3 partout où cela est possible (à droite et en dessous du 2), puis je remplis le reste du tableau avec 1. Certains des commentateurs précédents semblaient penser que ce type d’installation produirait un temps d’exécution pire pour les deux algorithmes. Pour chaque taille de tableau, j'ai sélectionné 100 emplacements aléatoires différents pour le 2 (cible de recherche) et exécuté le test. J'ai enregistré le temps d'exécution moyen et le pire temps d'exécution pour chaque algorithme. Comme cela se produisait trop rapidement pour obtenir de bonnes lectures ms en Java et parce que je ne fais pas confiance à nanoTime () de Java, j'ai répété chaque test 1 000 fois simplement pour ajouter un facteur de biais uniforme à toutes les époques. Voici les résultats:

enter image description here

ZigZag a battu le binaire à chaque test pour les temps moyen et moyen, mais ils sont tous plus ou moins identiques.

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

Ceci est une courte preuve de la limite inférieure du problème.

Vous ne pouvez pas le faire mieux que le temps linéaire (en termes de dimensions de tableau, pas de nombre d'éléments). Dans le tableau ci-dessous, chacun des éléments marqués avec * peut être 5 ou 6 (indépendamment des autres). Donc, si votre valeur cible est 6 (ou 5), l'algorithme doit toutes les examiner. 

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

Bien sûr, cela s’étend également à de plus grands tableaux. Cela signifie que cette réponse est optimale.

Mise à jour: comme l'a souligné Jeffrey L. Whitledge, elle n'est optimale que comme limite inférieure asymptotique du temps d'exécution par rapport à la taille des données d'entrée (traitée comme une variable unique). Le temps d'exécution traité comme une fonction à deux variables sur les deux dimensions du tableau peut être amélioré.

5
Rafał Dowgird

Je pense que voici la réponse et cela fonctionne pour tout type de matrice triée

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
Sridhar Ravipati

Question interessante. Considérez cette idée - créez une limite où tous les nombres sont supérieurs à votre cible et une autre où tous les nombres sont inférieurs à votre cible. S'il reste quelque chose entre les deux, c'est votre cible. 

Si je cherche 3 dans votre exemple, je lis sur la première ligne jusqu'à ce que je frappe 4, puis je cherche le plus petit nombre adjacent (y compris les diagonales) supérieur à 3: 

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

Maintenant, je fais la même chose pour les nombres inférieurs à 3: 

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

Maintenant, je demande s'il y a quelque chose à l'intérieur des deux limites Si oui, cela doit être 3. Si non, alors il n'y a pas 3. Comme indirect, puisque je ne trouve pas le nombre, je déduis juste qu'il doit être là. Cela a le bonus supplémentaire de compter TOUS les 3.

J'ai essayé ceci sur quelques exemples et cela semble fonctionner correctement. 

1
Grembo

La recherche binaire à travers la diagonale du tableau est la meilleure option ..__ Nous pouvons déterminer si l'élément est inférieur ou égal aux éléments de la diagonale.

1
Nikhil K R

Je suggère, stockez tous les caractères dans un 2D list. puis recherchez l’index de l’élément requis s’il existe dans la liste.

Si non présent, imprimer le message approprié, sinon, imprimer la ligne et la colonne comme suit:

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

Cela n'engendrera que le temps de recherche binaire dans une liste.

S'il vous plaît suggérer des corrections. :)

0
Abhi31jeet

A. Effectuez une recherche binaire sur les lignes où le numéro cible peut être indiqué.

B. Faites-en un graphique: recherchez le nombre en prenant toujours le plus petit noeud voisin non visité et en effectuant un retour arrière lorsqu'un nombre trop grand est trouvé

0
Tuomas Pelkonen

La recherche binaire serait la meilleure approche, imo. À partir de 1/2 x, 1/2 y le coupera en deux. IE un carré de 5x5 serait quelque chose comme x == 2/y == 3. J'ai arrondi une valeur vers le bas et une valeur vers le haut pour mieux définir la direction de la valeur ciblée. 

Pour plus de clarté, la prochaine itération vous donnerait quelque chose comme x == 1/y == 2 OR x == 3/y == 5

0
Woot4Moo

Si la solution O (M log (N)) convient pour un tableau 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;
}

Démonstration de travail en C++.

S'il vous plaît, faites-moi savoir si cela ne fonctionnerait pas ou s'il y avait un bug là-dessus.

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

MODIFIER:

J'ai mal compris la question. Comme le soulignent les commentaires, cela ne fonctionne que dans le cas le plus restreint.

Dans un langage tel que C qui stocke les données dans un ordre de lignes majeur, traitez-le simplement comme un tableau 1D de taille n * m et utilisez une recherche binaire.

0
Hugh Brackett

Eh bien, pour commencer, supposons que nous utilisons un carré.

1 2 3
2 3 4
3 4 5

1. Recherche d'un carré

Je voudrais utiliser une recherche binaire sur la diagonale. L’objectif est de localiser le plus petit nombre qui n’est pas strictement inférieur au nombre cible.

Supposons que je recherche 4 par exemple, puis je finirais par localiser 5 à (2,2).

Ensuite, je suis assuré que si 4 est dans la table, sa position est (x,2) ou (2,x) avec x dans [0,2]. Eh bien, c'est juste 2 recherches binaires.

La complexité n’est pas décourageante: O(log(N)) (3 recherches binaires sur des plages de longueur N)

2. Rechercher un rectangle, approche naïve

Bien sûr, cela devient un peu plus compliqué lorsque N et M diffèrent (avec un rectangle), considérons ce cas dégénéré:

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

Et disons que je cherche 9... L’approche diagonale est toujours bonne, mais la définition de la diagonale change. Ici, ma diagonale est [1, (5 or 6), 17]. Supposons que j'ai pris [1,5,17], alors je sais que si 9 est dans la table, il est soit dans la sous-partie:

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

Cela nous donne 2 rectangles:

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

Alors on peut recurse! en commençant probablement par celui avec moins d'éléments (bien que dans ce cas, il nous tue).

Je dois préciser que si l’une des dimensions est inférieure à 3, nous ne pouvons pas appliquer les méthodes diagonales et nous devons utiliser une recherche binaire. Ici, cela voudrait dire:

  • Appliquer la recherche binaire sur 10 11 12 13 14 15 16, introuvable
  • Appliquer la recherche binaire sur 5 6 7 8, introuvable
  • Appliquer la recherche binaire sur 6 7 8 9, introuvable

C'est délicat car pour obtenir de bonnes performances, il peut être utile de différencier plusieurs cas, en fonction de la forme générale ....

3. Recherche d'un rectangle, approche brutale

Ce serait beaucoup plus facile si nous avions affaire à un carré ... alors mettons les choses au clair.

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

Nous avons maintenant un carré.

Bien sûr, nous ne créerons probablement PAS ces lignes, nous pourrions simplement les imiter.

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

il se comporte donc comme un carré sans occuper plus de mémoire (au détriment de la vitesse, probablement en fonction de la mémoire cache ... ah bon: p)

0
Matthieu M.

J'ai une solution Divide & Conquer récursive . L'idée de base pour une étape est la suivante: Nous savons que le Left-Upper (LU) est le plus petit et que le right-bottom (RB) est le plus grand no. N) doit: N> = LU et N <= RB

SI N == LU et N == RB :::: Élément trouvé et Abort renvoyant la position/Index Si N> = LU et N <= RB = FALSE, Non n'est pas là et abandonne . Si N> = LU et N <= RB = TRUE, divisez le tableau 2D en 4 parties égales du tableau 2D, chacune de manière logique .. Et appliquez ensuite le même pas d'algo aux quatre sous-tableaux.

Mon Algo est correct, j'ai implémenté sur mon PC d'amis . Complexité: chaque 4 comparaisons peuvent être utilisées pour déduire le nombre total d'éléments à un quart dans le pire des cas .. Ma complexité devient donc 1 + 4 x lg (n) + 4 Mais je m'attendais vraiment à ce que cela fonctionne sur O (n)

Je pense que quelque chose ne va pas quelque part dans mon calcul de complexité, corrigez s'il vous plaît si oui.

0
Pervez Alam

La solution optimale consiste à commencer par le coin supérieur gauche, qui a une valeur minimale. Déplacez-vous en diagonale vers le bas à droite jusqu'à atteindre un élément dont la valeur> = valeur de l'élément donné. Si la valeur de l'élément est égale à celle de l'élément donné, retourne trouvé comme vrai.

Sinon, à partir de là, nous pouvons procéder de deux manières.

Stratégie 1:

  1. Déplacez-vous dans la colonne et recherchez l'élément en question jusqu'à la fin. Si trouvé, retourne comme vrai 
  2. Déplacez-vous à gauche dans la rangée et recherchez l'élément en question jusqu'à la fin. Si trouvé, retourne comme vrai 
  3. retour trouvé comme faux

Stratégie 2: Soit i l’indice de la ligne et j l’indice de la colonne de la diagonale à laquelle nous nous sommes arrêtés. (Ici, nous avons i = j, BTW). Soit k = 1.

  • Répétez les étapes ci-dessous jusqu'à ce que i-k> = 0
    1. Recherche si un [i-k] [j] est égal à l'élément donné. Si oui, retourne comme vrai.
    2. Recherche si un [i] [j-k] est égal à l'élément donné. Si oui, retourne comme vrai.
    3. Incrément k

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

0
Murali Mohan