O que mais cai
| Parte do tópico | Importância | O que a banca cobra |
|---|---|---|
| Definição de Algoritmos e Estruturas de Controle | Alta | Características do algoritmo; sequência, seleção e repetição; enquanto, repita e para; teste de mesa |
| Variáveis, Subprogramas e Passagem de Parâmetro | Alta | Escopo; procedimento e função; parâmetro formal e real; passagem por valor e por referência |
| Linguagens Educacionais e Pseudolinguagens | Média | Portugol e VisuAlg; fluxograma e seus símbolos; Pascal, Logo e Scratch no ensino |
| Algoritmos Diversos | Alta | Recursão; busca sequencial e binária; métodos de ordenação; complexidade em notação O |
Do que trata o tópico
Este tópico ensina a base de toda a programação: o que é um algoritmo, como ele se escreve com as estruturas de controle, como as variáveis e os subprogramas guardam e trocam dados, quais pseudolinguagens e linguagens servem ao ensino e quais algoritmos clássicos (recursão, busca e ordenação) aparecem nas provas.
Conteúdo
Definição de Algoritmos e Estruturas de Controle
Um servidor precisa calcular a média de 300 candidatos e dizer quem foi aprovado. Se cada colega fizer a conta do seu jeito, o resultado muda de pessoa para pessoa. A solução é uma sequência de passos que qualquer pessoa, ou computador, execute sempre da mesma forma.
Essa sequência de passos é o algoritmo: um conjunto finito de instruções bem definidas, em ordem, que resolve um problema em um tempo finito. A receita de bolo é o exemplo do dia a dia, mas o computador não interpreta frases vagas como "asse até ficar bom".
Características do algoritmo. Donald Knuth descreve cinco características que a banca cobra com frequência:
- Finitude: o algoritmo termina após um número finito de passos.
- Definição: cada passo é preciso e sem ambiguidade, isto é, só admite uma interpretação.
- Entrada: o algoritmo tem zero ou mais entradas, os dados que recebe antes ou durante a execução.
- Saída: o algoritmo tem uma ou mais saídas, os resultados que entrega.
- Efetividade: cada passo é simples o bastante para ser executado de fato, em tempo finito.
A entrada pode ser zero (um algoritmo que imprime os números de 1 a 10 não recebe nada), mas a saída não, porque um algoritmo sem resultado não resolve problema algum. Um laço que nunca termina pode estar num programa, mas não é algoritmo, porque viola a finitude.
Formas de representação. A descrição narrativa usa a língua natural ("leia as duas notas, some e divida por dois") e permite ambiguidade. O fluxograma usa símbolos ligados por setas, e o pseudocódigo usa uma linguagem estruturada em português. A parte de pseudolinguagens detalha os dois.
A programação estruturada. Corrado Böhm e Giuseppe Jacopini mostraram que qualquer algoritmo pode ser escrito com só três estruturas de controle: a sequência, a seleção (decisão) e a repetição (laço). Edsger Dijkstra criticou o desvio incondicional (o goto), que salta para qualquer ponto do código. A programação estruturada usa só as três estruturas, cada uma com uma entrada e uma saída.
Sequência. Na sequência, os comandos executam um depois do outro, na ordem escrita.
Seleção. A seleção escolhe qual bloco executar a partir de uma condição lógica, isto é, uma expressão que vale verdadeiro ou falso. Há quatro formas:
- Seleção simples (
se ... entao ... fimse): se a condição é verdadeira, o bloco executa; se é falsa, nada acontece. - Seleção composta (
se ... entao ... senao ... fimse): um bloco para o verdadeiro e outro para o falso. Sempre um dos dois executa. - Seleção encadeada: um
sedentro dosenaode outro, para testar várias faixas (nota abaixo de 5, de 5 a 7, acima de 7). - Seleção múltipla (
escolha ... caso, oswitchde C e Java): compara uma variável com vários valores fixos e executa o caso que bate.
No switch de C e Java, sem o break no fim do case, a execução continua nos casos de baixo, mesmo que o valor não bata com eles. Esse efeito se chama queda (fall-through).
Repetição. A repetição executa o mesmo bloco várias vezes: o cálculo da média dos 300 candidatos é escrito uma vez, dentro de um laço. Há três tipos de laço, que diferem pelo momento do teste da condição:
- Enquanto (
enquanto ... faca, owhile): testa a condição antes de cada volta. Se a condição é falsa logo na primeira vez, o bloco não executa nenhuma vez. - Repita ... até (
repita ... ate): testa a condição depois de cada volta. O bloco executa pelo menos uma vez, e o laço para quando a condição fica verdadeira. - Para (
para i de 1 ate 10 faca, ofor): usa uma variável de controle que começa num valor, avança a cada volta e para no valor final. Serve quando o número de voltas é conhecido antes de o laço começar.
Em C e Java, o laço com teste no fim é o do ... while, que continua enquanto a condição é verdadeira. O repita ... ate do Portugol faz o contrário: continua enquanto a condição é falsa e para quando ela fica verdadeira. As duas estruturas executam o bloco pelo menos uma vez.
O contador soma sempre o mesmo valor (cont <- cont + 1) e conta ocorrências. O acumulador soma um valor que muda (soma <- soma + nota) e guarda um total. Os dois recebem um valor inicial antes do laço, em geral zero.
Se o bloco não altera nenhuma variável da condição, o laço nunca termina: é o laço infinito. Para parar a leitura de dados sem saber quantos virão, o algoritmo usa um valor sentinela, um valor especial (como -1 numa lista de idades) que avisa o fim.
Operadores. As condições usam três grupos de operadores. Os aritméticos fazem conta: além de soma, subtração, multiplicação e divisão, há div (quociente da divisão inteira) e mod (resto da divisão inteira). Os relacionais comparam (=, <>, <, >, <=, >=), e os lógicos juntam condições: e (verdadeiro só se as duas partes são verdadeiras), ou (verdadeiro se ao menos uma é verdadeira) e nao (inverte o valor).
A ordem de avaliação, sem parênteses, é: primeiro os aritméticos, depois os relacionais e por último os lógicos. Entre os lógicos, o nao vem antes do e, e o e vem antes do ou. Assim, nao V e F ou V vale verdadeiro: nao V dá falso, falso e F dá falso e falso ou V dá verdadeiro.
Exemplo resolvido: o algoritmo abaixo soma os números pares de 1 a 6. A técnica para descobrir o que ele mostra é o teste de mesa, isto é, executar o algoritmo à mão e anotar o valor de cada variável a cada passo.
soma <- 0
i <- 1
enquanto i <= 6 faca
se i mod 2 = 0 entao
soma <- soma + i
fimse
i <- i + 1
fimenquanto
escreva(soma)
- Antes do laço:
somavale 0 eivale 1. - Com
i= 1, o resto de 1 por 2 é 1, a condição doseé falsa esomacontinua 0. Oipassa a 2. - Com
i= 2, o resto é 0 esomapassa a 0 + 2 = 2. Oipassa a 3. - Com
i= 3, nada muda na soma. Comi= 4,somapassa a 2 + 4 = 6. - Com
i= 5, nada muda. Comi= 6,somapassa a 6 + 6 = 12. - O
ipassa a 7, a condição7 <= 6é falsa e o laço termina. - Resultado: o algoritmo mostra 12.
Na prática, um setor de protocolo conta os processos atrasados de uma lista com um laço para que percorre a lista, um se que testa o prazo e um contador zerado antes do laço. A diferença que a banca mais troca é entre o enquanto, que pode não executar nenhuma vez, e o repita, que executa ao menos uma vez.
O erro comum é esquecer de atualizar a variável da condição dentro do laço (laço infinito) ou de iniciar o acumulador com zero.
x = 10
cont = 0
while x > 1:
if x % 2 == 0:
x = x // 2
else:
x = 3 * x + 1
cont = cont + 1
print(x, cont)
Considere o código acima, escrito em Python, em que % é o resto da divisão e // é a divisão inteira. Ao final da execução, o programa imprime:
- A) 1 5
- B) 2 6
- C) 1 6
- D) 1 7
- E) nada, porque o laço não termina
Comentário:
A) Errada.
Conta: o valor de x passa por 10, 5, 16, 8, 4, 2 e 1, e cada passagem soma 1 em cont, o que dá 6 voltas.
A alternativa erra em 1 5: são 6 voltas, e não 5. O erro vem de esquecer a volta em que x passa de 2 para 1.
B) Errada.
Conta: na sexta volta, x vale 2, é par e passa a 2 // 2 = 1; o teste x > 1 fica falso e o laço para com x igual a 1.
A alternativa erra em 2 6: o laço só para quando x deixa de ser maior que 1, e por isso x termina valendo 1.
C) Certa.
Conta: 10 vira 5 (1ª volta), 5 vira 16 (2ª), 16 vira 8 (3ª), 8 vira 4 (4ª), 4 vira 2 (5ª) e 2 vira 1 (6ª).
O laço termina com x igual a 1 e cont igual a 6, e o comando print mostra os dois valores separados por espaço.
D) Errada.
Regra: o laço while testa a condição antes de cada volta; quando x chega a 1, o teste falha e nenhuma volta a mais acontece.
A alternativa erra em 1 7: não há sétima volta, porque o teste x > 1 falha logo depois da sexta.
E) Errada.
Regra: o laço termina quando o bloco altera a variável da condição até ela ficar falsa.
A alternativa erra em o laço não termina: a variável x muda a cada volta e chega a 1 depois de 6 voltas.
Resultado: alternativa C.
int op = 2;
int r = 0;
switch (op) {
case 1: r = r + 1;
case 2: r = r + 10;
case 3: r = r + 100;
break;
default: r = r + 1000;
}
System.out.println(r);
Considere o código acima, escrito em Java. O valor impresso é:
- A) 10
- B) 0
- C) 1110
- D) 111
- E) 110
Comentário:
A) Errada.
Regra: no switch, o case sem break deixa a execução cair nos casos de baixo até achar um break ou o fim do bloco.
A alternativa erra em 10: o case 2 não tem break, e a execução cai no case 3, que soma mais 100.
B) Errada.
Regra: o switch executa o case cujo valor é igual ao da variável testada.
A alternativa erra em 0: op vale 2, o case 2 bate e r recebe pelo menos 10.
C) Errada.
Regra: o break encerra o switch, e os casos depois dele não executam.
A alternativa erra em 1110: o break do case 3 impede a soma do default, e o case 1 fica acima do ponto de entrada, por isso não executa.
D) Errada.
Regra: a execução entra no case que bate e segue para baixo, nunca para cima.
A alternativa erra em 111: o case 1 está acima do case 2 e não executa, e por isso o valor 1 não entra na soma.
E) Certa.
Conta: r começa em 0; o case 2 soma 10 (r = 10); sem break, o case 3 soma 100 (r = 110); o break encerra o switch.
O valor impresso é 110, porque a execução entra no case 2 e cai no case 3, que tem o break.
Resultado: alternativa E.
Na estrutura repita...até, o bloco de comandos é repetido enquanto a condição for verdadeira e pode não ser executado nenhuma vez, caso a condição já seja verdadeira na primeira avaliação.
Comentário:
Regra: o repita...até testa a condição no fim de cada volta, executa o bloco ao menos uma vez e para quando a condição fica verdadeira.
A afirmação erra em é repetido enquanto a condição for verdadeira: o repita continua enquanto a condição é falsa. Ela erra também em pode não ser executado nenhuma vez: o teste vem depois do bloco, e por isso a primeira volta sempre acontece. A descrição da afirmação mistura o repita com o enquanto.
Resultado: errado.
Como a banca cobra: a banca dá um trecho de código ou de pseudocódigo com laço e seleção e pede o valor impresso, ou troca o momento do teste entre o enquanto e o repita, ou esconde a falta de break num switch.
Variáveis, Subprogramas e Passagem de Parâmetro
Um algoritmo de folha de pagamento precisa guardar o salário, os descontos e o líquido de cada servidor. A variável é o lugar para isso: um espaço da memória com um nome, que guarda um valor e permite trocá-lo durante a execução.
Toda variável tem quatro elementos: o nome (identificador), o tipo (que valores ela aceita), o valor atual e o endereço na memória. A constante é parecida, mas o valor é fixado uma vez e não muda durante a execução, como o valor de pi.
Tipos de dados. Os tipos primitivos do pseudocódigo são quatro: inteiro (números sem parte decimal, como 25), real (números com parte decimal, como 7,5), caractere (texto, como "Ana") e lógico (verdadeiro ou falso). Os compostos juntam vários valores: o vetor (mesmo tipo, acesso por índice), a matriz (vetor de duas ou mais dimensões) e o registro (campos de tipos diferentes, como nome e salário).
Identificadores. O nome da variável segue regras comuns à maioria das linguagens: começa com letra ou sublinhado, não tem espaço nem caractere especial e não pode ser uma palavra reservada, isto é, uma palavra que a linguagem usa como comando (se, enquanto, inicio). Assim, salario_base e _total são nomes válidos, mas 2nota, nota final e enquanto não são.
Declaração e atribuição. Antes do uso, a variável é declarada: o algoritmo informa o nome e o tipo, e a linguagem reserva o espaço. A atribuição grava um valor na variável e, no pseudocódigo, usa a seta (soma <- 0). A seta não é igualdade matemática: x <- x + 1 lê o valor atual de x, soma 1 e grava o resultado de volta em x.
Escopo e tempo de vida. O escopo é a região do programa em que a variável pode ser usada. A variável global é declarada fora de qualquer subprograma e vale em todo o programa. A variável local é declarada dentro de um subprograma, só vale dentro dele e some quando o subprograma termina.
Quando uma variável local tem o mesmo nome de uma global, dentro do subprograma vale a local, e a global fica escondida: é o sombreamento. Alterar a local não muda a global.
Subprogramas. Um sistema de folha calcula o desconto de previdência em vários pontos, e copiar o cálculo em cada ponto gera erro quando a regra muda. O subprograma (sub-rotina ou módulo) é um bloco com nome, escrito uma vez e chamado quantas vezes for preciso. Ele traz reaproveitamento, leitura mais fácil, teste em separado e manutenção num lugar só. Há dois tipos:
- Procedimento: executa uma tarefa e não devolve valor para quem o chamou (por exemplo, imprimir um relatório). A chamada aparece sozinha, como um comando.
- Função: executa uma tarefa e devolve um valor (por exemplo, calcular a média). A chamada aparece dentro de uma expressão, como
m <- media(n1, n2).
Em C e Java, o procedimento é a função de retorno void, que não devolve nada. Em Pascal e no Portugol, os nomes são procedure e function, ou procedimento e funcao.
Parâmetros formais e reais. Os parâmetros formais são os nomes declarados no cabeçalho do subprograma (funcao media(a, b: real)). Os parâmetros reais, também chamados de argumentos, são os valores ou variáveis passados na chamada (media(n1, n2)). Na chamada, cada argumento se liga ao parâmetro formal da mesma posição.
Como funciona a chamada. A cada chamada, o sistema monta na pilha de execução um registro de ativação com os parâmetros, as variáveis locais e o ponto de retorno. Ao terminar, o subprograma devolve o valor (se for função), o registro sai da pilha e a execução volta ao comando seguinte ao da chamada.
Passagem de parâmetro. A forma como o argumento chega ao parâmetro formal decide se a alteração feita dentro do subprograma aparece fora dele:
- Por valor: o parâmetro formal recebe uma cópia do valor do argumento. Alterar o parâmetro dentro do subprograma não muda a variável de quem chamou. O argumento pode ser uma constante ou uma expressão (
dobra(3 + 2)). - Por referência: o parâmetro formal recebe o endereço da variável. Alterar o parâmetro altera a própria variável de quem chamou. O argumento precisa ser uma variável, porque uma constante ou expressão não tem endereço para ser alterado.
- Por valor-resultado (cópia e restauração): o parâmetro recebe uma cópia na entrada, e o valor final é copiado de volta para a variável quando o subprograma termina.
No Portugol e no Pascal, a palavra var antes do parâmetro formal marca a passagem por referência. Em C, toda passagem é por valor, e a passagem por referência é simulada com ponteiros: a função recebe o endereço (&x) e altera o conteúdo pelo ponteiro (*p). O C++ tem a referência de fato, marcada com & no parâmetro.
Em Java, toda passagem é por valor. Quando o argumento é um objeto, o método recebe uma cópia da referência, e por isso pode alterar os atributos do objeto original, mas não consegue fazer a variável de quem chamou apontar para outro objeto. Em Python, o parâmetro recebe o mesmo objeto do argumento: uma lista alterada por append muda também fora, mas uma nova atribuição ao parâmetro só troca o nome local.
Exemplo resolvido: o procedimento abaixo recebe x por valor e y por referência (marcado com var).
procedimento dobra(x: inteiro; var y: inteiro)
inicio
x <- x * 2
y <- y * 2
fimprocedimento
a <- 3
b <- 5
dobra(a, b)
escreva(a, " ", b)
- Antes da chamada,
avale 3 ebvale 5. - Na chamada,
xrecebe uma cópia dea(3), eypassa a apontar para a própria variávelb. - O comando
x <- x * 2fazxvaler 6, mas a cópia some ao fim do procedimento eacontinua 3. - O comando
y <- y * 2grava 10 na variávelb, porqueyé o endereço deb. - Resultado: o algoritmo mostra 3 e 10.
Na prática, a função calculaDesconto(salario) recebe o salário por valor e devolve o desconto sem risco de alterar o salário guardado. Já o procedimento reajusta(var salario, percentual) recebe o salário por referência, porque a tarefa dele é alterar o valor guardado.
A diferença que a banca mais troca é entre parâmetro formal (no cabeçalho) e parâmetro real (na chamada). O erro comum é achar que Java passa objetos por referência: Java passa a referência por valor.
Em Java, quando um método recebe um objeto como argumento e altera um atributo desse objeto, a alteração é vista por quem chamou o método; ainda assim, a passagem de parâmetros em Java é sempre por valor, porque o método recebe uma cópia da referência ao objeto.
Comentário:
Regra: em Java, todo argumento é passado por valor; no caso de objeto, o valor copiado é a referência, e as duas cópias apontam para o mesmo objeto.
Como a cópia da referência aponta para o mesmo objeto, a alteração de um atributo aparece fora do método. Se o método atribuir um novo objeto ao parâmetro, só a cópia local muda, e é isso que mostra que a passagem é por valor.
Resultado: certo.
def altera(lista, n):
lista.append(n)
n = n + 1
lista = [0]
return n
v = [1, 2]
k = 5
r = altera(v, k)
print(v, k, r)
Considere o código acima, escrito em Python. O programa imprime:
- A) [1, 2, 5] 5 6
- B) [0] 6 6
- C) [1, 2, 5] 6 6
- D) [0] 5 6
- E) [1, 2] 5 6
Comentário:
A) Certa.
Regra: em Python, o parâmetro recebe o mesmo objeto do argumento; o append altera esse objeto, mas a nova atribuição ao parâmetro só troca o nome local.
O append põe 5 na lista de v. Depois, n passa a 6 e lista passa a [0] só dentro da função. A função devolve 6, e por isso v vale [1, 2, 5], k continua 5 e r vale 6.
B) Errada.
Regra: a atribuição lista = [0] liga o nome local a uma lista nova e não altera a variável v.
A alternativa erra em [0] 6 6: v continua apontando para a lista original, que ficou [1, 2, 5], e k continua 5.
C) Errada.
Regra: o número inteiro não muda no lugar; n = n + 1 cria um valor novo só para o nome local n.
A alternativa erra em [1, 2, 5] 6 6: k continua valendo 5, porque a soma alterou só o nome local n.
D) Errada.
Regra: dentro da função, a nova atribuição a um parâmetro não alcança a variável de quem chamou.
A alternativa erra em [0] 5 6: a lista [0] existe só dentro da função; v mostra [1, 2, 5].
E) Errada.
Regra: o método append altera o próprio objeto da lista, que é o mesmo objeto de v.
A alternativa erra em [1, 2] 5 6: o append feito antes da nova atribuição já pôs 5 na lista de v.
Resultado: alternativa A.
total = 100
def f(total):
total = total + 1
return total
def g():
global total
total = total + 10
g()
print(f(total), total)
Considere o código acima, escrito em Python, em que global indica que a função usa a variável global de mesmo nome. O programa imprime:
- A) 101 110
- B) 111 111
- C) 101 100
- D) 111 110
- E) 110 110
Comentário:
A) Errada.
Regra: a função g roda antes do print e altera a variável global, porque declara total como global.
A alternativa erra em 101 110: quando f é chamada, a global já vale 110, e f devolve 110 + 1 = 111.
B) Errada.
Regra: o parâmetro total de f é uma variável local que esconde a global de mesmo nome (sombreamento).
A alternativa erra em 111 111: f soma 1 só na variável local, e a global continua 110.
C) Errada.
Regra: a declaração global dentro de g faz a atribuição alcançar a variável de fora da função.
A alternativa erra em 101 100: g soma 10 à global, que passa de 100 para 110 antes do print.
D) Certa.
Conta: g faz a global valer 100 + 10 = 110; f recebe 110, soma 1 na local e devolve 111; a global continua 110.
O print mostra primeiro o retorno de f (111) e depois a global (110).
E) Errada.
Regra: a função f devolve o valor da variável local depois da soma.
A alternativa erra em 110 110: o primeiro valor é o retorno de f, que soma 1 ao argumento e dá 111.
Resultado: alternativa D.
Como a banca cobra: a banca mostra um subprograma com um parâmetro por valor e outro por referência e pede os valores depois da chamada, ou afirma que Java passa objetos por referência, ou troca parâmetro formal com parâmetro real.
Linguagens Educacionais e Pseudolinguagens
Quem começa a programar enfrenta dois problemas ao mesmo tempo: pensar a lógica e decorar a sintaxe de uma linguagem em inglês. As pseudolinguagens e as linguagens educacionais separam os dois problemas: o aluno pensa a lógica primeiro, em português ou com blocos visuais, e só depois aprende a sintaxe.
Pseudocódigo e Portugol. O pseudocódigo é uma forma de escrever o algoritmo com palavras da língua natural, numa estrutura parecida com a de uma linguagem de programação. Ele não tem um padrão único: cada livro e cada professor usam a sua variação. O Portugol, também chamado de português estruturado, é a pseudolinguagem com palavras em português mais usada no Brasil.
O pseudocódigo em si não é executado, porque não segue uma gramática fixa, mas algumas ferramentas executam um dialeto fixo de Portugol. As mais citadas são o VisuAlg, que interpreta o Portugol e mostra o valor das variáveis durante a execução, e o Portugol Studio, um ambiente de ensino do Portugol.
A estrutura do VisuAlg. Um algoritmo no VisuAlg tem três partes, nesta ordem:
- O cabeçalho, com
algoritmo "nome". - A área de declaração, aberta por
var, com cada variável e o seu tipo (inteiro,real,caractere,logico). - O corpo, entre
inicioefimalgoritmo, com os comandos.
Os comandos principais são leia (entrada), escreva e escreval (saída, o segundo pula de linha), <- (atribuição), se, escolha, enquanto, repita e para, cada um fechado pelo seu fim (fimse, fimenquanto). A igualdade usa = e a diferença usa <>. No VisuAlg, a divisão inteira usa a barra invertida (\) e o resto usa mod ou %. No Portugol dos livros e no Pascal, a divisão inteira usa div.
Exemplo resolvido: o algoritmo abaixo lê duas notas, calcula a média e mostra se o aluno foi aprovado com média 7 ou mais.
algoritmo "media"
var
n1, n2, media: real
inicio
leia(n1, n2)
media <- (n1 + n2) / 2
se media >= 7 entao
escreval("Aprovado")
senao
escreval("Reprovado")
fimse
fimalgoritmo
- O usuário digita 6 e 9, que vão para
n1en2. - A média é (6 + 9) / 2 = 15 / 2 = 7,5.
- A condição
7,5 >= 7é verdadeira, e o bloco doentaoexecuta. - Resultado: o algoritmo mostra "Aprovado".
O fluxograma. O fluxograma desenha o algoritmo com símbolos ligados por setas que mostram a ordem de execução. Os símbolos mais cobrados são: o terminal (retângulo de cantos arredondados ou elipse), que marca o início e o fim; o processamento (retângulo), que representa um cálculo ou uma atribuição; a decisão (losango), que tem uma saída para o sim e outra para o não; a entrada e saída de dados (paralelogramo); e o conector (círculo), que liga partes do desenho. O algoritmo da média, em fluxograma, segue os passos abaixo.
- Início
- Ler as notas n1 e n2
- Calcular a média de n1 e n2
- A média é maior ou igual a 7?SimMostrar AprovadoNãoMostrar Reprovado
- Fim
O diagrama de Nassi-Shneiderman (ou de Chapin) desenha o algoritmo em retângulos encaixados, sem setas, e só admite as estruturas da programação estruturada.
Linguagens educacionais. Algumas linguagens de programação reais nasceram para o ensino:
- Pascal: criada por Niklaus Wirth para ensinar programação estruturada, com tipagem forte e estática e blocos
begin ... end. É a origem do estilo do Portugol, inclusive dovarpara passagem por referência. - BASIC: criada por John Kemeny e Thomas Kurtz para que estudantes de áreas fora da computação programassem com facilidade.
- Logo: ligada a Seymour Papert, conhecida pela tartaruga, um cursor que anda e risca a tela com comandos como avançar e girar. Ensina geometria e lógica para crianças.
- Scratch: linguagem visual por blocos, criada no MIT, em que o aluno encaixa blocos coloridos em vez de digitar comandos, o que elimina o erro de sintaxe.
Na prática, uma escola de governo que oferece um curso de lógica a servidores começa com o Portugol no VisuAlg, porque o aluno escreve em português e vê o valor das variáveis a cada passo. Depois, a mesma lógica é reescrita numa linguagem real, e o aluno aprende só a sintaxe nova.
A diferença que a banca troca é entre pseudocódigo, que descreve a lógica sem padrão único, e linguagem de programação, que tem gramática fixa. O VisuAlg fica no meio: interpreta um dialeto fixo de Portugol.
O erro comum é usar = para atribuir no Portugol. No VisuAlg, o = compara, e a atribuição usa <-.
Sobre pseudolinguagens e linguagens usadas no ensino de programação, assinale a alternativa correta.
- A) O pseudocódigo segue uma sintaxe única e padronizada, o que permite executá-lo em qualquer computador sem adaptação.
- B) O Portugol usa palavras em português e serve para escrever a lógica do algoritmo antes de traduzi-la para uma linguagem de programação.
- C) O Scratch é uma linguagem textual em que o aluno digita comandos em inglês, com ponto e vírgula no fim de cada linha.
- D) A linguagem Logo foi criada para o ensino de banco de dados e usa a tartaruga para representar as tabelas.
- E) O Pascal foi criado para uso comercial, sem preocupação com o ensino da programação estruturada.
Comentário:
A) Errada.
Regra: o pseudocódigo não tem padrão único; cada autor usa a sua variação, e só dialetos fixos, como o do VisuAlg, são executados por ferramentas próprias.
A alternativa erra em segue uma sintaxe única e padronizada: não existe um padrão único de pseudocódigo, e por isso ele não é executado em qualquer computador.
B) Certa.
Regra: o Portugol é o português estruturado, usado para escrever a lógica com palavras em português antes da sintaxe de uma linguagem real.
A alternativa descreve a função do Portugol no ensino: separar a lógica do algoritmo da sintaxe da linguagem.
C) Errada.
Regra: o Scratch é uma linguagem visual, em que o aluno monta o programa encaixando blocos.
A alternativa erra em linguagem textual em que o aluno digita comandos em inglês: no Scratch, o aluno não digita comandos, e por isso não há ponto e vírgula.
D) Errada.
Regra: a Logo ensina geometria e lógica com a tartaruga, um cursor que anda e risca a tela.
A alternativa erra em criada para o ensino de banco de dados: a Logo não trata de banco de dados, e a tartaruga desenha figuras, não tabelas.
E) Errada.
Teoria: Niklaus Wirth criou o Pascal para ensinar programação estruturada, com tipagem forte.
A alternativa erra em criado para uso comercial, sem preocupação com o ensino: o ensino da programação estruturada é a origem do Pascal.
Resultado: alternativa B.
No VisuAlg, o comando de atribuição é escrito com o sinal de igual, e o sinal formado pelo menor e pelo hífen é usado para comparar dois valores na condição de uma estrutura se.
Comentário:
Regra: no VisuAlg, a atribuição usa a seta formada pelo menor e pelo hífen, e o sinal de igual compara dois valores.
A afirmação erra em o comando de atribuição é escrito com o sinal de igual: o sinal de igual é o operador relacional de igualdade. A afirmação inverte os dois papéis: a seta grava o valor, e o igual compara.
Resultado: errado.
Como a banca cobra: a banca pede o símbolo de fluxograma de cada ação (losango para decisão, paralelogramo para entrada e saída), mostra um trecho de Portugol para leitura ou atribui a uma linguagem educacional a característica de outra.
Algoritmos Diversos
Um órgão tem um cadastro com 100 mil processos e precisa achar um processo em segundos, e procurar um por um demora. Os algoritmos clássicos de busca, de ordenação e de recursão resolvem esse tipo de problema com menos passos.
Complexidade e notação O. Para comparar dois algoritmos, conta-se o número de passos em função do tamanho da entrada, chamado de n. A notação O (O grande) mostra como esse número cresce quando n fica muito grande, no pior caso, ignorando constantes. Do mais rápido para o mais lento, as classes mais cobradas são:
- O(1), constante: o tempo não depende de n (acessar o elemento de um vetor pelo índice).
- O(log n), logarítmica: a cada passo, o problema cai pela metade (busca binária).
- O(n), linear: um passo por elemento (busca sequencial).
- O(n log n): ordenação eficiente (merge sort e heapsort).
- O(n²), quadrática: dois laços encaixados sobre os dados (bolha, seleção e inserção no pior caso).
- O(2ⁿ), exponencial: o número de passos dobra a cada elemento novo (Fibonacci recursivo simples).
O algoritmo é analisado no melhor caso, no caso médio e no pior caso, e a notação O descreve em geral o pior caso.
Recursão. A função recursiva é a que chama a si mesma para resolver uma versão menor do mesmo problema. Ela tem duas partes obrigatórias: o caso base, que resolve o problema sem nova chamada e encerra a recursão, e o passo recursivo, que chama a função com uma entrada menor, mais perto do caso base. Sem caso base, a função chama a si mesma até lotar a pilha de execução, o erro chamado de estouro de pilha (stack overflow).
O fatorial é o exemplo clássico: o fatorial de n é n vezes o fatorial de n - 1, e o fatorial de 0 é 1 (caso base). Cada chamada fica parada na pilha esperando o resultado da chamada seguinte:
- fatorial(4) espera 4 × fatorial(3).
- fatorial(3) espera 3 × fatorial(2).
- fatorial(2) espera 2 × fatorial(1).
- fatorial(1) espera 1 × fatorial(0).
- fatorial(0) chega ao caso base e devolve 1.
- Na volta, as contas se resolvem: 1 × 1 = 1, 2 × 1 = 2, 3 × 2 = 6 e 4 × 6 = 24.
Toda função recursiva pode ser reescrita com laço (forma iterativa). A recursão encurta o código de problemas que se dividem em partes iguais, mas gasta memória com a pilha. O Fibonacci recursivo simples recalcula os mesmos valores e tem custo exponencial; a versão com laço é linear.
Busca sequencial e busca binária. A busca sequencial (ou linear) compara o valor procurado com cada elemento do vetor, do primeiro ao último. Funciona em qualquer ordem e, no pior caso, faz n comparações: O(n).
A busca binária exige o vetor ordenado. Ela funciona assim:
- Marca o início e o fim da faixa de busca (todo o vetor).
- Compara o valor procurado com o elemento do meio da faixa.
- Se é igual, achou e termina.
- Se o valor procurado é menor, a nova faixa é a metade da esquerda; se é maior, a metade da direita.
- Repete os passos 2 a 4 até achar ou até a faixa ficar vazia.
Como a faixa cai pela metade a cada comparação, a busca binária é O(log n). No pior caso, faz a parte inteira do logaritmo de n na base 2, mais 1, comparações: com 1.000 elementos, no máximo 10.
Exemplo resolvido: procurar o valor 23 no vetor ordenado [2, 5, 8, 12, 16, 23, 38, 56, 72, 91], com as posições numeradas de 0 a 9 e o meio calculado como a divisão inteira de (início + fim) por 2.
- Faixa de 0 a 9: meio = (0 + 9) div 2 = 4, e o elemento da posição 4 é 16. Como 23 > 16, a nova faixa começa em 5.
- Faixa de 5 a 9: meio = (5 + 9) div 2 = 7, e o elemento é 56. Como 23 < 56, a nova faixa termina em 6.
- Faixa de 5 a 6: meio = (5 + 6) div 2 = 5, e o elemento é 23. Achou.
- Resultado: a busca binária faz 3 comparações. A busca sequencial faria 6, uma para cada posição de 0 a 5.
Ordenação. Os métodos simples de ordenação são três:
- Bolha (bubble sort): percorre o vetor comparando cada par de vizinhos e troca o par fora de ordem. Ao fim de cada passagem, o maior elemento restante chega à última posição livre, como uma bolha que sobe. Com um aviso de "nenhuma troca nesta passagem", para cedo, e o melhor caso (vetor já ordenado) cai para O(n).
- Seleção (selection sort): procura o menor elemento do trecho não ordenado e o troca com o primeiro desse trecho. Faz sempre o mesmo número de comparações, O(n²), mesmo com o vetor já ordenado, mas faz poucas trocas.
- Inserção (insertion sort): pega cada elemento e o insere na posição certa entre os já ordenados à esquerda, como quem arruma cartas na mão. Com o vetor já ordenado, faz só n - 1 comparações (O(n)), e por isso é bom para vetores quase ordenados.
Os métodos eficientes usam a estratégia de dividir para conquistar, que divide o problema em partes menores, resolve cada parte e junta os resultados:
- Merge sort (intercalação): divide o vetor ao meio até sobrar um elemento em cada parte e depois intercala as partes em ordem. É O(n log n) em todos os casos, mas precisa de memória auxiliar proporcional a n.
- Quicksort: escolhe um elemento, o pivô, e separa os menores à esquerda e os maiores à direita; depois ordena cada lado do mesmo jeito. É O(n log n) no caso médio e O(n²) no pior caso, que ocorre quando o pivô é sempre o menor ou o maior elemento, como no vetor já ordenado com o pivô no extremo.
- Heapsort: monta uma estrutura chamada heap, em que o maior elemento fica no topo, e retira o topo repetidas vezes. É O(n log n) em todos os casos e ordena no próprio vetor.
Um método é estável quando mantém a ordem original dos elementos de mesmo valor: bolha, inserção e merge sort são estáveis; seleção, quicksort e heapsort, nas versões comuns, não são. Um método é in-place quando ordena no próprio vetor, com memória extra fixa.
Exemplo resolvido: ordenar o vetor [5, 3, 8, 1] pelo método da bolha, em ordem crescente.
- Primeira passagem: 5 e 3 trocam ([3, 5, 8, 1]); 5 e 8 ficam; 8 e 1 trocam ([3, 5, 1, 8]). O 8 chegou ao fim.
- Segunda passagem: 3 e 5 ficam; 5 e 1 trocam ([3, 1, 5, 8]). O 5 chegou ao lugar.
- Terceira passagem: 3 e 1 trocam ([1, 3, 5, 8]).
- Resultado: o vetor fica [1, 3, 5, 8], depois de 3 passagens e 4 trocas.
Outros algoritmos que caem. Alguns algoritmos curtos aparecem em questões de teste de mesa:
- Troca de duas variáveis: usa uma variável auxiliar (
aux <- a,a <- b,b <- aux). Sem a auxiliar, o primeiro valor se perde. - Maior elemento de um vetor: guarda o primeiro elemento como maior e percorre o resto, trocando o maior quando acha um valor acima dele.
- MDC de Euclides: o máximo divisor comum de a e b é o de b e do resto de a por b, até o resto dar zero. Para 48 e 18: 48 mod 18 = 12; 18 mod 12 = 6; 12 mod 6 = 0, e o MDC é 6.
Na prática, um tribunal mantém a lista de processos ordenada pelo número e usa busca binária para achar cada um. Para pôr poucos processos novos no lugar, a inserção é a escolha, porque a lista já está quase ordenada. Para reordenar a base pela data, o merge sort garante O(n log n) e, por ser estável, mantém a ordem anterior entre processos da mesma data.
A diferença que a banca mais troca é entre o quicksort e o merge sort: só o merge sort garante O(n log n) no pior caso, mas o quicksort ordena no próprio vetor. O erro comum é aplicar a busca binária a um vetor fora de ordem ou esquecer o caso base na recursão.
A busca binária, aplicada a um vetor ordenado de 1.000 elementos, faz no pior caso no máximo 10 comparações entre o valor procurado e os elementos do vetor, enquanto a busca sequencial pode precisar de 1.000 comparações no mesmo vetor.
Comentário:
Conta: o pior caso da busca binária é a parte inteira do logaritmo de 1.000 na base 2, mais 1, o que dá 9 + 1 = 10, porque 2 elevado a 9 é 512 e 2 elevado a 10 é 1.024.
A busca binária corta a faixa pela metade a cada comparação, e 10 cortes bastam para 1.000 elementos. A busca sequencial, no pior caso, compara com todos os 1.000 elementos.
Resultado: certo.
def f(n):
if n <= 1:
return 1
return f(n - 1) + f(n - 2)
print(f(5))
Considere o código acima, escrito em Python. O valor impresso e o número total de chamadas da função f durante a execução, contando a primeira, são, respectivamente:
- A) 5 e 9 chamadas
- B) 8 e 9 chamadas
- C) 13 e 15 chamadas
- D) 8 e 5 chamadas
- E) 8 e 15 chamadas
Comentário:
A) Errada.
Conta: com o caso base f(0) = f(1) = 1, os valores são f(2) = 2, f(3) = 3, f(4) = 5 e f(5) = 5 + 3 = 8.
A alternativa erra em 5 e 9 chamadas: 5 e 9 são o valor e o número de chamadas de f(4), e não de f(5).
B) Errada.
Conta: o número de chamadas de f(n) é 1 mais as chamadas de f(n - 1) e de f(n - 2); com 1 chamada para f(0) e para f(1), dá 3 para f(2), 5 para f(3), 9 para f(4) e 15 para f(5).
A alternativa erra em 9 chamadas: f(5) gera 1 + 9 + 5 = 15 chamadas.
C) Errada.
Conta: f(5) = f(4) + f(3) = 5 + 3 = 8.
A alternativa erra em 13: 13 seria f(6), um termo à frente. O número de chamadas, 15, está certo.
D) Errada.
Regra: cada chamada com n maior que 1 faz duas chamadas novas, e por isso a recursão se abre em árvore e repete cálculos.
A alternativa erra em 5 chamadas: 5 chamadas seria o caso de uma recursão em linha, de f(5) até f(1). Aqui, f(3) é calculada duas vezes e f(2) três vezes.
E) Certa.
Conta: o valor é f(5) = 8, e as chamadas são 1 + 9 (de f(4)) + 5 (de f(3)) = 15.
O print mostra 8, e a execução fez 15 chamadas de f, porque a recursão dupla recalcula os mesmos valores.
Resultado: alternativa E.
O quicksort tem complexidade O(n log n) em todos os casos e, por isso, é preferível ao merge sort quando a aplicação exige garantia de tempo no pior caso.
Comentário:
Regra: o quicksort é O(n log n) no caso médio e O(n²) no pior caso, quando o pivô é sempre o menor ou o maior elemento; o merge sort é O(n log n) em todos os casos.
A afirmação erra em em todos os casos: o pior caso do quicksort é quadrático. Por isso, quando a aplicação exige garantia no pior caso, a escolha é o merge sort ou o heapsort, e não o quicksort.
Resultado: errado.
Acerca dos métodos de ordenação, assinale a alternativa correta.
- A) A ordenação por seleção faz um número linear de comparações quando o vetor já está ordenado.
- B) O merge sort ordena no próprio vetor, sem memória auxiliar proporcional ao número de elementos.
- C) A ordenação por inserção é estável e, com o vetor já ordenado, faz apenas n - 1 comparações.
- D) O método da bolha não é estável, porque troca de posição os elementos vizinhos iguais.
- E) O heapsort tem pior caso quadrático, assim como o quicksort.
Comentário:
A) Errada.
Regra: a seleção procura o menor elemento do trecho não ordenado em toda passagem, e por isso faz O(n²) comparações em qualquer caso.
A alternativa erra em faz um número linear de comparações: a seleção não percebe que o vetor já está ordenado e compara tudo de novo em cada passagem.
B) Errada.
Regra: o merge sort intercala as metades num vetor auxiliar, que tem o tamanho dos dados.
A alternativa erra em sem memória auxiliar proporcional ao número de elementos: a intercalação precisa de memória extra proporcional a n.
C) Certa.
Regra: a inserção só move um elemento quando acha um maior à esquerda, e por isso mantém a ordem dos iguais e, no vetor ordenado, faz uma comparação por elemento a partir do segundo.
Com o vetor já ordenado, cada elemento do segundo ao último é comparado uma vez com o vizinho da esquerda, o que dá n - 1 comparações.
D) Errada.
Regra: a bolha só troca vizinhos quando o da esquerda é maior, e dois elementos iguais nunca trocam.
A alternativa erra em não é estável, porque troca de posição os elementos vizinhos iguais: a bolha é estável, justamente porque não troca iguais.
E) Errada.
Regra: o heapsort é O(n log n) em todos os casos, inclusive no pior.
A alternativa erra em tem pior caso quadrático: só o quicksort, entre os dois, tem pior caso O(n²).
Resultado: alternativa C.
Como a banca cobra: a banca pede a saída de uma função recursiva, o número de comparações de uma busca binária, o estado do vetor depois de uma passagem de um método de ordenação ou a complexidade e a estabilidade de cada método.
- Algoritmos e lógica de programação
- Algoritmo
- Finitude, definição, efetividade
- Entrada e saída
- Narrativa, fluxograma, pseudocódigo
- Estruturas de controle
- Sequência
- Seleção simples, composta, múltipla
- Enquanto, repita, para
- Contador e acumulador
- Variáveis e subprogramas
- Nome, tipo, valor, endereço
- Escopo global e local
- Procedimento e função
- Parâmetro formal e real
- Passagem por valor e referência
- Pseudolinguagens e ensino
- Portugol e VisuAlg
- Símbolos do fluxograma
- Pascal, BASIC, Logo, Scratch
- Algoritmos diversos
- Notação O
- Recursão e caso base
- Busca sequencial e binária
- Bolha, seleção, inserção
- Merge sort, quicksort, heapsort
- Algoritmo
Quadros e comparações
A tabela abaixo compara as estruturas de repetição.
| Laço | Onde testa | Execuções mínimas | Quando para |
|---|---|---|---|
| Enquanto (while) | Antes de cada volta | Zero | Quando a condição fica falsa |
| Repita ... até | Depois de cada volta | Uma | Quando a condição fica verdadeira |
| Faça ... enquanto (do while) | Depois de cada volta | Uma | Quando a condição fica falsa |
| Para (for) | Antes de cada volta | Zero | Quando o contador passa do valor final |
A tabela abaixo compara as formas de passagem de parâmetro.
| Aspecto | Por valor | Por referência |
|---|---|---|
| O que o parâmetro recebe | Uma cópia do valor | O endereço da variável |
| Alteração dentro do subprograma | Não muda a variável de fora | Muda a variável de fora |
| Argumento aceito | Variável, constante ou expressão | Só variável |
| Como aparece no Portugol e no Pascal | Parâmetro sem marca | Parâmetro com var |
| Em Java | Tudo, inclusive a referência ao objeto | Não existe |
A tabela abaixo reúne os métodos de ordenação.
| Método | Melhor caso | Pior caso | Estável |
|---|---|---|---|
| Bolha (com aviso de troca) | O(n) | O(n²) | Sim |
| Seleção | O(n²) | O(n²) | Não |
| Inserção | O(n) | O(n²) | Sim |
| Merge sort | O(n log n) | O(n log n) | Sim |
| Quicksort | O(n log n) | O(n²) | Não |
| Heapsort | O(n log n) | O(n log n) | Não |
Para decorar
- Características do algoritmo (Knuth): finitude, definição, entrada (zero ou mais), saída (uma ou mais) e efetividade.
- Programação estruturada (Böhm e Jacopini): sequência, seleção e repetição bastam.
- Enquanto: testa antes, pode executar zero vezes.
- Repita ... até: testa depois, executa ao menos uma vez e para com a condição verdadeira.
- Precedência: aritméticos, depois relacionais, depois lógicos; nao, depois e, depois ou.
- Procedimento: não devolve valor. Função: devolve valor.
- Parâmetro formal: no cabeçalho. Parâmetro real (argumento): na chamada.
- Por valor: cópia. Por referência: endereço, só aceita variável.
- Java: sempre por valor, inclusive a referência ao objeto.
- VisuAlg: atribuição com seta, comparação com igual.
- Recursão: caso base mais passo recursivo; sem caso base, estouro de pilha.
- Busca binária: vetor ordenado, O(log n); 1.000 elementos, no máximo 10 comparações.
- O(n log n) sempre: merge sort e heapsort.
- Estáveis: bolha, inserção e merge sort.
Pegadinhas
| # | Errado | Certo |
|---|---|---|
| 1 | Todo algoritmo precisa de pelo menos uma entrada. | O algoritmo tem zero ou mais entradas e uma ou mais saídas. |
| 2 | O laço enquanto executa o bloco ao menos uma vez. | O enquanto testa antes e pode executar zero vezes. Quem executa ao menos uma vez é o repita. |
| 3 | O repita ... até continua enquanto a condição é verdadeira. | O repita para quando a condição fica verdadeira. |
| 4 | No switch, a execução para sozinha ao fim do case que bateu. | Sem break, a execução cai nos casos de baixo. |
| 5 | No Portugol, o operador e é avaliado depois do ou. | O nao vem primeiro, depois o e, e por último o ou. |
| 6 | A função não devolve valor, e o procedimento devolve. | Só a função devolve valor. |
| 7 | Na passagem por referência, o argumento pode ser uma constante. | A passagem por referência exige uma variável, que tem endereço. |
| 8 | Java passa objetos por referência. | Java passa tudo por valor; no objeto, copia a referência. |
| 9 | A busca binária funciona em qualquer vetor. | A busca binária exige o vetor ordenado. |
| 10 | O quicksort é O(n log n) no pior caso. | O pior caso do quicksort é O(n²). O merge sort garante O(n log n). |
| 11 | A seleção é O(n) com o vetor já ordenado. | A seleção faz O(n²) comparações em qualquer caso. A inserção é O(n) no vetor ordenado. |
Fontes e data
- [1] Conhecimento consolidado de livro didático de algoritmos e lógica de programação (características do algoritmo segundo Knuth, programação estruturada de Böhm e Jacopini, passagem de parâmetros, Portugol e VisuAlg, recursão, busca e ordenação).
Texto conferido em 15/10/2026.