quarta-feira, 31 de outubro de 2007

Uso de memória da matriz e lista

showMemoryUse()



O método exibe a quantidade de memória usada pela matriz de adjacência e pela lista de adjacência.
O cálculo do uso de memória é o seguinte:



  • Para a matriz

    • É pego a quantidade de nós da matriz e elevado ao quadrado. O resultado é multiplicado por 4 (tamanho de inteiros, em bytes)
    • qtdnos*qtdnos*4

  • Para a lista

    • Primeiro se é multiplicado a quantidade de nós por 4. Isso é para calcular o tamanho do vetor que armazenará as listas encadeadas.
    • O resultado da multiplicação da quantidade de nós por 4 é somado com a quantidade de arestas multiplicada por 12. Isso por que cada elemento da lista (que é, na verdade, uma aresta) armazena um peso e o indice do nó para onde se tem a ligação, ambos inteiros (tamanho 4 bytes) e um ponteiro para o próximo elemento, sendo que o ponteiro utiliza também 4 bytes.
    • (qtdnos*4) + (qtdarestas * 12);





Realizando alguns testes, observamos que a lista de adjacência só é eficiente caso seja usada para representar uma matriz esparsa. Matrizes não-esparsas ainda são eficientes, na questão de uso de memoria, na forma de matrizes de adjacência.



Exemplo



Levando em consideração um grafo de 100 nós, o uso da matriz de adjacência é de 40000 bytes. A lista de adjacência, por sua vez, usa apenas 400 bytes se não houver arestas. Se preenchermos o grafo com 10000 arestas, fazendo todos os nós se conectarem, o uso de memória da matriz permanece inalterado enquanto o uso de memória da lista sobe para 120400 bytes.



void Grafo::showMemoryUse()
{
int mem_matr;
int mem_list;
mem_matr = qtdnos*qtdnos*4;
mem_list = (qtdnos*4) + (qtdarestas * 12);
cout << "Memória usada pela matriz: " << mem_matr << endl;
cout << "Memória usada pela lista: " << mem_list << endl;
}

quarta-feira, 24 de outubro de 2007

Gerar o grafo a partir de um arquivo de texto


Aqui está o método para preencher o grafo a partir de um arquivo de texto.
O arquivo deve seguir o seguinte padrão para que seja interpretado corretamente:



N
p0
p1
p2
...
pN-1
A
X Y P D
X Y P D
X Y P D
X Y P D
X Y P D


  • N - número de nós que o grafo irá conter
  • p0 ~ pn-1 - o peso de cada nó a ser inserido
  • A - número de arestas a serem inseridos no grafo
  • X, Y - coordenadas do da aresta a ser inserida
  • P - Peso da aresta
  • D - Diz se a aresta é direcional(1) ou não (0)




Um exemplo de arquivo válido:



2
1
2
4
0 0 2 1
0 1 1 1
1 0 5 1
1 1 6 1


O seguinte formato de arquivo também é válido, mas não é bom usar pois é difícil identificar os itens do arquivo



2 1 2 4 0 0 2 1 0 1 1 1 1 0 5 1 1 1 6 1


O método para ler arquivos:




void Grafo::readFile(string filename){
ifstream arquivo (filename.c_str());
if( arquivo.is_open()){
int contador, temp, x, y, p;
arquivo >> qtdnos;
dimensionar(qtdnos);
for( int i = 0; i < qtdnos; i++)
arquivo >> pesos[i];
arquivo >> contador;
for(int i = 0; i < contador; i++){
arquivo >> x >> y >> p >> temp;
setAresta(x,y,p);
if(temp == 0)
setAresta(y,x,p);
}
} else {
cout << "erro: arquivo inexistente/inválido" << endl;
}
}



O método recebe o nome do arquivo como parametro (uma string) e tenta abri-lo. Caso consiga, ou seja, caso o arquivo exista, é iniciada a interpretação do arquivo na seguinte lógica:



  • É lida a primeira linha, que contém o numero de nós do grafo. Com isso ele é redimensionado pelo método dimensionar(int tamanho)
  • Na próximas N linhas (N sendo a quantidade de nós lido) são lidos e armazenados os pesos de cada nó.
  • Assim que a última linha de pesos for lida, é lido a quantidade de arestas a serem inseridas na matriz de adjacência do grafo
  • Cada linha que contém as informações da aresta é lida a aresta adicionada conforme o peso. Caso a aresta não seja direcional(0), uma nova aresta com o mesmo peso é inserido nas coordenadas inversas (se as coordenadas era 1,2 e a aresta não é direcional, uma nova aresta é colocada em 2,1)



