• Nenhum resultado encontrado

CIC 110 Análise e Projeto de Algoritmos I

N/A
N/A
Protected

Academic year: 2022

Share "CIC 110 Análise e Projeto de Algoritmos I"

Copied!
41
0
0

Texto

(1)

CIC 110

Análise e Projeto de Análise e Projeto de

Algoritmos I Algoritmos I

Universidade Federal de Itajubá

Prof. Roberto Affonso da Costa Junior

(2)

AULA 04 AULA 04

– Data structures

(3)

Estrutura de Dados Estrutura de Dados

Uma estrutura de dados é uma maneira de armazenar dados na memória de um computador. É importante escolher uma estrutura de dados adequada para um problema, pois cada estrutura de dados tem suas próprias vantagens e desvantagens. A questão crucial é: quais operações são eficientes na estrutura de dados escolhida?

Vamos ver as estruturas de dados mais importantes

na biblioteca padrão C++. É uma boa ideia usar a

biblioteca padrão sempre que possível, porque

economizará muito tempo.

(4)

Vetor Dinâmico Vetor Dinâmico

Um vetor dinâmico é um vetor cujo tamanho pode ser alterado durante a execução do programa. O vetor dinâmico mais popular em C++ é a estrutura vector, que pode ser usada quase como um vetor comum, como fizemos na aula 2.

O código a seguir cria um vetor vazio:

vector<int> v;

vector<float> v;

vector<double> v;

vector<char> v;

vector<string> v;

(5)

Problema URI 1237 Problema URI 1237

Comparação de Substring

Encontre a maior substring comum entre as duas strings informadas. A substring pode ser qualquer parte da string, inclusive ela toda. Se não houver subsequência comum, a saída deve ser “0”. A comparação é case sensitive ('x' != 'X').

Entrada

A entrada contém vários casos de teste. Cada caso de teste é composto por duas linhas, cada uma contendo uma string. Ambas strings de entrada contém entre 1 e 50 caracteres ('A'-'Z','a'-'z' ou espaço ' '), inclusive, ou no mínimo uma letra ('A'-'Z','a'-'z').

Saída

O tamanho da maior subsequência comum entre as

duas Strings.

(6)

Problema URI 1237 Problema URI 1237

#include <bits/stdc++.h>

using namespace std;

vector<int> compute_z(const string &s) {

int n = s.size();

vector<int> z(n,0);

int l,r;

r = l = 0;

for(int i = 1; i < n; ++i) {

if(i > r) {

l = r = i;

while(r < n and s[r - l] == s[r])r++;

z[i] = r - l;r--;

(7)

Problema URI 1237 Problema URI 1237

} else {

int k = i-l;

if(z[k] < r - i +1) z[i] = z[k];

else { l = i;

while(r < n and s[r - l] == s[r])r++;

z[i] = r - l;r--;

} }

}

return z;

}

(8)

Problema URI 1237 Problema URI 1237

int main() {

string a, b;

while (getline(cin, a) && getline(cin, b)) { int ans = 0;

for (int i = 0; i < a.size(); ++i) {

string c = a.substr(i);

int t = c.size();

c = c + '$' + b;

vector<int> z = compute_z(c);

for (int j = t; j < c.size(); ++j) {

ans = max(ans, z[j]);

} }

cout << ans << endl;

}

return 0;

}

(9)

Estrutura Set Estrutura Set

Um conjunto é uma estrutura de dados que mantém uma coleção de elementos. As operações básicas dos conjuntos são a inserção, busca e remoção de elementos. A biblioteca padrão C++ contém duas implementações definidas: o conjunto de estrutura é baseado em uma árvore binária balanceada e suas operações funcionam no tempo O(log n). A estrutura unordered_set usa hash, e suas operações funcionam em tempo de O(1) em média.

A escolha da implementação definida para usar é

muitas vezes uma questão de gosto. O benefício da

estrutura definida é que ele mantém a ordem dos

