sábado, 22 de janeiro de 2022

Lógica de Programação - Ordenação Heap Sort

 O algoritmo Heapsort consiste de duas fases

1. construir um Heap de um vetor arbitrário

2. usar o Heap para ordenar os dados


Heaps são árvores binárias. É importante deixar claro desde já que são árvores binárias, mas não são árvores binárias de pesquisa. Mais especificamente, duas propriedades definem o Heap:

1. O valor de um nó é maior ou igual ao valor de seus filhos;

2. O Heap é uma árvore binária completa ou quase-completa da esquerda para a direita.


A adição de um novo elemento sempre é feita na próxima posição livre do array. Essa estratégia garante que o Heap sempre será completo ou quase completo da esquerda para a direita.



Dado n números a serem ordenados

1. Coloque os dados em uma árvore binária

2. Repetir os seguintes passos, 

2.1. Verificar se um nó filho é menor que o pai, 

2.2 Se for, realize a troca


Vetor transformado em árvore binária




Árvore Binária (heap), após a troca de dados entre pais e filhos onde os filhos tenham valores maiores que os pais



Agora pegamos e eliminamos o maior valor da árvore (raiz) e jogamos na última casa do vetor, usamos heap para reorganizar os dados e refazemos o processo, pegando a nova raiz e colocando na penúltima posição do vetor.

Refazemos o processo até que a árvore esteja totalmente eliminada e o vetor ordenado



Código fonte:

Algoritmo "heapsort"  
 // Descrição  : Ordenação por heapsort  
 // Autor(a)  : Cléuber José  
 // Data atual : 22/12/2020  
 Const  
   TAM_VETOR=10  
 Var  
 // Seção de Declarações das variáveis   
    vet :vetor [1..TAM_VETOR] de inteiro  
    i,aux, filho: inteiro  
 procedimento mostrar_vetor()  
 inicio  
   escreval("")  
   escreval("------Dados do vetor------")  
   para i de 1 ate TAM_VETOR faca  
    escreva(vet[i]," ")  
   fimpara  
   escreval("")  
   escreval("--------------------------")  
 fimprocedimento  
 procedimento gerar_dados_vetor()  
 var  
   j:inteiro  
 inicio  
   para i de 1 ate TAM_VETOR faca  
    vet[i]<-i  
   fimpara  
   para i de 1 ate int(TAM_VETOR/2) passo 2 faca  
    aux<-vet[i]  
    vet[i]<-vet[TAM_VETOR+1-i]  
    vet[TAM_VETOR+1-i]<-aux  
   fimpara  
   para i de 1 ate int(TAM_VETOR/2) passo 1 faca  
    j<-randI(TAM_VETOR)+1  
    aux<-vet[i]  
    vet[i]<-vet[j]  
    vet[j]<-aux  
   fimpara  
 fimprocedimento  
 procedimento criar_heap(pai, tamanho:inteiro)  
 inicio  
   aux<-vet[pai]  
   filho<-pai*2  
   enquanto filho<=tamanho faca  
    se(filho <tamanho) entao  
      se vet[filho]<vet[filho+1] entao  
       filho<-filho+1  
      fimse  
    fimse  
    se aux<vet[filho] entao  
      vet[pai]<-vet[filho]  
      pai<-filho  
      filho<-2*pai  
    senao  
      filho<-tamanho+1  
    fimse  
   fimenquanto  
   vet[pai]<-aux  
 fimprocedimento  
 procedimento heap_sort()  
 inicio  
   escreval(int(TAM_VETOR/2))  
   para i de int(TAM_VETOR/2) ate 1 passo -1 faca  
    criar_heap(i,TAM_VETOR)  
   fimpara  
   para i de TAM_VETOR ate 2 passo -1 faca  
    aux<-vet[1]  
    vet[1]<-vet[i]  
    vet[i]<-aux  
    criar_heap(1, i-1)  
   fimpara  
 fimprocedimento  
 Inicio  
 // Seção de Comandos, procedimento, funções, operadores, etc...   
   escreval("---Ordenação por Heapsort---")  
   gerar_dados_vetor()  
   mostrar_vetor()  
   heap_sort()  
   mostrar_vetor()  
 Fimalgoritmo  