Usando exemplo de arquivo de entrada dado aqui, o grafo é preenchido desta maneira:



Matriz
0 1
0|2 1
1|5 6
Pesos
0|1|
1|2|



A saída acima foi gerada usando o método showAll(). Para ficar mais legível, alteramos um pouco o método os métodos showPesos() e showMatriz(), além de adicionar um teste em showAll() para verificar se o grafo está vazio ou não



void Grafo::showMatriz(){
cout << " ";
for(int i = 0; i < qtdnos; i++)
cout << i << " ";
cout << endl;
for(int i = 0; i < qtdnos; i++){
cout << i << "|";
for (int j = 0; j < qtdnos; j++){
cout << matriz[i][j] << " ";
}
cout << endl;
}
}

void Grafo::showPesos(){
for(int i = 0; i < qtdnos; i++)
cout << i << "|";
cout << endl;

for(int i = 0; i < qtdnos; i++)
cout << pesos[i] << "|";
cout << endl;
}

void Grafo::showAll(){
if(qtdnos != 0){
cout << "Matriz" << endl;
showMatriz();
cout << "Pesos" << endl;
showPesos();
} else {
cout << "Grafo vazio"<< endl;
}
}



O código fonte da classe grafo com todas as alterações até agora pode ser baixado aqui.

terça-feira, 23 de outubro de 2007

Correções na classe grafo


Pois é. Haviam alguns erros na classe grafo que postamos. Nenhum deles foi sério (apenas esquemos de colocar a declaração de alguns métodso dentro da classe, e coisas assim). No entanto, o método dimensionar(int tamanho) pode ser problemático dependendo do compilador usado. O problema está no final do post (assim como uma possível solução).


Classe



class Grafo{
private:
int qtdnos;
int **matriz;
int *pesos;
public:
Grafo();
Grafo(int tamanho);
void dimensionar(int tamanho);
void setAresta(int linha, int coluna);
void setAresta(int linha, int coluna, int peso);
void setPeso(int no, int peso);
int getPeso(int no);
int getAresta(int linha, int coluna);
int size();
void showMatriz();
void showPesos();
void showAll();
};



Construtores



Grafo::Grafo(){
dimensionar(0);
}
Grafo::Grafo(int tamanho){
dimensionar(tamanho);
}




dimensionar(int tamanho)



Este método funcionou sem problemas usando o compilador g++. No entanto, quando o compilamos usando o Builder, os ponteiros para inteiro alocados no vetor de pesos e na matriz de adjacência não foram setados para NULL por padrão, o que faz o grafo ser gerado com lixo.
Criamos um workaround para resolver o problema, mas é apresentado dois warnings ao compilar com o g++ (não sei se com o Builder estes warnings aparecem):



grafo.cpp:18: warning: converting to non-pointer type ‘int’ from NULL
grafo.cpp:20: warning: converting to non-pointer type ‘int’ from NULL

linha 18: pesos[i] = NULL;
linha 20: matriz[i][j] = NULL;


Então se alguém pensar em uma solução melhor, por favor, avise :)




void Grafo::dimensionar(int tamanho) {
if (tamanho > 0) {
qtdnos = tamanho;
pesos = new int[qtdnos];
matriz = new int*[qtdnos];
for(int i = 0; i < qtdnos; i++){
matriz[i] = new int[qtdnos];
pesos[i] = NULL;
for(int j = 0; j < qtdnos; j++)
matriz[i][j] = NULL;
}
} else {
qtdnos = 0;
pesos = NULL;
matriz = NULL;
}
}

quinta-feira, 18 de outubro de 2007

Classe para Grafos


