J'ai un problème que j'ai trouvé sur un forum d'algorithmes ailleurs.
J'ai un tableau avec des nombres aléatoires (par exemple, [5, 2, 7, 9, 2, 3, 8, 4]
) Qui devrait me être retourné trié par impair puis pair. L'ordre des cotes/égalités n'est pas important donc dans l'exemple ce serait probablement [5, 7, 9, 3, 2, 2, 8, 4]
.
Ma première pensée a été d'utiliser une fonction .reduce()
pour simplement les pousser dans deux tableaux différents, puis les réunir, mais il y a quelques complexités supplémentaires à cette tâche.
Les règles supplémentaires de la tâche sont que le tri doit conserver un espace supplémentaire constant et modifier le tableau en place. Il doit également utiliser un algorithme qui évolue de manière cohérente avec la taille du tableau.
Quel algorithme?
A en juger par liste de Wikipedia , il existe un certain nombre d'algorithmes qui évoluent de manière cohérente et utilisent une quantité constante d'espace. J'ai également trouvé ce site Web qui a un bon type de bulle à utiliser (également affiché ci-dessous), qui semble stable et suit mes règles (je pense?).
Je ne sais pas si c'est le bon algorithme à utiliser, ou comment aborder cette partie de la tâche.
Bulle:
function comparator(a, b) {
return a - b;
}
/**
* Bubble sort algorithm.<br><br>
* Complexity: O(N^2).
*
* @example
* var sort = require('path-to-algorithms/src/' +
* 'sorting/bubblesort').bubbleSort;
* console.log(sort([2, 5, 1, 0, 4])); // [ 0, 1, 2, 4, 5 ]
*
* @public
* @module sorting/bubblesort
* @param {Array} array Input array.
* @param {Function} cmp Optional. A function that defines an
* alternative sort order. The function should return a negative,
* zero, or positive value, depending on the arguments.
* @return {Array} Sorted array.
*/
function bubbleSort(array, cmp) {
cmp = cmp || comparator;
var temp;
for (var i = 0; i < array.length; i += 1) {
for (var j = i; j > 0; j -= 1) {
if (cmp(array[j], array[j - 1]) < 0) {
temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
return array;
}
Aucun tri de comparaison ne sera mis à l'échelle de façon linéaire. Heureusement, vous pouvez le faire avec deux index dans le tableau. Voici l'idée de base.
Étant donné un tableau, a
, qui est déjà rempli.
startIndex = 0
endIndex = a.length - 1
while (startIndex < endIndex)
{
// Skip over odd numbers at the front of the array
while (startIndex < endIndex && (a[startIndex] % 2) != 0)
++startIndex;
if (startIndex >= endIndex)
break;
// startIndex points to an even number
// now look for the first odd number at the end of the array
while (startIndex < endIndex && (a[endIndex] % 2) == 0)
--endIndex;
if (startIndex >= endIndex)
break;
// swap the two items, placing the even number
// towards the end of the array, and the odd number
// towards the front.
temp = a[startIndex];
a[startIndex] = a[endIndex];
a[endIndex] = temp;
// and adjust the indexes
++startIndex;
--endIndex;
}
Donc, étant donné votre exemple de tableau:
[5, 2, 7, 9, 2, 3, 8, 4]
La première fois dans la boucle, il constate que le "2" à l'index 1 est pair. Ensuite, il recherche en arrière depuis la fin et trouve le "3" à l'index 5. Il les échange:
[5, 3, 7, 9, 2, 2, 8, 4]
La prochaine fois dans la boucle, startIndex
sera 2 et endIndex
sera 4. startIndex
est incrémenté au-delà du '7' et du '9', point auquel startIndex
est égal à endIndex
et vous sortez de la boucle.
Pour un problème similaire et un algorithme similaire, voir le problème du drapeau national néerlandais .
Essayez d'utiliser un tri simple, il donne ce que vous attendez
var data = [5, 2, 7, 9, 2, 3, 8, 4];
data.sort(function(a, b) {
return b % 2 - a % 2
});
console.log(data);
// [5, 7, 9, 3, 2, 2, 8, 4]
// % is mod, 5 % 2 = 1; 2 % 2 = 0;
Juste par un seul index produisant conditionnellement des swaps simples, en O(n) temps en O(1) espace vous pouvez faire comme suit. Notez que j'ai utilisé la déstructuration des tableaux pour permuter. On pourrait aussi choisir d'utiliser une variable temporaire.
function sortByParity(a){
var ec = 0; // Even count
for (var i = 0; i < a.length; i++){
a[i] & 1 ? [a[i],a[i-ec]] = [a[i-ec],a[i]] // If odd then swap accordingly
: ec++; // If even increment even count
}
return a;
}
var arr = [5,2,7,9,2,3,8,1,6,4],
brr = [0,2,4,6,8,1,3,5,7,9],
crr = [1,2,3,4,5,6,7,8,9,0];
console.log(JSON.stringify(sortByParity(arr)));
console.log(JSON.stringify(sortByParity(brr)));
console.log(JSON.stringify(sortByParity(crr)));
Edit: Donc, comme @Nina Scholz l'a suggéré, le même algorithme peut être implémenté de la même manière en comptant les cotes, mais pourrait sembler encore plus simple. (e & 1
est pratiquement identique à e % 2
pour tester la parité. Les chances entraîneront un 1 (vrai) tandis que les égalités entraîneront un 0 (faux))
function sortByParity(arr){
var oc = 0;
arr.forEach((e,i,a) => e & 1 && ([a[i], a[oc++]] = [a[oc], a[i]]));
return arr;
}
var arr = [5,2,7,9,2,3,8,1,6,4],
brr = [0,2,4,6,8,1,3,5,7,9],
crr = [1,2,3,4,5,6,7,8,9,0];
console.log(JSON.stringify(sortByParity(arr)));
console.log(JSON.stringify(sortByParity(brr)));
console.log(JSON.stringify(sortByParity(crr)));
De nombreuses implémentations ont déjà été publiées, je voudrais donc souligner comment ce problème est déjà résolu dans pratiquement toutes les infrastructures.
Lorsque vous attaquez cette question, il est instructif de regarder le tri rapide. Ou pas exactement quicksort, mais la sous-opération "partition" de quicksort, qui résout essentiellement le même problème.
La sous-opération "partition" accomplit la tâche suivante: étant donné une séquence de nombres et un pivot, en place permutez les éléments de la séquence de telle sorte que tous les nombres inférieurs au pivot se trouvent sur le côté gauche de la séquence, et tous les nombres supérieurs à les pivots sont sur le côté droit de la séquence. (Les nombres égaux au pivot se retrouvent au milieu, mais à moins que vous ne vous efforciez vraiment de vous tromper, cela se produira automatiquement.)
C'est presque exactement ce que nous essayons de faire ici: permuter sur place les éléments de sorte qu'ils soient séparés en deux côtés sur la base d'un test binaire. Pour le tri rapide, c'est moins que; pour ce problème, c'est bizarre.
Donc, si vous regardez un problème de cette nature (partition en place basée sur un prédicat binaire), le moyen le plus pratique pour l'attaquer est de copier-coller l'opération de partition de votre base de code. La seule considération pertinente est qu'il n'y a pas de pivot ici, alors gardez à l'esprit où quicksort l'a déplacé et ne sautez pas cet index.
Vous pouvez trier le tableau en place dans un espace constant en utilisant une méthodologie similaire aux algorithmes de tri populaires.
Avoir une variable pour marquer les index des parties actuellement triées du tableau, puis parcourir les éléments entre les deux en les laissant en place (dans le cas où ils sont impairs - en place est déjà trié), ou en les déplaçant vers la fin si elles sont égales.
Cet algorithme est O(n)
et l'espace requis est la taille du tableau. Ces deux échelles linéairement avec la taille du tableau.
var data = [5, 2, 7, 9, 2, 3, 8, 4];
// end holds the position of the last unsorted number
var end = data.length - 1;
// start holds the position of the first unsorted number
var start = 0;
// position is the current index of the number which is of interest
var position = start;
// while the two sorted halves have not met
while (start < end) {
var subject = data[position];
if (subject % 2 === 1) {
// it's odd, it's already in the correct half of the array
// so move to the next number, and increase start, marking this
// number as sorted
position++;
start++;
} else {
// it's even, move it to the even sorted section. The means that
// the number in "position" has not been looked at yet, so do not
// increase "position", but there's an extra sorted number at the
// end, so decrease "end"
var temp = data[end];
data[end] = subject;
data[position] = temp;
end--;
}
}
console.log(data);
Si vous acceptez de dupliquer la mémoire, vous pouvez le faire avec de vieilles fonctions JavaScript, car vous ne voulez pas que les sous-tableaux soient eux-mêmes triés:
var res = [];
for(var i = 0; i < input.length; ++i)
If (input [i] % 2)
res.unshift (input[i])
else
res.Push(input [i])
Oui, votre première idée de calculer des nombres pairs puis impairs était correcte:
var input = [5, 2, 7, 9, 2, 3, 8, 4];
var result = input.filter(x => x%2).sort().concat(input.filter(x => x%2 === 0).sort());
console.log(result);
Il s'agit d'une proposition qui recherche des paires continues de nombres pairs et inégaux. Une fois trouvé, la paire échange.
L'algorithme garde l'ordre des groupes de nombres pairs et impairs.
Fondamentalement, il utilise un compteur d'index et une boucle while avec un mécanisme de suivi arrière si une paire est trouvée, puis l'indice est, s'il est supérieur à zéro, décrémenté.
Par exemple, il s'agit du parcours d'un tableau:
array comment --------------------- -------------------------------- 5 7 2 8 7 9 2 1 4 6 8 found even odd couple at index 3 ^ ^ swap items, decrement index by 1 5 7 2 7 8 9 2 1 4 6 8 found even odd couple at index 2 ^ ^ 5 7 7 2 8 9 2 1 4 6 8 found even odd couple at index 4 ^ ^ 5 7 7 2 9 8 2 1 4 6 8 found even odd couple at index 3 ^ ^ 5 7 7 9 2 8 2 1 4 6 8 found even odd couple at index 6 ^ ^ 5 7 7 9 2 8 1 2 4 6 8 found even odd couple at index 5 ^ ^ 5 7 7 9 2 1 8 2 4 6 8 found even odd couple at index 4 ^ ^ 5 7 7 9 1 2 8 2 4 6 8 final result
var a = [5, 2, 7, 8, 7, 9, 2, 1, 4, 6, 8],
i = 0,
temp;
while (i < a.length - 1) {
if (!(a[i] % 2) && a[i + 1] % 2) {
temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
i && i--;
continue;
}
i++;
}
console.log(a); // [5, 7, 7, 9, 1, 2, 8, 2, 4, 6, 8]
.as-console-wrapper { max-height: 100% !important; top: 0; }
C'est fondamentalement la même chose, mais avec moins d'itérations pour les éléments déjà visités.
var a = [0, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 1],
i = 0,
j,
temp;
while (i < a.length - 1) {
j = i;
while (!(a[j] % 2) && a[j + 1] % 2) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
if (!j) {
break;
}
j--;
}
i++;
}
console.log(a);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Commencez un pointeur par l'avant qui s'arrête aux valeurs paires et un autre par le dernier qui s'arrête aux valeurs impaires. Échangez les deux valeurs et continuez jusqu'à ce que les deux pointeurs se croisent.
De plus, cet algorithme a une complexité O (n).
function SortThisArray(arr) {
var startIndex = 0;
var endIndex = arr.length - 1;
while(startIndex < endIndex) {
if(arr[startIndex] % 2 == 0 && arr[endIndex] % 2 == 1) {
var temp = arr[startIndex];
arr[startIndex] = arr[endIndex];
arr[endIndex] = temp;
}
if(arr[startIndex] % 2 == 1)
startIndex++;
if(arr[endIndex] % 2 == 0)
endIndex--;
}
return arr;
}
Selon moi, c'est le moyen le plus simple - si vous avez besoin d'une explication, lisez la réponse de @Dinesh Verma
C'est un seul passage et n'utilisez que 3 variables comme stockage supplémentaire
function sort(list)
{
var bottom=0
var top=list.length-1
var swap
while(bottom < top)
{
if (list[bottom] % 2 == 1)
++bottom
else if (list[top] % 2 == 0)
--top
else {
swap = list[bottom]
list[bottom] = list[top]
list[top] = swap
}
}
return list
}
Le plus simple et le plus facile dans ce cas est d'utiliser des "opérandes au niveau du bit" pour accéder au bit le moins significatif de chaque élément du tableau avec la méthode de tri:
function sortArray(array) {
let safe = [];
let mysort = Array.prototype.sort;
let mysplice = Array.prototype.splice;
function zipit(arg){return arg[0];}
array.forEach(function(e){safe.Push(e & 1);});
let odds = array.filter(function(e){
return (e & 1);}).sort(function(a,b){return a-b;});
let evens = array.filter(function(e){
return !(e & 1);}).sort(function(a,b){return b-a;});
let res = safe.map(function(ele,x){ return ( ele ) ? zipit(mysplice.call(odds,0,1)) : zipit(mysplice.call(evens,0,1));});
return res;
}
console.log(sortArray([2,3,2,1,5,6,7,8]));
et c'est tout. Vous obtenez un tableau de cotes parfaitement ordonné et trié, ... même.