Você está na página 1de 3

Alocação dinâmica de vetores (malloc): conceito q permite alocar memória durante a execução do programa para armazenar dados em um vetor.

A função `mallo
alocar a quantidade necessária de memória e retorna um ponteiro para o início da região alocada. É importante liberar a memória alocada usando a função `free`
mais necessária, para evitar vazamentos de memória.

// Alocação dinâmica de memória para o vetor


vetor = (int *)malloc(tamanho * sizeof(int));
if (vetor == NULL) {
printf("Falha na alocação de memória\n");
return 1;
}
// Liberando a memória alocada dinamicamente
free(vetor);
return 0;
}
Tipos estruturados e Vetor de Estruturas: Além da alocação dinâmica de vetores, tbm é possível realocar memória dinamicamente usando a função `realloc`.
Essa função permite alterar o tamanho de uma região de memória previamente alocada, preservando os dados existentes. Além disso, é possível alocar memória
dinamicamente para estruturas personalizadas, criando tipos estruturados. Dessa forma, é possível criar um vetor de estruturas, onde cada elemento do vetor é
uma instância da estrutura definida.
typedef struct aluno {
char nome[40];
float A1,A2,A3;
float media; }Aluno,* PAluno, **PPAluno;
V1: Declaração = “Aluno a;” / Acesso = “a.A1 = 10”;
V2: Declaração = “Aluno a;” / Acesso = “void incluir_aluno(PAluno a) {a->A1 = 10;}”
V3: Declaração = “Aluno alunos[MAX];” / Acesso = “void inclui_aluno(PAluno a){a[0].A1=10;}”
V4: Declaração = “PAluno alunos=(PAluno) malloc(MAX*sizeof(Aluno));” / Acesso = “a[0].A1=10;”
V5: Declaração = “PAluno alunos[MAX]” / Acesso = ”alunos[0]=(PAluno)malloc(sizeof(Aluno));”
V6: Declaração = “PAluno alunos=(PAluno) malloc(MAX*sizeof(Aluno));”
Acesso = ”alunos[0]=(PAluno)malloc(sizeof(Aluno));”
Realloc = ”alunos=(PAluno)realloc(alunos,NOVO_MAX*sizeof(Aluno));”
Lista Encadeada - Agrupamento de informações não contíguos: é uma estrutura de dados na qual os elementos estão armazenados em nós. Cada nó contém um
valor e um ponteiro para o próximo nó da lista. A lista encadeada permite a inserção e remoção eficientes de elementos em qualquer posição da lista, uma vez
que não requer realocação de memória, ao contrário dos vetores.
typedef struct lista{
int info;
struct lista* prox;}TLSE; //Tipo Lista Simplemente Encadeada
/*Inserir no início - versão iterativa*/
TLSE* insere_ini(TLSE* l, int elem){
TLSE* novo = (TLSE*) malloc(sizeof(TLSE));
if(!novo) exit(1);
novo->info = elem;
novo->prox = l;
return novo;}
/*Inserir no fim - versão iterativa*/
TLSE* insere_fim(TLSE* l, int elem){
if(!l)
return insere_ini(l,elem);
TLSE* p = l;
while(p->prox) p=p->prox;
p->prox = insere_ini(NULL,elem);
return l;}
Lista Encadeada recursiva: é uma variação da lista encadeada em que cada nó contém um ponteiro para o próximo nó da lista. Essa estrutura pode ser implement
recursiva, onde a função de inserção e remoção é chamada recursivamente para percorrer a lista até a posição desejada.
/*Inserir no fim - usando recursividade*/
TLSE* insere_fim_rec(TLSE* l, int elem){
if(!l)
return insere_ini(l,elem);
l->prox = insere_fim_rec(l->prox,elem);
return l;}
Lista Duplamente encadeada (descritores): ): é uma estrutura de dados na qual cada nó contém um valor, um ponteiro para o próximo nó e um ponteiro para o
nó anterior. Essa estrutura permite a navegação em ambas as direções da lista. Os nós de cabeça (header) e cauda (tail) são chamados de descritores e são
utilizados para marcar o início e o fim da lista, facilitando a manipulação da lista.

typedef struct no{


Produto info;
struct no* ant;
struct no* prox;}No;
struct lista{
int qtd;
No* prim;
};//Descritor da lista guarda e informações da lista total e início

void inserir_inicio(Lista* l, Produto* prod){


No* no = (No*) malloc(sizeof(No));
no->ant = NULL;
no->info.codigo = prod->codigo;
no->info.preco = prod->preco;
no->info.qtdEstoque = prod->qtdEstoque;
no->prox = l->prim;
if(l->prim)
l->prim->ant = no;
l->prim = no;
l->qtd++;}

Pilha (vetor + encadeada com descritores): ): é uma estrutura de dados na qual os elementos são inseridos e removidos somente em uma extremidade, chamada
ser implementada utilizando um vetor ou uma lista encadeada com descritores. As operações básicas de uma pilha são `push` (inserir elemento no topo) e `pop` (r
topo).
typedef struct no{
float info;
struct no* prox;}No;
struct pilha{
No* topo; // guarda o último inserido
int qtd;}

Fila (circular com vetor + encadeada com descritores): é uma estrutura de dados na qual os elementos são inseridos em uma extremidade (fim) e removidos na ou
(início). A fila pode ser implementada utilizando um vetor de forma circular ou uma lista encadeada com descritores. As operações básicas de uma fila são `enqueu
no fim) e `dequeue` (remover elemento do início).