quinta-feira, 13 de janeiro de 2022

Estruturas de Dados Avançadas - Árvore Binária com VisuAlg

Para a implementação e uma árvore binária há duas estratégias de alocação de memória, estática ou dinâmica

A mais utilizada é a dinâmica, nela, cada nó da árvore é tratado como um ponteiro alocado dinamicamente a medida que os dados são inseridos

Não precisa saber o tamanho da árvore, os nós são criados a medida que a árvore vai crescendo


Como vamos utilizar a ferramenta VisuAlg para implementação da árvore, não vamos poder utilizar ponteiro, com isso, utilizaremos como estratégia a alocação estática de memória.

Na alocação estática de memória é utilizado um vetor para guardar os dados


Para saber o filho da esquerda ou da direita utilizamos a seguinte operação:

filho esq=2*pai (Filho da esquerda é igual a pai vezes dois)

filho dir=2*pai+1 (Filho da direita é igual a pai vezes dois mais um)

Utilizamos desta forma devido o nosso vetor iniciar com 1 (um), se iniciasse com 0 (zero) ficaria desta forma:

filho esq=2*pai+1 (Filho da esquerda é igual a pai vezes dois mais um )

filho dir=2*pai+2 (Filho da direita é igual a pai vezes dois mais dois)

É uma implementação interessante quando a árvore é completa, pois ocupa bem os espaços, quando a árvore possui um lado maior que o outro, o vetor fica com diversos locais vagos, tendo desperdício de memória.

Exemplo de alocação estática sequencial



Para representar a árvore binária da imagem anterior, o vetor ficaria desta forma:



Então, vamos ver com fica o código de uma árvore binária com inclusão, busca, exclusão e percurso no VisuAlg


 Algoritmo "arvore_binaria"  
 // Descrição  : implentação de uma árvore binária  
 // Autor(a)  : Cléuber José  
 // Data atual : 9/1/2022  
 Const  
   TAM_VETOR=10  
 Var  
 // Seção de Declarações das variáveis   
   vet :vetor[1..TAM_VETOR] de inteiro  
   i,aux, elemento, opcao: inteiro  
 procedimento adicionar()  
 inicio  
   limpatela  
   repita  
    escreval("=========Adicinar elemento=====")  
    escreva("Informe o elemento, 0 (zero) para sair:")  
    leia(elemento)  
    se elemento<>0 entao  
      adicionar_com_posicao(1,elemento)  
      mostrar_vetor()  
    fimse  
   ate elemento=0 faca  
 fimprocedimento  
 procedimento adicionar_com_posicao(posicao, elemento: inteiro)  
 inicio  
   se elemento=vet[posicao] entao  
     escreval("Elemento já adiconado anteriormente")  
   senao  
     se vet[posicao]=0 entao  
      vet[posicao]<-elemento  
      escreval("Elemento adicionado com sucesso")  
     senao  
      se elemento <vet[posicao] entao  
        aux<-2*posicao  
        se aux>TAM_VETOR entao  
         escreval("Vetor já está cheio")  
        senao  
         adicionar_com_posicao(aux,elemento)  
        fimse  
      senao  
        aux<-2*posicao+1  
        se aux>TAM_VETOR entao  
         escreval("Vetor já está cheio")  
        senao  
         adicionar_com_posicao(aux,elemento)  
        fimse  
      fimse  
     fimse  
   fimse  
 fimprocedimento  
 procedimento buscar()  
 inicio  
   limpatela  
   repita  
    escreval("============Buscar Elemento=======")  
    escreva("Informe o elemento, 0 (zero) para sair:")  
    leia(elemento)  
    se elemento<>0 entao  
      aux<- buscar_com_posicao(1,elemento)  
      se aux<>0 entao  
       escreval("Elemento encontrato na posição: ",aux)  
      senao  
       escreval("Elemento não encontrado")  
      fimse  
      mostrar_vetor()  
    fimse  
   ate elemento=0 faca  
 fimprocedimento  
 funcao buscar_com_posicao(posicao, elemento:inteiro): inteiro  
 inicio  
   se vet[posicao]=0 entao  
    retorne 0  
   senao  
    se vet[posicao]=elemento entao  
      retorne posicao  
    senao  
      se elemento< vet[posicao] entao  
       aux <-posicao*2  
       se aux>TAM_VETOR entao  
         retorne 0  
       senao  
          retorne buscar_com_posicao(aux,elemento)  
       fimse  
      senao  
       aux<-posicao*2+1  
       se aux> TAM_VETOR entao  
         retorne 0  
       senao  
         retorne buscar_com_posicao(aux,elemento)  
       fimse  
      fimse  
    fimse  
   fimse  
 fimfuncao  
 procedimento remover()  
 inicio  
   limpatela  
   repita  
    escreval("============Remover Elemento=======")  
    escreva("Informe o elemento, 0 (zero) para sair:")  
    leia(elemento)  
    se elemento<>0 entao  
      aux<-buscar_com_posicao(1,elemento)  
      se aux=0 entao  
       escreval("Elemento não encontrado!")  
      senao  
       remover_com_posicao(aux)  
       escreval("Elemento excluído com sucesso")  
      fimse  
      mostrar_vetor()  
    fimse  
   ate elemento=0 faca  
 fimprocedimento  
 procedimento remover_com_posicao(posicao:inteiro)  
 var  
   filho_esquerda, filho_direita, qtde_filho:inteiro  
 inicio  
   filho_esquerda<-posicao*2  
   filho_direita<-posicao*2+1  
   se filho_esquerda> TAM_VETOR entao  
     filho_esquerda<-0  
   senao  
     se vet[filho_esquerda]=0 entao  
       filho_esquerda<-0  
     senao  
       qtde_filho<-qtde_filho+1  
     fimse  
   fimse  
   se filho_direita> TAM_VETOR entao  
     filho_direita<-0  
   senao  
     se vet[filho_direita]=0 entao  
       filho_direita<-0  
     senao  
       qtde_filho<-qtde_filho+1  
     fimse  
   fimse  
   se qtde_filho=0 entao  
    vet[posicao]<-0  
   senao  
    se qtde_filho=1 entao  
      se filho_esquerda>0 entao  
       vet[posicao]<- vet[filho_esquerda]  
       arrastar_arvore(filho_esquerda,posicao)  
      senao  
       vet[posicao]<- vet[filho_direita]  
       arrastar_arvore(filho_direita, posicao)  
      fimse  
    senao  
      elemento<-minimo_com_posicao(filho_direita)  
      aux<-buscar_com_posicao(filho_direita, elemento)  
      vet[posicao]<-vet[aux]  
      remover_com_posicao(aux)  
    fimse  
   fimse  
 fimprocedimento  
 procedimento arrastar_arvore(posicao_anterior, posicao_atual:inteiro)  
 inicio  
   vet[posicao_anterior]<-0  
   aux<-posicao_anterior*2  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      vet[posicao_atual*2]<-vet[aux]  
      arrastar_arvore(aux, posicao_atual*2)  
    fimse  
   fimse  
   aux<-posicao_anterior*2+1  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      vet[posicao_atual*2+1]<-vet[aux]  
      arrastar_arvore(aux, posicao_atual*2+1]  
    fimse  
   fimse  
 fimprocedimento  
 procedimento pre_ordem()  
 inicio  
   escreval("============Pré Ordem=======")  
   mostrar_vetor()  
   escreval("")  
   escreva("Sequência: ")  
   pre_ordem_com_posicao(1)  
   escreval()  
   repita  
    escreva("Informe 0 (zero) para sair: ")  
    leia(aux)  
   ate aux=0 faca  
 fimprocedimento  
 procedimento pre_ordem_com_posicao(posicao:inteiro)  
 inicio  
   escreva(vet[posicao]," ")  
   aux<-posicao*2  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      pre_ordem_com_posicao(aux)  
    fimse  
   fimse  
   aux<-posicao*2+1  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      pre_ordem_com_posicao(aux)  
    fimse  
   fimse  
 fimprocedimento  
 procedimento em_ordem()  
 inicio  
   escreval("============Em-Ordem=======")  
   mostrar_vetor()  
   escreval("")  
   escreva("Sequência: ")  
   em_ordem_com_posicao(1)  
   escreval()  
   repita  
    escreva("Informe 0 (zero) para sair: ")  
    leia(aux)  
   ate aux=0 faca  
 fimprocedimento  
 procedimento em_ordem_com_posicao(posicao:inteiro)  
 inicio  
   aux<-posicao*2  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      em_ordem_com_posicao(aux)  
    fimse  
   fimse  
   escreva(vet[posicao]," ")  
   aux<-posicao*2+1  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      em_ordem_com_posicao(aux)  
    fimse  
   fimse  
 fimprocedimento  
 procedimento pos_ordem()  
 inicio  
   escreval("============Pós-Ordem=======")  
   mostrar_vetor()  
   escreval("")  
   escreva("Sequência: ")  
   pos_ordem_com_posicao(1)  
   escreval()  
   repita  
    escreva("Informe 0 (zero) para sair: ")  
    leia(aux)  
   ate aux=0 faca  
 fimprocedimento  
 procedimento pos_ordem_com_posicao(posicao:inteiro)  
 inicio  
   aux<-posicao*2  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      pos_ordem_com_posicao(aux)  
    fimse  
   fimse  
   aux<-posicao*2+1  
   se aux<=TAM_VETOR entao  
    se vet[aux]<>0 entao  
      pos_ordem_com_posicao(aux)  
    fimse  
   fimse  
   escreva(vet[posicao]," ")  
 fimprocedimento  
 procedimento menu_principal()  
 inicio  
   repita  
    limpatela  
    escreval("=======Árvore binária===========")  
    escreval("---Informe uma opção----")  
    escreval("1 - Adicionar elemento")  
    escreval("2 - Buscar elemento")  
    escreval("3 - Mínimo")  
    escreval("4 - Máximo")  
    escreval("5 - Remover")  
    escreval("6 - Pré-ordem")  
    escreval("7 - Em-ordem")  
    escreval("8 - Pós-ordem")  
    escreval("0 - Sair")  
    escreva("Informe: ")  
    leia(opcao)  
    escolha (opcao)  
    caso 1  
     adicionar()  
    caso 2  
     buscar()  
    caso 3  
     minimo()  
    caso 4  
     maximo()  
    caso 5  
     remover()  
    caso 6  
     pre_ordem()  
    caso 7  
     em_ordem()  
    caso 8  
     pos_ordem()  
    fimescolha  
  ate opcao=0 faca  
 fimprocedimento  
 procedimento mostrar_vetor()  
 inicio  
   escreval("")  
   escreval("--------Dados do Vetor----")  
   para i de 1 ate TAM_VETOR faca  
    escreva(vet[i], " ")  
  fimpara  
  escreval("")  
  escreval("")  
 fimprocedimento  
 funcao vazio():logico  
 inicio  
  retorne vet[1]=0  
 fimfuncao  
 procedimento minimo()  
 inicio  
   limpatela  
   escreval("-------Menor Elemento da Árvore---")  
   se vazio() entao  
    escreval("Árvore vazia")  
   senao  
    aux <- minimo_com_posicao(1)  
    escreval("Menor elemento da árvore é: ",aux )  
   fimse  
   repita  
    escreva("Informe 0 (zero) para sair: ")  
    leia(aux)  
   ate aux=0 faca  
 fimprocedimento  
 funcao minimo_com_posicao(posicao: inteiro):inteiro  
 inicio  
   aux<-posicao*2  
   se (aux> TAM_VETOR) entao  
    retorne vet[posicao]  
   senao  
    se vet[aux]=0 entao  
      retorne vet[posicao]  
    senao  
      se vet[aux]<vet[posicao] entao  
       retorne minimo_com_posicao(aux)  
      fimse  
    fimse  
   fimse  
 fimfuncao  
 procedimento maximo()  
 inicio  
   limpatela  
   escreval("-------Maior Elemento da Árvore---")  
   se vazio() entao  
    escreval("Árvore vazia")  
   senao  
    aux <- maximo_com_posicao(1)  
    escreval("Maior elemento da árvore é: ",aux )  
   fimse  
   repita  
    escreva("Informe 0 (zero) para sair: ")  
    leia(aux)  
   ate aux=0 faca  
 fimprocedimento  
 funcao maximo_com_posicao(posicao: inteiro):inteiro  
 inicio  
   aux<-posicao*2+1  
   se (aux> TAM_VETOR) entao  
    retorne vet[posicao]  
   senao  
    se vet[aux]=0 entao  
      retorne vet[posicao]  
    senao  
      se vet[aux]>vet[posicao] entao  
       retorne maximo_com_posicao(aux)  
      fimse  
    fimse  
   fimse  
 fimfuncao  
 Inicio  
 // Seção de Comandos, procedimento, funções, operadores, etc...   
   menu_principal()  
 Fimalgoritmo  