elementos e fornece funções que não estão disponíveis

no unordered_set. Por outro lado, unordered_set pode

ser mais eficiente.

(10)

Estrutura Set Estrutura Set

O código a seguir cria um conjunto que contém

números inteiros e mostra algumas das operações. A

inserção de função adiciona um elemento ao

conjunto, a contagem de função retorna o número de

ocorrências de um elemento no conjunto e a função

apaga remove um elemento do conjunto.

(11)

Estrutura Set Estrutura Set

#include <bits/stdc++.h>

using namespace std;

int main() {

set<int> s;

s.insert(3);

s.insert(2);

s.insert(5);

printf("%d\n", s.count(3));

printf("%d\n", s.count(4));

s.erase(3);

s.insert(4);

printf("%d\n", s.count(3));

printf("%d\n", s.count(4));

return 0;

}

(12)

Estrutura Set Estrutura Set

Um conjunto pode ser usado principalmente como

um vetor, mas não é possível acessar os elementos

usando a notação []. O código a seguir cria um

conjunto, imprime o número de elementos e itera

através de todos os elementos:

(13)

Estrutura Set Estrutura Set

#include <bits/stdc++.h>

using namespace std;

int main() {

set<int> s = {2, 5, 6, 8};

printf("Tamanho do Conjunto: %d\n", s.size());

printf("s = {");

for (auto x : s) {

printf(" %d", x);

}

printf("}\n");

return 0;

} Tamanho do Conjunto: 4

s = { 2 5 6 8}

(14)

Estrutura Set Estrutura Set

Uma propriedade importante dos conjuntos é que

todos os seus elementos são distintos. Assim, a

contagem de função sempre retorna 0 (o elemento

não está no conjunto) ou 1 (o elemento está no

conjunto) e a inserção de função nunca adiciona um

elemento ao conjunto se ele já estiver lá. O código a

seguir ilustra isso:

(15)

Estrutura Set Estrutura Set

Tamanho do Conjunto: 4 s = { 2 5 6 8}

Tamanho do Conjunto: 5 s = { 2 5 6 7 8}

#include <bits/stdc++.h>

using namespace std;

int main() {

set<int> s = {2, 5, 6, 8};

printf("Tamanho do Conjunto: %d\n", s.size());

printf("s = {");

for (auto x : s) {

printf(" %d", x);

}

printf("}\n");

s.insert(5);

s.insert(7);

s.insert(8);

printf("Tamanho do Conjunto: %d\n", s.size());

printf("s = {");

for (auto x : s) {

printf(" %d", x);

}

printf("}\n");

return 0;

}

(16)

Estrutura Multiset Estrutura Multiset

C++ também contém as estruturas multiset e unordered_multiset que, de outra forma, funcionam como set e unordered_set, mas podem conter várias instâncias de um elemento. Por exemplo, no seguinte código, as três instâncias do número 5 são adicionadas a um multiset:

multiset<int> s;

s.insert(5); s.insert(5); s.insert(5);

printf("Quantidade de 5: %d\n", s.count(5));

(17)

Estrutura Multiset Estrutura Multiset

A função apagar remove todas as instâncias de um elemento de um multiset:

Muitas vezes, apenas uma instância deve ser removida, o que pode ser feito da seguinte maneira:

s.erase(5);

printf("Quantidade de 5: %d\n", s.count(5));

s.erase(s.find(5));

printf("Quantidade de 5: %d\n", s.count(5));

(18)

Estrutura Multiset Estrutura Multiset

#include <bits/stdc++.h>

using namespace std;

int main() {

multiset<int> s;

s.insert(5); s.insert(5); s.insert(5);

printf("Quantidade de 5: %d\n", s.count(5));

printf("s = {");

for (auto x : s) {

printf(" %d", x);

}

printf("}\n");

s.erase(5);

printf("Quantidade de 5: %d\n", s.count(5));

(19)

Estrutura Multiset Estrutura Multiset

printf("s = {");

for (auto x : s) {

printf(" %d", x);

}

printf("}\n");

s.insert(5); s.insert(5); s.insert(5);

s.erase(s.find(5));

printf("Quantidade de 5: %d\n", s.count(5));

printf("s = {");

for (auto x : s) {

printf(" %d", x);

}

printf("}\n");

return 0;

}