/* Estrutura Ultilizada para representar o nó dos elementos da Fila*/


struct no {
float info;
struct no* prox;};
/* Estrutura que armazena as informações da fila*/
struct fila {
int total;
No* ini;
No* fim;};
Árvore Binária de Busca (n = nós) (complexidade busca de pior caso O(h), com altura mínima,O(log n) )
Altura máxima = n; Altura mínima = piso (log2n) + 1
é uma estrutura de dados não linear composta por nós. Cada nó pode ter zero ou mais filhos, formando uma estrutura hierárquica. Existem diferentes tipos de
árvores, sendo a árvore binária um dos tipos mais comuns. A árvore binária de busca é uma árvore binária onde cada nó possui um valor único e a propriedade de
que todos os valores menores que o valor do nó à esquerda e todos os valores maiores estão à direita.
Árvore Estritamente Binária - Se uma árvore tem n > 0 nós, então ela possui n+1 subárvores vazias.
Uma árvore estritamente binária é aquela em que cada nó tem 0 ou 2 subárvores, ou seja, nenhum nodo tem “filho único”.
Árvore Binária Cheia / completa - é aquela cujos nodos, exceto os do último nível, tem exatamente duas sub-árvores. O número de nodos de uma árvore binária
cheia é 2h-1, em que h é a sua altura.
Uma árvore binária é dita completa se todos os níveis, exceto o último (sem contar o nível das folhas), estão cheios; Os nós do último nível estão o mais à
esquerda possível;

Busca em uma Árvore Binária


Percursos: R = Raiz , E = subárvore da Esquerda, D= subárvore da Direita

4
2 6
1 3 5 7

Pré-ordem: 4 2 1 3 6 5 7 - Inicia em profundidade, sempre iniciando pela esquerda.


In ordem: 1 2 3 4 5 6 7 – Respeita a ordem numérica
Pós ordem: 1 3 2 5 7 6 4 – Inicia com o último número da esquerda, lê o do lado e vai subindo, até a raiz.
Em nível: 4 2 6 1 3 5 7 – Le considerando os níveis.

1. Percurso em Pré-Ordem (R, E, D)


