Escolar Documentos
Profissional Documentos
Cultura Documentos
CDU 004
Questões e exercícios
1. a. Encontre um algoritmo para solucionar o seguinte problema: dado um inteiro positivo n,
encontre a lista de inteiros positivos cujo produto seja o maior entre todas as listas de intei-
ros positivos cuja soma é n. Por exemplo, se n for 4, a lista desejada é 2, 2 porque 2 ⫻ 2 é
maior que 1 ⫻ 1 ⫻ 1 ⫻ 1, 2 ⫻ 1 ⫻ 1 e 3 ⫻ 1. Se n for 5, a lista desejada é 2 e 3.
b. Qual é a lista desejada se n ⫽ 2001?
c. Explique como você deu o primeiro passo.
2. a. Suponha que nos seja dado um tabuleiro de damas consistindo em 2n linhas e 2n colunas
de quadrados, para algum inteiro positivo n, e uma caixa de ladrilhos em forma de L, cada
um dos quais pode cobrir exatamente três quadrados do tabuleiro. Se qualquer quadrado for
cortado do tabuleiro, podemos cobrir o tabuleiro restante com ladrilhos de forma que eles
não se sobreponham ou caiam fora da borda do tabuleiro?
b. Explique como sua solução para o item (a) pode ser usada para mostrar que 22n – 1 é divi-
sível por 3 para todos os inteiros positivos n.
c. Como os itens (a) e (b) são relacionados às fases de Polya da resolução de problemas?
3. Decodifique a mensagem a seguir, escrita em inglês, e então explique como você deu o pri-
meiro passo. Pdeo eo pda yknnayp wjosan.
4. Você estaria seguindo uma metodologia descendente se tentasse resolver um quebra-cabeça
simplesmente colocando as peças sobre a mesa e tentando agrupá-las? Sua resposta mudaria
se você olhasse na caixa do quebra-cabeça para ver como é a imagem montada?
Controle de laços
O uso repetitivo de uma instrução ou de uma sequência de instruções é
um conceito algorítmico importante. Um método de implementar tal repe-
tição é a estrutura iterativa conhecida como laço, na qual uma coleção de
instruções, chamada de corpo do laço, é executada de maneira repetida sob
a direção de algum processo de controle. Um exemplo típico é encontrado
no algoritmo de busca sequencial representado na Figura 5.6. Neste caso,
usamos uma sentença enquanto para controlar a repetição da sentença úni-
ca Selecione a próxima entrada da lista como EntradaDeTeste. Na
verdade, a sentença enquanto
enquanto (condição) faça (corpo)
exemplifica o conceito de uma estrutura de laço, já que sua execução mostra
o padrão cíclico
verificar a condição.
executar o corpo.
verificar a condição.
executar o corpo.
.
.
.
verificar a condição.
até que a condição falhe.
Primeira Segunda
Lista original
sublista sublista
Alice
Bob
Carol
David
Elaine
Fred
George
Harry
Irene Irene Irene
John John John
Kelly Kelly Kelly
Larry Larry
Mary Mary
Nancy Nancy
Oliver Oliver
Figura 5.12 Aplicação da nossa estratégia para buscar a entrada John em uma lista.
iniciar nossa busca pela entrada localizada no “meio” da lista. Aqui, escre-
vemos a palavra meio entre aspas porque a lista pode ter um número par
de entradas e, logo, nenhuma entrada do meio existirá de fato. Neste caso,
vamos convencionar que a entrada do “meio” se refere à primeira entrada
da segunda metade da lista.
Se a entrada no meio da lista for o valor buscado, podemos declarar a
busca um sucesso. Caso contrário, podemos ao menos restringir o processo
de busca para a primeira ou para a segunda metade da lista, dependendo de
o valor buscado ser menor ou maior que a entrada que estamos consideran-
do. (Lembre-se de que a lista é ordenada.)
Para buscar na porção remanescente da lista, poderíamos aplicar a
busca sequencial, mas ao invés disso vamos aplicar a mesma abordagem
para essa porção da lista que usamos para a lista inteira. Ou seja, selecio-
namos a entrada do meio na porção remanescente da lista como a próxima
entrada a ser considerada. Como antes, se esta entrada for o valor visado,
terminamos. Caso contrário, podemos restringir nossa busca para uma por-
ção ainda menor da lista.
Essa abordagem para o processo de busca é resumida na Figura 5.12,
na qual consideramos a tarefa de fazer uma busca na lista mais à esquerda
na figura, pela entrada John. Primeiro, consideramos a entrada do meio,
Harry. Como nosso alvo está após esta entrada, a busca continua, conside-
rando a metade inferior da lista original. O meio da sublista agora é calcu-
lado como Larry. Como nosso alvo deve preceder Larry, movemos nossa
atenção para a primeira metade da sublista atual. Quando interrogamos o
meio desta sublista secundária, encontramos nosso alvo, John, e declara-
mos a busca um sucesso. Em resumo, nossa estratégia é dividir sucessiva-
mente a lista em segmentos menores, até que o alvo seja encontrado ou a
busca esteja restrita a um segmento vazio.
Precisamos enfatizar esse último ponto. Se o valor buscado não estiver
na lista original, nossa abordagem para a busca na lista será conduzida ao di-
se (Lista vazia)
então
(Relate que a busca falhou.)
senão
[Selecione a entrada do “meio” da Lista como sendo a EntradaDeTeste;
Execute o bloco de instruções abaixo que
está associado com o caso apropriado.
caso 1: ValorAlvo = EntradaDeTeste
(Relate que a busca foi bem-sucedida.)
caso 2: ValorAlvo < EntradaDeTeste
(Busque a porção da lista anterior a EntradaDeTeste pelo
ValorAlvo e relate o resultado dessa busca.)
caso 3: ValorAlvo > EntradaDeTeste
(Busque a porção da lista após EntradaDeTeste pelo
ValorAlvo, e relate o resultado dessa busca.)
] fim se
Busca e ordenação
Os algoritmos de busca sequencial e de busca binária são apenas dois dos muitos algoritmos
para realizar o processo de busca. De maneira similar, a ordenação por inserção é apenas um de
muitos algoritmos de ordenação. Outros algoritmos clássicos para ordenação incluem a ordena-
ção por mesclagem (merge sort), discutida no Capítulo 12, a ordenação por seleção (Questão/
Exercício 6 da Seção 5.4), a ordenação por bolha (Questão/Exercício 7 da Seção 5.4), a ordena-
ção rápida (quick sort), que aplica uma abordagem dividir para conquistar sobre o processo de
ordenação) e a ordenação por monte (heap sort), que usa uma técnica bastante inteligente para
encontrar as entradas que devem ser movidas para frente na lista. Você encontrará discussões
sobre esses algoritmos nos livros listados na Leitura Adicional, ao final deste capítulo.
vidirmos a lista em segmentos menores até que o segmento que está sendo
considerado esteja vazio. Nesse momento, nosso algoritmo deve reconhecer
que a busca falhou.
A Figura 5.13 é um primeiro rascunho de nossos pensamentos usando
nosso pseudocódigo. Ele nos conduz a começar uma busca tentando ver
se a lista está vazia. Se estiver, ele nos diz para relatar que a busca é uma
falha. Caso contrário, o rascunho nos diz para considerar a entrada do meio
da lista. Se a entrada não for o valor buscado, ele nos diz para buscar na
primeira metade ou na segunda metade da lista. Ambas as possibilidades
requerem uma busca secundária. Seria bom realizar essas buscas por meio
de chamadas aos serviços de uma ferramenta abstrata. Em particular, nossa
abordagem é aplicar um procedimento chamado Buscar para conduzir essas
buscas secundárias. Para completar nosso programa, logo, devemos forne-
cer tal procedimento.
No entanto, esse procedimento deve realizar a mesma tarefa expressa
pelo pseudocódigo que já escrevemos. Ele primeiro deve verificar se a lista
que ele recebeu está vazia e, se não estiver, ele deve continuar considerando
a entrada do meio dessa lista. Logo, podemos fornecer o procedimento que
precisamos simplesmente identificando a rotina atual como o procedimento
chamado Buscar e inserindo referências para esse procedimento, no qual as
buscas secundárias são requeridas. O resultado é mostrado na Figura 5.14.
Note que esse procedimento contém uma referência para si mesmo. Se
o estivéssemos seguindo e chegássemos na instrução
Aplique o procedimento Buscar...
aplicaríamos na lista menor o mesmo procedimento que estávamos aplican-
do na lista original. Se essa busca fosse bem sucedida, retornaríamos para
declarar que nossa busca original havia sido bem sucedida; se essa busca
secundária falhasse, declararíamos que nossa busca original falhou.
Para ver como o procedimento na Figura 5.14 realiza sua tarefa, vamos
segui-lo enquanto ele faz uma busca na lista Alice, Bill, Carol, David, Evelyn,
Fred e George, pelo valor Bill. Nossa busca começa selecionando David (a
entrada do meio) como a entrada de teste a ser considerada. Como o valor
buscado (Bill) é menor que essa entrada de teste, o algoritmo nos instrui
para aplicar o procedimento Buscar na lista de entradas que precede David
– ou seja, a lista Alice, Bill e Carol. Ao fazer isso, criamos uma segunda cópia
do procedimento de busca e atribuímos a ela essa tarefa secundária.
Agora, temos duas cópias de nosso procedimento de busca sendo exe-
cutadas, como resumido na Figura 5.15. O progresso na cópia original é tem-
porariamente suspenso na instrução
Estamos aqui.
Lista Lista
Alice
Bill
Carol
David (EntradaDe
Evelyn Teste)
Fred David
David
George Evelyn
Evelyn
Fred
Fred
George
George
Figura 5.15
Estamos aqui.
Lista Lista
Alice
Carol
Evelyn (EntradaDe
Fred Teste)
George David
David
Evelyn
Evelyn
Fred
Fred
George
George
Figura 5.16
dessas duas peças. Essa abordagem de divisão por dois é a razão pela qual o
algoritmo é conhecido como a busca binária.
Controle recursivo
O algoritmo de busca binária é similar à busca sequencial no sentido de que
cada algoritmo requer a execução de um processo repetitivo. Entretanto, a
implementação dessa repetição é significativamente diferente. Enquanto a
busca sequencial envolve uma forma circular de repetição, a busca binária
executa cada estágio da repetição como uma subtarefa do estágio anterior.
Essa técnica é conhecida como recursão.
Como vimos, a ilusão criada pela execução de um procedimento recur-
sivo é a da existência de múltiplas cópias do procedimento, cada uma das
quais é chamada de uma ativação do procedimento. Essas ativações são cria-
das dinamicamente em uma maneira telescópica e, por fim, desaparecem à
medida que o algoritmo avança. Das ativações existentes em dado momen-
to, apenas uma está ativamente progredindo. As outras estão efetivamente
no limbo, cada uma delas esperando pelo término de outra ativação antes
de poder continuar.
Sendo um processo repetitivo, os sistemas recursivos são tão depen-
dentes de controles apropriados quanto são as estruturas de laço. Assim
como no controle de laço, os sistemas recursivos são dependentes de testes
em relação a uma condição de término e de um projeto que garanta que
essa condição seja alcançada. Na verdade, um controle recursivo apropriado