Quantidade de 5: 3 s = { 5 5 5}

Quantidade de 5: 0 s = {}

Quantidade de 5: 2 s = { 5 5}

(20)

Estrutura Map Estrutura Map

Um mapa é uma matriz generalizada que consiste em pares de valores chave. Enquanto as teclas em uma matriz comum são sempre os inteiros consecutivos 0, 1, ... , N - 1, onde n é o tamanho da matriz, as chaves em um mapa podem ser de qualquer tipo de dados e não precisam ser valores consecutivos.

A biblioteca padrão C++ contém duas implementações de mapa que correspondem às implementações definidas: o mapa de estrutura é baseado em uma árvore binária equilibrada e os elementos de acesso levam tempo O(log n), enquanto a estrutura unordered_map usa hash e acessar elementos leva O(1) de tempo em média.

O código a seguir cria um mapa onde as chaves são

strings e os valores são inteiros:

(21)

Estrutura Map Estrutura Map

#include <bits/stdc++.h>

using namespace std;

int main() {

map<string,int> m;

m["monkey"] = 4;

m["banana"] = 3;

m["harpsichord"] = 9;

printf("monkey: %d\n", m["monkey"]);

printf("banana: %d\n", m["banana"]);

printf("harpsichord: %d\n", m["harpsichord"]);

return 0;

}

monkey: 4 banana: 3

harpsichord: 9

(22)

Estrutura Map Estrutura Map

O código a seguir imprime todas as chaves e valores em um mapa e se o valor de uma chave for solicitado, mas o mapa não o contém, a chave é automaticamente adicionada ao mapa com um valor padrão. Por exemplo, no código a seguir, a chave

"roberto" com valor 0 é adicionada ao mapa.

(23)

Estrutura Map Estrutura Map

roberto - 0 banana - 3

harpsichord - 9 monkey - 4 roberto - 0

#include <bits/stdc++.h>

using namespace std;

int main() {

map<string,int> m;

m["monkey"] = 4;

m["banana"] = 3;

m["harpsichord"] = 9;

printf("roberto - %d\n", m["roberto"]);

for (auto x : m) {

cout << x.first << " - " << x.second << "\n";

}

return 0;

}

(24)

Estrutura Bitset Estrutura Bitset

Um bitset é um vetor cujo cada valor seja 0 ou 1.

O benefício do uso de bitsets é que eles exigem menos memória do que matrizes comuns, porque cada elemento em um bitset usa apenas um bit de memória. Por exemplo, se n bits forem armazenados em uma matriz int, serão utilizados 32n bits de memória, mas um bitetro correspondente apenas requer n bits de memória. Além disso, os valores de um bitset podem ser manipulados de forma eficiente usando operadores de bits, o que permite otimizar algoritmos usando conjuntos de bits.

O código a seguir mostra como manipular o bitset:

(25)

Estrutura Bitset Estrutura Bitset

#include <bits/stdc++.h>

using namespace std;

int main() {

bitset<10> s(string("0010011010"));

// from right to left

cout << "s = (" << s << ")\n";

cout << "s[4] = " << s[4] << "\n";

cout << "s[5] = " << s[5] << "\n";

printf("Total = %d\n", s.count());

bitset<10> a(string("0010110110"));

bitset<10> b(string("1011011000"));

cout << "a = (" << a << ")\n";

cout << "b = (" << b << ")\n";

cout << "a&b = (" << (a&b) << ")\n";

cout << "a|b = (" << (a|b) << ")\n";

cout << "a^b = (" << (a^b) << ")\n";

return 0;

}

s = (0010011010) s[4] = 1

