Hier, on m'a posé la question suivante dans mon entretien:
Considérez un Java ou tableau C++ dites X
qui est trié et il n'y a pas deux éléments identiques. Comment trouver au mieux un index disons i
tel que l'élément à cet index est également i
. C'est-à-dire X[i] = i
.
À titre de clarification, elle m'a également donné un exemple:
Array X : -3 -1 0 3 5 7
index : 0 1 2 3 4 5
Answer is 3 as X[3] = 3.
Le mieux que je pouvais penser était une recherche linéaire. Après l'entretien, j'ai beaucoup réfléchi à ce problème mais je n'ai pas trouvé de meilleure solution. Mon argument est: l'élément avec la propriété requise peut être n'importe où dans le tableau. Donc, cela pourrait également être à la fin du tableau, nous devons donc vérifier chaque élément.
Je voulais juste confirmer auprès de la communauté ici que j'avais raison. Veuillez me dire que j'ai raison :)
Cela peut être fait dans O(logN)
temps et O(1)
espace en utilisant une recherche binaire légèrement modifiée .
Considérons un nouveau tableau Y
tel que Y[i] = X[i] - i
Array X : -3 -1 0 3 5 7
index : 0 1 2 3 4 5
Array Y : -3 -2 -2 0 1 2
Puisque les éléments dans X
sont dans l'ordre croissant, les éléments du nouveau tableau Y
seront dans non décroissant = commande. Ainsi, une recherche binaire pour 0
Dans Y
donnera la réponse.
Mais la création de Y
prendra O(N)
espace et O(N)
temps. Ainsi, au lieu de créer le nouveau tableau, vous modifiez simplement la recherche binaire de sorte qu'une référence à Y[i]
Est remplacée par X[i] - i
.
Algorithme:
function (array X)
low = 0
high = (num of elements in X) - 1
while(low <= high)
mid = (low + high) / 2
// change X[mid] to X[mid] - mid
if(X[mid] - mid == 0)
return mid
// change here too
else if(X[mid] - mid < 0)
low = mid + 1;
else
high = mid - 1;
end while
return -1 // no such index exists...return an invalid index.
end function
Il existe des solutions plus rapides, faisant la moyenne de O (log n) ou dans certains cas O (log log n) au lieu de O (n). Ayez un google pour "recherche binaire" et "recherche d'interpolation", vous trouverez probablement de très bonnes explications.
Si le tableau n'est pas trié, alors oui, l'élément est n'importe où et vous ne pouvez pas passer sous O (n), mais ce n'est pas le cas avec les tableaux triés.
-
Quelques explications sur la recherche d'interpolation comme demandé:
Alors que la recherche binaire ne concerne que la comparaison de deux éléments en termes de "supérieur/non supérieur", la recherche d'interpolation essaie également d'utiliser valeurs numériques. Le point est le suivant: vous avez une plage de valeurs triée de 0 à, disons, 20000. Vous recherchez 300 - la recherche binaire commencerait à la moitié de la plage, à 10000. La recherche d'interpolation suppose que 300 serait probablement quelque part plus proche de 0 que 20000, il vérifierait donc l'élément 6000 en premier au lieu de 10000. Ensuite - s'il est trop haut, récursif dans la sous-plage inférieure, et il est trop bas - récursif dans la sous-plage supérieure.
Pour un grand tableau avec une distribution uniforme + des valeurs, la recherche par interpolation devrait se comporter beaucoup plus rapidement que la recherche binaire - codez-la et voyez par vous-même. En outre, fonctionne mieux si vous utilisez d'abord une étape de recherche d'interpolation, puis une étape de recherche binaire, etc.
Notez que c'est la chose qu'un homme fait intuitivement lorsqu'il recherche quelque chose dans un dictionnaire.
Il n'est pas nécessaire de penser en termes de tableau Y
comme suggéré dans réponse par @codaddict.
Utilisez la recherche binaire et vérifiez l'élément central du tableau donné, s'il est inférieur à son index, alors nous n'avons pas besoin de rechercher un index inférieur car le tableau est trié et donc si nous nous déplaçons vers la gauche, soustrayons m index et ( au moins) valeur m, tous les éléments suivants seront également trop petits. Par exemple. si arr[5] = 4
alors arr[4] <= (4 - 1)
et arr[3] <= (4 - 2)
et ainsi de suite. Une logique similaire peut être appliquée si l'élément central est supérieur à son indice.
Voici une implémentation simple de Java
:
int function(int[] arr) {
int low = 0;
int high = arr.length - 1;
while(low <= high) {
int mid = high - (high - low) / 2;
if(arr[mid] == mid) {
return mid;
} else if(arr[mid] < mid) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1; // There is no such index
}
Notez que la solution ci-dessus ne fonctionnerait que si tous les éléments sont distincts.
Je pense que ce serait plus rapide.
Commencez au milieu de la liste
Si X [i]> i, allez au milieu du côté gauche restant
si X [i] <i, allez au milieu de la droite restante
Continuez ainsi et cela réduira de moitié le nombre d'éléments possibles pour chaque boucle
Vous pouvez effectuer une recherche binaire: recherchez au milieu, si la valeur est inférieure à l'index, aucun index inférieur ne contiendra la même valeur.
Ensuite, vous recherchez la moitié supérieure et continuez jusqu'à ce que vous trouviez l'élément ou atteignez un intervalle d'élément.
C'est une solution que j'ai trouvée et elle fonctionne s'il y a des doublons (j'ai par erreur ignoré cette mise en garde de l'absence de doublons).
//invariant: startIndex <= i <= endIndex
int modifiedBsearch(int startIndex, int endIndex)
{
int sameValueIndex = -1;
int middleIndex = (startIndex + endIndex) /2;
int middleValue = array[middleIndex];
int endValue = array[endIndex];
int startValue = array[startIndex];
if(middleIndex == middleValue)
return middleValue;
else {
if(middleValue <= endIndex)
sameValueIndex = modifiedBsearch(middleIndex + 1, endIndex)
if(sameValueIndex == -1 && startValue <= middleIndex)
sameValueIndex = modifiedBsearch(startIndex, middleIndex -1);
}
return sameValueIndex;
}
Je suppose que cela prend du temps O (log n), mais ce n'est pas clair à première vue ???
Si vous n'avez pas de chance, cela prendra du temps O (n log n) (la hauteur de l'arborescence de la pile sera log n, et ce sera une arborescence complète, avec n nœuds au dernier niveau, n/2 au suivant pour durer, etc.).
Ainsi, en moyenne, il sera compris entre O (log n) et O (n log n).
Java:
public static boolean check (int [] array, int i)
{
if (i < 0 || i >= array.length)
return false;
return (array[i] == i);
}
C++:
bool check (int array[], int array_size, int i)
{
if (i < 0 || i >= array_size)
return false;
return (array[i] == i);
}
du haut de ma tête, le fractionnement binaire pourrait être plus rapide.
regardez la valeur moyenne, si elle est élevée, alors ce dont vous avez besoin, effectuez une nouvelle recherche dans la moitié inférieure.
Après une comparaison, vous avez déjà renversé votre ensemble de données de moitié
Après avoir lu la question, il semble qu'un scénario puisse être utilisé pour accélérer la recherche. Lorsque vous comparez la position à la valeur, si la valeur est supérieure à la position, la valeur peut être utilisée comme position suivante à évaluer. Cela vous aidera à parcourir le tableau plus rapidement. Cela peut être fait car le tableau est trié. Les valeurs que nous ignorons sont conceptuellement décalées vers la gauche dans le tableau et sont au mauvais endroit.
Exemple:
int ABC[] = { -2, -5, 4, 7, 11, 22, 55 };
Si ma position actuelle est 2 et qu'elle a une valeur de 4, elles ne sont pas égales et conceptuellement, la valeur 4 est décalée vers la gauche. Je peux utiliser la valeur de 4 comme ma prochaine position, car si la valeur 4 est hors position, tout ce qui est inférieur à 4 est également hors position.
Un exemple de code juste pour le plaisir de la discussion:
void main()
{
int X[] = { -3, -1, 0, 3, 5, 7};
int length = sizeof(X)/sizeof(X[0]);
for (int i = 0; i < length;) {
if (X[i] > i && X[i] < length)
i = X[i]; // Jump forward!
else if (X[i] == i) {
printf("found it %i", i);
break;
} else
++i;
}
}
Une version modifiée de la recherche binaire suffirait, je suppose
Supposons que la séquence soit
Array : -1 1 4 5 6
Index : 0 1 2 3 4
Result : 1
ou
Array : -2 0 1 2 4 6 10
Index : 0 1 2 3 4 5 6
Result: 4
D'après les deux exemples, nous voyons que le résultat requis ne se trouvera jamais du bon côté si le pseudocode mid <a [mid] ... ressemblerait à quelque chose comme ceci
mid <- (first + last )/2
if a[mid] == mid then
return mid
else if a[mid] < mid then
recursive call (a,mid+1,last)
else
recursive call (a,first,mid-1)