Exercícios de Fundamentos
Dez práticas em C++ para transformar os conceitos das aulas em programas: tipos primitivos, coleções, registros, TAD, memória, ponteiros, alocação dinâmica e análise de complexidade.
Antes de programar
Use a solução somente depois de tentar
Identifique entradas, processamento e saídas.
Escolha tipos, registros e operações.
Complete, compile e corrija seu código.
Experimente casos comuns e extremos.
Prática guiada
Do valor isolado à estrutura conectada
Ficha de um aluno
Leia nome, idade e média. Mostre os dados formatados e informe se o aluno é maior de idade e se foi aprovado, considerando média mínima 6,0.
stringintdoubleboolifAna Silva 19 8.7
Aluno: Ana Silva Idade: 19 Média: 8.7 Maior de idade: sim Situação: aprovado
Ver dica
Use getline para o nome e fixed << setprecision(1) para uma casa decimal.
Ver resolução comentada
#include <iomanip>
#include <iostream>
#include <string>
int main() {
std::string nome;
int idade;
double media;
std::getline(std::cin, nome);
std::cin >> idade >> media;
bool maiorDeIdade = idade >= 18;
bool aprovado = media >= 6.0;
std::cout << std::fixed << std::setprecision(1);
std::cout << "Aluno: " << nome << '\n';
std::cout << "Idade: " << idade << '\n';
std::cout << "Média: " << media << '\n';
std::cout << "Maior de idade: "
<< (maiorDeIdade ? "sim" : "não") << '\n';
std::cout << "Situação: "
<< (aprovado ? "aprovado" : "reprovado") << '\n';
}Ideia: cada variável tem tipo e significado. Os booleanos armazenam resultados lógicos usados nas decisões.
Cinco notas, uma coleção
Substitua cinco variáveis numeradas por um std::array. Leia as notas, calcule a média e encontre a maior usando laços.
nota1, nota2, nota3...array<double, 5> notasVer dica
Inicialize a maior com notas[0] e percorra a coleção com um for.
Ver resolução comentada
#include <array>
#include <iomanip>
#include <iostream>
int main() {
std::array<double, 5> notas{};
for (double& nota : notas) {
std::cin >> nota; // A referência altera a posição original.
}
double soma = 0.0;
double maior = notas[0];
for (double nota : notas) {
soma += nota;
if (nota > maior) maior = nota;
}
std::cout << std::fixed << std::setprecision(1);
std::cout << "Média: " << soma / notas.size() << '\n';
std::cout << "Maior nota: " << maior << '\n';
}Complexidade: generalizando para n notas, o percurso custa O(n).
Turma organizada com struct
Modele um aluno com matrícula, nome e média. Leia três alunos em um vector e mostre o registro com a maior média.
Ver dica
Crie um Aluno por repetição, use push_back e guarde um ponteiro para o melhor registro.
Ver resolução comentada
#include <iostream>
#include <string>
#include <vector>
struct Aluno {
int matricula;
std::string nome;
double media;
};
int main() {
std::vector<Aluno> turma;
for (int i = 0; i < 3; ++i) {
Aluno aluno;
std::cin >> aluno.matricula;
std::cin >> std::ws;
std::getline(std::cin, aluno.nome);
std::cin >> aluno.media;
turma.push_back(aluno);
}
const Aluno* melhor = &turma[0];
for (const Aluno& aluno : turma) {
if (aluno.media > melhor->media) melhor = &aluno;
}
std::cout << "Maior média: " << melhor->nome
<< " (" << melhor->media << ")\n";
}Organização: a struct reúne os campos da entidade; o vector reúne vários registros.
Projetando o TAD Conta
Implemente uma conta cujo saldo não possa ser alterado diretamente. Depósitos devem ser positivos e saques não podem ultrapassar o saldo.
double saldodepositar · sacar · consultarVer dica
Mantenha o saldo em private. As operações públicas devem preservar saldo >= 0.
Ver resolução comentada
#include <iostream>
class Conta {
private:
double saldo = 0.0;
public:
bool depositar(double valor) {
if (valor <= 0.0) return false;
saldo += valor;
return true;
}
bool sacar(double valor) {
if (valor <= 0.0 || valor > saldo) return false;
saldo -= valor;
return true;
}
double consultarSaldo() const { return saldo; }
};
int main() {
Conta conta;
conta.depositar(150.0);
if (!conta.sacar(200.0)) std::cout << "Saque recusado\n";
conta.sacar(40.0);
std::cout << "Saldo: " << conta.consultarSaldo() << '\n';
}TAD: o cliente conhece as operações, enquanto a representação e o invariante ficam protegidos.
Lista de tarefas por operações
Crie um TAD apoiado por vector<string> com operações para adicionar, remover por posição, contar e listar tarefas.
Ver dica
O índice é válido quando indice < tarefas.size(). Use erase para remover.
Ver resolução comentada
#include <iostream>
#include <string>
#include <vector>
class ListaDeTarefas {
private:
std::vector<std::string> tarefas;
public:
void adicionar(const std::string& tarefa) {
tarefas.push_back(tarefa);
}
bool remover(std::size_t indice) {
if (indice >= tarefas.size()) return false;
tarefas.erase(tarefas.begin() + indice);
return true;
}
std::size_t quantidade() const { return tarefas.size(); }
void listar() const {
for (std::size_t i = 0; i < tarefas.size(); ++i)
std::cout << i << ": " << tarefas[i] << '\n';
}
};
int main() {
ListaDeTarefas lista;
lista.adicionar("Estudar TAD");
lista.adicionar("Compilar");
lista.adicionar("Testar");
lista.remover(1);
lista.listar();
}Custos: adicionar no fim é O(1) amortizado; remover no meio é O(n).
Alterando o valor original
Implemente dobrar usando referência e zerar usando ponteiro. A segunda função deve aceitar nullptr com segurança.
int&int*Ver dica
A referência é usada como a variável. O ponteiro deve ser verificado e desreferenciado com *.
Ver resolução comentada
#include <iostream>
void dobrar(int& numero) {
numero *= 2;
}
bool zerar(int* numero) {
if (numero == nullptr) return false;
*numero = 0;
return true;
}
int main() {
int valor = 21;
dobrar(valor);
std::cout << valor << '\n'; // 42
zerar(&valor);
std::cout << valor << '\n'; // 0
zerar(nullptr);
}Diferença: o ponteiro armazena um endereço e pode ser nulo; a referência funciona como outro nome para um objeto válido.
Lista encadeada sem vazamento
Implemente inserção no início e percurso de uma lista simplesmente encadeada. Use unique_ptr para a desalocação automática.
Ver dica
O novo nó recebe o antigo início com std::move(inicio); depois, inicio recebe o novo nó.
Ver resolução comentada
#include <iostream>
#include <memory>
struct No {
int valor;
std::unique_ptr<No> proximo;
};
class Lista {
private:
std::unique_ptr<No> inicio;
public:
void inserirNoInicio(int valor) {
auto novo = std::make_unique<No>();
novo->valor = valor;
novo->proximo = std::move(inicio);
inicio = std::move(novo);
}
void imprimir() const {
const No* atual = inicio.get();
while (atual != nullptr) {
std::cout << atual->valor << ' ';
atual = atual->proximo.get();
}
std::cout << '\n';
}
};
int main() {
Lista lista;
lista.inserirNoInicio(10);
lista.inserirNoInicio(20);
lista.inserirNoInicio(30);
lista.imprimir();
} // A cadeia é desalocada automaticamente.RAII: cada nó possui exclusivamente o próximo. Destruir o início libera toda a cadeia.
Quantidade conhecida em execução
Leia n temperaturas e mostre a menor, a maior e quantas ficaram acima da média. Escolha uma coleção redimensionável.
array<double, 7>vector<double>(n)Ver dica
Leia e some no primeiro percurso; calcule a média; conte os valores acima dela no segundo.
Ver resolução comentada
#include <iostream>
#include <vector>
int main() {
std::size_t n;
std::cin >> n;
if (n == 0) return 0;
std::vector<double> temperaturas(n);
double soma = 0.0;
for (double& valor : temperaturas) {
std::cin >> valor;
soma += valor;
}
double menor = temperaturas[0];
double maior = temperaturas[0];
double media = soma / n;
std::size_t acima = 0;
for (double valor : temperaturas) {
if (valor < menor) menor = valor;
if (valor > maior) maior = valor;
if (valor > media) ++acima;
}
std::cout << "Menor: " << menor << '\n';
std::cout << "Maior: " << maior << '\n';
std::cout << "Acima da média: " << acima << '\n';
}Análise: tempo O(n) e espaço O(n). O vector administra a memória dinâmica.
Reconhecendo padrões de crescimento
Associe cada trecho à complexidade de tempo correspondente.
cout << valores[0];for (int i = 0; i < n; ++i)
visitar(i);for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
comparar(i, j);while (n > 1)
n /= 2;Ver explicação
Busca que reduz o problema pela metade
Implemente busca binária iterativa em um vetor ordenado. Devolva o índice do alvo ou -1.
Ver dica
Mantenha inicio e fim. Compare o alvo com o meio e descarte metade do intervalo.
Ver resolução comentada
#include <iostream>
#include <vector>
int buscaBinaria(const std::vector<int>& valores, int alvo) {
int inicio = 0;
int fim = static_cast<int>(valores.size()) - 1;
while (inicio <= fim) {
int meio = inicio + (fim - inicio) / 2;
if (valores[meio] == alvo) return meio;
if (valores[meio] < alvo)
inicio = meio + 1;
else
fim = meio - 1;
}
return -1;
}
int main() {
std::vector<int> valores{3, 8, 12, 19, 27, 34, 51};
int alvo;
std::cin >> alvo;
std::cout << "Índice: " << buscaBinaria(valores, alvo) << '\n';
}Complexidade: tempo O(log n) e espaço auxiliar O(1).
Depois dos 10 exercícios
Desafio integrador: mapa de amizades
Combine registros, classe, coleção dinâmica, referências e análise de custo em um pequeno grafo social. Este desafio não entra na contagem.
Implemente estas operações
adicionarPessoa(nome)adicionarAmizade(a, b)listarAmigos(pessoa)saoAmigos(a, b)
Use vector<Pessoa> para os registros e vector<vector<int>> como lista de adjacência.