Segue abaixo a classe de Grafos.
É usada uma matriz de adjacência para marcar onde há arestas e o peso delas.
Um vetor separado é usado para armazenar o peso de cada nó do grafo.


  class Grafo{
private:
int qtdnos;
int **matriz;
int *pesos;
public:
Grafo();
Grafo(int tamanho);
void dimensionar(int tamanho);
void setAresta(int linha, int coluna);
void setAresta(int linha, int coluna, int peso);
void setPeso(int no, int peso);
int getPeso(int no);
int getAresta(int linha, int coluna);
int size();
void showMatriz();
void showPesos();
void showAll();
};





Construtores



Possui dois contrutores. Ambos tem o procedimento interno tratado pelo método dimensionar(int tamanho)


  Grafo::Grafo(){
dimensionar(0);
}
Grafo::Grafo(int tamanho){
dimensionar(tamanho);
}





dimensionar



O método dimensionar é responsavél pela criação das dimensões da matriz de adjacência, vetor de peso e a quantidade de nós.



Seu funcionamento é o seguinte:



  • Se receber um valor maior que zero:

    • A quantidade de nós será definada como esse valor
    • O vetor de pesos será criado dinâmicamente.
    • A matriz de adjacência é criada, utilizando um vetor de ponteiros para inteiros e, em cada posição, um vetor de inteiros


  • Se o valor recebido foir igual ou menor a zero

    • A quantidade de nós será definica como zero
    • Tanto o vetor de pesos quanto a matriz de adjacência apontarão para NULL



  void Grafo::dimensionar(int tamanho) {
if (tamanho > 0) {
qtdnos = tamanho;
pesos = new int[qtdnos];
matriz = new int*[qtdnos];
for(int i = 0; i < qtdnos; i++)
matriz[i] = new int[qtdnos];
} else {
qtdnos = 0;
pesos = NULL;
matriz = NULL;
}
}




.:Uma dificuldade encontrada pelo grupo foi na alocação da matriz.



Inicialmente usamos o seguinte código para tentar aloca-la:


  *matriz = new int[qtdnos];
for(int i = 0; i < qtdnos; i++)
matriz[i] = new int[qtdnos];



O erro estava logo na primeira alocação da matriz. Ali está para atribuir um vetor de inteiros ao ponteiro. No entanto, o objetivo é fazer com que seja atribuido um vetor de ponteiros para inteiros e depois, para cada posição do vetor, atribuir um vetor de inteiros.






setAresta e getAresta



Existem dois métodos com o nome de setAresta. O primeiro, que recebe apenas coordenadas X,Y (neste caso, representados por i e j), chama o segundo método setAresta, desta vez passando o valor 1 como parametro adicional. O segundo método setAresta, que recebe coordenadas X,Y e um peso, faz com que o peso recebido seja inserido na matriz, na posição especificada por i e j.



O método getAresta recebe apenas uma coordenada X,Y e retorna o valor armazenado na matriz naquela posição.


  void Grafo::setAresta(int i, int j){
setAresta(i, j, 1);
}

void Grafo::setAresta(int i, int j, int peso){
matriz[i][j] = peso;
}

int Grafo::getAresta(int i, int j){
return matriz[i][j];
}





setPeso e getPeso




O método setPeso recebe apenas dois valores como parametro: o primeiro é o nó que terá seu peso alterado; o segundo é o valor a ser atribuido ao nó especificado.



O método getPeso recebe como parametro apenas o nó a ser retornado.


  void Grafo::setPeso(int i, int peso){
pesos[i] = peso;
}

int Grafo::getPeso(int i){
return pesos[i];
}





size



Este método apenas retorna a quantidade de nós existentes no grafo


  int Grafo::size(){
return qtdnos;
}





showMatriz, showPesos e showAll



Métodos para exibir o conteúdo do gráfo. Nada mais.


  void Grafo::showMatriz(){
for(int i = 0; i < qtdnos; i++){
for (int j = 0; j < qtdnos; j++){
cout << matriz[i][j] << " ";
}
cout << endl;
}
}

void Grafo::showPesos(){
for(int i = 0; i < qtdnos; i++)
cout << i << " ";
cout << endl;

for(int i = 0; i < qtdnos; i++)
cout << pesos[i] << " ";
cout << endl;
}

void Grafo::showAll(){
cout << "Matriz" << endl;
showMatriz();
cout << "Pesos" << endl;
showPesos();
}

quinta-feira, 13 de setembro de 2007

BTREE Completa!!!