terça-feira, 4 de janeiro de 2022

Estruturas de Dados Avançadas - Árvore Binária

Existem vários tipos de árvores, no entanto, as árvores binárias são as mais utilizadas na computação, porque quando ordenadas, permitem que pesquisas, inclusões e exclusões de dados em sua estrutura sejam extremamente rápidas.

As árvores binárias possuem um nó superior também chamado de raiz que aponta para outros nós, chamados de nós filhos, que podem ser pais de outros nós.

Pode ser caracterizada por

* Não ter elemento algum (Árvore Vazia)

* Ter um elemento distinto, denominado raiz, com dois filhos, denominados subárvore esquerda e subárvore direita.


Inserção de elemento

* Se ainda não há nó raiz, então o novo elemento será o próprio nó raiz

* Se há nó raiz, então compare o novo elemento com o nó raiz

** caso o novo elemento seja menor que o nó raiz, ele vai ser inserido na subárvore da esquerda

** caso o novo elemento seja maior que o nó raiz, ele será inserido na subárvore da direita

Observação: se a subárvore já tiver um valor, a regra acima será usada em recursividade


Para a gente entender um pouquinho melhor como funciona isso, vamos para o seguinte exemplo

Vamos fazer a inserção de seis elementos:  30, 15, 50, 25, 10, 31.