void imprimir_preOrdem(TAB* t){
if(!vazia_arv(t)) {
printf("%d ", t->num); // mostra a raiz
imprimir_preOrdem(t->sae); // mostra a sae (subárvore à esquerda)
imprimir_preOrdem(t->sad); // mostra a sad (subárvore à direita)}}
2. Percurso In-Ordem (E, R, D)
void imprimir_emOrdem(TAB* t){
if(!vazia_arv(t)) {
imprimir_emOrdem(t->sae); // mostra a sae (subárvore à esquerda)
printf("%d ", t->num); // mostra a raiz
imprimir_emOrdem(t->sad); // mostra a sad (subárvore à direita)}}
3. Percurso em Pós-Ordem (E, D, R)
void imprimir_posOrdem(TAB* t){
if(!vazia_arv(t)){
imprimir_posOrdem(t->sae); // mostra a sae (subárvore à esquerda)
imprimir_posOrdem(t->sad); // mostra a sad (subárvore à direita)
printf("%d ", t->num); // mostra a raiz
} }
typedef struct arvoreTAB{
int num;
struct arvoreTAB* sad;
struct arvoreTAB* sae;
} TAB; //Tipo Árvore Binária
/*Imprimir em Nível*/
void imprimir_emNivel(TAB* t){
if(!t) return;
Fila* f = cria_f();
insere_f(f,t);
while(!vazia_f(f)){
TAB* aux = retira_f(f);
printf("%d ",aux->num);
if(aux->sae) insere_f(f,aux->sae);
if(aux->sad) insere_f(f,aux->sad); }
libera_f(f);}
Árvore AVL Árvores Balanceadas Complexidade O(log2n)
Definição: A definição de uma árvore binária de altura equilibrada (AVL) requer que cada sub-árvore seja também de altura equilibrada

Fator de balanceamento ou fator de equilíbrio de um nó T em uma árvore binária é definido como sendo hE – hD onde hE e hD são as alturas das sub-
árvores esquerda e direita de T, respectivamente. (h= altura, E=esquerda, D=Direita)

Para qualquer nó T numa árvore AVL, o fator de balanceamento assume o valor -1, 0 ou +1
Rebalanceamento FB(fator de balanceamento):
LL => FB(pai) = +2 ; FB(esq) >= 0 | Rotação Simples à Direita (RSD)
RR => FB(pai) = -2 ; FB(dir) <= 0 | Rotação Simples à esquerda (RSE)
LR => FB(pai) = +2 ; FB(esq) < 0 | RSE(esq) depois RSD(pai)
RL => FB(pai) = -2 ; FB(dir) > 0 | RSD(dir) depois RSE(pai)
static no* rot_dir( no* k2 ){
no* k1 = NULL;
k1 = k2->esq;
k2->esq = k1->dir;
k1->dir = k2;
k2->alt = max( calc_alt( k2->esq ), calc_alt( k2->dir ) ) + 1;
k1->alt = max( calc_alt( k1->esq ), k2->alt ) + 1;
return k1; /* nova raiz */}

static no* rot_esq( no* k1 ){


no* k2;
k2 = k1->dir;
k1->dir = k2->esq;
k2->esq = k1;
k1->alt = max( calc_alt( k1->esq ), calc_alt( k1->dir ) ) + 1;
k2->alt = max( calc_alt( k2->dir ), k1->alt ) + 1;
return k2; /* nova raiz */}
Árvore B e B+
1) Seja T uma árvore B com raiz (root[T]). Ela possuirá então as seguintes propriedades:
Todo o nó x tem os campos:
A) n[x]: o número de chaves atualmente armazenadas em x,
B) as n[x] chaves armazenadas em ordem crescente, i.e., key[x] <= key1[x] <= . . . <= keyn[x][x]
C) folha [x]: um valor booleano que vale true se x é uma folha e false se x é um nó interno.2
2) Cada nó interno x tbm contém n[x] + 1 apontadores c0[x], c1[x], ...,cn[x][x] para os filhos. As folhas têm todos seus apontadores nulos; assim seus
campos ci são indefinidos (null).
3) Todas as folhas têm a mesma profundidade, que é a altura da árvore: h
4) As chaves keyi[x] separam os intervalos de chaves armazenadas em cada sub-árore. Assim, se ki é uma chave armazenada na sub-árvore com raiz
ci[x],então: k0≤key0[x]≤k1≤key1[x]≤...≤keyn[x]−1[x]≤ kn[x]
5) Existem limites superiores e inferiores para o número de chaves num nó. Eles são expressos em termos de um inteiro fixo t ≥ 2 chamado grau
mínimo da árvore:
A) Todo nó que não seja raiz deve ter pelo menos t − 1 chaves. Portanto, todo nó interno que não seja a raiz tem pelo t ou mais filhos. Se a árvore for
não vazia, a raiz deve ter pelo menos uma chave.
B) Todo nó pode conter no máximo 2t − 1 chaves. Portanto, um nó interno, pode ter no máximo 2t filhos. O nó dito estar cheio quando ele contém
exatamente 2t − 1 chaves
Heap (heapsort=> O(nlogn))
Um Heap Binário é uma estrutura de dados que representa uma árvore binária completa,
Máximo: onde o valor de cada nó é maior ou igual ao valor de seus filhos.
Mínimo: onde o valor de cada nó é menor ou igual ao valor de seus filhos.
Tabela Hash: é uma estrutura de dados que permite armazenar e recuperar valores associados a chaves de maneira eficiente, podem ser usadas para buscar
um elemento da tabela em ordem constante: O(1).O preço pago por essa eficiência será um uso maior de memória.
Algoritmo:
- Defina o tamanho da tabela de hash, geralmente uma potência de 2 para melhor distribuição dos dados.
- Crie uma função de dispersão que converta a chave em um índice de tabela. Essa função deve distribuir as chaves de forma uniforme e minimizar colisões.
- Inicialize a tabela de hash com os buckets vazios.
- Para cada par chave-valor a ser inserido na tabela de hash:
- Aplique a função de dispersão na chave para obter o índice de tabela correspondente.
- Verifique se ocorre uma colisão no índice de tabela. Se ocorrer, utilize uma técnica de resolução de colisão para determinar um novo índice disponível.
- Insira o par chave-valor no bucket correspondente ao índice de tabela.
- Para recuperar o valor associado a uma chave específica:
- Aplique a função de dispersão na chave para obter o índice de tabela correspondente.
- Acesse o bucket do índice de tabela e verifique se a chave existe. Se existir, retorne o valor correspondente.
- Opcionalmente, implemente operações adicionais, como remoção de pares chave-valor e redimensionamento da tabela de hash para lidar com a carga e o
desempenho.
- Ela cria um índice baseado numa função hash dos dados, o que permite descobrir o índice a partir do dado que se pretende buscar

Você também pode gostar