Escolar Documentos
Profissional Documentos
Cultura Documentos
PROGRAMAÇÃO I
ESPECIALISTA JOSÉ HENRIQUE HONJOYA
ESTRUTURA DE DADOS I
“
A Faculdade Católica Paulista tem por missão exercer uma
ação integrada de suas atividades educacionais, visando à
geração, sistematização e disseminação do conhecimento,
para formar profissionais empreendedores que promovam
a transformação e o desenvolvimento social, econômico e
cultural da comunidade em que está inserida.
Av. Cristo Rei, 305 - Banzato, CEP 17515-200 Marília - São Paulo.
www.uca.edu.br
Nenhuma parte desta publicação poderá ser reproduzida por qualquer meio ou forma
sem autorização. Todos os gráficos, tabelas e elementos são creditados à autoria,
salvo quando indicada a referência, sendo de inteira responsabilidade da autoria a
emissão de conceitos.
ALGORITMOS E LÓGICA DE
PROGRAMAÇÃO I
ESPECIALISTA JOSÉ HENRIQUE HONJOYA
SUMÁRIO
AULA 01 CONCEITOS INICIAIS SOBRE LINGUAGEM DE 05
PROGRAMAÇÃO C
INTRODUÇÃO
Caro(a) aluno(a), seja bem-vindo(a) a este livro que foi elaborado especialmente
para que você possa conhecer os conceitos básicos e iniciais de lógica de programa-
ção e estrutura de dados. O livro está dividido em diversas aulas, nas quais aprende-
remos os conceitos iniciais da linguagem de programação C, que é uma linguagem
muito popularizada por ser considerada base para o desenvolvimento das linguagens
atuais, como C#, JAVA, PHP, entre outras. Todas as unidades apresentam exemplos
utilizando a linguagem C.
Veremos um breve histórico da linguagem C, suas características e os conceitos
iniciais sobre programação. Estudaremos como é a estrutura básica de um programa
em linguagem C, a declaração de variáveis e manipulação de dados através de entrada
e saída de dados. Conheceremos também os operadores e expressões, entre outros
conceitos que o(a) ajudarão no desenvolvimento de seus programas em linguagem
C, através disso, você, aluno(a), terá o conhecimento necessário para desenvolver os
seus primeiros programas em linguagem C.
Será apresentado a você as estruturas de dados em forma de árvore, que é muito
utilizada para a organização dos dados na memória por ser de fácil manipulação pelo
sistema. Iremos estudar também sobre as listas lineares que são um tipo de vetor
para armazenarmos e recuperamos dados. Veremos ainda as listas encadeadas, du-
plamente encadeadas, circulares e ordenadas. Por fim, será visto o conceito das duas
estruturas de dados mais importantes, estrutura de FILA e PILHA, conceitos esses
utilizados para diversos algoritmos de busca e ordenação de dados.
Conheceremos os conceitos sobre ordenação de dados, pois quanto mais organi-
zados e ordenados os elementos, mais rápida será a manipulação deles. Serão com-
preendidos alguns dos algoritmos de ordenação mais comuns como o BubbleSort
(método bolha), InsertionSort, ShellSort (concha), MergeSort (dividir para conquistar)
entre outros.
Por fim, vamos estudar sobre a teoria dos grafos, como esta teoria surgiu, a sua
importância para a área da computação, suas características e representações.
Desejo a você, aluno(a), uma ótima leitura e bons estudos!
AULA 1
CONCEITOS INICIAIS SOBRE
LINGUAGEM DE PROGRAMAÇÃO C
Fonte: https://br.freepik.com/fotos-gratis/codigo-de-programacao-com-fundo-laptop_902700.htm
A linguagem Estruturada C
Uma das características do C é o fato de ser uma linguagem estruturada. Embora
não apresente rigorosamente os mesmos atributos de outras linguagens estrutura-
das em blocos, ainda assim trata-se de uma linguagem estruturada, que permite a
compartimentalização do código e seus respectivos dados.
Por meio das sub-rotinas (funções), a linguagem emprega o uso de variáveis lo-
cais, dessa forma, eventuais ações que acontecem dentro de cada sub-rotina não
ocasionam efeitos inesperados em outras partes do código. O uso de variáveis locais
torna-se uma grande vantagem em relação às variáveis globais, visto que o uso exces-
sivo de variáveis globais pode resultar em erros de compilação ou até mesmo outras
situações indesejadas no programa.
A estrutura apresentada pela linguagem C permite ao programador compartilhar
funções de maneira fácil e dinâmica, de modo que não é necessário saber exatamen-
te como a função executa uma determinada ação. Basta que o programador saiba
o que ela faz e, então, chamá-la em um trecho de código que deseja contemplar. O
uso de funções estabelece a codificação em partes separadas, de acordo com suas
funcionalidades, permitindo um desenvolvimento modular. Assim como em outras lin-
#include <stdio.h>
Além da biblioteca, outro elemento obrigatório em nosso código é a função int main.
A função main define a estrutura contida dentro dela como sendo a parte principal do
programa. No padrão ANSI, esse tipo de função retorna um valor do tipo inteiro (int).
Assim, a declaração da função deve ser feita da seguinte forma:
#include <stdio.h>
int main(){
Após incluir a biblioteca e declarar a função int main, estamos prontos para apre-
sentar nosso primeiro programa em linguagem C com a expressão “Olá Mundo!”. Se-
gue abaixo o exemplo:
#include <stdio.h>
int main(){
printf(“Ola Mundo!!!”);
#include <stdio.h>
int main(){
printf(“Ola Mundo!!!”);
return (0);
Após realizar esta breve sequência de passos, temos então nosso primeiro código
em linguagem C executado com sucesso.
#include <stdio.h>
int main(){
// este printf deverá imprimir a frase Olá Mundo!!!
printf(“Ola Mundo!!!”);
return (0);
printf(“Ola Mundo!!!”);
A Biblioteca Padrão de C
Nos exemplos que já foram apresentados, pudemos perceber o uso de funções e
comandos que são responsáveis por realizar ações específicas dentro do programa.
Isso porque, a linguagem C é caracterizada pela chamada de funções que está presen-
te em sua biblioteca padrão. Esta biblioteca está presente em todos os compiladores
da linguagem C e é formada por um conjunto mínimo de arquivos de cabeçalho que
compõem o padrão C ANSI.
Para Schildt (1997, p. 3), “todo compilador C vem com uma biblioteca C padrão de
funções que realizam as tarefas necessárias mais comuns, O padrão C ANSI especifi-
ca o conjunto mínimo de funções que estará contido na biblioteca”.
Na tabela a seguir podemos conferir a descrição referente a cada arquivo de cabe-
çalho:
Biblioteca Descrição
assert.h Auxilia na identificação de erros em versões de depuração de programas.
complex.h Permite o tratamento no que se refere à manipulação de números complexos.
Realiza o tratamento de caracteres, como, por exemplo, a conversão de
ctype.h
maiúsculas, minúsculas.
errno.h Teste de códigos de erro reportados pelas funções de bibliotecas.
Definição de funções para tratamento de exceções em variáveis do tipo
fenv.h
ponto flutuante.
float.h Definição de limites e precisão para variáveis do tipo ponto flutuante.
inttypes.h Utilizada para o tratamento de conversão entre variáveis do tipo inteiro.
Permite a programação a partir da codificação de caracteres de acordo com
iso646.h
a ISO646.
limits.h Define a limitação de recursos
Permite a formação de acordo com a localização, como moeda, data,
locale.h
acentuação, etc.
math.h Utilizada na manipulação de funções matemáticas.
Permite definir macros (setjmp e longjmp) para saídas não locais e realização
setjmp.h
do tratamento de exceções.
signal.h Permite receber e realizar o tratamento de sinais específicos.
Utilizada no acesso aos argumentos passados para funções com parâmetro
stdarg.h
variável.
stdbool.h Tratamento de dados do tipo booleano.
stdint.h Padrões de definição de tipos de dados inteiros.
stddef.h Padrões de definições de tipos.
stdio.h Utilizada para funções de entrada/saída.
Possibilita o uso de funções nas mais diversas operações, como conversão,
stdlib.h
alocação de memória, controle de processo, funções de busca e ordenação.
string.h Utilizada para funções no tratamento de strings.
tgmath.h Permite implementar facilidades na utilização de funções matemáticas.
time.h Permite o tratamento de tipos de data e hora.
wchar.h Possibilita o tratamento de caracteres para suportar idiomas diversos.
wctype.h Contém funções para classificação de caracteres wide.
Biblioteca C padrão
Fonte: elaborado pelo autor
será executado.
#include <stdio.h>
main(){
Por vezes, teremos que declarar muitas variáveis do mesmo tipo para utilizar
no programa, podemos efetuar a declaração na mesma expressão, declarando o
tipo e, logo em seguida, os nomes das variáveis separadas por vírgula, conforme
apresentado na figura “Declaração de variáveis do mesmo tipo”, a seguir, em lin-
guagem C.
#include <stdio.h>
int main(){
}
Declaração de variáveis do mesmo tipo
Um detalhe importante que devemos atentar é para com os nomes das vari-
áveis, os nomes de referência não podem conter caracteres especiais (acentos,
%, &, #, dentre outros), espaços e também não podem começar com números,
vejamos, a seguir, na tabela “Declaração de variáveis de modo incorreto”, algumas
declarações incorretas em linguagem C.
PALAVRAS RESERVADAS: dessa linguagem são nomes utilizados pelo compilador para re-
presentar comandos de controle do programa, operadores e diretivas.
AULA 2
ENTRADA E SAÍDA DE DADOS,
EXPRESSÕES E OPERADORES
Fonte: https://br.freepik.com/fotos-gratis/linguagem-de-programacao-no-local-de-trabalho_902701.htm
Exemplo:
Sintaxe básica:
printf(“Mensagem que será apresentada na tela para o usuário:”);
Utilizando argumentos:
printf(“Sua renda mensal é: R$ %f”, renda);
Neste segundo exemplo, o %f define o local onde será escrita a variável do tipo
float. E o “total” representa a variável apresentada na posição %f.
Já a função scanf possui uma sintaxe um pouco diferente, pois obrigatoriamen-
te requer o uso de argumentos que definem o tipo de dado da variável que será
lida. A sintaxe básica é dada da seguinte forma:
caractere char %c
inteiro int %d ou %i
real float %f
Certamente, estes tipos de dados são apenas uma breve representação, for-
mando uma composição básica ou tipos primitivos. Mas, boa parte do que iremos
aprender em linguagem C será contemplado por estes tipos de dados.
Operadores Relacionais
Operadores relacionais também são conhecidos como operadores de compara-
ção, pois, possibilitam estabelecer uma relação de comparação entre dois valores
ou duas variáveis. Também é possível que esta relação de comparação seja feita
entre um determinado valor e uma variável. Um exemplo simples disso é quando,
pretende-se saber se o valor da variável Saldo é maior do que o valor 0. Neste
caso, estamos apenas fazendo uma comparação entre uma variável e um valor.
Ao escrever o código, essa comparação deve ser apresentada da seguinte forma:
Saldo > 0
== Igual a 1 == 1, X == Y
Operadores Lógicos
Já os operadores lógicos realizam um outro tipo de verificação, mas que tam-
bém está baseada na ideia de verdadeiro ou falso. Em diversas linguagens de pro-
gramação são utilizados três operadores que foram definidos a partir da Álgebra
Booleana. Vejamos a tabela “Operadores lógicos” a seguir:
!, != NOT (Negação)
Maior prioridade
&& AND (E lógico)
|| OR (OU lógico)
Menor prioridade
=, <>, >, <, >=, <= Operadores relacionais
?: Operador ternário
Tabela 6: Precedência operadores
Fonte: elaborado pelo autor
#include <stdio.h>
int main(){
int a, b, maior;
printf(“Informe o primeiro numero: “);
scanf(“%d”, &a);
printf(“Informe o segundo numero: “);
scanf(“%d”, &b);
Neste trecho será feito o teste relacional para verificar qual dos dois valores in-
formados é o maior. Caso o valor de “a” seja maior, então a variável “maior” recebe
o seu valor. Caso contrário será o valor de “b”.
#include <stdio.h>
#include <stdlib.h>
main(){
int A = 55;
int B = 68;
int C = 38;
int D = 2;
int resultado1, resultados2;
resultado1 = A + B;
printf(“%d”, resultado1);
printf(“%n”); // utilizamos o \n quando queremos forçar uma quebra de linha
resultado2 = A + B / C – D;
printf(“%d”, resultado2);
}
Código fonte, operadores aritméticos
Devemos atentar para dois pontos muito importantes neste código que acaba-
mos de desenvolver:
1º Ponto: quando trabalhamos com variáveis do tipo int os resultados sempre
serão valores inteiros como 1, 63, 157, entre outros. Quando precisamos trabalhar
com valores que se utilizam de separações, como a altura 1.75, aplicamos o tipo
de variável float.
2º Ponto: note a que o resultado 2 obteve o valor de 54 na expressão A + B / C
- D, o valor correto seria 1, mas o porquê deste valor 54, então? A resposta é bem
simples, o computador não efetua o cálculo de modo linear: resultado de A + B
depois / C e posteriormente - D. O que acontece é que o computador elenca priori-
dades, operadores aritméticos como multiplicação (*) e divisão (/) recebem peso
maior na expressão do que os operadores de soma (+) e subtração (-).
Para resolver o problema apontado no segundo ponto basta priorizar o cálculo
manualmente, atribuindo o que deseja executar primeiro dentro dos parentes, igual
a cálculos matemáticos convencionais, vejamos, na figura “Código fonte, operado-
res aritméticos com prioridades”, o código fonte com esta atribuição.
#include <stdio.h>
#include <stdlib.h>
main(){
int A = 55;
int B = 68;
int C = 38;
int D = 2;
int resultado1, resultados2;
resultado1 = A + B;
printf(“%d”, resultado1);
printf(“%n”); // utilizamos o \n quando queremos forçar uma quebra de linha
resultado2 = (A + B) / C – D;
printf(“%d”, resultado2);
}
Código fonte, operadores aritméticos com prioridades
Elaborado pelo autor.
Com a atribuição da prioridade o resultado da operação aritmética está de acor-
do com o esperado, dependendo do cálculo que desejamos, podemos ter vários
pontos de prioridades em uma operação, como exemplo (A+B) / (C-D), a leitura
desta operação será: o resultado de A+B será dividido por C, o resultado é poste-
riormente subtraído por D, desta forma conseguimos obter o resultado esperado
sem problemas. Observe que o valor apresentado é 1 e não 1,23 este resultado não
é apresentado, pois a declaração da variável é como inteiro.
+= A += B Equivale a A = A + B
-= A -= B Equivale a A = A - B
*= A *= B Equivale a A = A * B
/= A /= B Equivale a A = A / B
%= A %= B Equivale a A = A % B
++ C++ Equivale a C = C + 1
-- F-- Equivale a F = F - 1
#include <stdio.h>
#include <stdlib.h>
main(){
int A = 55;
int B = 68;
int C = 38;
int D = 2;
int h, i, j, k;
B+=D;
D-=A;
C++;
A--;
}
Código fonte, operadores atribuição e acumulação
#include <stdio.h>
#include <stdlib.h>
#define bim 4
main(){
}
Código fonte, média de notas
Fonte: elaborado pelo autor
3 Afirmação: O cálculo da média se dá pela soma dos grupos e depois dividindo pela quantida-
de de grupos. Ex.: (1+2+3+4)/4.
Podemos observar as entradas de dados para as variáveis, as quais compõem
o cálculo da média (6.8+7.5+5.5+6.3), como já foi definido a quantidade de bimes-
tres no início do código, basta então pegar o resultado da soma das entradas de
dados (21.1) e dividir por 4, assim obtendo a média do aluno e a quantidade de
valores que deve aparecer após o ponto.
Neste exemplo, vamos imaginar que você está testando os seus conhecimen-
tos em linguagem C, e que, gostaria de efetuar um programa que perguntasse para
um usuário qual o seu primeiro nome, sobrenome, idade e altura, e após todas as
informações preenchidas, imprimisse na tela os dados digitados.
Vejamos na figura “Código fonte, recebendo e imprimindo dados”, a seguir, como
seria o seu código fonte em linguagem C.
#include <stdio.h>
#include <stdlib.h>
main(){
}
Código fonte, recebendo e imprimindo dados
Fonte: elaborado pelo autor
Perceba, aluno(a), que neste código-fonte mesclamos vários tipos que irão rece-
ber os dados digitados pelos usuários, isso é muito comum nos sistemas desen-
volvidos, nos quais contamos com diversas declarações de variáveis diferentes.
Após o usuário ter digitado todos os seus dados, a tela é limpa, por meio do sys-
tem(“cls”), sendo retornado apenas os dados impressos na tela, conforme a figura
“Programa em execução, imprimindo dados”, a seguir.
Perceba que os dados de entrada digitados pelo usuário são apagados da tela,
mas ainda estão armazenados na memória do computador, por isso que consegui-
mos imprimir os caracteres de entrada na tela sem problemas, podemos utilizar
a função system() para deixar a tela mais limpa, sem uma poluição visual para o
usuário assim ele pode compreender mais fácil as informações que estão sendo
impressas na tela.
Cálculo de IMC
Imaginamos que você é um programador recém-contratado em uma empresa
de desenvolvimento de software, e o seu primeiro programa a ser desenvolvido em
linguagem C é o sistema de acompanhamento de cálculo de IMC de uma acade-
mia, vejamos a seguir os requisitos apresentados pelos stakeholders4.
Requisitos do sistema
O sistema deve armazenar e mostrar na tela o nome e a idade do aluno.
Efetuar o cálculo de IMC do 1º e 2º mês, com base nos dados forneci-
dos pelo professor na academia.
No final, deve-se mostrar na tela, de modo limpo, apenas os dados do
aluno, seguido dos dados dos cálculos de IMC e a diferença entre os dois pri-
meiros meses.
Também é muito importante apresentar a quantidade de meses que o
aluno terá um acompanhamento junto ao professor da academia.
Após analisar dos requisitos levantados foi se desenvolvido o seguinte
código fonte, apresentado no Quadro Código fonte, sistema de cálculo de IMC,
a seguir.
#include<stdio.h>
#include<stdlib.h>
#include<locale.h>
#define plano 4
int main() {
setlocale(LC_ALL, “Portuguese”);
float peso1, altura1, IMC1;
float peso2, altura2, IMC2;
float diferenca;
char nome[50];
int idade;
Podemos observar que o código-fonte desenvolvido traz todos os elementos
que estudamos nesta unidade, conforme já apresentado em tópicos anteriores é
comum nos sistemas desenvolvidos termos várias funções e entradas de dados
em um sistema, onde contamos com diversas declarações de variáveis diferentes.
Vejamos um passo a passo até a execução final do programa.
Conforme os dados de entrada são digitados pelo usuário, este são apagados
da tela, mas continuam na memória do computador, após o preenchimento de
todos os dados e os cálculos efetuados, imprimimos na tela o que foi requisitado
pelos stakeholders do projeto.
Podemos dizer que ao término de seu primeiro programa desenvolvido em lin-
guagem C é possível compreender os diversos elementos que irão lhe ajudar a de-
senvolver os seus primeiros algoritmos e além de enriquecer o seu conhecimento
sobre a linguagem C.
AULA 3
INSTRUÇÕES CONDICIONAIS
DE DECISÃO
Fonte: https://pixabay.com/pt/photos/decis%c3%a3o-dist%c3%a2ncia-lista-jun%c3%a7%c3%a3o-5291766/
A expressão lógica ou condição a ser se refere a uma condição que deve ser
retornar como verdadeiro. Para lhe ajudar a entender, vamos imaginar que temos
duas variáveis de entrada A e B e os seus valores respectivos são 1 e 2, vejamos
possíveis condições:
(falsa), pois 1 não é maior que 2 tendo um retorno falso para expressão. Para
compor a expressão condicional, podemos utilizar qualquer um dos operadores
relacionais. Em linguagem C a forma de escrever a função if no código fonte é da
seguinte forma, observe a figura “Comando condicional simples”, a seguir.
mai(){
float nota1, nota2, nota3, nota4, media;
media=(nota1+nota2+nota3+nota4)/bim;
printf(“Media final do Aluno: %.1f \n”, media);
Agora, para que o aluno seja aprovado, ele tem que atender às duas condições,
neste caso, ambas devem retornar como verdadeiro (operador lógico &&), acompanhe
o teste de mesa na tabela “Verdade condição &&”, a seguir:
TABELA VERDADE
TABELA VERDADE
A estrutura do algoritmo irá ficar a mesma, apenas será inserida outra expres-
são condicional para a tomada de decisão, junto com um operador lógico, podemos
também ter no mesmo código várias funções IF, efetuando outros testes com outras
expressões condicionais, vejamos o exemplo, a seguir:
#include <stdio.h>
#include <stdlib.h>
#define bim 4
mai(){
float nota1, nota2, nota3, nota4, media;
media=(nota1+nota2+nota3+nota4)/bim;
printf(“Media final do Aluno: %.1f \n”, media);
if(media >= 7.0 && nota4 >=6){ // expressão condicional a ser testada
com operador lógico &&
printf(“Aluno Aprovado :) \n”);
}
if(nota2 > nota1 || nota4 > nota3){ // expressão condicional a ser testada
com operador lógico ||
printf(“Entre os bimestres o aluno aumentou a sua nota.”);
}
}
Função If com operadores lógicos
Fonte: elaborado pelo autor
Veja que no mesmo algoritmo temos duas funções IF, cada função tem expressões
condicionais e operadores lógicos, vejamos o algoritmo em execução na figura “Re-
torno do algoritmo atendendo apenas a segunda função IF” e na figura “Retorno do
algoritmo atendendo apenas a primeira função IF”.
#include <stdio.h>
#include <stdlib.h>
#define bim 4
mai(){
float nota1, nota2, nota3, nota4, media;
media=(nota1+nota2+nota3+nota4)/bim;
printf(“Media final do Aluno: %.1f \n”, media);
// estrutura IF - ELSE
if(media >= 7.0 && nota4 >=6){ // expressão condicional a ser testada
com operador lógico &&
printf(“Aluno Aprovado :) \n”);
}else{
printf(“Aluno Reprovado :( \n”);
}
// estrutura IF - ELSE
if(nota2 > nota1 || nota4 > nota3){ // expressão condicional a ser testada
com operador lógico ||
printf(“Entre os bimestres o aluno aumentou a sua nota.”);
}else{
Printf(“O aluno nao aumentou a sua nota dentre os bimestres”);
}
}
Com base no código fonte apresentado, agora temos duas opções de respostas
caso o aluno tenha sido aprovado ou reprovado, isso também se reflete sobre as no-
Observe que agora temos uma resposta para cada desvio condicional, na primei-
ra estrutura, como a média das notas do aluno foi maior que 7 e também a nota do
quarto bimestre foi maior que 6, a mensagem que apareceu foi “Aluno Aprovado”, caso
uma das duas expressões não fosse atendida, o bloco de código presente no else
seria executado, neste caso, a informação impressa na tela seria “Aluno Reprovado”.
Compreende-se, então, que temos dois IFs, um dentro de outro, em que a resposta
final é associada às expressões condicionais de ambos, esta estrutura é chamada de
IFs aninhados.
É possível notar que no bloco de execução do primeiro IF(i), temos outras duas es-
truturas IF(j) e IF(k) - ELSE, poderíamos ter outros IFs no bloco de execução do ELSE
também se fosse o caso. Podemos melhorar o nosso algoritmo de média de notas
do aluno, caso ele tenha sido aprovado e suas notas entre os bimestres forem boas,
#include <stdio.h>
#include <stdlib.h>
#define bim 4
mai(){
float nota1, nota2, nota3, nota4, media;
media=(nota1+nota2+nota3+nota4)/bim;
printf(“Media final do Aluno: %.1f \n”, media);
// estrutura IF - ELSE
if(media >= 7.0 && nota4 >=6){ // expressão condicional a ser testada
com operador lógico &&
if(nota4 >= 9){ // IF Aninhado
printf(“Aluno Aprovado, notas acima da media :) \n”);
}else{
printf(“Aluno Aprovado :) “)
}
}else{
printf(“Aluno Reprovado :( \n”);
}
// estrutura IF - ELSE
if(nota2 > nota1 || nota4 > nota3){ // expressão condicional a ser testada
com operador lógico ||
if(nota4 >= 9){ // IF Aninhado
printf(“Entre os bimestres o aluno aumentou a sua nota,
parabens pelo empenho.”);
}else{
printf(“Entre os bimestres o aluno aumentou a sua nota.”);
}
}else{
Printf(“O aluno nao aumentou a sua nota dentre os bimestres”);
}
}
Algoritmo com IFs aninhados
Fonte: elaborado pelo autor
Agora, caso o aluno seja aprovado, efetuamos uma nova validação: caso a nota do
quarto bimestre deste aluno for maior ou igual a nove, a mensagem será “Aluno Apro-
vado, notas acima da média :)”, caso não, será apresentado apenas “Aluno Aprovado”.
A estrutura de IFs aninhados também foi implementada na condição de notas entre os
bimestres, vejamos agora o algoritmo em execução
O resultado que foi apresentado na tela está ligado diretamente com as estruturas de
desvio condicional IF e IF - ELSE podemos aplicar o conceito de IFs aninhados também
com IF - ELSE, assim iremos gerar uma cascata, cada vez vamos criando novos níveis e
blocos de código, mas devemos tomar cuidado ao usar IFs aninhados para não nos perder
no raciocínio entre estes deixando o código-fonte extremamente confuso.
A estrutura de seleção múltipla switch deixa os algoritmos mais dinâmicos, pois po-
demos testar diversas condições a partir de uma variável. Vamos pensar no seguinte ce-
nário: um sistema de cadastro de indicação de filmes de uma locadora devemos fazer o
cadastro dos itens seguindo as classificações indicativas que são regulamentações sobre
as idades indicadas para consumir cada obra. Elas são feitas pela Secretaria Nacional de
Justiça (SNJ), do Ministério da Justiça brasileiro.
Ao desenvolvermos um algoritmo que efetue a leitura do dado informado, podemos ter
diversas opções para a indicação, podemos utilizar a estrutura switch em conjunto com
a expressão case, assim podemos compreender sendo uma lista com a validação dos
desvios.
A execução da estrutura switch compara todos os cases até encontrar um desvio con-
dicional satisfatório, assim executando seu bloco de código, após iniciar a sequência de
códigos, chamamos o comando break, o qual veremos com mais detalhes no próximo
tópico.
Caso nenhum dos itens do switch seja atendido, a estrutura default é acionada, este
item é identificado sendo a saída padrão, caso nenhum dos case atenda à necessidade,
neste momento, o bloco de código a ser executado será apenas o default. Vejamos, a se-
guir, na figura “Composição do SWITCH CASE textual”, os comandos em linguagem C:
Mesmo sendo uma estrutura mais poderosa que as de IF - ELSE, devemos atentar
para alguns pontos importantes. Segundo Schildt (1997 p. 70-71), estes itens são:
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
main (){
setlocale(LC_ALL, “Portuguese”);
int classificacao;
printf(“-----------------------\n”);
printf(“0 = Livre \n1 = 10 anos \n”);
printf(“2 = 12 anos \n3 = 14 anos \n”);
printf(“4 = 16 anos \n5 = 18 anos \n”);
printf(“9 = Sem Classificação \n”);
printf(“-----------------------\n”);
printf(“Digite a classificação indicativa do filme: “);
scanf(“%d”, &classificacao);
switch (classificacao){
case 0 :
printf(“\n Classificação livre: desenho animados.\n”);
break;
case 1 :
printf(“\n Classificação 10: presença de armas, medo e ten-
são.\n”);
break;
case 2 :
printf(“\n Classificação 12: violência, lesão corporal, des-
crição de violência.\n”);
break;
case 3 :
printf(“\n Classificação 14: morte, vulgaridade, drogas ilíci-
tas.\n”);
break;
case 4 :
printf(“\n Classificação 16: tortura, mutilação, violência,
morte.\n”);
break;
case 5 :
printf(“\n Classificação 18: violência de forte impacto,
crueldade.\n”);
break;
default:
printf(“\n Sem classificação.\n”);
}
return(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
main (){
setlocale(LC_ALL, “Portuguese”);
char operacao;
switch (operacao){
case ‘+’ :
printf(“\n Adição.\n”);
break;
case ‘-’ :
printf(“\nSubtração”);
break;
case ‘*’ :
printf(“\nMultitplicação”);
break;
case ‘/’ :
printf(“\nDivisão”);
break;
default:
printf(“\nNenhuma operação válida”);
}
return(0);
}
Este algoritmo é bem simples, ele requisita para o usuário escolher uma opera-
ção aritmética, após isso, executamos a estrutura switch - case, observe que neste
código-fonte desenvolvido em linguagem C não utilizamos o comando break após a
comparação de cada case, neste caso, vejamos este algoritmo em execução na figura
“Retorno do algoritmo sem o comando break”.
Ao executar o algoritmo e digitarmos o sinal de adição a estrutura switch é proces-
sada, note que mesmo tendo um caminho de desvio compatível, todos os cases foram
acessados e impressos na tela. O comando break é necessário nesta e em outras es-
truturas para dar a saída da estrutura e não executar o bloco de códigos errados, com
base nisto, vemos que é sempre necessário utilizar o comando break na estrutura de
seleção múltipla.
AULA 4
ESTRUTURAS DE REPETIÇÃO,
VETORES E MATRIZES
Fonte: https://br.freepik.com/vetores-gratis/simbolo-do-infinito-com-efeito-de-reflexo-de-lente_2547657.htm
Sempre que a condição for verdadeira o programa irá executar um fluxo alternativo,
seguindo as instruções previamente definidas para este caso, garantindo assim que
alguma ação será executada. No entanto, caso a condição seja falsa, o programa irá
seguir o fluxo normal do algoritmo, caracterizando a saída do loop.
Temos que ter muito cuidado também na hora de trabalhar com estruturas de re-
petição para não criarmos um loop infinito. As estruturas de repetição tem como base
uma condição que é responsável por definir o elemento de parada do loop, sendo,
então, fundamental para que o número de repetições desejadas aconteça da maneira
correta. Quando esse elemento não é informado da maneira adequada ou simples-
mente não é informado, o programa estará sujeito à ocorrência de um loop infinito.
Isto acontece sempre que uma condição nunca se torna falsa ou que seu elemento de
parada nunca é alcançado ou satisfeito.
Exemplo utilizando o comando for:
#include<stdio.h>
int main()
{
int x;
for(x=0;.......; x++)
printf(“%d”, x);
}
Loop infinito.
Fonte: elaborado pelo autor
Neste exemplo, note que não existe uma condição de parada, ou seja, até quando
o laço deve ser executado. Dessa forma, ao executar este programa, a impressão da
variável X acontecerá de maneira infinita.
A declaração do laço for deve ser escrita utilizando o próprio nome da estrutura se-
guido de parênteses (). Dentro dos parênteses deve ser informada a variável que será
testada, bem como a condição e o incremento/decremento. Cada uma das seções
precisa ser separada das demais, utilizando ponto e vírgula. Toda essa informação é
apresentada em uma única linha, não sendo permitido realizar a quebra de linha. Na
sequência, define-se a instrução que deverá ser executada. Para escrever a instrução,
podemos utilizar chaves ({}) separá-la das demais instruções que podem ser inseridas
dentro de uma mesma estrutura de repetição, como, por exemplo, em estruturas de
repetição encadeadas. Quando informamos apenas uma instrução, o uso das chaves
é dispensado. Segue abaixo a sintaxe da estrutura de repetição for:
O laço de repetição for pode ser encontrado em muitas outras linguagens de pro-
gramação, além da C. Isso se dá devido a sua flexibilidade e capacidade de estruturar
o código de uma forma bastante organizada e simples.
Como forma de exemplificar o que foi apresentado até este momento, vamos es-
crever um código em linguagem que permita inserir um nome e que este nome seja
repetido na tela 10 vezes, conforme podemos perceber:
#include <stdio.h>
int main()
{
char nome[20];
int i;
printf(“\n Informe o nome: “);
scanf(“%s”, nome);
for(i=1; i<=10; i++)
{
printf(“\n %s”, nome);
}
return(0);
}
Neste exemplo, temos a variável “nome” que é do tipo caractere (char) que será uti-
lizada para receber o nome que será digitado e, também a variável “i”, que irá controlar
o número de vezes que o laço será repetido. Ao informar o nome, o programa arma-
zenará a informação dentro da variável e em seguida executará o laço de repetição.
Perceba que dentro do próprio laço de repetição a variável “i” é iniciada com o valor 1.
Sendo assim, o laço deverá ser repetido até que a variável “i” seja menor/igual a 10. A
verificação desta condição é feita por meio do operador relacional menor igual (<=).
Durante a execução do laço for a variável “i”, que inicialmente havia sido definida com
o valor 1, passa a ser atualizada a cada ciclo de repetição do laço. Para isso, utilizamos
o sinal de incremento (++), para que a cada iteração do laço, o valor da variável seja
acrescido de mais 1. Definida a condição do laço de repetição, o programa irá executar
as instruções configuradas dentro da estrutura, conforme abaixo:
A instrução informada será repetida até que o laço alcance seu limite de iterações,
que, neste caso, é 10. Assim, o nome informado inicialmente será impresso na tela 10
vezes.
while (condição){
<bloco de código a ser executado>
função de incremento
}
Composição da estrutura de repetição While
Fonte: elaborado pelo autor
Para compreendermos melhor como podemos utilizar a estrutura while, vamos com-
preender melhor o algoritmo que foi implementado quando trabalhamos estudamos so-
bre if aninhados, o algoritmo no qual o usuário tenta descobrir qual o número oculto, a
diferença, neste caso, é que ele não terá mais um limite de chances e o número está em
torno de 0 a 100, em que o usuário irá contar com as dicas, se o número oculto é maior ou
menor do que o digitado pelo usuário. Vejamos o código fonte a seguir.
#include<stdio.h>
#include<stdlib.h>
#include<locale.h>
int main()
{
setlocale(LC_ALL, “Portuguese”);
int nOculto, nUsuario = 0;
nOculto = rand() % 100;
printf(“Descubra qual o numero oculto, entre 0 e 100. \nVocê não tem
um limite de chances\n\n”);
while(nUsuario != nOculto)
{
printf(“Qual o seu primeiro palpite? “);
scanf(“%d”, &nUsuario);
// estrutura IF - ELSE
if (nUsuario == nOculto){
printf(“Parabéns você descobriu o número %d \n”, nOcul-
to);
break; // força a parada do algoritmo
}else{
if(nOculto > nUsuario){
printf(“O Número oculto é maior que %d \n\n”, nU-
suario);
}else{
printf(“O Número oculto é menor que %d \n\n”, nU-
suario);
}
}
}
}
Estrutura de repetição while
Fonte: elaborado pelo autor
Note que a expressão no laço de repetição while é nUsuario != nOculto, sendo que se
o número digitado pelo usuário foi diferente do número oculto, o laço de repetição ficará
em constante loop até que o usuário digite o número correto, para que possamos forçar
a entrada no laço de repetição while, já que este só se inicia se a expressão foi atendida.
Testando a expressão no início, inicializamos a variável nUsuario sendo 0, desta forma,
podemos considerar que o laço sempre vai ser executado, e mesmo que o número oculto
seja realmente zero, o laço não será executado. Vejamos o algoritmo em execução.
Perceba que a aplicação só parou de executar quando o número oculto foi encon-
trado, este tipo de estrutura de repetição não entra em um loop infinito, pois, em algum
momento, o número oculto será descoberto, parando o processo é apresentado na
tela qual é o número, conforme demonstrado na imagem acima.
Vimos como é útil utilizar uma estrutura de repetição; uma derivação da while é a
estrutura DO --- WHILE que também é considerada como um laço de repetição, ambas
têm a sua composição bem parecida, entretanto, com alguns pequenos detalhes.
do{
<bloco de código a ser executado no loop>
}while(condição);
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
main(void){
setlocale(LC_ALL, “Portuguese”);
int numero, i=0;
printf(“Digite a quantidade de repetições: “);
scanf(“%d”, &numero);
do{
i++;
printf(“%dº Número\n”, i);
}while (i < numero);
printf(“\nFim da Estrutura DO -- WHILE\n\n”);
}
Vetores
Um vetor pode ser identificado como array por autores diferentes ou em linguagem
específicas, em tese, ambos podem ser considerados a mesma coisa e têm as mes-
mas propriedades e características.
Um array é um conjunto de espaços de memória que se caracterizam pelo fato de
que todos têm o mesmo nome e o mesmo tipo de variável (CHAR, INT, FLOAT) (Adap-
Ao trabalharmos com vetores devemos nos atentar a sua declaração, pois con-
forme as variáveis em geral a entrada de dados nesse vetor só será aceito se ele for
de igual tipo declarado. Exemplo: se o vetor for declarado como CHAR, só receberá
caracteres, caso o vetor for declarado com o tipo INT, só serão armazenados dados
numéricos inteiros.
Para que possamos acessar algum local no vetor, devemos informar qual o local:
#include <stdio.h>
int idade[3];
int i, j;
main(){
for(i=0; i<3; i++){
printf(“Digite sua Idade:”);
scanf(“%d”, &idade[i]);
}
Através do laço de repetição for, foram inseridas 3 entradas de dados nas posições
do vetor número, posteriormente, em outro laço de repetição for, foram apresentados
os dados na tela.
Um ponto muito importante que devemos observar é que todo e qualquer vetor ou
matriz tem sua posição inicial como zero, por exemplo:
ao declararmos um vetor de 3 posições idade[3]
as posições respectivamente para acessar os dados armazenados serão ida-
de[0], idade[1] e idade [2]
Matrizes
Podemos considerar que uma matriz é um vetor melhorado, um vetor trabalha com
elementos em apenas 1 dimensão já uma matriz trabalha em 2 dimensões, pois os
vetores guardam dados do mesmo tipo de variável em uma única linha, através de
colunas, já uma matriz trabalha com linhas e colunas, sendo a primeira posição a
quantidade de linha e a segunda, a de colunas, assim podemos multiplicar os espaços
de armazenamento, por isso a matriz é considerada uma estrutura bidimensional.
A forma de entrada de dados é muito parecida com a do vetor, mas dessa vez te-
mos que utilizar dois laços de repetição, um que representa a linha, e outro, a coluna.
#include <stdio.h>
main (){
int numero[3][4];
int m, p;
AULA 5
FUNÇÕES E PONTEIROS
Fonte: https://pixabay.com/pt/photos/codifica%c3%a7%c3%a3o-computador-hacker-1841550/
Em certos pontos de um programa, temos que repetir um bloco de código por vá-
rias vezes, essa repetição deixará nosso código fonte muito extenso podendo causar
lentidão quando executado. Uma solução para esta situação seria um laço de repe-
tição, mas imagine que você deseja aproveitar apenas pequenos trechos de códigos
em locais específicos, a intenção não seria fazer um repetição, mas sim aproveitar
trechos já existentes.
Para fazer o reuso de trechos de código podemos utilizar funções para evitar que
um trecho seja repetido por várias vezes de modo desnecessário, assim havendo um
reaproveitamento do código já desenvolvido.
Podemos também aprimorar o nosso código utilizando os ponteiros, os quais
podem assumir o armazenamento de outras variáveis, posteriormente efetuando o
apontamento do local.
5.1 Funções
Podem-se declarar as variáveis que serão utilizadas em função de três formas: glo-
bais, locais e parâmetros formais.
Variáveis globais: são declaradas fora da função, como as demais, Albano
e Albano (2010 p.132). Podemos dizer que são as variáveis declaradas no início do
código fonte, podendo ser utilizadas ou manipuladas por qualquer função ou bloco de
comando.
Variáveis locais: são declaradas dentro da função a ser utilizada, Albano e Al-
bano (2010 p.132) afirmam que “as variáveis locais pertencem apenas à função onde
foram declaradas, não podendo, portanto, ser acessadas por meio de outra função de
forma direta”.
Parâmetros formais: são declarados na passagem de dados para a função,
veremos com mais detalhe no próximo tópico.
É possível ver claramente que, ao se utilizar a função, nosso código fonte ficou me-
nor, podendo agora efetuar a chamada da função soma() em qualquer parte do código
fonte.
Compreenda prezado(a) aluno(a), que para o usuário final, ambas as soluções pa-
recem as mesmas, visto que o usuário final não tem acesso ao código fonte, mas
perceba que o código em si sofre modificações consideráveis, sendo menor e mais
organizado.
mesmo tipo”. Temos também dois tipos de parâmetros a serem utilizados: os reais,
que são dados obtidos na entrada pelo usuário, e os formais, que são os parâmetros
declarados na função.
5.3 Ponteiros
Pode-se utilizar um ponteiro com variável de diversos tipos como INT, FLOAT, CHAR
e também STRUCT, vejamos a seguir um código no qual se utiliza um ponteiro do tipo
inteiro
AULA 6
LISTAS ENCADEADAS E
DUPLAMENTE ENCADEADAS
Fonte: https://www.pexels.com/pt-br/foto/papel-de-parede-de-graficos-de-computador-1544947/
Uma lista linear nada mais é que um vetor onde os dados são armazenados e agru-
pados, os dados armazenados podem estar dispostos de maneira sequencial ou não,
isso não fará diferença, pois cada elemento possui uma identificação sobre qual será
o próximo elemento no vetor. Os elementos do vetor não precisam estar necessaria-
mente na sequência física, alocados na memória, mas sim, logicamente, na ordem
crescente no vetor. Podemos utilizar como exemplo o atendimento de um banco, con-
forme as pessoas chegam ao local é atribuído a elas uma senha, estas pessoas pode-
rão estar dispersas no interior do banco, mas conforme forem sendo chamadas o que
prevalece é a senha.
As listas lineares sempre dão sentido na ordem, por exemplo, um elemento que
está armazenado na posição 3 do vetor irá apontar para o próximo, este poderá estar
na posição 4 do vetor ou não, a ideia é que o elemento atual sempre irá apontar para
alguma outro ou atribuir null e assim por diante. Temos vários tipos de listas, mas
todas têm o mesmo princípio.
Conforme visto anteriormente, uma lista é um vetor no qual são armazenados vá-
rios elementos, tais elementos poderão estar ou não ordenados, como também não
precisam ser necessariamente valores numéricos, como uma lista encadeada se re-
fere a um vetor o qual é possível percorrer de forma ordenada, mesmo se os dados
não estiverem em ordem dentro do vetor, ou seja, o que irá interferir na forma de per-
correr é a informação contida no apontamento.
Em uma lista encadeada simples consiste em diversos nós (posições do vetor) pre-
enchidos uma sequência de elementos, sendo que cada alocação segue a posição de
preenchimento do vetor, o elemento em si poderá estar fora de ordem, o que dará sen-
tido a lista será os apontamentos. Uma lista se inicia com um elemento e um ponteiro
que indica o próximo nó, dessa forma, a partir do primeiro elemento podemos percor-
rer todo o vetor, seguindo o encadeamento. O último elemento aponta para NULL, o
que sinaliza que não temos mais elementos.
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
struct elem {
int dado;
elemento *prox;
};
Caso seja necessário ler um elemento anterior, temos que concluir toda a leitura e
reiniciá-la. Rangel (2008 p. 18) afirma que uma solução é a lista duplamente encade-
ada:
Vemos como é percorrida a lista ao ser iniciada pelo elemento que está armazena-
do no X1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
while (op != 0) {
printf(“\nOpções do Menu:\n”);
printf(“\t1 - mostrar lista.\n”);
printf(“\t2 - inserir novo elemento na lista.\n”);
printf(“\t0 - sair do programa.\n”);
Podemos notar que o código fonte sofreu modificação, mas o ponto mais relevante
está na função insere().
novo->prox = l;
novo->ant = NULL;
if (l!=NULL) l->ant = novo;
l = novo;
return l;
}
AULA 7
LISTAS ORDENADAS
E CIRCULARES
Fonte: https://br.freepik.com/vetores-gratis/maquete-de-estante-branca-livros-na-prateleira-na-biblioteca_8548736.htm#page=3&query=livros&position=46
Com a lista ordenada, o sistema pode encontrar mais fácil os elementos utilizando
menos recursos, perceba, caro(a) aluno(a), que o apontamento de próximo e anterior
se permanecem da mesma forma, mas por estarem próximos no vetor isso diminui
processamento, outro aspecto é que caso estivermos procurando o elemento X3 este
está mais próximo no vetor. Vejamos alguns exemplos de ordenação de listas, utilizan-
do elementos numéricos e caracteres.
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
lista *aux = p;
lista *t;
int c;
As listas circulares foram pensadas para que a leitura chegue ao fim e possa retor-
nar ao início sem que seja necessário realizar diversas iterações, uma possibilidade
também seria incluir no nó inicial a informação que o nó anterior seria o último. Va-
mos pensar que você está procurando um valor número em uma lista duplamente
encadeada e ordenada, este valor seria um elemento com um valor alto 150, seria
mais interessante iniciar a busca de modo a contrário. O método de lista circular pode
ser aplicado em qualquer conceito estudado até o momento, listas simples, duplas e
ordenadas.
Vemos como é percorrida a lista ao ser iniciada pelo elemento que está arma-
zenado no X1:
temos que percorrer todos os demais elementos até chegar no nosso destino, sendo
também possível ir para frente ou para trás a qualquer momento.
Dessa forma, é possível visitar todos os elementos a partir do ponteiro inicial até
alcançá-lo novamente ou a partir do primeiro elemento facilmente chegar no último
elemento iniciando uma busca inversa. A lista circular só retorna para o início atra-
vés do último elemento, para isso, é preciso percorrê-la por completo. Por padrão, o
elemento inicial possui o ponteiro que irá identificar o próximo elemento, mas o que
identifica o elemento anterior irá identificar-se como NULL, mas como dito isso pode
ser modificado.
AULA 8
LISTAS DINÂMICAS
Fonte: https://br.freepik.com/fotos-gratis/vista-superior-azul-e-amarelo-ganchos_8002927.htm
Olá prezado(a) aluno(a), em nossas aulas anteriores foi nos oportunizado falar so-
bre as características das estruturas de listas lineares, estas como a encadeada sim-
ples, dupla, circular ou ordenada. Um detalhe muito importante que devemos desta-
car é que o modo de inclusão ou remoção de elementos pode acontecer a qualquer
momento, ou seja, a inclusão de elementos pode acontecer no início, meio ou final da
lista, mesmo estando ordenada ou não.
Nesta aula veremos as propriedades sobre inclusão e remoção de elementos em
uma lista, como também ao final vamos analisar um algoritmo completo em lingua-
gem C contendo as funcionalidades em uma lista.
Conforme informado, caso a inclusão seja em uma lista ordenada, antes da inclu-
são, é efetuada uma leitura para encontrar o local onde será inserido o elemento na
ordem correta. Para compreendermos melhor vejamos a simulação da inclusão do
número 23, o qual irá ocupar uma nova posição no vetor.
O que devemos observar é que com a inclusão do valor 23 em uma lista ordenada,
as informações contidas no vetor (nó) 1 para traz não sofrem modificações, já do
vetor (nó) 2 em diante sim, agora o vetor 2 recebe as informações dos novos valores,
isso faz com que o valor dos demais vetores sejam alterados, recebendo um novo ele-
mento assim e ao final criando uma nova posição para a inclusão do último elemento.
O procedimento de inclusão de valores ou dados no meio de uma lista é um pouco
completo, mesmo que isso seja perdido, o indicado é incluir no final e depois fazer o
processo de ordenação.
Agora caso desejássemos excluir o número 67, teríamos que fazer uma movimen-
tação maior no vetor, pois temos que fazer as modificações em seus nós adjacentes.
Com a exclusão do elemento 67, os valores que estavam abaixo do mesmo não
sofrem modificações, entretanto os que tinham seus valores acima sim, pois além
de ocuparem uma nova posição as informações de anterior e próximo também são
modificados.
A exclusão de elementos é comum e muito útil, em um primeiro momento parece
ser trabalhoso, mas com o algoritmo certo isso se torna um processo fácil, por isso
em nosso último tópico desta aula vamos ver a implementação das funcionalidades
completas em uma lista.
8.3 Implementação de listas em linguagem C
#include <stdio.h>
#include <stdlib.h>
struct Node{
int num;
struct Node *prox;
};
typedef struct Node node;
int tam;
printf(“Lista vazia!\n\n”);
return ;
}
node *tmp;
tmp = LISTA->prox;
printf(“Lista:”);
while( tmp != NULL){
printf(“%5d”, tmp->num);
tmp = tmp->prox;
}
printf(“\n “);
int count;
for(count=0 ; count < tam ; count++)
printf(“ ^ “);
printf(“\nOrdem:”);
for(count=0 ; count < tam ; count++)
printf(“%5d”, count+1);
printf(“\n\n”);
}
void libera(node *LISTA)
{
if(!vazia(LISTA)){
node *proxNode,
*atual;
atual = LISTA->prox;
while(atual != NULL){
proxNode = atual->prox;
free(atual);
atual = proxNode;
}
}
}
void insere(node *LISTA)
{
int pos,
count;
printf(“Em que posicao, [de 1 ate %d] voce deseja inserir: “, tam);
scanf(“%d”, &pos);
if(pos>0 && pos <= tam){
if(pos==1)
insereInicio(LISTA);
else{
node *atual = LISTA->prox,
*anterior=LISTA;
node *novo=aloca();
for(count=1 ; count < pos ; count++){
anterior=atual;
atual=atual->prox;
}
anterior->prox=novo;
novo->prox = atual;
tam++;
}
}else
printf(“Elemento invalido\n\n”);
}
node *retiraInicio(node *LISTA)
{
if(LISTA->prox == NULL){
printf(“Lista ja esta vazia\n”);
return NULL;
}else{
node *tmp = LISTA->prox;
LISTA->prox = tmp->prox;
tam--;
return tmp;
}
}
node *retiraFim(node *LISTA)
{
if(LISTA->prox == NULL){
printf(“Lista ja vazia\n\n”);
return NULL;
}else{
node *ultimo = LISTA->prox,
*penultimo = LISTA;
while(ultimo->prox != NULL){
penultimo = ultimo;
ultimo = ultimo->prox;
}
penultimo->prox = NULL;
tam--;
return ultimo;
}
}
node *retira(node *LISTA)
{
int opt,
count;
printf(“Que posicao, [de 1 ate %d] voce deseja retirar: “, tam);
scanf(“%d”, &opt);
if(opt>0 && opt <= tam){
if(opt==1)
return retiraInicio(LISTA);
else{
node *atual = LISTA->prox,
*anterior=LISTA;
for(count=1 ; count < opt ; count++){
anterior=atual;
atual=atual->prox;
}
anterior->prox=atual->prox;
tam--;
return atual;
}
}else{
printf(“Elemento invalido\n\n”);
return NULL;
}
}
COMO fazer uma lista em C -Implementação completa (inserindo e retirando nós de qualquer posição). C progressivo
Fonte: https://www.cprogressivo.net/2013/10/Como-fazer-uma-lista-em-C.html
AULA 9
ESTRUTURAS DE
PILHAS E FILAS
Fonte: https://pixabay.com/pt/photos/livros-pilha-biblioteca-estantes-768426/
As estruturas de dados pilha e fila são consideradas com lista lineares, mas com
características próprias. Podemos abstrair o entendimento sobre a estrutura de uma
pilha como se fosse uma pilha de livros, um em cima do outro, mas se pegarmos os
mesmo livros e colocarmos todos um a frente do outro temos uma estrutura de lista.
As funções básicas de vistas em listas como incluir elementos, remover elementos
ou buscar estão presentes nessa estrutura. O que irá diferenciar essas listas das que
já vimos até o momento é o seu modo de manutenção, pois cada uma possui uma
característica própria.
Essas estruturas são muito utilizadas na busca de dados em uma árvore ou em um
grafo, assuntos estes que vamos estudar em aulas mais a frente, utilizamos o concei-
to de busca em largura e em profundidade associado a estrutura de um lista e pilha.
ANOTE ISSO
A busca em profundidade se utiliza de uma pilha e se inicia pelo nó raiz e desce pela
árvore até o último nível ou até encontrar uma folha, após isso, volta na estrutura da
árvore e vai visitando os demais nós até efetuar a busca completa.
A busca em largura se utiliza de uma fila e também se inicia pelo nó raiz e desce
para o próximo nó adjacente, a diferença é que olha todos os filhos do nó antes de
descer na árvore.
9.1 Filas
Fonte: https://br.freepik.com/vetores-gratis/fila-de-pessoas-em-pe-para-usar-atm_7416575.htm
Conceito de FIFO
Fonte: Adaptado pelo autor
Em uma lista linear, algumas das regras que devem ser seguidas nesse conceito de
FIFO são:
Os novos elementos sempre entram por último, no final da fila, mesmo se
essa estiver ordenada.
O elemento que é lido será o primeiro que entrou ou o que ocupa a primeira
posição.
O tempo de espera na lista depende do programa em execução.
A lista pode ficar vazia.
Prezado(a) aluno(a), agora que você sabe como funciona uma fila de modo teórico,
vejamos um código no qual foi implementado esse conceito, trata-se da Implementa-
ção da Estrutura FILA em linguagem C. Devemos lembrar que para estrutura de FILA
tem como base uma lista encadeada simples.
//FIFO
//Bibliotecas utilizadas
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <locale.h>
#include <unistd.h>
#define LIMPA_TELA system(“/usr/bin/clear”)
#endif
//Espera 3 segundos
#define ESPERA sleep(3)
void insere();
void exclui();
void mostra();
void mostra_erro();
main(){
setlocale(LC_ALL, “”); // ajuste do idioma
char escolha;
//Laço que irá mostrar o menu esperando uma opção (char)
do {
//Limpando a tela, e mostrando o menu
LIMPA_TELA;
printf(“\nMétodo Fila\n\n”);
printf(“Escolha uma opção: \n”);
printf(“\t1 - Inserir valor na Fila\n”);
printf(“\t2 - Remover valor da Fila\n”);
printf(“\t3 - Mostrar valores da Fila\n”);
printf(“\t9 - Sair\n\n”);
printf(“Resposta: “);
scanf(“%c”, &escolha);
switch(escolha) {
//Inserindo
case ‘1’:
insere();
break;
//Excluindo
case ‘2’:
if(principal!=NULL){
exclui();
}
else{
printf(“\nA Fila está vazia!\n”);
getchar();
}
break;
//Mostrando
case ‘3’:
if(principal!=NULL){
mostra();
}
else{
printf(“\nA Fila está vazia!\n”);
getchar();
}
break;
case ‘9’:
printf(“\nObrigado por utilizar esse programa!\n”);
printf(“------>Terminal de Informação<------\n\n”);
ESPERA;
exit(0);
break;
//Se foi algum valor inválido
default:
mostra_erro();
break;
}
//Impedindo sujeira na gravação da escolha
getchar();
}
while (escolha > 0); //Loop Infinito
}
//Inserção
void insere(){
int val;
LIMPA_TELA;
printf(“\nInserção: \n”);
printf(“--------------------------------------\n”);
printf(“Insira o valor a ser inserido: “);
scanf(“%d”,&val);
Dados *atual = (Dados*)malloc(sizeof(Dados));
printf(“\nValor inserido!\n”);
printf(“--------------------------------------”);
getchar();
}
//Exclusão
void exclui(){
Dados *auxiliar;
printf(“\nExclusão: \n”);
printf(“--------------------------------------\n”);
//o auxiliar será o próximo da principal
auxiliar=principal->proximo;
//limpando a principal
free(principal);
//a principal será a auxiliar
principal=auxiliar;
printf(“\nValor excluido!\n”);
printf(“--------------------------------------”);
getchar();
}
//Mostrando
void mostra(){
int posicao=0;
Dados *nova=principal;
LIMPA_TELA;
printf(“\nMostrando valores: \n”);
printf(“--------------------------------------\n”);
//laço de repetição para mostrar os valores
for (; nova != NULL; nova = nova->proximo) {
posicao++;
Fonte: https://terminaldeinformacao.com/2013/07/23/entendendo-pilha-e-fila/
9.2 Pilhas
Fonte: https://br.freepik.com/fotos-gratis/pilha-de-varios-livros-sobre-uma-mesa_991451.htm
Como exemplo, pode-se utilizar o conceito de uma pilha de sacos de arroz em uma
prateleira de supermercado, onde estes são empilhados, conforme cada cliente retira
um pacote a pilha diminui, os pacotes são retirados sempre do topo da pilha, quando
alguém for abastecer (incluir) mais pacotes de arroz, estes serão empilhados um em
cima do outro no topo.
Como em filas o sentido da leitura poderá variar com base no programa implemen-
tando, sendo o Início a esquerda ou direita, mas o processo de inclusão de elementos
sempre será o mesmo.
Prezado(a) aluno(a), agora que você sabe como funciona uma pilha de modo teó-
rico, vejamos um código no qual foi implementado esse conceito, trata-se da Imple-
mentação da Estrutura PILHA em linguagem C. Devemos lembrar que para estrutura
de PILHA tem como base uma lista encadeada simples.
//LIFO
//Bibliotecas utilizadas
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <locale.h>
//Espera 3 segundos
#define ESPERA sleep(3)
void insere();
void exclui();
void mostra();
void mostra_erro();
main(){
setlocale(LC_ALL, “”); // ajuste do idioma
char escolha;
//Laço que irá mostrar o menu esperando uma opção (char)
do {
//Limpando a tela, e mostrando o menu
LIMPA_TELA;
printf(“\nMétodo Pilha\n\n”);
printf(“Escolha uma opção: \n”);
printf(“\t1 - Inserir valor na Pilha\n”);
printf(“\t2 - Remover valor da Pilha\n”);
printf(“\t3 - Mostrar valores da Pilha\n”);
printf(“\t9 - Sair\n\n”);
printf(“Resposta: “);
scanf(“%c”, &escolha);
switch(escolha) {
//Inserindo
case ‘1’:
insere();
break;
//Excluindo
case ‘2’:
if(principal!=NULL){
exclui();
}
else{
printf(“\nA Pilha está vazia!\n”);
getchar();
}
break;
//Mostrando
case ‘3’:
if(principal!=NULL){
mostra();
}
else{
printf(“\nA Pilha está vazia!\n”);
getchar();
}
break;
case ‘9’:
printf(“\nObrigado por utilizar esse programa!\n”);
printf(“------>Terminal de Informação<------\n\n”);
ESPERA;
exit(0);
break;
//Se foi algum valor inválido
default:
mostra_erro();
break;
}
//Impedindo sujeira na gravação da escolha
getchar();
}
while (escolha > 0); //Loop Infinito
}
//Inserção
void insere(){
int val;
LIMPA_TELA;
printf(“\nInserção: \n”);
printf(“--------------------------------------\n”);
printf(“Insira o valor a ser inserido: “);
scanf(“%d”,&val);
//gerando a posição atual
Dados *atual = (Dados*)malloc(sizeof(Dados));
atual -> valor = val;
//próximo do atual será a principal
atual -> proximo = principal;
//principal volta a ser a atual
principal = atual;
printf(“\nValor inserido!\n”);
printf(“--------------------------------------”);
getchar();
}
//Exclusão
void exclui(){
Dados *auxiliar;
printf(“\nExclusão: \n”);
printf(“--------------------------------------\n”);
//guardando o valor depois da principal
auxiliar=principal->proximo;
//limpando a principal
free(principal);
//a principal será a auxiliar
principal=auxiliar;
printf(“\nValor excluido!\n”);
printf(“--------------------------------------”);
getchar();
}
//Mostrando
void mostra(){
int posicao=0;
Dados *nova=principal;
LIMPA_TELA;
printf(“\nMostrando valores: \n”);
printf(“--------------------------------------\n”);
//laço de repetição para mostrar os dados
for (; nova != NULL; nova = nova->proximo) {
posicao++;
printf(“Posição %d, contém o valor %d\n”, posicao, nova->valor);
}
printf(“--------------------------------------”);
getchar();
}
//Mostra erro de digitação
void mostra_erro(){
LIMPA_TELA;
printf(“\nErro de Digitação: \n”);
printf(“--------------------------------------\n”);
printf(“\nDigite uma opção válida (pressione -Enter- p/ continuar)!\n\n”);
printf(“--------------------------------------”);
getchar();
}
Fonte: https://terminaldeinformacao.com/2013/07/23/entendendo-pilha-e-fila/
AULA 10
ÁRVORES BINÁRIA E
ESTRITAMENTE BINÁRIA
Fonte: https://br.freepik.com/fotos-gratis/3d-rendem-de-uma-arvore-de-carvalho-na-paisagem-graminea-2701_1036771.htm
Acredito eu que você já deve ter visto alguma árvore na sua vida :) Em um ambiente
computacional uma árvore é representada de modo visual bem parecido, inclusive
adota alguns termos usados em uma árvore do mundo real, a diferença está no for-
mato de implementação, visto que sua estrutura é aplicada de modo binário. Segundo
Tenenbaum (1995, p. 303):
Uma árvore binária tem no máximo 2 filhos a partir do pai, outra característica im-
portante é que a árvore binária computacional é vista e montada de ponta-cabeça
se comparado com uma árvore normal, ou seja, a raiz fica na extremidade superior e
seus galhos e folhas são expostos de cima para baixo, conforme a imagem a seguir:
Podemos agora identificar qual o nível da árvore, seus nós e folhas e também quais
são os pais e seus filhos:
Descrição Elemento
Nós no nível 0 A
Nós no nível 1 B-C
Nós no nível 2 D-E-F
Nós no nível 3 G-H-I
Nó Raiz A
Nós Pai A-B-C-E-F
Nós Folhas D- G-H-I
Fonte: elaborado pelo autor
B A D E No Pai
C A - F No Pai
D B - - Folha
E B G - No Pai
F C H I No Pai
G E - - Folha
H F - - Folha
I F - - Folha
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
int arvore[21];
int i,j,aux;
int resp=0,troca,busca;
int menu (){
system(“cls”);
printf(“\n____________________________________________________________\n\n”);
printf(“\t\t|------------->MENU<-----------|”);
printf(“\n\t\t|______________________________|\n”);
printf(“\t\t| 1 - Inserir numeros |\n”);
printf(“\t\t| 2 - Exibir numeros inseridos |\n”);
printf(“\t\t| 3 - Exibir arvore binaria |\n”);
printf(“\t\t| 4 - Sair |”);
printf(“\n\t\t|______________________________|\n”);
printf(“\t\t\n\t\t\t Digite sua opcao: \a”);
scanf(“%d”,&resp);
return resp;
}
void comandos(int resp){
if(resp==0){ resp = menu();}
switch(resp){
//insere numeros
case 1:
system(“cls”);
printf(“\n______________________________________________________\n\n”);
printf(“ -->>Informe os números a serem inseridos na arvore\n\n”);
for (i=0; i<15; i++){
printf(“\t\tPosicao %d : “,i+1);
scanf(“%d”,&arvore[i]);
//arvore[i]=rand()%100;
}
printf(“\n__________________________________________________________\n\n”);
comandos(0);
break;
case 2:
system(“cls”);
printf(“\n__________________________________________________\n\n”);
printf(“ -->>Elementos no vetor da arvore!\n\n”);
for (i=0; i<15; i++){
printf(“\n\t -->> POSICAO %d = %d”,i,arvore[i]);
}
printf(“\n______________________________________________________________\n\n”);
system(“pause”);
comandos(0);
break;
case 3:
system(“cls”);
printf(“\n___________________________________________________________\n\n”);
printf(“\n\n -->>ARVORE BINARIA!\n\n”);
printf(“ %d\n”,arvore[0]);
printf(“ / \\ \n”);
printf(“ / \\ \n”);
printf(“ / \\ \n”);
printf(“ / \\ \n”);
printf(“ / \\ \n”);
printf(“ / \\ \n”);
printf(“ / \\ \n”);
printf(“ %d %d \n”,arvore[1],arvore[2]);
printf(“ / \\ / \\ \n”);
printf(“ / \\ / \\ \n”);
printf(“ / \\ / \\ \n”);
printf(“ %d %d %d %d \n”,arvore[3],arvore[4],arvore[5],arvore[6]);
printf(“ / \\ / \\ / \\ / \\ \n”);
printf(“ %d %d %d %d %d %d %d %d \n”,arvore[7], arvore[8],arvore[9],arvo-
re[10], arvore[11],arvore[12],arvore[13], arvore[14]);
printf(“\n___________________________________________________________\n\n”);
system(“pause”);
comandos(0);
break;
//finaliza
case 4:
resp = 0;
system(“cls”);
printf(“\n_________________________________________________\n”);
printf(“ -->> Ate Logo!”);
printf(“\n______________________________________________________\n\n\n”);
}
}
main(){
for (i = 0; i<21; i++){
arvore[i] = i;
}
comandos(0); }
Inclusão de 15 elementos
Vejamos um exemplo.
Podemos observar na imagem que todos os nós PAI têm dois filhos (esquerda ou
direita), exceto os nós folhas que não possuem filhos. Caso algum dos nós folha (C -
D - F - G) tivesse pelo menos 1 um filho, esta árvore já não seria estritamente binária.
Em uma árvore estritamente binária é possível encontrar a quantidade de nós uti-
AULA 11
ÁRVORE BINÁRIA COMPLETA
E IMPLEMENTAÇÃO
EM LINGUAGEM C
Fonte: https://br.freepik.com/fotos-gratis/foto-de-grande-angular-de-uma-unica-arvore-crescendo-sob-um-ceu-nublado-durante-um-por-do-sol-cercado-por-
grama_11342065.htm
Olá, prezado(a) aluno(a)! Vimos na aula anterior o conceito sobre a estrutura de ár-
vores binárias, seus princípios básicos sobre os nós (pai) e seus nós adjacentes (filho
a esquerda ou direita). Também observamos a estrutura de uma árvore estritamente
binária e suas características.
Além da estrutura de uma árvore binária comum é estritamente binária podemos
ter a estrutura de uma árvore completa, conforme veremos a seguir.
Devemos lembrar que o nó raiz de uma árvore binária está localizado no nível 0.
A partir do nó raiz temos os nós adjacentes, onde podemos dizer que estamos des-
cendo na estrutura, conforme vamos descendo os níveis vão aumentando um a um.
A altura de uma árvore corresponde a profundidade do nó raiz até o mais baixo, isso
equivale ao tamanho do percurso mais distante da raiz até uma folha.
Podemos observar que no último nível temos apenas nós folhas, sendo os elemen-
tos H, I J, K, L, M, N e O, no nível superior temos todos nós PAI com todos dois filhos
(esquerda e direita) caracterizando uma árvore estritamente binária. Vale lembrar que
caso seja incluído qualquer outro elemento sendo filho de uma folha, esta árvore já
não se torna mais completa nem estritamente binária, sendo uma árvore comum.
O nosso primeiro passo é criar a estrutura de nós e as informações que serão ar-
mazenadas, como também fazer a declaração da estrutura como um vetor para ar-
mazenar as posições.
//Estrutura do nó
typedef struct str_no {
char dado;
int FilhoEsq;
int FilhoDir;
int pai;
} str_no;
Após criarmos a estrutura do nó, devemos criar a função que irá fazer a inclusão
dos dados do nó na árvore, sendo necessário informar quem é o seu pai, o valor a ser
armazenado e se ele é um filho esquerda ou direita, como também identificar se o nó
inserido é o raiz.
//Inserir nó
void InserirElemento(int pai, char dado, int lado){
switch (lado){
case E:
arvore[pai].FilhoEsq = indice;
arvore[indice].dado = dado;
arvore[indice].pai = pai;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
case D:
arvore[pai].FilhoDir = indice;
arvore[indice].dado = dado;
arvore[indice].pai = pai;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
case R:
arvore[indice].dado = dado;
arvore[indice].pai = -1;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
}
}
//Inserir nó
void InserirElemento(int pai, char dado, int lado){
switch (lado){
case E:
arvore[pai].FilhoEsq = indice;
arvore[indice].dado = dado;
arvore[indice].pai = pai;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
case D:
arvore[pai].FilhoDir = indice;
arvore[indice].dado = dado;
arvore[indice].pai = pai;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
case R:
arvore[indice].dado = dado;
arvore[indice].pai = -1;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
}
}
//Procura nó
int ProcurarElemento(char dado){
int i;
if (indice != 0){
for (i = 0; i<indice; i++){
if (arvore[i].dado == dado) {
return (i);
}
}
}
else {
return (0);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
//Estrutura do nó
typedef struct str_no {
char dado;
int FilhoEsq;
int FilhoDir;
int pai;
} str_no;
//Constantes
#define tamanho 100
#define E 0
#define D 1
#define R -1
//Variáveis
struct str_no arvore[tamanho];
int lado,indice=0;
int opt=-1;
char pai, no;
//Procura nó
int ProcurarElemento(char dado){
int i;
if (indice != 0){
for (i = 0; i<indice; i++){
if (arvore[i].dado == dado) {
return (i);
}
}
}
else {
return (0);
}
}
//Inserir nó
void InserirElemento(int pai, char dado, int lado){
switch (lado){
case E:
arvore[pai].FilhoEsq = indice;
arvore[indice].dado = dado;
arvore[indice].pai = pai;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
case D:
arvore[pai].FilhoDir = indice;
arvore[indice].dado = dado;
arvore[indice].pai = pai;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
case R:
arvore[indice].dado = dado;
arvore[indice].pai = -1;
arvore[indice].FilhoEsq = -1;
arvore[indice].FilhoDir = -1;
indice++;
break;
}
}
//Função principal
int main(void){
setlocale(LC_ALL, “”); // ajuste do idioma
int temp;
do {
menu_mostrar();
scanf(“%d”, &opt);
fflush(stdin);
switch (opt){
case 1:
printf(“\nInforme qual é o Elemento PAI (deixar em
Branco caso for a Raiz): “);
scanf(“%c”,&pai);
fflush(stdin);
//scanf(“%c”,&pai);
printf(“Digite o elmento do NO: “);
scanf(“%c”,&no);
fflush(stdin);
//scanf(“%c”,&no);
printf(“Digite o lado da subarvore \n Filho a Esquer-
da = %d \n Filho a Direita = %d \n Nó Raiz = %d: “, E,D,R);
scanf(“%d”,&lado);
fflush(stdin);
temp = ProcurarElemento(pai);
InserirElemento(temp,no,lado);
break;
case 2:
printf(“Digite o Elemento a ser pesquisado: “);
scanf(“%c”,&no);
fflush(stdin);
//scanf(“%c”,&no);
temp = ProcurarElemento(no);
printf(“No %c\nFilho Esquerda: %c \nFilho Direita:
%c\n\n”, arvore[temp].dado, arvore[arvore[temp].FilhoEsq].dado, arvore[arvo-
re[temp].FilhoDir].dado);
system(“pause”); break;
}
}while (opt!=0);
system(“pause”);
return(0);
}
AULA 12
MONTAGEM DE ÁRVORES
BINÁRIAS E BUSCAS EM
ÁRVORES BINÁRIAS
Fonte: https://br.freepik.com/fotos-gratis/parque-verde_1278420.htm
Vimos em aulas anteriores a estrutura em árvore e como que é utilizada para orga-
nizar os dados, sendo considerada uma excelente estrutura pelo fácil acesso às infor-
mações, estamos falando da árvore binária. Observamos também as características
de várias estruturas, tais como a árvore binária simples, a completa, a estritamente
binária. Acredito que você deve ter se relembrando como são estas estruturas e tam-
bém revistos os algoritmos de implementação sobre a criação de uma árvore binária.
Ao analisar os algoritmos anteriores podemos associar que os dados que são armaze-
nados em uma árvore binária, podem ser derivados de outras estruturas, além da en-
trada de dados comum a ser digitada pelo usuário, dentre estas outras possibilidades
temos um vetor. Conforme visto em aulas anteriores em um vetor é possível armaze-
nar informações em uma única variável, guardando os dados em posições no vetor.
Agora que você relembrou o que são as árvores binárias e também o que são veto-
res, vamos aprender a montar uma árvore binária a partir de um vetor, estando este or-
denado ou não. Vamos utilizar como exemplo um vetor de 10 posições não ordenado.
Como já visto, uma árvore binária é composta por um nó raiz e seus adjacentes, en-
tão, nosso primeiro passo é identificar no vetor o elemento raiz. Um vetor é composto
por posições, sendo assim, devemos encontrar a posição do elemento raiz, para isso
vamos utilizar um cálculo simples para este fato, iniciando encontrar o meio do vetor
na busca binária. Devo lembrar que um vetor se inicia com a posição 0 e termina com
o tamanho do vetor-1, no caso de nosso vetor de dez posições a posição final seria 9
(10 -1 = 9).
Ao final do processo o valor encontrado foi 4,5, como um vetor só possui posições
representadas por números inteiros, o valor após a vírgula é ignorado sendo assim a
posição do meio será 4, ao analisar o vetor veremos que o número 35 ocupa a 4ª po-
sição, assim encontramos o nosso nó raiz.
Agora que nossa árvore possui um nó raiz, vamos dar andamento na inclusão dos
demais elementos do vetor para a árvore binária. Agora que já incluímos a nota raiz,
vamos retornar para a posição 0 do vetor. Assim, iniciamos uma comparação do ele-
mento na posição 0 com a raiz, caso o valor armazenado seja maior que o elemento
raiz, ele será uma subárvore à direita, caso seja menor, será uma subárvore à esquer-
da. Vejamos o exemplo a seguir:
Ao analisar o elemento alocado nessa posição é o 2, como este é menor que a raiz,
então este é alocado para esquerda, entretanto como no lado esquerdo já temos um
elemento armazenado na subárvore devemos descer mais um nível.
Ao analisar o elemento alocado na segunda posição é o 15, que é menor que a raiz,
mas maior que o nó seguinte que é 7. Nesse caso, vamos inserir o elemento à direita
do nó 7, um filho à direita do nó 7
O elemento na posição três do vetor é o 52, nesse caso, ele é maior que a raiz, as-
sim, é alocado para subárvore direita da raiz:
Vamos efetuar o processo de comparação dos elementos no vetor, até que este
seja inteiramente lido e incluído os elementos na árvore binária. Vejamos como a ár-
vore binária ficará após todos os elementos inseridos.
Ao analisar a árvore binária montada é possível identificar que possui três níveis e
quatro nós folhas (5, 13, 50, 95), repare, inclusive, que ela não é uma árvore completa
ou estritamente binária.
Agora que já conhecemos a estrutura e sabemos como montar uma árvore binária,
vamos compreender como efetuar uma busca nesta estrutura. Compreenda que bus-
car um elemento dentro dessa estrutura significa navegar pelos nós e seus pontos de
ligação.
Para simplificar o nosso processo de busca será utilizada a árvore binária montada
anteriormente. O procedimento de busca em estrutura de árvores binária é bem sim-
ples: se elege o número a ser buscado e inicia a verificação pelo nó raiz, onde é ana-
lisado se o elemento a ser encontrado é maior ou menor que a raiz, caso seja maior
que a raiz, todo lado esquerdo é descartado, se não, todo o lado direito é descartado.
Este processo de eliminação de galhos da árvore acontece de modo recursivo, a cada
iteração no próximo nó.
Dando sequência com a eliminação do lado direito, vamos à busca descendo pelo
lado esquerdo da árvore até o próximo nó, onde se efetua novamente a comparação
do elemento do nó com o que estamos buscando, neste caso chegamos ao nós 7,
como 15 é maior que 7 todo o lado esquerdo a partir do nó 7 é eliminado também.
Dando andamento, vamos descer novamente para mais um nível, desta vez, para a
direita do nó 7, chegando até o nó 15, onde é verificado que este se trata do valor de
busca em questão, assim, a busca se encerra. Por fim, podemos dizer que o caminho
percorrido para encontrar o elemento 15 na árvore binária foi 35 -> 7 -> 15, sendo utili-
zados apenas 3 iterações para encontrá-lo.
Caso o número buscado fosse 38 o processo de busca iria iniciar normalmente, pois
o sistema computacional não sabe se este valor estará armazenado ou não, após 4 ite-
rações o algoritmo para e indica que o valor não foi encontrado. Mas por que 4 iterações:
1ª Iteração: é comparado se o valor 38 é maior ou menor que a raiz, neste caso
desloca-se para a direita.
2ª Iteração: é comparado se o valor 38 é maior ou menor que 52, neste caso deslo-
ca-se para a esquerda.
3ª Iteração: é comparado se o valor 38 é maior ou menor que 40, neste caso deslo-
ca-se para a esquerda.
4ª Iteração: ao tentar se deslocar novamente o algoritmo irá identificar que não exis-
te elemento de comparação, parando a execução e informando número não encontrado.
Lembrando que é possível criar a estrutura de uma árvore binária em listas ou vetores,
mas este tipo de busca apresentada é diferente, pois em listas ou vetores onde é efetua-
do um laço de repetição para percorrer estes por completo desde o vetor 0 até o último,
comparando os elementos armazenados sem a eliminação de trechos do mesmo. Deste
modo, o processo de busca apresentado é diferente quando trabalhamos diretamente na
estrutura em árvore.
AULA 13
TÉCNICAS DE ORDENAÇÃO
BUBBLESORT E INSERTIONSORT
Fonte: https://br.freepik.com/fotos-gratis/postura-plana-de-arranjo-de-lapis-coloridos_6864396.htm
Prezado(a) aluno(a), vamos imaginar uma caixa de lápis de cores, onde temos apro-
ximadamente 25 lápis, cada cor tem uma numeração e desejamos colocar o mesmo
em ordem do menor para maior número podemos utilizar alguns procedimentos para
fazer este processo de ordenação, no final o nosso objetivo é ter os lápis ordenados e
organizados. No mundo computacional temos por vezes o mesmo problema, dados
armazenados de modo desordenado e desejamos deixar estes em ordem, o que mais
se busca no ambiente computacional é uma melhora constante de desempenho, isso
significa procurar desenvolver técnicas que melhorem o desempenho dos algoritmos.
As técnicas e algoritmos de ordenação são de grande importância para efetuar
processos massantes e desgastantes de ordenação de estruturas de dados. Você irá
13.1 BubbleSort
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
main(){
13.2 InsertionSort
AULA 14
TÉCNICAS DE ORDENAÇÃO
SELECTIONSORT E QUICKSORT
Fonte: https://www.freeimages.com/pt/photo/abstract-1182296
#include <stdio.h>
#include <conio.h>
main(){
int X[5], j, i, select, menor, troc;
// inserindo os números no vetor
for(i=0; i<=4; i++){
printf(“Digite um numero:”);
scanf(“%d”, &X[i]);
}
printf(“\n\nVetor antes do metodo SelectionSort\n”);
for(i=0;i<=4;i++){
printf(“Vetor %d: %d \n”, i, X[i]);
}
// ordenando de forma crescente
for(i=0;i<=3;i++){
select = X[i];
menor = X[i+1];
troc = i+1;
for(j=i+1;j<=4;j++){
if(X[j] < menor) {
menor = X[j];
troc=j;
}
}
if(menor < select){
X[i]=X[troc];
X[troc] = select;
}
}
printf(“\n\nVetor depois do metodo SelectionSort\n”);
// monstrando o vetor ordenado
for(i=0;i<=4;i++){
printf(“Vetor %d: %d \n”, i, X[i]);
}
}
Script em linguagem C, SelectionSort
Fonte: elaborado pelo autor
Este método poderá percorrer toda a estrutura para descobrir que têm algum ele-
mento menor poderá acontecer também que o mesmo percorra toda a estrutura não
encontre um elemento menor, isso faz que aconteça uma processamento desneces-
sário. Este método executa N operações, onde N representa o número de elementos
do a serem analisados. No pior caso, são feitas operações. A complexidade desse al-
goritmo é de ordem quadrática, igualmente ao InsertionSort e BubbleSort, onde temos
os valores de seu resultado são proporcionais ao quadrado do valor do seu argumento.
O método Quicksort aplica uma técnica de ordenação diferente das que vimos nas
aulas anteriores, sendo considerado o algoritmo de ordenação mais utilizado no mun-
do por não diferenciar o tamanho do vetor ou se este está parcialmente ordenado ou
não. O método foi criado em 1960 pelo cientista britânico Sir Charles Antony Richard
Hoare e publicado em 1962 com várias melhorias. Segundo Cormem (2012), a lógica
deste método é dividir a estrutura em partes menores para depois ordenar, também é
conhecido por classificação por troca de partição.
Podemos observar que no segundo exemplo o elemento escolhido foi o 5, após isso
foi sendo separado os elementos até que todo o vetor fique ordenado, a cada lista or-
denada os elementos eram realocados. O processo de ordenação pode ser realizado
toda a esquerda e depois a direita, isso não irá interferir no processo, visto que ambos
os lados vão ter praticamente o mesmo tamanho sempre.
Esse algoritmo tem uma complexidade maior por conta da separação, entretanto,
é mais eficaz do que os já apresentados, nos quais é efetuada a comparação sempre
em dois elementos do vetor, caso o elemento de comparação seja menor se efetua
a troca, nesses métodos, a lista sempre vai ser lida por completo várias vezes. No
método QuickSort, o vetor ou lista já irá ser separada de forma ordenada. Vejamos o
exemplo desse método em linguagem C.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void quick_sort (int *vet, int total) {
int i, j, p, t;
if(total < 2)
return;
int X[5], i;
AULA 15
TÉCNICAS DE ORDENAÇÃO
SHELLSORT E MERGESORT
Fonte: https://br.freepik.com/vetores-gratis/fundo-de-formas-abstratas-brancas_12628435.htm
concha (shell, em inglês), mas sendo na verdade o nome de seu criador. Esse algorit-
mo se utiliza da ordenação por inserção. Esta técnica de ordenação seria a junção de
duas técnicas vistas QuickSort e do InsertionSort, pois é dividido o vetor em sublistas,
depois aplica a busca e inserção de elementos na mesma.
Método ShellSort
Fonte: Adaptado pelo autor
#include <stdio.h>
#include <conio.h>
main(){
int X[8], j, i, value, gap = 1, size;
// inserindo os números no vetor
for(i=0; i<=7; i++){
printf(“Digite um numero:”);
scanf(“%d”, &X[i]);
}
printf(“\n\nVetor antes do metodo ShellSort\n”);
for(i=0;i<=7;i++){
printf(“Vetor %d: %d\n”, i, X[i]);
}
// ordenando de forma crescente
while(gap < 8) {
gap = 3*gap+1;
}
while ( gap > 1) {
gap /= 3;
for(i = gap; i <=7; i++) {
value = X[i];
j = i - gap;
while (j >= 0 && value < X[j]) {
X[j + gap] = X[j];
j -= gap;
}
X[j + gap] = value;
}
}
printf(“\n\nVetor depois do metodo ShellSort\n”);
// mostrando o vetor ordenado
for(i=0;i<=7;i++){
printf(“Vetor %d: %d\n”, i, X[i]);
}
}
#include <stdio.h>
#include <conio.h>
{
meio = (inicio+fim)/2;
merge (X,inicio, meio);
merge (X,meio+1, fim);
intercala(X,inicio, fim, meio);
}
}
main(){
int X[8], i;
for(i=0; i<=7; i++){
printf(“Digite um numero:”);
scanf(“%d”, &X[i]);
}
printf(“\n\nVetor antes do metodo MergeSort\n”);
for(i=0;i<=7;i++){
printf(“Vetor %d: %d\n”, i, X[i]);
}
// ordenando de forma crescente
merge(X,0,7);
printf(“\n\nVetor depois do metodo MergeSort\n”);
// mostrando o vetor ordenado
for(i=0;i<=7;i++){
printf(“Vetor %d: %d\n”, i, X[i]);
}
}
O método MergeSort foi criado em 1945 pelo matemático húngaro chamado John.
Von Newmann nome este presente até hoje no mundo da computação ao nos refe-
rirmos sobre a arquitetura computacional de um computador e sobre o processamen-
to de dados. Mesmo este sendo um método com um bom desempenho em vetores
não muito grandes, sua implementação pode ser ineficiente ao compararmos com
outros métodos como o Bubblesort e Selectionsort.
AULA 16
TEORIA DOS GRAFOS
Fonte: https://br.freepik.com/vetores-gratis/fundo-de-tecnologia-com-linhas-de-conexao-digital-low-poly_12572903.htm
A utilização da estrutura de um grafo vai muito além do que estes pequenos exem-
plos apresentados, por isso nesta aula vamos conhecer um pouco da estrutura de um
Para iniciarmos a nossa conversa sobre a teoria dos Grafos devemos conhecer um
pouco da história de onde surgiu a ideia desta estrutura, que na verdade a Teoria dos
Grafos surgiu por uma acaso em 1736, quando o matemático e físico suíço Leonhard
Paul Euler (1707 - 1783), por meio do seu artigo Solutio problematis ad geometriam
situs pertinentes, apresentou uma proposta como solução de um famoso conto so-
bre as Sete pontes de Königsberg.
Na região de Königsberg (atual Kaliningrado) que é contada pelo Rio Prególia, divi-
dindo partes das cidades em duas grandes ilhas e duas faixas continentais. Na época
havia 7 pontes que interligam toda a cidade, dizia ser possível atravessar todas as sete
pontes de Königsberg sem repetir nenhuma delas no trajeto.
Diversas pessoas tentaram solucionar este enigma, mas infelizmente sem sucesso
por anos e na verdade até hoje não teve uma solução para isso. Leonhard Paul Euler
provou matematicamente que não era possível chegar em todas as regiões usando
todas as 7 pontes e passando por elas uma única vez. Para seu entendimento e dos
demais Euler elaborou uma figura onde considerava cada ponto da imagem uma por-
ção de terra e as pontes por linhas que interligam os pontos, eis que surgiu a forma
mais antiga de um grafo registrado.
Segundo o estudo realizado por Euler, o mesmo notou que o mesmo possui algu-
mas características sobre a quantidade de linhas que interligam cada ponto, chegan-
do a conclusão que só seria possível percorrer todo o trajeto atravessando cada uma
das pontes uma única vez caso houvesse exatamente zero ou dois pontos onde tives-
sem um número ímpar de arestas. Para compreender vejamos o exemplo a seguir da
teoria de Euler:
Com base no Grafo apresentado vamos perceber que temos zero pontos que têm
a ligação por um número ímpar de linhas, todos os pontos tem um número par de co-
nexões. Assim podemos percorrer todo o Grafo em um sentido único e não repetindo
nenhuma linha, saindo do ponto 1:
Início 1 -> 2 -> 3 ->4 -> 1 Chegada.
Neste segundo exemplo notamos que o Grafo apresentado tem no máximo pontos
com interligações que têm um número ímpar de linhas, exatamente os pontos 2 e 4
tem o número ímpar todos os pontos tem um número par de conexões. Com o número
ímpar de arestas conexões é possível entrar e sair usando duas delas, sobrando uma
terceira. Um ponto de observação é que uma dessas conexões será obrigatoriamente
o início e outra o final do trajeto e o ponto de início deve ser uma destas conexões
ímpares.
Início 4 -> 1 -> 2 ->3 -> 4->2 Chegada. Utilizamos todas as conexões apenas 1 única
vez e visitamos todos os pontos.
No grafo originalmente elaborado por Euler, vamos notar que todos os pontos pos-
suem conexões ímpares, por isso é impossível percorrer todos os pontos sem repetir
alguma conexão, Euler conseguiu comprovar isso com a sua teoria e explicações.
Agora que você já sabe a história de onde surgiu o conceito de Grafos, mas conhe-
cer um pouco mais sobre esta estrutura para a visão computacional. Na teoria dos
grafos teremos a representação Gráfica de um Grafo contendo vértices (ponto ou local
de armazenamento de dados), em geral, representado por meio de um círculo, como
também por arestas que são as linhas de conexão entre dois vértices.
De modo geral, um Grafo é uma estrutura G = (V,A), onde V é um conjunto de vértices
(nós) e A é um conjunto de arestas (arcos). O Conjunto de V são todos os nós do Grafo,
o conjunto de A e interligação de todos os nós A = {(F,H)}. O que devemos compreender
é que uma aresta está contida em um conjunto de Vértices ((F,H) ∈ V) e que cada aresta
está contida em um conjunto de Arestas (A ∈ A). Agora que você já sabe como fazer a
apresentação forma de um grafo, vamos analisar o Grafo elaborado por Euler.
Podemos ter algumas variações dos tipos de Grafo, por exemplo, este pode ter setas
que indiquem a direção das arestas, este é considerado um grafo orientado (ou dígrafo).
Grafo Orientado
Fonte: elaborado pelo autor
Ao observar o Grafo apresentado notamos que as setas dão o sentido de onde po-
de-se navegar no Grafo, além disso este Grafo apresenta outros aspectos sendo: não
é um Grafo fechado, temos Vértices que ao chegar no mesmo não temos direciona-
mento para nenhum outro como 5 e 4, Vértices que são inacessíveis como o vértice 2,
pois não temos arestas indicando o caminho, por fim o caso do vértice 1 que temos
uma seta que liga a ele mesmo como se representa-se um loop. Vejamos a apresen-
tação formal deste Grafo.
G = (V, A)
V = {1, 2, 3, 4, 5, 6}
A = {(1,1), (1,3), (2,1), (2,3), (3,4), (3,5), (3,6), (6,4)}
Podemos ter um Grafo conexo quando é possível partir de um vértice V por meio
de suas arestas incidentes chegar até o mesmo, conforme os já apresentados. En-
tretanto, podemos ter também um grafo é dito desconexo, quando algum vértice é
totalmente inacessível, nenhuma aresta sai ou chega ao mesmo.
Grafo Desconexo
Fonte: elaborado pelo autor
Vimos até agora que é possível desenhar um grafo a partir de um conhecido con-
junto G = (V,A), mas estes só apresentam linhas e setas, além destas estrutura de
Grafos, existem variações. Por exemplo, existem os grafos mistos, que onde o mesmo
pode ter arestas orientadas e não orientadas, podendo ser inclusive desconexo, como
por fim este pode ser ponderado (rotulado), onde as arestas recebem pesos.
CONCLUSÃO
Prezado(a) aluno(a)! É com muita satisfação que apresentamos neste material alguns
temas sobre estrutura de dados e classificação de dados. O conhecimento sobre estru-
tura de dados e alguns de seus pontos são fundamentais para o desenvolvimento de
softwares e tecnologias em inovação. Esse conhecimento se faz importante em sua vida
acadêmica e profissional.
Com essa abordagem, vamos relembrar o que estudamos e os pontos principais do
material.
Abordamos inicialmente o conceito de linguagem C, vimos que ela é considerada a
mãe de várias outras linguagens de alto nível como o JAVA, PHP, DELPHI, entre outras.
Aprendemos que a linguagem C foi utilizada por muitos anos no desenvolvimento de sof-
twares, por isso, estudamos os seus conceitos básicos e suas principais funcionalidades,
o que dá a você conhecimento necessário para desenvolver seus primeiros scripts em
linguagem C. Vimos também o que são variáveis, funções e expressões, desvios condicio-
nais simples em compostos ( IF e ELSE ) e alguns dos tipos de laços de repetição como o
FOR, WHILE, DO WHILE, entre diversas funcionalidades.
Conhecemos algumas das formas de estrutura de dados mais utilizadas, como as lis-
tas lineares, que podem ser do tipo encadeadas, duplamente encadeadas, circulares, entre
outras. Vimos também as estruturas em árvores binárias, uma das formas mais fáceis de
se acessar uma informação ou encontrar um elemento. Outras duas estruturas de grande
importância são as de Pilha e Fila, também conhecidas como FIFO e LIFO.
Foram trabalhados conceitos mais avançados com a ordenação dos elementos no
vetor, utilizando os métodos do BubbleSort (método bolha), MergeSort, QuickSort, entre di-
versos outros. Apresentamos a você alguns dos mais utilizados, mas ainda temos vários
para explorar. Nossa intenção foi fazer com que você vivenciasse na prática a manipu-
lação dessas estruturas de dados, por isso, sempre buscamos em cada unidade trazer
exemplos de implementações dos conteúdos em linguagem C.
Foi apresentado também como montar uma árvore binária a partir de um vetor, sendo
esse ordenado ou não e também como efetuar a busca em árvores binárias.
Por fim, abordamos as informações relevantes sobre a teoria dos Grafos e suas aplica-
ções, como este conceito surgiu suas características e representação.
Esperamos ter alcançado nosso objetivo em passar nosso conhecimento a você, ca-
ro(a) aluno(a). Desejamos que você seja muito feliz ao percorrer o mundo profissional.
Muito sucesso e paz!
ELEMENTOS COMPLEMENTARES
LIVRO
C Completo e Total
Autor: Herbert Schildt
Edição: São Paulo, Makron Books, 1996
Resumo: Herb Schildt, cujos livros sobre C e C++
venderam mais de um milhão de cópias em todo
mundo, revisou e atualizou a melhor referência
que você pode ter sobre C. Não importa se você é
programador iniciante ou um profissional veterano, as
respostas a todas as suas perguntas sobre C podem
ser encontradas nesta obra.
LIVRO
WEB
https://csedweek.org/learn
Descrição: É a plataforma da campanha da Semana do Ensino da Ciência da
Programação, lançado em 2016. Nela, estão disponíveis vários tutoriais para todos
gostos e idades, alguns, inclusive, em português. Como os puzzles com Angry Birds.
Há tutoriais para aprender a programar até mesmo sem computadores, usando papel
e caneta para escrever um algoritmo capaz de cumprir determinada tarefa
LIVRO
LIVRO
LIVRO
REFERÊNCIAS
SCHILDT, Herbert. C Completo e Total: 3. edição revisada e atualizada. Pearson Makron
Books, São Paulo, 1997.
DEITEL, H. M.; DEITEL, P. J. C: como programar. 6 ed. São Paulo, Pearson Prentice
Hall. 2011.
ASCENCIO, Ana Fernanda Gomes; ARAÚJO, Graziela Santos de. Estrutura de dados:
algoritmos, análise de complexidade e implementações em JAVA e C/C++. Pearson
Prentice Hall. São Paulo 2010.
NORTON, Peter. Introdução à informática. Pearson Makron Books. São Paulo 1996
OLIVEIRA, Álvaro B. de; PRADA, Adriana; SILVA, Reginaldo R. da. Métodos de Ordenação
Interna: Implementação, Análise e Desempenho. Visual Books, 2002
PAPPAS, C. H.; MURRAY, W. H. Turbo C++ Completo e Total. São Paulo: Makron, McGraw-
Hill, 1991.