Remoção de elemento

* Se tiver somente um nó, ele será apagado


* se for apagar um nó folha, também não haverá problema, só excluí-lo



* se for deletar um nó que tenha apenas um filho, deleta o nó e o substitui pelo seu filho, trazendo toda a subárvore do filho





* se for deletar um nó que tenha dois filhos

** O nó deletado só pode ser substituído pelo maior nó da subárvore da esquerda ou o menor da subárvore da direita

Observação: utilizar isso recursivamente





Percursos em árvore


São algoritmos que percorrem caminhos pré-definidos em uma estrutura de árvore, cobrindo todos os nós.

Eles tem nomes e aplicações específicas

Há percursos de Pré-ordem, Pós-ordem, Em-ordem, Percurso em largura, Percurso em profundidade e Caminho Euleriano, mas aqui, vamos focar somente nos três primeiros


Pré-ordem

1. Executa a operação do algoritmo primeiro no nó atual

2. Se tem filho à esquerda: executa o pré-ordem no filho da esquerda

3. Se tem filho a direita: executa o pré-ordem no filho da direita

Sequência em pré-ordem: 30, 15, 10, 25 e 50


Em-ordem

1. Se tem filho a esquerda: executa o em ordem no filho da esquerda

2. Executa a operação do algoritmo no nó atual

3. Se tem filho a direita: executa o em-ordem no filho da direita

Sequência em em-ordem: 10, 15, 25, 30 e 50


Pós-ordem

1. Se tem filho à esquerda: executa o pós ordem no filho da esquerda

2. Se tem filho à direita: executa o pós ordem no filho da direita

3. Executa a operarão do algoritmo no nó atual

Sequência em pós-ordem: 10, 25, 15, 50 e 30


Com isso a gente fez uma introdução a árvore binária, no próximo post, vamos ver isso codificado, utilizando a ferramenta VisuAlg

Até lá