s[5] = 0 Total = 4

a = (0010110110) b = (1011011000) a&b = (0010010000) a|b = (1011111110) a^b = (1001101110)

(26)

Estrutura Deque Estrutura Deque

Um deque é um vetor dinâmica cujo tamanho pode ser eficientemente alterado nas duas extremidades da matriz. Como um vetor, um deque fornece as funções push_back e pop_back, mas também inclui as funções push_front e pop_front que não estão disponíveis em um vetor.

Um deque pode ser usado da seguinte forma:

(27)

Estrutura Deque

Estrutura Deque

(28)

Estrutura Deque Estrutura Deque

#include <bits/stdc++.h>

using namespace std;

int main() {

deque<int> d;

d.push_back(5);

printf("d = {");

for (auto x : d) {

printf(" %d", x);

}

printf("}\n");

d.push_back(2);

printf("d = {");

for (auto x : d) {

printf(" %d", x);

}

printf("}\n");

d.push_front(3);

printf("d = {");

for (auto x : d) {

printf(" %d", x);

}

printf("}\n");

d.pop_back();

printf("d = {");

for (auto x : d) {

printf(" %d", x);

}

printf("}\n");

d.pop_front();

printf("d = {");

for (auto x : d) {

printf(" %d", x);

}

printf("}\n");

return 0;

}

d = { 5}

d = { 5 2}

d = { 3 5 2}

d = { 3 5}

d = { 5}

(29)

Estrutura Deque Estrutura Deque

A implementação interna de um deque é mais

complexa do que a de um vetor, e por esse motivo, um

deque é mais lento do que um vetor. Ainda assim, a

adição e a remoção de elementos levam o tempo de

O(1) em média nas duas extremidades.

(30)

Estrutura Stack Estrutura Stack

Uma pilha é uma estrutura de dados que fornece duas operações de tempo de O(1): adicionando um elemento na parte superior e removendo um elemento da parte superior. Só é possível acessar o elemento superior de uma pilha.

O código a seguir mostra como uma pilha pode ser

usada:

(31)

Estrutura Stack

Estrutura Stack

(32)

Estrutura Stack Estrutura Stack

#include <bits/stdc++.h>

using namespace std;

int main() {

stack<int> s;

s.push(3);

printf("Elemento do topo: %d\n", s.top());

s.push(2);

printf("Elemento do topo: %d\n", s.top());

s.push(5);

printf("Elemento do topo: %d\n", s.top());

s.pop();

printf("Elemento do topo: %d\n", s.top());

return 0;

}

Elemento do topo: 3 Elemento do topo: 2 Elemento do topo: 5 Elemento do topo: 2

(33)

Estrutura Queue Estrutura Queue

Uma fila também fornece duas operações de tempo de O(1): adicionando um elemento no final da fila e removendo o primeiro elemento na fila. Só é possível acessar o primeiro e último elemento de uma fila.

O código a seguir mostra como uma fila pode ser

usada:

(34)

Estrutura Queue

Estrutura Queue

(35)

Estrutura Queue Estrutura Queue

#include <bits/stdc++.h>

using namespace std;

int main() {

queue<int> q;

q.push(3);

printf("Elemento do inicio: %d\n", q.front());

q.push(2);

printf("Elemento do inicio: %d\n", q.front());

q.push(5);

printf("Elemento do inicio: %d\n", q.front());

q.pop();

printf("Elemento do inicio: %d\n", q.front());

return 0;

}

Elemento do inicio: 3 Elemento do inicio: 3 Elemento do inicio: 3 Elemento do inicio: 2

(36)

Prioridade queue Prioridade queue

Uma fila de prioridade mantém um conjunto de elementos. As operações suportadas são inserção e, dependendo do tipo de fila, recuperação e remoção do elemento mínimo ou máximo. A inserção e remoção levam o tempo O(log n) e a recuperação leva O(1) tempo.

Enquanto um conjunto ordenado suporta de forma

