29 votes

Comment vérifier efficacement s'il manque des éléments dans une liste de nombres consécutifs ?

J'ai ce tableau

var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];

J'ai essayé de trouver un algorithme qui me dira quelle s sont absents. Comme vous pouvez le voir, la liste se compose de consécutifs s s ( s1 , s2 etc.).

Au début, j'ai opté pour cette solution :

    var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
for (var i=1;i<arr.length;i++){
    var thisI = parseInt(arr[i].toLowerCase().split("s")[1]);
    var prevI = parseInt(arr[i-1].toLowerCase().split("s")[1]);
    if (thisI != prevI+1)
      console.log(`Seems like ${prevI+1} is missing. thisI is ${thisI} and prevI is ${prevI}`)
}

Mais cette méthode échoue pour plus d'un numéro consécutif manquant ( s15 , s16 ). J'ai donc ajouté un while boucle qui fonctionne.

var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
for (var i=1;i<arr.length;i++){
  var thisI = parseInt(arr[i].toLowerCase().split("s")[1]);
  var prevI = parseInt(arr[i-1].toLowerCase().split("s")[1]);
  if (thisI != prevI+1) {
    while(thisI-1 !== prevI++){
       console.log(`Seems like ${prevI} is missing. thisI is ${thisI} and prevI is ${prevI}`)
    }
   }
}

Cependant, j'ai l'impression de compliquer excessivement les choses. J'ai pensé à créer un tableau idéal :

var idealArray = [];
for (var i =0; i<200;i++) {
  idealArray.push(i)
}

Et ensuite, pendant la vérification, trafiquer mon tableau ( arr ) afin que la boucle vérifie deux tableaux de même longueur. Autrement dit, utilisez cette solution :

var idealArray = [];
for (var i =0; i<200;i++) {
  idealArray.push(i)
}
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
for (let i = 0; i<idealArray.length;i++){
  if (parseInt(arr[i].toLowerCase().split("s")[1]) != idealArray[i]) {
    console.log(`Seems like ${idealArray[i]}is missing`);
    arr.splice(i,0,"dummyel")
  }
}

Mais, encore une fois, j'ai l'impression que la création de ce deuxième tableau n'est pas non plus très efficace (en pensant à une grande liste, je gaspillerais de l'espace inutile).

Alors... comment puis-je réaliser efficacement cette tâche en JavaScript ? (Efficace signifie aussi proche de O(1) que possible à la fois pour la complexité temporelle et pour la complexité spatiale).

9 votes

Hmm, ma première idée serait de faire une recherche linéaire naïve. En gros, on démarre un compteur avec le premier nombre du tableau, puis on parcourt le tableau en incrémentant le compteur et en le comparant à l'élément actuel. Évidemment, c'est O(n), cependant. Pour une solution plus efficace, je suppose que l'on peut faire quelque chose de binaire - prendre le premier nombre, le dernier nombre et la longueur du tableau - est-ce qu'ils correspondent ? Si ce n'est pas le cas, prenez la moitié du tableau et procédez de la même manière. Répétez l'opération jusqu'à ce que vous trouviez l'élément manquant. Cela nécessite un peu de travail pour de multiples éléments manquants, mais c'est O(log n) en exécution mais pas en espace.

2 votes

@TanDuong J'avais l'impression que la révision du code ne consistait pas à trouver un meilleur algorithme, mais à peaufiner la solution existante. À moins que vous n'ayez essayé de réinventer la roue au lieu d'utiliser une fonctionnalité intégrée ou autre.

0 votes

Pouvez-vous supposer que le nombre k d'éléments manquants croît strictement plus lentement que O(N) , donde N est la taille de la gamme ? Si vous pouvez donner une limite supérieure plus forte pour k (par exemple, quelque chose comme log(N) o sqrt(N) o N^\alpha pour certains \alpha < 1.0 ), alors les algorithmes dont le comportement dans le pire des cas est supérieur à O(N) pourrait être possible.

15voto

Mark Meyer Points 31911

Puisque vous savez que vous attendez un tableau séquentiel, je ne vois pas pourquoi cela devrait être plus compliqué qu'une boucle à travers des nombres. arr[0] par le biais de arr[end] tout en gardant un compteur pour savoir où vous êtes dans le tableau. Cela fonctionnera à O(n), mais je ne pense pas que vous puissiez améliorer cela - vous devez regarder chaque élément au moins une fois dans le pire des cas.

var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];

let first = parseInt(arr[0].substring(1))
let last =  parseInt(arr[arr.length-1].substring(1))
let count = 0
for (let i = first; i< last; i++) {
   if (parseInt(arr[count].substring(1)) == i) {count++; continue}
   else console.log(`seem to be missing ${'s'+i.toString().padStart(2,'0')} between: ${arr[count-1]} and ${arr[count]}` )
}

EDITAR:

