--- title: Ordenação sidebar_position: 9 --- # Ordenação
Thumbnail da atividade Ordenação

Ordenação apresenta um dos problemas mais estudados da computação — colocar uma lista em ordem crescente — e o explora por múltiplos ângulos. O estudante implementa, fase a fase, cinco estratégias diferentes para o mesmo problema e percebe que cada uma tem uma lógica distinta, um custo diferente e situações em que funciona melhor ou pior.

Ao contrário de atividades que permitem soluções abertas, aqui cada fase exige um algoritmo específico. A validação verifica não só se a lista ficou ordenada, mas se o padrão de execução corresponde ao algoritmo pedido. Isso leva o estudante a entender o que torna cada algoritmo diferente — não apenas a chegar no resultado.

## O que esta atividade ensina sobre programação Ordenação é um contexto muito fértil para trabalhar estruturas de repetição aninhadas, variáveis de controle e operações sobre listas. Mas o que a distingue das demais atividades é que ela introduz uma ideia central da computação: diferentes algoritmos podem resolver o mesmo problema, com estratégias e custos radicalmente distintos. Nas fases iniciais, o estudante pratica comparação e troca de dois ou três elementos com condicionais simples — consolidando o básico. A progressão avança para os algoritmos clássicos: **Bubble Sort**, **Selection Sort**, **Insertion Sort**, **Shell Sort** e **Counting Sort**. Cada um representa uma abordagem diferente, dos mais intuitivos aos que fogem completamente do paradigma de comparação direta. A atividade também é uma porta de entrada concreta para análise de algoritmos. Sem precisar de formalismo matemático, o estudante pode observar quantas operações cada estratégia realiza, comparar resultados e começar a pensar em eficiência — uma competência que vai além da programação e se aplica a qualquer tipo de problema que exige raciocínio sobre custo e escala. ## Os algoritmos Cada algoritmo desta atividade representa uma estratégia diferente para o mesmo problema. Os diagramas abaixo mostram o fluxo lógico de cada um. ### Bubble Sort O Bubble Sort percorre a lista repetidamente, comparando pares de elementos adjacentes e trocando os que estão fora de ordem. A cada passada completa, o maior elemento ainda desordenado chega à sua posição final. O processo se repete até que nenhuma troca seja necessária. ```mermaid flowchart TD A[Início] --> B[Nova passada pela lista] B --> C[i = 0] C --> D{elemento atual maior que o próximo?} D -->|Sim| E[Trocar os dois] D -->|Não| F[i = i + 1] E --> F F --> G{Fim da passada?} G -->|Não| D G -->|Sim| H{Alguma troca ocorreu?} H -->|Sim| B H -->|Não| I[Lista ordenada] ``` A versão com otimização acrescenta uma variável de controle que detecta quando nenhuma troca ocorreu na passada — o que significa que a lista já está ordenada — encerrando o algoritmo antes das passadas restantes. #### Quando é vantajoso O Bubble Sort brilha quando a lista já está quase ordenada: com a otimização de parada antecipada, ele pode terminar em uma única passada, com custo proporcional ao tamanho da lista. É também o algoritmo mais fácil de implementar e depurar, o que o torna o ponto de partida natural para quem está aprendendo ordenação. #### Quando não é a melhor escolha Em listas grandes e desordenadas, o Bubble Sort realiza um número quadrático de comparações e trocas — o que o torna impraticável para volumes acima de alguns milhares de elementos. Em performance real, costuma ser o mais lento entre os algoritmos O(n²) porque acumula muitas trocas por passada. #### Onde é usado Por seu custo quadrático, o Bubble Sort raramente é escolhido em sistemas reais. Sua importância é quase inteiramente pedagógica: a lógica de "empurrar o maior elemento para o final a cada passada" é uma das mais intuitivas para entender o conceito de ordenação. Versões educacionais e ferramentas de visualização de algoritmos o utilizam amplamente por esse motivo. ### Selection Sort O Selection Sort divide a lista em duas partes: a porção já ordenada, que cresce da esquerda, e a porção desordenada. A cada passada, o algoritmo varre toda a parte desordenada para encontrar o menor elemento e o posiciona no início dela — com uma única troca por passada. ```mermaid flowchart TD A[i = 0] --> B[mínimo = i] B --> C[j = i + 1] C --> D{elemento j menor que elemento mínimo?} D -->|Sim| E[mínimo = j] D -->|Não| F[j = j + 1] E --> F F --> G{j chegou ao fim?} G -->|Não| D G -->|Sim| H[Trocar posições i e mínimo] H --> I{i chegou ao penúltimo?} I -->|Não| J[i = i + 1] J --> B I -->|Sim| K[Lista ordenada] ``` Uma característica do Selection Sort é que ele realiza exatamente `n - 1` trocas, independente do estado inicial da lista — ao contrário do Bubble Sort, que pode realizar muitas mais. #### Quando é vantajoso O Selection Sort é vantajoso quando o custo de escrever na memória é significativamente mais alto do que o de ler — por exemplo, em memórias flash, onde cada escrita tem custo real de desgaste. Nesse contexto, fazer no máximo `n - 1` trocas é uma vantagem concreta. Ele também é simples de implementar e tem comportamento previsível: o número de comparações é sempre o mesmo, independente da entrada. #### Quando não é a melhor escolha Assim como o Bubble Sort, o Selection Sort tem custo quadrático no número de comparações — mesmo que a lista já esteja ordenada, ele percorre toda a parte não ordenada a cada passada. Não existe versão com parada antecipada eficaz. Para listas grandes, algoritmos O(n log n) são muito mais eficientes. #### Onde é usado O Selection Sort aparece em contextos embarcados com memória muito limitada e escrita cara, como microcontroladores simples e dispositivos EEPROM. Em sistemas de uso geral, é substituído por algoritmos mais eficientes. Pedagogicamente, é valioso porque separa claramente a fase de busca do mínimo da fase de troca, tornando a lógica de duas etapas por passada muito explícita. ### Insertion Sort O Insertion Sort constrói a lista ordenada de forma incremental. Para cada elemento da parte desordenada, ele retrocede na parte já ordenada deslocando os elementos maiores uma posição para a direita, até encontrar o lugar certo para inserir o elemento atual. ```mermaid flowchart TD A[i = 1] --> B[guardar elemento da posição i] B --> C[j = i - 1] C --> D{j válido e elemento j maior que o guardado?} D -->|Sim| E[Deslocar elemento j uma posição para a direita] E --> F[j = j - 1] F --> D D -->|Não| G[Inserir o guardado na posição j + 1] G --> H{i chegou ao fim?} H -->|Não| I[i = i + 1] I --> B H -->|Sim| J[Lista ordenada] ``` O Insertion Sort é muito eficiente para listas quase ordenadas, pois precisa de poucos deslocamentos quando os elementos já estão próximos de sua posição final. É também a base do Shell Sort. #### Quando é vantajoso O Insertion Sort é uma das melhores escolhas para listas pequenas (tipicamente abaixo de 20 elementos) ou quase ordenadas. Nesses casos, ele supera algoritmos teoricamente mais eficientes como o Merge Sort, que carregam custo fixo maior de divisão e combinação. Para listas já ordenadas, seu custo é linear — O(n) — pois nenhum deslocamento é necessário. #### Quando não é a melhor escolha Em listas grandes e aleatórias, o custo quadrático domina. Cada novo elemento pode precisar ser deslocado até o início da lista, resultando em muitas operações de escrita. Para esses casos, algoritmos O(n log n) são necessários. #### Onde é usado O Insertion Sort está presente em implementações híbridas de algoritmos avançados. O Timsort — usado por padrão em Python e na JVM — aplica o Insertion Sort em blocos pequenos antes de combiná-los com Merge Sort. O IntroSort — base do `std::sort` do C++ — também recorre ao Insertion Sort quando a recursão chega a sublistas pequenas. É um algoritmo com papel real e ativo nos sistemas de produção mais difundidos. ### Shell Sort O Shell Sort é uma generalização do Insertion Sort. Em vez de inserir comparando apenas elementos vizinhos, ele começa comparando elementos separados por um intervalo inicial de metade do tamanho da lista. A cada rodada, esse intervalo é dividido ao meio. Quando o intervalo chega a 1, o comportamento é idêntico ao Insertion Sort — mas a lista já está bem próxima da ordem, então o trabalho restante é mínimo. ```mermaid flowchart TD A[intervalo = tamanho dividido por 2] --> B{intervalo maior ou igual a 1?} B -->|Não| Z[Lista ordenada] B -->|Sim| C[i = intervalo] C --> D[guardar elemento da posição i] D --> E[j = i - intervalo] E --> F{j válido e elemento j maior que o guardado?} F -->|Sim| G[Deslocar elemento j para a posição j + intervalo] G --> H[j = j - intervalo] H --> F F -->|Não| I[Inserir o guardado na posição j + intervalo] I --> J{i chegou ao fim?} J -->|Não| K[i = i + 1] K --> D J -->|Sim| L[intervalo = intervalo dividido por 2] L --> B ``` A intuição por trás do Shell Sort é que comparações de longa distância movem os elementos para perto de sua posição final muito mais rapidamente do que comparações vizinhas, reduzindo o trabalho das rodadas seguintes. #### Quando é vantajoso O Shell Sort é especialmente adequado para sistemas embarcados e ambientes sem suporte a recursão, onde algoritmos O(n log n) baseados em divisão (como Merge Sort e Quick Sort) são difíceis de implementar. Ele entrega desempenho subquadrático — na prática próximo de O(n log n) para sequências de intervalos bem escolhidas — com código iterativo simples e memória O(1). #### Quando não é a melhor escolha A complexidade do Shell Sort depende da sequência de intervalos escolhida, e a análise teórica ainda é um problema em aberto para algumas sequências. Em sistemas com suporte a recursão e memória suficiente, o Merge Sort ou o Timsort oferecem garantias mais sólidas e melhor desempenho em listas grandes. #### Onde é usado O Shell Sort é encontrado em firmwares, sistemas operacionais compactos e ambientes com pilha de chamadas limitada. O kernel do Linux e o uClibc utilizaram implementações de Shell Sort em contextos onde simplicidade e ausência de recursão eram mais importantes do que performance máxima. É também um ponto de passagem didático natural entre o Insertion Sort e os algoritmos baseados em divisão. ### Counting Sort O Counting Sort é fundamentalmente diferente dos anteriores: ele **não compara elementos entre si**. Em vez disso, conta quantas vezes cada valor aparece na lista e, a partir dessas contagens, reconstrói a lista já ordenada. Por isso, ele escapa do limite teórico inferior O(n log n) que vale para qualquer algoritmo baseado em comparação. ```mermaid flowchart TD A[Criar lista de contagem com zeros] --> B[Para cada elemento da lista] B --> C[Incrementar a contagem do valor correspondente] C --> D{Todos os elementos contados?} D -->|Não| B D -->|Sim| E[v = 0] E --> F{v menor que tamanho da contagem?} F -->|Não| G[Lista reconstruída] F -->|Sim| H{contagem de v maior que zero?} H -->|Não| I[v = v + 1] I --> F H -->|Sim| J[Adicionar v à próxima posição da lista] J --> K[Decrementar contagem de v] K --> H ``` #### Quando é vantajoso O Counting Sort tem complexidade O(n + k), onde **k** é o valor máximo dos dados. Quando k é pequeno em relação a n, ele supera qualquer algoritmo de comparação: ordenar um milhão de notas escolares no intervalo 0–10 é muito mais rápido com Counting Sort do que com Bubble, Insertion ou até Merge Sort. Ele também é a base do **Radix Sort**, que o aplica dígito a dígito para ordenar inteiros arbitrários em O(d · n), onde d é o número de dígitos. #### Quando não é a melhor escolha Quando k >> n — por exemplo, ordenar 10 números no intervalo [0, 1.000.000] — o algoritmo aloca um array de 1 milhão de posições desnecessariamente, com memória O(k) dominando o custo. Além disso, o Counting Sort não se aplica diretamente a dados com ponto flutuante, strings ou objetos complexos, nem a dados com distribuição desconhecida ou muito esparsa. #### Comparação de complexidade | Algoritmo | Tempo médio | Memória | |---|---|---| | Bubble / Insertion | O(n²) | O(1) | | Merge / Heap | O(n log n) | O(n) / O(1) | | Quick Sort | O(n log n) | O(log n) | | Counting Sort | O(n + k) | O(k) | Nas fases desta atividade, a lista tem 10 valores com máximo 55. O Counting Sort usa um array de 56 posições — mais memória do que o estritamente necessário para n=10, mas eficiente caso houvesse muitos elementos nessa faixa. Sua importância aqui é pedagógica: ilustrar que existem algoritmos que exploram propriedades dos dados para escapar do limite O(n log n), ao custo de restrições sobre o tipo e a faixa dos valores. #### Onde é usado O Counting Sort aparece como subroutine do **Radix Sort**, que o aplica dígito a dígito para ordenar inteiros de qualquer magnitude. Sistemas de contagem de votos, classificação de idades, notas ou qualquer dado inteiro com faixa pequena e conhecida são contextos naturais. Implementações de ordenação estável em sistemas de banco de dados também o utilizam quando os dados satisfazem as restrições de tipo e faixa. No ecossistema de algoritmos, ele representa a categoria dos algoritmos não comparativos — junto com o Radix Sort e o Bucket Sort — que quebram o limite teórico O(n log n) ao explorar a estrutura interna dos dados. ## Mediação pedagógica Uma abordagem eficaz é pedir que o estudante descreva o algoritmo em palavras antes de implementá-lo em blocos. "O que o Bubble Sort faz a cada passada?" ou "Como o Selection Sort escolhe o próximo elemento?" ajudam a separar a compreensão do algoritmo da habilidade de montá-lo. Se o estudante trava nos blocos, o problema está quase sempre na compreensão do algoritmo — não na ferramenta. Outro recurso valioso é comparar soluções após a implementação. Dois algoritmos que produzem o mesmo resultado final podem ter realizado quantidades muito diferentes de operações. Perguntar "qual deles você acha que fez mais trabalho?" e em seguida observar o número de comparações e trocas — disponíveis nos dados de execução — torna a noção de eficiência concreta e baseada em evidência. O Shell Sort e o Counting Sort merecem atenção especial na mediação. O Shell Sort exige que o estudante entenda por que comparar elementos distantes pode ser útil antes de comparar os vizinhos — uma ideia contraintuitiva que vale explorar antes de implementar. O Counting Sort, por sua vez, rompe com o modelo mental de "comparar e decidir" e pode gerar uma boa discussão sobre o que é realmente necessário para ordenar uma lista quando se conhece o intervalo dos dados. ## Progressão das fases | Fase | Nome | O que o estudante pratica | |---|---|---| | 1 | Ordenar 2 Números | usar condicional para comparar e trocar dois valores | | 2 | Ordenar 3 Números | combinar repetição e condicionais para ordenar três elementos | | 3 | Bubble Sort | comparar pares adjacentes em passadas repetidas | | 4 | Bubble Sort com Otimização | adicionar controle de parada antecipada quando não há trocas | | 5 | Selection Sort | encontrar o mínimo da parte desordenada e posicioná-lo com uma troca | | 6 | Insertion Sort | inserir cada elemento na posição correta deslocando os maiores | | 7 | Shell Sort | usar comparações com intervalo variável que diminui progressivamente até 1 | | 8 | Counting Sort | contar ocorrências e reconstruir a lista sem comparar elementos | ## Quando usar Ordenação é indicada quando a turma já tem fluência com laços, variáveis e condicionais, e está pronta para um problema com múltiplas soluções corretas e eficiências distintas. É especialmente adequada em aulas que queiram discutir análise de algoritmos de forma introdutória, ou que queiram mostrar que a escolha da estratégia depende das características dos dados — não apenas da correção do resultado. A atividade funciona bem como continuação natural após Cripto, já que ambas trabalham com transformação sistemática de informação regida por regras precisas. Se o objetivo incluir conceitos como memória auxiliar, troca entre espaço e tempo, ou a categoria de algoritmos não comparativos, o Counting Sort oferece um gancho concreto e didático para essa discussão. ## Referências - Algoritmos de ordenação: https://pt.wikipedia.org/wiki/Algoritmo_de_ordena%C3%A7%C3%A3o - Bubble Sort: https://pt.wikipedia.org/wiki/Bubble_sort - Selection Sort: https://pt.wikipedia.org/wiki/Selection_sort - Insertion Sort: https://pt.wikipedia.org/wiki/Insertion_sort - Shell Sort: https://pt.wikipedia.org/wiki/Shell_sort - Counting Sort: https://pt.wikipedia.org/wiki/Counting_sort - Complexidade computacional: https://pt.wikipedia.org/wiki/Complexidade_computacional