eficiente todas as operações de uma fila de

prioridade, o benefício de usar uma fila de prioridade

é que ele tem fatores menores e constantes. Uma fila

de prioridade geralmente é implementada usando

uma estrutura de heap que é muito mais simples do

que uma árvore binária equilibrada usada em um

conjunto ordenado.

(37)

Prioridade queue Prioridade queue

Por padrão, os elementos em uma fila de prioridade

C++ são classificados em ordem decrescente, e é

possível encontrar e remover o maior elemento na

fila. O código a seguir ilustra isso:

(38)

Prioridade queue Prioridade queue

Elemento do inicio: 3 Elemento do inicio: 5 Elemento do inicio: 7 Elemento do inicio: 7 Elemento do inicio: 5 Elemento do inicio: 3 Elemento do inicio: 6

#include <bits/stdc++.h>

using namespace std;

int main() {

priority_queue<int> q;

q.push(3);

printf("Elemento do inicio: %d\n", q.top());

q.push(5);

printf("Elemento do inicio: %d\n", q.top());

q.push(7);

printf("Elemento do inicio: %d\n", q.top());

q.push(2);

printf("Elemento do inicio: %d\n", q.top());

q.pop();

printf("Elemento do inicio: %d\n", q.top());

q.pop();

printf("Elemento do inicio: %d\n", q.top());

q.push(6);

printf("Elemento do inicio: %d\n", q.top());

return 0;

}

(39)

Prioridade queue Prioridade queue

Se quisermos criar uma fila de prioridade que suporte a descoberta e remoção do elemento mais pequeno, podemos fazê-lo da seguinte maneira:

priority_queue<int,vector<int>,greater<int>> q;

(40)

Prioridade queue Prioridade queue

Por padrão, os elementos em uma fila de prioridade

C++ são classificados em ordem decrescente, e é

possível encontrar e remover o maior elemento na

fila. O código a seguir ilustra isso:

(41)

Exercício Exercício

UVa 127 UVa 514 UVa 673 UVa 727 UVa 336 UVa 10901 UVa 11034 UVa 10226 UVa 11239 UVa 11308 UVa 11136 UVa 908 UVa 11492

LiveArchive 3135 Timus 1930

Você pode fazer eles no codepit.io

CIC 110 4 senha: unifei

Você pode fazer eles no codepit.io

CIC 110 4

senha: unifei

Referências

Documentos relacionados

Local de realização da avaliação: Centro de Aperfeiçoamento dos Profissionais da Educação - EAPE , endereço : SGAS 907 - Brasília/DF. Estamos à disposição

De seguida, vamos adaptar a nossa demonstrac¸ ˜ao da f ´ormula de M ¨untz, partindo de outras transformadas aritm ´eticas diferentes da transformada de M ¨obius, para dedu-

DEPARTAMENTO DE GENÉTICA Unidade de Citogenética Unidade de Genética Médica Unidade de Genética Molecular Unidade de Rastreio Neonatal Unidade de Investigação e

- Se o estagiário, ou alguém com contacto direto, tiver sintomas sugestivos de infeção respiratória (febre, tosse, expetoração e/ou falta de ar) NÃO DEVE frequentar

Mas ele é ( verbo ser, no Presente do Indicativo ) apenas um gato e não tinha tido ( verbo ter, no Pretérito Mais-Que-Perfeito Simples do Indicativo ) tempo de aprender (

Os Credores Operacionais que realizarem novos fornecimentos com Prazo Médio mínimo superior a 60 (sessenta) dias para pagamento, receberão 1% (um por cento) a mais, do

Como objetivos específicos pretendeu-se iden- tificar os taxa existentes nesta gruta, determinar a riqueza de es- pécies de sua comunidade; verificar a influência de fatores

A extensão (na maioria dos casos) não é requerida pelo sistema operacional GNU/Linux, mas é conveniente o seu uso para determinarmos facilmente o tipo de arquivo e que