Après avoir réfléchi un peu aux commentaires ci-dessous, j'ai fait une approche récursive qui divise le tableau et vérifie chaque moitié. C'est surtout une expérience, pas une solution pratique. En fait, cela fonctionne avec moins de n itérations dans la plupart des cas, mais je n'ai pas pu trouver un cas où il était réellement plus rapide Aussi, j'ai juste poussé des index montrant où sont les lacunes pour rendre la structure plus facile à voir et à tester. Et comme vous le verrez, comme c'est récursif, les résultats ne sont pas dans l'ordre.

var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];

let missingGaps = []

function missing(arr, low, high) {
  if (high <= low) return

  let l = parseInt(arr[low].substring(1))
  let h = parseInt(arr[high].substring(1))

  if (h - l == high - low) return
  if (high - low === 1) {
    missingGaps.push([low, high])
    return
  } else {
    let mid = ((high - low) >> 1) + low

    missing(arr, low, mid)

    // need to check case where split might contain gap
    let m = parseInt(arr[mid].substring(1))
    let m1 = parseInt(arr[mid + 1].substring(1))
    if (m1 - m !== 1) missingGaps.push([mid, mid + 1])

    missing(arr, mid + 1, high)
  }
}

missing(arr, 0, arr.length-1)
missingGaps.forEach(g => console.log(`missing between indices ${arr[g[0]]} and ${arr[g[1]]}`))

Peut-être qu'une autre réponse ou un autre commentaire apportera une amélioration qui le rendra un peu plus rapide.

10 votes

This will run at O(n), but I don't think you can improve on that Comme je l'ai mentionné dans un commentaire, vous devriez être en mesure de le ramener à O(log n). Vous pouvez seulement faire un balayage linéaire pour les petites sections du tableau, et un découpage binaire pour les plus grandes, en éliminant toutes les sections qui n'ont pas de numéros manquants. Par exemple, si vous réduisez à ["s00","s01","s02","s03"] vous saurez qu'il ne manque rien car le premier élément est 0 le dernier est 3 et la longueur est 4 - cela signifie qu'il ne manque rien. Il est vrai que cela demande plus de travail, mais c'est plus rapide pour les grands tableaux.

0 votes

