A organização de listas de dados é essencial no desenvolvimento de aplicações, facilitando a exibição e a busca de informações. Não surpreende que programadores experientes dominem a arte de ordenar arrays. Este artigo explora alguns dos algoritmos mais empregados para essa finalidade em JavaScript.
O que é Ordenação e Qual a Sua Relevância?
Fonte: Remover respingo
Ordenação, nesse contexto, refere-se ao processo de organizar dados de acordo com uma ordem específica, seja ela crescente ou decrescente. Em JavaScript, a capacidade de ordenar arrays é vital, pois permite apresentar dados de maneira mais intuitiva. Por exemplo, pode-se querer visualizar arquivos ordenados pelos mais recentes ou produtos listados por preço. Além disso, a ordenação é fundamental para algoritmos de busca binária, que funcionam apenas em dados pré-ordenados.
Embora existam funções e bibliotecas que simplificam a ordenação, entender os mecanismos internos é valioso para entrevistas de emprego ou quando se necessita escrever código de baixo nível.
Algoritmos de Ordenação de Array em JavaScript
Ordenação por Bolha (Bubble Sort)

O Bubble Sort destaca-se pela simplicidade de compreensão e implementação. A lógica reside em percorrer o array repetidamente, comparando elementos adjacentes e trocando-os de lugar se estiverem na ordem incorreta. O número de passagens é igual ao número de elementos do array. A cada passagem, o maior elemento “borbulha” para a extremidade direita do array. Abaixo, o pseudocódigo para ordenar números em ordem ascendente:
1. Seja n o número de elementos no array. 2. Repita n vezes, usando i como contador de loops: a. Percorra o array do segundo elemento até o (n - i)-ésimo elemento. b. Se o elemento anterior for maior que o atual, troque-os.
A tradução desse pseudocódigo para JavaScript resulta em:
function sort(arr) {
const n = arr.length;
for (let i = 0; i < n; i++) {
for (let j = 1; j < n - i; j++) {
if (arr[j - 1] > arr[j]) {
const temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
Para um entendimento mais profundo do processo, sugiro adicionar instruções `console.log` dentro dos loops, permitindo acompanhar a evolução do array a cada passagem.
O código abaixo exibe uma versão modificada da função `sort` com `console.log`s, juntamente com um exemplo de array não ordenado:
function sort(arr) {
const n = arr.length;
for (let i = 0; i < n; i++) {
console.log(`Pass: ${i}`);
for (let j = 1; j < n - i; j++) {
if (arr[j - 1] > arr[j]) {
const temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
console.log(arr);
}
}
return arr;
}
const array = [9, 2, 7, 4, 1];
sort(array);
console.log(array);
O resultado da execução desse código será:

O Bubble Sort apresenta uma complexidade de tempo de O(n^2), pois executa n passagens, que iteram pelo array a cada passagem. Isso o torna ineficiente para grandes conjuntos de dados. No entanto, sua complexidade de espaço é O(1), já que as modificações são feitas diretamente no array original.
Ordenação por Inserção (Insertion Sort)

O Insertion Sort é outro algoritmo popular para ordenar arrays em JavaScript. A ideia é escolher um elemento (chamado `num`) e movê-lo para a esquerda até que todos os elementos à sua esquerda sejam menores que ele. Se repetirmos esse processo do segundo elemento até o último, o array estará ordenado. Veja o pseudocódigo:
1. Seja n o número de elementos no array.
2. Repita i de 1 até n - 1 (iniciando do segundo elemento):
a. Defina currentElement como array[i].
b. Defina j como i - 1.
c. Enquanto j >= 0 e array[j] > current_element:
i. Mova array[j] para array[j+1].
ii. Decremente j em 1.
d. Defina array[j+1] como current_element.
E a implementação em JavaScript:
function insertionSort(array) {
const n = array.length;
for (let i = 1; i < n; i++) {
const currentElement = array[i];
let j = i - 1;
while (j >= 0 && array[j] > currentElement) {
array[j + 1] = array[j];
j -= 1;
}
array[j + 1] = currentElement;
}
return array;
}
A inclusão de `console.log`s também facilita a visualização do processo. O código abaixo ilustra o funcionamento do Insertion Sort:
function sort(array) {
const n = array.length;
for (let i = 1; i < n; i++) {
const currentElement = array[i];
let j = i - 1;
console.log("Placing element:", currentElement);
while (j >= 0 && array[j] > currentElement) {
array[j + 1] = array[j];
j -= 1;
}
array[j + 1] = currentElement;
console.log("Placed it at position:", j + 1);
console.log(array);
}
return array;
}
const array = [4, 1, 2, 5, 3];
sort(array);
A execução desse código produzirá o seguinte resultado:

Ordenação por Mistura (Merge Sort)

Diferentemente do Insertion Sort e do Bubble Sort, que apresentam um desempenho quadrático, o Merge Sort opera em um tempo quase linear, com complexidade de O(n * log(n)).
O Merge Sort adota a estratégia de “dividir para conquistar”. O array é subdividido recursivamente em arrays menores, até que cada um contenha apenas um elemento. Em seguida, esses arrays são combinados, de forma ordenada.
A subdivisão é recursiva, para permitir que o array seja remontado. Na fase de combinação, os subarrays são mesclados de forma ordenada. A mesclagem é realizada de maneira semelhante à mesclagem de dois arrays já ordenados. Abaixo, o pseudocódigo desse processo:
1. Se o comprimento do array for menor ou igual a 1, retorne o array (caso base).
2. Encontre o índice do meio:
a. Defina mid como a parte inteira de (comprimento do array / 2).
3. Divida o array em dois subarrays:
a. Crie leftArray e copie a primeira metade do array (do índice 0 até mid).
b. Crie rightArray e copie a segunda metade do array (de mid+1 até o final).
4. Chame recursivamente MergeSort em leftArray.
5. Chame recursivamente MergeSort em rightArray.
6. Mescle os dois subarrays ordenados:
a. Crie um resultArray vazio.
b. Enquanto leftArray e rightArray não estiverem vazios:
i. Se o primeiro elemento de leftArray for menor ou igual ao primeiro elemento de rightArray, adicione-o a resultArray.
ii. Caso contrário, adicione o primeiro elemento de rightArray a resultArray.
c. Adicione quaisquer elementos restantes em leftArray a resultArray (se houver).
d. Adicione quaisquer elementos restantes em rightArray a resultArray (se houver).
7. Retorne resultArray.
A implementação em JavaScript seria:
function sort(array) {
// Caso base para interromper a subdivisão do array
if (array.length == 1) {
return array;
}
// Encontrando o ponto médio do array
const m = Math.round(array.length / 2);
// Dividindo o array em duas metades
const leftUnsorted = array.slice(0, m);
const rightUnsorted = array.slice(m);
// Chamando o merge sort recursivamente
const leftSorted = sort(leftUnsorted);
const rightSorted = sort(rightUnsorted);
// Retornando um array mesclado e ordenado
return merge(leftSorted, rightSorted);
}
function merge(left, right) {
// Mesclando duas listas ordenadas
let result = [];
let leftIndex = 0;
let rightIndex = 0;
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex += 1;
} else {
result.push(right[rightIndex]);
rightIndex += 1;
}
}
return result.concat(left.slice(leftIndex), right.slice(rightIndex));
}
Ao executar o código com um array de exemplo, ele deverá funcionar corretamente.

Ordenação Rápida (Quick Sort)

Semelhante ao Merge Sort, o Quick Sort também emprega a estratégia de “dividir para conquistar”. O algoritmo escolhe um elemento pivô e move todos os elementos maiores que o pivô para a direita e os menores para a esquerda. Após essa etapa, o pivô estará em sua posição correta.
Para movimentar os elementos em relação ao pivô, o algoritmo inicia transferindo o pivô para o final do array.
Em seguida, um ponteiro percorre o array da esquerda para a direita, buscando o primeiro número maior que o pivô. Simultaneamente, outro ponteiro percorre o array da direita para a esquerda, buscando o primeiro número menor que o pivô. Quando ambos os números são localizados, eles são trocados. Esse processo é repetido até que o ponteiro da esquerda se torne maior que o ponteiro da direita.
Ao interromper o loop, o maior dos dois últimos números trocados é trocado com o pivô. Neste ponto, o pivô estará na posição correta; os números menores que ele estarão à esquerda, enquanto os maiores estarão à direita.
Esse procedimento é repetido recursivamente para os subarrays à esquerda e à direita do pivô até que os subarrays tenham apenas um elemento.
Segue o pseudocódigo para a ordenação rápida:
1. Se lessThanPointer for menor que greaterThanPointer:
a. Escolha um elemento pivô do array.
b. Mova os elementos de modo que os menores fiquem à esquerda e os maiores à direita.
c. Chame recursivamente Quicksort no subarray esquerdo.
d. Chame recursivamente Quicksort no subarray direito.
E a sua conversão para JavaScript:
function sort(array, low, high) {
if (low < high) {
// Escolhe o índice do pivô e particiona o array
const pivotIndex = move(array, low, high);
// Ordena recursivamente os subarrays à esquerda e à direita do pivô
sort(array, low, pivotIndex - 1);
sort(array, pivotIndex + 1, high);
}
}
function move(array, low, high) {
// Seleciona o elemento pivô (neste caso, o último elemento)
const pivotElement = array[high];
// Inicializa o índice para o elemento menor
let i = low - 1;
for (let j = low; j < high; j++) {
// Se o elemento atual for menor ou igual ao pivô, troca-o com o elemento no índice i+1
if (array[j] <= pivotElement) {
i += 1;
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// Troca o elemento pivô para sua posição correta
const temp = array[i];
array[i] = array[j];
array[j] = temp;
// Retorna o índice do elemento pivô
return i + 1;
}
A ordenação de um array de exemplo com Quick Sort em Node.js deve gerar o seguinte resultado:

Na melhor das hipóteses, o Quicksort apresenta um desempenho em tempo quase linear. O uso de espaço também escala logaritmicamente. Portanto, é relativamente eficiente em comparação com outros algoritmos de ordenação de arrays em JavaScript.
Dicas para Suas Entrevistas de Programação
❇️ A prática é fundamental. Ela ajuda a memorizar diversos algoritmos, mas o mais importante é que aprimora suas habilidades de resolução de problemas e pensamento computacional. Você também pode praticar em plataformas como LeetCode e AlgoExpert.
❇️ Tente resolver o problema por conta própria primeiro. Em vez de ir direto para a solução, esforce-se para resolvê-lo, pois isso aprimora suas capacidades de resolução de problemas.
❇️ Se um problema estiver demorando muito, vá direto para a solução; você ainda pode aprender o método de resolução. A maioria das plataformas oferece soluções. O ChatGPT ou o Google Bard também são úteis para esclarecer conceitos.
❇️ Além disso, não comece a escrever código imediatamente; utilize um quadro branco para rascunhar suas soluções e refletir sobre elas antes de escrever o código. O pseudocódigo também é uma forma eficiente de anotar ideias rapidamente.
Conclusão
Neste artigo, exploramos os algoritmos de ordenação mais populares. Aprender todos eles pode parecer desafiador inicialmente. Por isso, recomenda-se sempre consultar diversas fontes de informação em vez de se basear em apenas uma. Boa programação!
Em seguida, veja como entender a função `sorted` em Python.