Prévia do material em texto
Listas
Filas
Pilhas
Estrutura de Dados
*agrupamento
*organização
*ordenação
Lista, filas, pilhas, árvores e grafos
Complexidade algorítmica
Letra O representa o custo máximo que o algorítimo gera.
N Custo Mínimo Máximo Tipo
Operações
envolvidas
(tempo)
O(n) Listas
log(n) Árvores
O (1) Tabela Hash
Lista Estática – Estrutura Física
PROJETO CODEBLOCKS: Aula_17_08
Desvantagem:
- Tamanho fixo definido, o que
gera pouca flexibilidade;
Qt Me Fcp Fcm
? ? ? ?
vDados[TAM_LISTA];
#define TAM_LISTA 4
TAM_LISTA - #define TAM_LISTA 4
É a quantidade de elementos que a lista poderá ter, lembrando que a lista é estática, ou seja, isso não
poderá mudar em tempo de execução.
Qt - int QtdeElementos;
Irá armazenar a quantidade de elementos presentes na lista.
Me - int MaxElementos;
Irá armazenar o número máximo de elementos suportado pela lista.
vDados – void vDados[TAM_LISTA];
Representa o vetor de dados que a lista conterá, é do tipo void pois não é possível saber o tipo de dados
que a lista vai receber, uma vez que ela é genéria.
Fcm - int (* FncComparar)( void Elemento, void ChaveBusca);
Ponteiro para função que recebe dois parâmetros e é utilizada para comparar dois itens da lista. Nota-se
que os parâmetros “Elemento” e “ChaveBusca” são do tipo void, pois a lista é genérica e não sabemos
com qual tipo de dado ela trabalhará.
Essa função recebe um elemento da lista e uma chave de busca para que possa ser possível efetuar a
comparação entre esses elementos.
Fcp - int (*FncCopiar)(void Origem, void Destino);
Ponteiro para função utilizada para copiar um item da lista. Nota-se que os parâmetros “Origem” e
“Destino” são do tipo void, pois a lista é genérica e não sabemos com qual tipo de dado ela trabalhará.
Essa função recebe um dado origem que será copiado no dado destino.
Lista Estática – Estrutura da lista (Struct)
ListaEstatica.h
Lista Estática – Funções da lista
A lista deve possuir algumas funções básicas para seu funcionamento, são elas:
void InicializaLista(ListaEstatica *ptrLista, int (*ptrFncComparar)(void *ptrElemento, void *ptrChaveBusca),
int (*ptrFncCopiar)(void *ptrDestino, void *ptrOrigem));
Essa função irá inicializar nossa lista, ou seja, prepará-la para o uso, no caso iniciar as variáveis do struct
e fazer com que os ponteiros pras funções comparar e copiar apontem para as funções certas. Essa
função recebe como parâmetro um ponteiro para lista (*ptrLista) pois não podemos modificar a lista
diretamente, e também recebe os ponteiros para as funções de comparação e cópia.
int Consulta(ListaEstatica *ptrLista, void *ptrChaveBusca);
Essa função servirá para buscar elementos dentro da lista. Ela receberá por parâmetro um ponteiro para
lista e um ponteiro para chave de busca que será usado para identificar o elemento.
int ConsultaPosicao(ListaEstatica *ptrLista, int idxPosicao);
Essa função funciona como a primeira porém ao invés de receber a chave de busca ela já vai receber
diretamente o índice do elemento procurado.
int Inclui(ListaEstatica *ptrLista, void *ptrNovoElemento);
Essa função servirá para incluirmos elementos na lista (dentro de vDados). Ela recebe um ponteiro para
lista e um ponteiro para o novo elemento que será adicionado. Em uma lista o novo elemento entra ao
final.
int IncluiPosicao(ListaEstatica *ptrLista, int idxPosicao);
Essa função servirá para incluirmos elementos na lista (dentro de vDados), diferente da outra função,
nessa poderemos incluir o elemento em qualquer posição da lista, por isso ela também recebe por
parâmetro o índice da posição desejada.
ListaEstatica.h
int Altera(ListaEstatica *ptrLista, void *ptrNovoElemento, void *ptrChaveBusca);
Essa função servirá para alterarmos um item da lista. Ela recebe os seguintes parâmetros: um ponteiro
para lista, um ponteiro para o novo elemento e um ponteiro para a chave de busca.
int AlteraPosicao(ListaEstatica *ptrLista, void *ptrNovoElemento, int idxPosicao);
Essa função servirá para alterarmos um item determinado pelo índice. Ela recebe os seguintes
parâmetros: um ponteiro para lista, um ponteiro para o novo elemento e o índice do elemento que será
alterado.
void *Exclui(ListaEstatica *ptrLista, void *ptrChaveBusca);
Essa função servirá para excluir um elemento da lista, por meio de sua chave de busca. Ela recebe por
parâmetro um ponteiro para lista e um ponteiro para chave de busca.
void *ExcluiPosicao(ListaEstatica *ptrLista, int idxPosicao);
Essa função servirá para excluir um elemento da lista, por meio de um índice. Ela recebe por parâmetro
um ponteiro para lista e o índice da posição.
ListaEstatica.h
Me
Fcp
Qt
Pilha Estática – Estrutura Física (LiFo – Last in First out)
?
?
?
?
PROJETO CODEBLOCKS: Aula_02_09_PilhaEst
Qt - int QtdeElementos
Irá armazenar a quantidade de elementos presentes na lista.
Me - int MaxElementos
Irá armazenar o número máximo de elementos suportado pela lista.
Fcp - int FncCopiar()
Função utilizada para copiar um item da lista
vDados[TAM_PILHA];
#define TAM_PILHA 4
Qt Me Fcp
Fila Estática – Estrutura Física (FiFo – First in First out)
Organização lógica da fila circular:
Para que a fila consiga funcionar de forma circular devemos aplicar o seguinte incremento
idx = (idx + 1)%MaxElementos
? ? ? ?
PROJETO CODEBLOCKS: Aula_09_09_FilaEst
Qt - int QtdeElementos
Irá armazenar a quantidade de elementos presentes na lista.
Me - int MaxElementos
Irá armazenar o número máximo de elementos suportado pela lista.
Fcp - int FncCopiar()
Função utilizada para copiar um item da lista
EXERCÍCIO
7
13
10
30
Pilha
(recursos)
c1 c2 c3 c4
Fila
(consumo)
Relação quantidade x consumidor
1 4 20 50
Descrição do problema:
1 - Produção (insere pilha)
2 - Consumo (insere fila)
3 - Atender(executar alterações fila/pilha)
Primeiro elemento da fila irá interagir com o topo da pilha, se o topo da pilha satisfazer o pedido
do primeiro elemento da fila ele será “removido da fila”, caso contrário o topo da lista será
removido para que o segundo elemento (de cima pra baixo) tente satisfazer o pedido do
primeiro elemento da fila.
DESAFIO: refazer o exercício usando uma pilha de filas para representar diferentes produtos
cn
n
n
PROJETO CODEBLOCKS: Exercicio_09_09_ListaDinamica
Lista Estática → Vetores (tamanho fixo)
( SUPER / SUB ) - Dimensionamento do vetor
Falta de espaço
Consome memória desnecessária
* No nosso caso o problema é minimizado por estarmos usando vetores de ponteiros
Problema das listas Estáticas
FCopiar
FCompararQtdeElementos
MaxElementos
1 2 3 4
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica → alocação de memória
Qt Me *pi Fcm Fcp*pf
1 2
Nó Nó Nó
vDados[TAM_LISTA]
Lista Dinâmica – Estrutura Física
Vantagem
+ Solucionar o problema das listas estáticas
Desvantagens
- Não há acesso direto ao nó do índice / posição x
- Maior consumo memória para referências
Qt Me *ptrI Fcm Fcp*ptrF
1 2
Nó
Descritor
Descritor
É outra struct (ListaDinamica)
Qt – int QtdeElementos
Me – int MaxElementos
*ptrI – No ptrInicio;
*ptrF – No ptrFim;
Fcm – int ptrFncComparar
Fcp – int ptrFncCopiar
Nós
São estruturas (struct)
*NoA Da *NoP
*NoA – void* ptrNoAnt
Endereço para o nó anterior.
Da – void* Dado
Endereço para o dado que o nó vai
armazenar.
*NoP – void* ptrNoProx
Endereço para o próximo nó.
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Nó Nó Nó
Nó
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
0 Lista vazia
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dosnós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
?
1 - Surge o dado
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
?
2 - Cria-se o nó
*NoA *Da *NoP
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
?
3 – Referência o dado para o
ptrDado do nó
*NoA *Da *NoP
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
?
4 - Seta o ptrNoProx como nulo
*NoA *Da *NoP
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
?
5 – Verifica se a lista esta vazia
if (!ptrLista->QtdeElementos)
/* nesse caso será true */
*NoA *Da *NoP
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
?
6 – Seta o ptrNoAnt para null,
pois não há elementos antes do
primeiro
*NoA *Da *NoP
NULL;
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
NULL;
?
6 - Seta o início da lista para o
Nó
*NoA *Da *NoP
NULL;
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
6 – Atualiza o campo ptrFim da
lista para o último elemento.
Incrementa a quantidade de
elementos
*NoA *Da *NoP
NULL;
++
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
0 – Lista com 1 elemento
*NoA *Da *NoP
NULL;
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
1 – Surge outro dado
*NoA *Da *NoP
NULL;
?NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
2 – Cria-se outro nó
*NoA *Da *NoP
NULL;
?
*NoA *Da *NoP
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
3 – Referência o dado para o
campo ptrDado do novo nó
*NoA *Da *NoP
NULL;
?
*NoA *Da *NoP
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
4 – Campo ptrNoAnt = ptrLista-
>ptrFim
Ou seja, o campo anterior do
novo nó vai receber o nó anterior
a ele.
*NoA *Da *NoP
NULL;
?
*NoA *Da *NoP
NULL;
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
5 – ptrLista→ptrFim→ptrNoProx
= ptrNoAux;
Ou seja, o campo próximo do nó
anterior recebe o novo nó
*NoA *Da *NoP
NULL;
?
*NoA *Da *NoP
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
?
6 – ptrLista→ptrFim = ptrNoAux;
Atualiza o campo ptrFim da lista.
Incrementa a quantidade de
elementos da lista
*NoA *Da *NoP
NULL;
++
?
*NoA *Da *NoP
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Qt Me *ptrI Fcm Fcp*ptrF
*NoA *Da *NoP *NoA *Da *NoP *NoA *Da *NoP *NoA *Da *NoP
? ? ? ?
Lista estática
A lista estática é usada para armazenar dados de um determinado tipo, ela possui um número
Máximo de elementos e também um índice para controle deles. A contiguidade é de extrema
Importância para que a lista mantenha sua estrutura.
A lista
Inserindo elemento no meio uma lista dinâmica:
Será necessário:
● Criar o nó
● Referenciar o dado C para o campo dado do nó
● Verificar se a lista está vazia
● Fazer seu campo Anterior apontar para o próximo de A
● Seu ponteiro próximo para o anterior de B
● e após isso o Campo próximo de A deve apontar para o anterior de C(ou pro nó em que C está)
● e o campo Anterior de B deve apontar para o próximo de C
● o campo anterior de A agora é null
● campo próximo de B null
Adicionando elementos em uma lista estática:
Apenas desloca o
PROJETO CODEBLOCKS: Aula_05_10_ExercicioLD
main.c
ListaDinamica c;
c = ExcluiTudo(parâmetros);
/* Retorno da função (apenas o descritor é retornado com seus devidos
ponteiros) */
ListaDinamica ExcluiTudo(ListaDinamica *ptrLista, void *ptrChaveBusca);
ListaDinamica d;
return d;
Função que retorna uma ListaDinamica d.
A única coisa que será retornada é struct ListaDinamica
Qt Me *PI Fcm Fcp*PFQt Me *ptrI Fcm Fcp*ptrF
Nó
Descritor
Nó Nó
PROJETO CODEBLOCKS: Aula_28_09_PilhaDinamica
Qt Me Fcm Fcp
1
*ptrDado
?
Pilha Dinâmica – Estrutura Física
*ptrProx
*ptrDado
?*ptrProx
*ptrDado
?*ptrProx
Cria o nó, o campo dado aponta
pro A, o ptrProx recebe o topo e
o topo recebe ele.
Qt Me *PI Fcm Fcp*PF
1 2
Da *NoP Da *NoP Da *NoP Da *NoP
? ? ? ?
Fila Dinâmica – Estrutura Física
PROJETO CODEBLOCKS: Aula_30_09_FilaDinamica
Cria o nó, seta o dado pro elemento, seta o próximo para nulo, seta inicio e fim para o
nó criado (para primeiro elemento).
Incrementa um;
Inserir o outro dado, cria um nó, o dado aponta pra b, não cai no if pois já existe um
elemento, ptrprox = noaux, ptrfila→ptrfim = noaux
APS 1 – ESTRUTURA DE DADOS
Lista Produtos / Estoque
Lista de compra do usuário e partir delas serão geradas sub-listas.
A cada novo cliente zera a lista do cliente anterior para criar a lista do cliente atual.
2 – Poder alterar o estoque a qualquer momento, o que pode implicar um novo produto
ou apenas atualizar a quantidade presente no estoque.
3 – Ler a lista de compra dos clientes (mesmo do 2)
4 – O cliente poderá alterar a lista de compras antes de confirmar a compra
“Deseja alterar a lista de comprar”
“não”
Após isso ele não pode mais modificar a lista pois foi confirmada a lista de compra.
Struct Produto{
Char[10] nome;
Int quantidade;
Int precoUnitario;
}
EXERCÍCIO – LISTA DINÂMICA
Excluir e retornar todos os elementos com uma chave de busca qualquer
Duas maneiras:
- utilizando rotinas prontas
- reescrevendo todo o código
ListaDinamica ExcluiTudo(ListaDinamica *ptrLista, void *ptrChaveBusca);
ListaDinamica d;
ListaDinamica *ExcluiTudo(ListaDinamica *ptrLista, void *ptrChaveBusca);
ListaDinamica *p = (ListaDinamica *) malloc(sizeof(ListaDinamica));
int ExcluiTudo(ListaDinamica *ptrListaEntrada, void *ptrChaveBusca, ListaDinamica
*ptrListaSaida);
(pode ser void)
Qt Me *ptrI Fcm Fcp*ptrF
*NoA *Da *NoP *NoA *Da *NoP *NoA *Da *NoP *NoA *Da *NoP
? ? ? ?
PROJETO CODEBLOCKS: Aula_16_09_ListaDinamica
Lista Dinâmica – Funcionamento dos nós
Recursão
A recursão é uma ferramenta computacional que foi inspirada no comportamento de alguns
elementos da natureza.
Ex: A casca de um caracol, uma folha de árvore ou seus galhos, etc.
Quando se utiliza recursão a pilha do sistema é usada para auxiliar no funcionamento da função.
A condição (if) vem sempre antes da chamada recursiva.
Vantagens:
+ Código mais simples e enxuto
+ Facilidade de implementação de problemas complexos (I.A), simulação numérica, busca e
ordenação em árvore binária etc.
Desvantagens:
- Maior complexidade para entender o código
- Consumo de memória e processamento, pois a cada chamada de rotina, seu status deve ser
armazenado na pilha do sistema.
- Possível StackOverflow se possuir erros na implementação
Observação: Sempre que puder usar iteração (for) ao invés de recursividade, use.
Quando usar?
Usa-se recursão quando parte do problema se assemelha ao todo.
Mais exemplos dentro da computação:
● Cálculo de fatorial
● Sequência de Fibonacci
Recursãoindireta
Recursão direta (mais comum)
F1 F2 F3
F1
F1 F2
F1 F2 F3
/* rotina escreve inteiros positivos menores
que n */
void escreve(long n){
if(n <= 0)
return;
escreve(n -1);
printf(“%d”, n);
}
Recursão direta exemplo de algorítimo:
/* rotina escreve o fatorial de um número */
long fatorial(long n){
}
/* rotina escreve o fatorial de um número
usando iteração */
long fatorial(long n){
long fat = 1;
while (n > 0){
fat = fat * n;
n--;
}
return fat;
}
Exercícios Recursão
● Escrever um valor (decimal) nas Bases binária, octal e hexadecimal
● Escrever uma rotina pra Caminhar em String e escrever seu conteúdo ao contrário
● Escrever uma rotina recursiva para exibir os primeiros n termos da PA de razão q e termo
inicial a1 (an = a1 + (n – 1) * r)
● Escrever uma rotina recursiva para exibir os primeiros n termos da PG de razão q e termo
inicial a1 (an = a1 * q
n-1)
Implementar rotinas recursivas para DIV e MOD utilizando operações de subtração
● Escrever uma rotina para realizar busca em Strings, Listas e Árvores.
● Escrever uma rotina para realizar busca binária em uma lista linear estática ordenada
● Escrever uma rotina recursiva para inverter uma lista simplesmente encadeada dinâmica
PROJETO CODEBLOCKS: Aula_19_10_Recursao
Árvores
Árvores AVL
Árvores B
Árvores são generalizações de listas ou podemos dizer que é um grafo conexo sem ciclos.
Árvores AVL: são árvores que possuem uma diferença de no máximo 2 níveis entre os folhas
e os outros elementos
Árvores B: muito usada para buscas em disco (HD)
Árvores possuem um direcionamento (da raiz para as folhas)
Caminhamentos de árvores:
Profundidade (recursividade/pilha):
Utiliza recursividade e a pilha do sistema
Amplitude (fila):
Utiliza fila da seguinte forma:
Visita o nó e adiciona seus filhos na fila
Grafos
Conceito Básico da Teoria de Grafos
Um grafo é um conjunto de N nós, que possuem ligações/relações entre qualquer pares de nós
(como uma árvore sem direcionamento)
Os grafos podem ser representado por matrizes (arrumar desenho)
0
2
1
X X
X X
X X
0 1 2
0
1
2
Conceito Básico da Teoria de Grafos
Um grafo é um conjunto de N nós, que possuem ligações/relações entre qualquer pares de nós
(como uma árvore sem direcionamento)
Os grafos podem ser representado por matrizes (arrumar desenho)
A ordem do grafo é dada pelo número de vértices que ele possui.
Grau de emissão: é o número de arestas que saem de um nó
Grau de recepção: é o número de arestas que chegam a um nó
Nó fonte: nós que possuem apenas grau de emissão e grau de recepção 0, ou seja, apenas
saem arestas dele.
Nó semidouro: nós que possuem apenas grau de recepção e grau de emissão 0, ou seja,
apenas chegam arestas nele.
Grafos parciais;
Hipergrafo;
Cadeia é uma sequências de quaisquer arestas adjacentes que ligam dois vértices (nós).
Se o grafo não possuir orientação, pode-se obter o caminhamento para qualquer lado
Cadeia elementar não passa duas vezes pelo mesmo vértice.
Cadeia simples não passa duas vezes pela mesma aresta(arco).
O comprimento de uma cadeia é o número de arestas (arcos) que compõe.
Ciclo: uma cadeia simples fechada (vértice inicial é o mesmo que o vértice final, porém não
exige orientação).
Circuito: é um caminho simples e fechado (exige orientação).
Grafo Conexo: há pelo menos uma cadeia ligando cada par de nós.
Grafo Desconexo: há pelo menos um par de nós que não esta ligado.
Redes: é um grafo composto por dois nós especiais, apenas um fonte e apenas um semidouro.
Grafo planar: um grafo que pode ser representado de forma plana sem que suas arestas se
cruzem.
Busca em Grafos
Caminha rofundidade (usando pilha)Ṕ
Tamo um nó v qualquer
Poe v na pilha dos nós a serem visitados
Enquanto a pilha dos nós a serem visitados não estiver vazia
Retira próximo nó a ser visitado da pilha (v = nó[topo] da pilha)
Se v não estiver marcado
Visita v
Marca v
Para cada nó w adjascente à v
Põe w na pilha dos nós a serem visitados
CaminhaProfundidadeRecursivo(arvore *pRaiz)
Se v não estiver marcado
Visita v
Marca v
Para cada nó w adjascente à v
CaminhaProfundidadeRecursivo(pRaiz→ptr???)
Tabela Hash
Tabela Hash
Não há ordenação, mas simplesmente organização dos dados / elementos
f(chave) = calculo % n → garante que o endereço esta no intervalo [o … n -1] do vetor
Chave + c
C e N primos entre si (não ter divisores em comum,
garante melhor espalhamento)
Custo: O(1)
*Utiliza vetor 0 | | | | | n-1
Elemento → aplico f (elemento.chave) → endereço na tabela hash
Hash aberto
-Localiza próxima posição livre na tabela hash
Hash fechado
-Utiliza listas ou árvores p/ cada posição da Tabela Hash
N elementos armazenados
K elementos com chave qualquer
O(1) + O(K) → lista
O(1) + o(log(K)) → árvore
Tabela Hash
K
Lista
4 9 24
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 11
Slide 12
Slide 13
Slide 14
Slide 15
Slide 16
Slide 17
Slide 18
Slide 19
Slide 20
Slide 21
Slide 22
Slide 23
Slide 24
Slide 25
Slide 26
Slide 27
Slide 28
Slide 29
Slide 30
Slide 31
Slide 32
Slide 33
Slide 34
Slide 35
Slide 36
Slide 37
Slide 38
Slide 39
Slide 40
Slide 41
Slide 42
Slide 43
Slide 44
Slide 45
Slide 46
Slide 47
Slide 48
Slide 49
Slide 50
Slide 51