@vlaz Je pensais justement à ça. Il est clair que cela améliore les meilleurs cas (par exemple quand il n'y a pas de numéros manquants), mais je ne suis pas sûr de savoir comment évaluer le pire cas.

3 votes

Pour l'algo de @vlaz, il est plus précis de mesurer la complexité en termes de taille du tableau (N) et du nombre d'éléments manquants à identifier (M). Chaque élément manquant doit être imprimé et chaque élément manquant peut être trouvé en O(log N). Le temps le plus défavorable est donc O(M log N). Cependant, dans les tableaux très clairsemés, les éléments manquants vont se trouver dans des étendues ; dans ce cas, une étendue entière peut être trouvée en O(log N). Si S est le nombre d'intervalles, la complexité est de O(M + S log N). Puisque S <= N + 1, le pire cas est O(M + N log N). (O(M) consiste à imprimer les éléments manquants). De plus, S <= M, fwiw.

6voto

Veselin Davidov Points 5935

Si je comprends bien votre solution de tableau idéal, vous connaissez la taille maximale du tableau ( ?). Donc si vous avez 100 valeurs maximales et que vous attendez S00 - S99, vous pouvez le faire :

var arrayIndex=0;
for (var i =0; i<100;i++) {
   var idealValue="s"+("00"+i).slice(-2); // To get S01-S99
   if(arr.length <= arrayIndex || arr[arrayIndex]!=idealValue){
        console.log(idealValue + 'is missing');
   }
   arrayIndex++;
}

Ou quelque chose comme ça. Je ne peux pas le tester pour l'instant ;) Mais itérer à travers la liste des valeurs idéales et comparer la même valeur dans le tableau. Si elle ne correspond pas, imprimez-la.

0 votes

Oui, je connais la longueur maximale du tableau car je connais la valeur de l'élément le plus haut. Je ne sais pas pourquoi je n'y ai pas pensé. Bien que cela puisse être la réponse acceptée, il y en a une autre encore meilleure que je vais accepter, mais c'est quand même une excellente solution simple.

5voto

Andrey Tyukin Points 29032

Votre solution avec l'intérieur while -La boucle semble déjà très bien fonctionner, il suffit d'omettre les éléments inutiles. if et de garder la trace du nombre que vous attendez actuellement au lieu d'analyser le nombre précédent à chaque fois.

Quelque chose comme ça :

var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
var expectedI = 0
for (var i = 0; i < arr.length; i++) {
  var currentI = parseInt(arr[i].toLowerCase().split("s")[1]);
  while (expectedI < currentI) {
    console.log(`Seems like ${expectedI} is missing.`)
    expectedI++
  }
  expectedI = currentI + 1
}

vous donne :

Seems like 6 is missing.
Seems like 15 is missing.
Seems like 16 is missing.
Seems like 18 is missing.
Seems like 23 is missing.
Seems like 29 is missing.
Seems like 31 is missing.
Seems like 35 is missing.
Seems like 37 is missing.
Seems like 40 is missing.
Seems like 42 is missing.
Seems like 57 is missing.
Seems like 59 is missing.
Seems like 66 is missing.
Seems like 68 is missing.

L'idée est très simple : si vous ne voyez pas le nombre que vous vous attendiez à voir, imprimez-le sur la console (ou enregistrez-le ailleurs), puis continuez avec le nombre suivant.

Notez que vous ne pouvez pas obtenir le temps d'exécution ci-dessous O(N) parce qu'il faut regarder chaque élément de la liste au moins une fois, et il peut aussi arriver que l'on doive imprimer O(N) les éléments manquants à la console.

L'algorithme ci-dessus examine chaque élément de la liste une fois, et peut fonctionner avec un encombrement constant.

EDITAR: Le site commentaire fait par vlaz semble proposer un algorithme qui devrait fonctionner plus rapidement pour les tableaux avec peu de trous. Cependant, cela ne change toujours pas le comportement dans le pire des cas, car dans le pire des cas (si tout est manquant), vous devez toujours imprimer tous les tableaux N nombres. Si vous supposez que le nombre k de numéros manquants est "beaucoup plus faible" que N (c'est-à-dire k pas dans Theta(N) ), des algorithmes plus efficaces pourraient être envisagés.

3voto

Nina Scholz Points 17120

Vous pouvez réduire le tableau en prenant deux éléments du tableau et remplir les vides, s'ils existent.

const
    getNumber = s => +s.slice(1),
    pad = i => ('00' + i).slice(-2);

var array = ["s00", "s01", "s02", "s03", "s04", "s05", "s07", "s08", "s09", "s10", "s11", "s12", "s13", "s14", "s17", "s19", "s20", "s21", "s22", "s24", "s25", "s26", "s27", "s28", "s30", "s32", "s33", "s34", "s36", "s38", "s39", "s41", "s43", "s44", "s45", "s46", "s47", "s48", "s49", "s50", "s51", "s52", "s53", "s54", "s55", "s56", "s58", "s60", "s61", "s62", "s63", "s64", "s65", "s67", "s69", "s70"],
    result = [];

array.reduce((left, right) => {
    var l = getNumber(left),
        r = getNumber(right);

    while (++l < r) {
        result.push('s' + pad(l));
    }
    return right;
});

console.log(result);

1 votes

Il s'agit de la même solution que le PO a déjà implémentée en utilisant simplement plus de fonctions intégrées et un code succinct. Le PO a demandé une solution plus efficace en termes de "complexité temporelle et... spatiale".

0 votes

@, en fait, n'importe quel code itérera le tableau donné et remplira les trous. alors, à quoi sert la question ?

0 votes

Je ne suis pas sûr, mais une réponse possible est qu'il n'y a pas de solution plus efficace que quelque chose comme ceci :)

3voto

Emeeus Points 3255

Il s'agit simplement d'une approche permettant de déterminer si, dans un tableau donné, il manque un élément dans une séquence de chiffres. Nous pourrions utiliser (n*(n+1))/2 qui résout l'addition sur les n premiers nombres. De même, si le tableau commence par exemple par 10, nous supprimons la somme 1-10. Cela nous indique simplement si quelque chose manque, mais pas ce qui manque. L'avantage est que le tableau peut être non trié. Calculer le minimum est moins coûteux que de commander le tableau entier.

var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];

let total = 0;

for(let i = 0; i<arr.length; i++){
    arr[i] = parseInt(arr[i].replace("s", ""));
    total += arr[i];
}

let hipFirstSum = ((arr[0]-1)*(arr[0]))/2;  //or minimun
let n = arr[arr.length -1];
let hipSum = (n*(n+1))/2;
let realSum = hipSum - hipFirstSum;

(realSum != total)?console.log("wrong"):console.log("good");

0 votes

Je n'avais pas pensé à tout résumer. Cela fonctionne certainement avec les tableaux non triés. Cependant, la question est de trouver ce qui manque et malheureusement ce code ne répond pas à cette question.

0 votes

@valz Je sais, cela pourrait être une solution pour une autre situation. Quand j'ai lu la question pour la première fois, j'ai pensé : que se passe-t-il si nous ne savons pas si le tableau est trié ?

1 votes

Cette solution est beaucoup trop compliquée. Vous n'avez pas besoin de tout additionner. En fait, il suffit de trouver les valeurs min et max. Ces valeurs vous indiqueront le nombre d'éléments qu'il devrait y avoir et vous pourrez simplement le comparer à la longueur du tableau. Si le tableau est trié, cela signifie qu'il suffit de prendre a[0] y a[a.length - 1] .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X