#include <stdio.h>
#include <conio.h>
 
/**
* Classe de nó binário
*/
class bnode;
typedef bnode *ptrno;
class bnode{
 
private:
int valor;
ptrno esquerda;
ptrno direita;
 
public:
bnode(){valor=0;esquerda=direita=NULL;}
void setValor(int v);
void setEsquerda(ptrno n);
void setDireita(ptrno n);
int getValor();
ptrno getEsquerda();
ptrno getDireita();
};
 
 
/**
* Define um valor para o nó
*/
void bnode::setValor(int v){
valor = v;
}
 
 
/**
* Define o nó a esquerda
*/
void bnode::setEsquerda(ptrno n){
esquerda = n;
}
 
 
/**
* Define um nó a direita
*/
void bnode::setDireita(ptrno n){
direita = n;
}
 
 
/**
* Retorna o valor do nó
*/
int bnode::getValor(){
return valor;
}
 
 
/**
* Retorna o endereço do nó a esquerda
*/
ptrno bnode::getEsquerda(){
return esquerda;
}
 
 
/**
* Retorna o endereço do nó a direita
*/
ptrno bnode::getDireita(){
return direita;
}
 
 
/**
* Classe de arvore binária
*/
class btree{
 
private:
ptrno raiz;
void push(ptrno &r, int v);
void mostreCrescente(ptrno r);
void mostreDecrescente(ptrno r);
int maior(ptrno r);
int menor(ptrno r);
int numeroNos(ptrno r);
int altura(ptrno r);
void nosInternos(ptrno r);
int numeroFolhas(ptrno r);
bool isDegenerada(ptrno r);
bool isCompleta(ptrno r, int n,int h);
bool isCheia(ptrno r, int n,int h);
void posOrdem(ptrno r);
void parentAnin(ptrno r);
void hierar2(ptrno r, int h);
bool isEstrBinary(ptrno r);
 
public:
btree(){raiz = NULL;}
void push(int v);
void mostreCrescente();
void mostreDecrescente();
int maior();
int menor();
int numeroNos();
int altura();
void nosInternos();
int numeroFolhas();
bool isDegenerada();
bool isCompleta();
bool isCheia();
void posOrdem();
void parentAnin();
void hierar2();
bool isEstrBinary();
};
 
 
/**
* Interface para o método push
*/
void btree::push(int v){
push(raiz,v);
}
 
 
/**
* Insere um valor na btree (menor a esquerda, maior a direita)
*/
void btree::push(ptrno &r, int v){
ptrno aux;
if(r==NULL){
r = new(bnode);
r->setValor(v);
r->setEsquerda(NULL);
r->setDireita(NULL);
}else if(v > r->getValor()){
aux = r->getDireita();
push(aux,v);
r->setDireita(aux);
}else{
aux = r->getEsquerda();
push(aux,v);
r->setEsquerda(aux);
}
}
 
 
/**
* Interface para o método mostreCrescente
*/
void btree::mostreCrescente(){
mostreCrescente(raiz);
}
 
 
/**
* Método que exibe os valores em ordem crescente (ordem central, esquerda - raiz - direita)
*/
void btree::mostreCrescente(ptrno r){
if(r != NULL){
mostreCrescente(r->getEsquerda());
printf("%d ",r->getValor());
mostreCrescente(r->getDireita());
}
}
 
 
/**
* Interface para o método mostreDecrescente
*/
void btree::mostreDecrescente(){
mostreDecrescente(raiz);
}
 
 
/**
* Exibe os valores da arvore binaria em ordem decrecente (direita - raiz - esquerda)
*/
void btree::mostreDecrescente(ptrno r){
if(r != NULL){
mostreDecrescente(r->getDireita());
printf("%d ",r->getValor());
mostreDecrescente(r->getEsquerda());
}
}
 
 
/**
* Interface para o método maior
*/
int btree::maior(){
return maior(raiz);
}
 
 
/**
* Retorna o maior valor na árvore binária (extrema direita)
*/
int btree::maior(ptrno r){
if(r->getDireita() != NULL)
return maior(r->getDireita());
else
return r->getValor();
}
 
/**
* Interface para o método menor
*/
int btree::menor(){
return menor(raiz);
}
 
 
/**
* Retorna o menor valor na árvore binária (extrema esquerda)
*/
int btree::menor(ptrno r){
if(r->getEsquerda() != NULL)
return menor(r->getEsquerda());
else
return r->getValor();
}
 
 
/**
* Interface para o método numeroNos
*/
int btree::numeroNos(){
return numeroNos(raiz);
}
 
 
/**
* Retorna a quantidade de nós existentes na árvore binária
*/
int btree::numeroNos(ptrno r){
if(r != NULL)
return numeroNos(r->getEsquerda()) + numeroNos(r->getDireita()) +1;
else
return 0;
}
 
 
/**
*métodos que serão utilizados nos métodos a seguir
*/
 
/**
*método max compara dois valores inteiros e retorna o valor maior
*/
int max(int a, int b){
if(a > b)
return a;
else
return b;
}
 
 
/**
*método equal compara dois dados do tipo bool e se os dois dados em questão
*forem true, o método retorna true, senão retorna false
*/
bool equal(bool a, bool b){
if(a==true && b==true)
return true;
else
return false;
}
 
 
/**
* Interface para o método altura
*/
int btree::altura(){
return altura(raiz);
}
 
 
/**
* Retorna a altura da árvore binária
*/
int btree::altura(ptrno r){
if(r != NULL)
return 1+max(altura(r->getEsquerda()),altura(r->getDireita()));
else
return 0;
}
 
 
/**
* Interface para o método nosInternos
*/
void btree::nosInternos(){
nosInternos(raiz);
}
 
 
/**
* Exibe os valores dos nós internos da árvore binária
*/
void btree::nosInternos(ptrno r){
if(r!=NULL){
nosInternos(r->getEsquerda());
if(r->getEsquerda() == NULL){
if(r->getDireita() != NULL)
printf("%d ", r->getValor());
}else
printf("%d ", r->getValor());
nosInternos(r->getDireita());
}
}
 
 
/**
* Interface para o método numeroFolhas
*/
int btree::numeroFolhas(){
return numeroFolhas(raiz);
}
 
 
/**
* Retorna a quantidade de nós folha
*/
int btree::numeroFolhas(ptrno r){
if(r!=NULL){
if(r->getEsquerda() == NULL && r->getDireita() == NULL)
return 1;
else
return numeroFolhas(r->getEsquerda())+numeroFolhas(r->getDireita());
}
else
return 0;
}
 
 
/**
* Interface para o método isDegenerada
*/
bool btree::isDegenerada(){
return isDegenerada(raiz);
}
 
 
/**
* Verifica se a árvore é degenerada e, caso seja, retorna true. Caso não seja degenerada, retorna false
*/
bool btree::isDegenerada(ptrno r){
if(r != NULL){
if(r->getEsquerda() != NULL && r->getDireita() != NULL)
return false;
else if(r->getDireita() == NULL && r->getEsquerda() == NULL)
return true;
else{
//return true;
if(r->getEsquerda() != NULL)
return isDegenerada(r->getEsquerda());
else
return isDegenerada(r->getDireita());
}
}
else
return true;
}
 
 
/**
* Interface para o método isCheia
*/
bool btree::isCheia(){
return isCheia(raiz, 1, altura());
}
 
 
/**
* Verifica se a árvore é cheia e, caso seja, retorna true
*/
bool btree::isCheia(ptrno r, int n,int h){
if(r != NULL){
if(r->getEsquerda()!=NULL && r->getDireita()!=NULL)
return equal(isCheia(r->getEsquerda(),n+1,h), isCheia(r->getDireita(),n+1,h));
else{
if(n >= h)
return true;
else
return false;
}
}
else
return true;
}
 
 
/**
* Interface para o método isCompleta()
*/
bool btree::isCompleta(){
return isCompleta(raiz, 1, altura());
}
 
 
/**
* Verifica se a árvore é completa e, caso seja, retorna true
*/
bool btree::isCompleta(ptrno r, int n,int h){
if(r != NULL){
if(r->getEsquerda()!=NULL && r->getDireita()!=NULL)
return equal(isCompleta(r->getEsquerda(),n+1,h), isCompleta(r->getDireita(),n+1,h));
else{
if(n >=(h-1))
return true;
else
return false;
}
}
else
return true;
}
 
 
/**
* Interface para o método parentAnin
*/
void btree::parentAnin(){
parentAnin(raiz);
}
 
 
/**
* Exibe a árvore como parenteses aninhados
*/
void btree::parentAnin(ptrno r){
if(r!=NULL){
printf("(%d",r->getValor());
parentAnin(r->getEsquerda());
parentAnin(r->getDireita());
printf(")");
}
}
 
 
/**
* Interface para o método hierar2
*/
void btree::hierar2(){
hierar2(raiz,1);
}
 
 
/**
* Exibe a árvore como Hierarquica 2
*/
void btree::hierar2(ptrno r, int h){
if(r!=NULL){
int i;
printf("%d\n",r->getValor());
if(r->getEsquerda()!=NULL)
for(i=0;i<h;i++)
printf(" ");
hierar2(r->getEsquerda(),h+1);
if(r->getDireita()!=NULL)
for(i=0;i<h;i++)
printf(" ");
hierar2(r->getDireita(),h+1);
}
}
 
 
/**
* Interface para o método posOrdem
*/
void btree::posOrdem(){
posOrdem(raiz);
}
 
 
/**
* Exibe o conteúdo da árvore em pós ordem (esquerda - direita - raiz)
*/
void btree::posOrdem(ptrno r){
if(r!=NULL){
posOrdem(r->getEsquerda());
posOrdem(r->getDireita());
printf("%d ",r->getValor());
}
}
 
 
/**
* Interface para o método isEstrBinary
*/
bool btree::isEstrBinary(){
return isEstrBinary(raiz);
}
 
 
/**
* Verifica se a árvore é estritamente binária e retorna true caso seja
*/
bool btree::isEstrBinary(ptrno r){
if(r!=NULL){
if(r->getEsquerda()!=NULL && r->getDireita()!=NULL || r->getEsquerda()==NULL && r->getDireita()==NULL)
return equal(isEstrBinary(r->getEsquerda()),isEstrBinary(r->getDireita()));
else
return false;
}
else
return true;
}
 
 
/*Old method, by Sanja
bool btree::isEstrBinary(ptrno r){
if (r != NULL){
if (r->getEsquerda() == NULL && r->getDireita() == NULL)
return true;
if (r->getEsquerda() == NULL && r->getDireita() == NULL || r->getEsquerda() != NULL && r->getDireita() != NULL){
if (isEstrBinary(r->getEsquerda()) == true && isEstrBinary(r->getDireita()) == true)
return true;
else
return false;
}
else
return false;
}
else
return false;
}
*/
 
 
int main(){
btree a;
a.push(50);
a.push(30);
a.push(70);
a.push(20);
a.push(40);
a.push(60);
a.push(90);
a.push(35);
a.push(45);
a.push(55);
 
printf("\nConteudo da arvore em pos-ordem: "); a.posOrdem();
 
printf("\n\nNumeros de Nos = %d",a.numeroNos());
 
printf("\nAltura da arvore = %d",a.altura());
 
printf("\n\nMenor valor = %d", a.menor());
 
printf("\nMaior valor = %d",a.maior());
 
printf("\n\nCrescente: ");a.mostreCrescente();
 
printf("\nDecrescente: ");a.mostreDecrescente();
 
printf("\n\nNos Internos: ");a.nosInternos();
 
printf("\nNumero de nos Folha: %d",a.numeroFolhas());
 
printf("\n\ne uma arvore completa? "); a.isCompleta() ? printf("Sim") : printf
("Nao");
 
printf("\n\ne uma arvore cheia? "); a.isCheia() ? printf("Sim") : printf("Nao");
 
printf("\n\ne uma arvore degenerada? "); a.isDegenerada() ? printf("Sim") : printf
("Nao");
 
printf("\n\ne uma arvore estritamente binaria? "); a.isEstrBinary() ? printf("Sim") : printf("Nao");
 
printf("\n\nRepresentaçao do conteudo da arvore em parenteses aninhados:\n"); a.parentAnin();
 
printf("\n\nRepresentaçao do conteudo da arvore em Hierarquico II\n"); a.hierar2();
 
 
getch();
return 0;
}
 

Flickr agora em português. Você clica, todo mundo vê. Saiba mais.