Árvore de busca binária em C++[resolvido]

Oi pessoal tudo bom?
Será que alguem pode me ajudar com um problema de 0xC0000005: Access violation reading location?
É o seguinte, ele compila no Dev C++, porém da pau no primeiro segundo. O Código é grande e está separado em 3 arquivos : header.h , main.cpp e classes.cpp.

#include "header.h"
int main(){
	Tree teste;
cout << teste.root()->get_valor();

	

	fflush(stdin);
	getchar();
	return 0;
}
//MAIN ACIMA




#include "header.h"
bool Tree::Busca(int x, No* j){
	if(j->get_valor()==x)
		return true;
	else if(j->get_valor()>x && j->get_esq()!=NULL)
		Busca(x,j->get_esq());
		else if(j->get_valor()<x && j->get_dir()!=NULL)
			Busca(x,j->get_dir());
	return false;
}
void Tree::Insere(int x, No* j){
	No *p;
	if(j==NULL){
		p=new No();
		p->set_valor(x);
		j=p;
	}
	else if(j->get_valor()>x)
		Insere(x,j->get_esq());
		else if(j->get_valor()<x)
			Insere(x,j->get_dir());
}
void Tree::Imprime(No* p){
	
	if (p->get_esq()!=NULL)
		Imprime(p->get_esq());
	cout << p->get_valor()<< " ";
		if (p->get_dir()!=NULL)
			Imprime(p->get_dir());
}
 // FUNCOES DAS CLASSES ACIMA



#include <iostream>
#include <cstdlib>
using namespace std;

class No{
private:
	No *esq,*dir;
	
	int valor;
public:
	No(){
		this->valor=0;
		this->esq=NULL;
		this->dir=NULL;
		
		
	}
	void set_esq(No* p){esq=p;}
	void set_dir(No* p){dir=p;}
	No* get_esq(){return esq;}
	No* get_dir(){return dir;}
	int get_valor(){return valor;}
	void set_valor(int x){valor=x;}
};
class Tree{
private:
	No* raiz;
public:
	Tree(){
		raiz=NULL;
	}
	No *root(){return raiz;}
	bool Busca(int,No*);
	void Insere(int,No*);
	void Imprime(No*);
};

Acredito que o erro está no construtor No(), porém não sei onde…
Sou iniciante e desde já agradeço a paciência e a colaboração de vocês.
Obrigado !! :slight_smile: :slight_smile:

O seu main pede o valor da raiz. Mas como você nunca inseriu nenhum valor, a raiz ainda é NULL. Quando vc faz getValor() sobre NULL você tem o seu problema.

Algumas dicas:

  1. Não use o devcpp. Além de ser pré-histórico e usar uma versão antiquíssima do MinGW, ele é cheio de bugs. Um deles, o mais irritante, é ocultar parte das mensagens de erro, o que pode deixar o código 300x mais difícil de se corrigir. No lugar, use o Code::Blocks, que é tão pequeno quanto, moderno e já vem com o mesmo compilador numa versão mais nova: http://www.codeblocks.org

  2. Use a lista de inicialização para inicializar variáveis no construtor. Sem ela, o C++ irá inicializa-la duas vezes e isso pode ser especialmente caro se você tiver objetos como seus atributos. Ou seja no lugar de:

No(){ this-&gt;valor=0; this-&gt;esq=NULL; this-&gt;dir=NULL; }

Faça simplesmente:

No() : esq(NULL), dir(NULL), valor(0) { }

  1. Cuidado com a identação. E, lembre-se, se você tiver um return dentro de um if, você não precisa de um else (pois o código vai cair fora mesmo se entrar no if).

Por exemplo, esse método:

bool Tree::Busca(int x, No* j){ if(j->get_valor()==x) return true; else if(j->get_valor()>x && j->get_esq()!=NULL) Busca(x,j->get_esq()); else if(j->get_valor()<x && j->get_dir()!=NULL) Busca(x,j->get_dir()); return false; }

Fica assim:

O seu main pede o valor da raiz. Mas como você nunca inseriu nenhum valor, a raiz ainda é NULL. Quando vc faz getValor() sobre NULL você tem o seu problema.

Algumas dicas:

  1. Não use o devcpp. Além de ser pré-histórico e usar uma versão antiquíssima do MinGW, ele é cheio de bugs. Um deles, o mais irritante, é ocultar parte das mensagens de erro, o que pode deixar o código 300x mais difícil de se corrigir. No lugar, use o Code::Blocks, que é tão pequeno quanto, moderno e já vem com o mesmo compilador numa versão mais nova: http://www.codeblocks.org

  2. Use a lista de inicialização para inicializar variáveis no construtor. Sem ela, o C++ irá inicializa-la duas vezes e isso pode ser especialmente caro se você tiver objetos como seus atributos. Ou seja no lugar de:

No(){ this-&gt;valor=0; this-&gt;esq=NULL; this-&gt;dir=NULL; }

Faça simplesmente:

No() : valor(0), esq(NULL), dir(NULL) { }

  1. Cuidado com a identação. E, lembre-se, se você tiver um return dentro de um if, você não precisa de um else (pois o código vai cair fora mesmo se entrar no if).

Por exemplo, esse método:

[code]
bool Tree::Busca(int x, No* j){
if(j->get_valor()==x)
return true;

if(j->get_valor()>x && j->get_esq()!=NULL)  
    Busca(x,j->get_esq());  
else if(j->get_valor()<x && j->get_dir()!=NULL)  
    Busca(x,j->get_dir());  
return false;  

} [/code]

Então, eu continuo com o problema… Mas agora acho que sei onde fica ele :
A função Insere(int,No*) não altera o nó raiz da classe Tree. Eu tenho dificuldades com ponteiro, mas se eu fizesse essa função de modo iterativo eu até conseguiria numa boa…
O problema é que Insere é uma função recursiva o que dificulta o meu aprendizado… Realizei as mudanças que você me pediu, mas continuo com problemas. A seguir o código:

//MAIN.cpp
#include "header.h"
int main(){
	Tree teste;
	teste.Insere(10,teste.root());
	teste.Insere(9,teste.root());
	teste.Imprime(teste.root());
	

	fflush(stdin);
	getchar();
	return 0;
}
//Header.h
#include <iostream>
#include <cstdlib>
using namespace std;

class No{
private:
	No *esq,*dir;
	
	int valor;
public:
	No():valor(0),esq(NULL),dir(NULL){
	}
	void set_esq(No* p){esq=p;}
	void set_dir(No* p){dir=p;}
	No* get_esq(){return esq;}
	No* get_dir(){return dir;}
	int get_valor(){return valor;}
	void set_valor(int x){valor=x;}
};
class Tree{
private:
	No* raiz;
public:
	Tree(){
		raiz=NULL;
	}
	No *root(){return raiz;}
	bool Busca(int,No*);
	void Insere(int,No*);
	void Imprime(No*);
};

//Classes.cpp

#include "header.h"
bool Tree::Busca(int x, No* j){
	if(j->get_valor()==x)
		return true;
		if(j->get_valor()>x && j->get_esq()!=NULL)
			Busca(x,j->get_esq());
			else if(j->get_valor()<x && j->get_dir()!=NULL)
				Busca(x,j->get_dir());
	return false;
}
void Tree::Insere(int x, No* j){
	No *p;
	if(j==NULL){
		p=new No();
		p->set_valor(x);
		j=p;
	}
	else if(j->get_valor()>x)
		Insere(x,j->get_esq());
		else if(j->get_valor()<x)
			Insere(x,j->get_dir());
}
void Tree::Imprime(No* p){
	
	if (p->get_esq()!=NULL)
		Imprime(p->get_esq());
	cout << p->get_valor()<< " ";
		if (p->get_dir()!=NULL)
			Imprime(p->get_dir());
}

Obrigado! Agradeço desde já a resposta que foi me dada quase que imediatamente e espero ansiosamente por outra. Grato!!

Ahh, estou usando o Visual C++ 2008 Express Edition… Foi nele que descobri que não estava alterando o No raiz. Obrigado!
by the way, o visual c++ é bom?

Ele é excelente. Mas o compilador dele é o MSCPP. Não creio que para o caso dos exercícios da faculdade vá haver algum problema de compatibilidade, até pq a versão 2008 é bem mais próxima do standard que as anteriores.

No caso do Code::Blocks o compilador é o MinGW, o mesmo do Dev, mas numa versão muitíssimo mais nova.
Se você não tem problemas em usar o Visual, continue usando, ele é incomparavelmente melhor que essas outras duas IDES.

Certo, então vou usar o Visual a partir de agora…

Mas confesso que ainda não consegui achar o erro… esse último código que eu passei, mudei a Main e mesmo assim continua o erro:

[code]
#include "header.h"
int main(){
Tree teste;
teste.Insere(10,teste.root());
teste.Insere(9,teste.root());
teste.Imprime(teste.root());

fflush(stdin);
getchar();
return 0;

}[/code]

Parece-me que o erro se encontra na função-membro da classe Tree::Insere que é a seguinte:

void Tree::Insere(int x, No* j){ No *p; if(j==NULL){ p=new No(); p->set_valor(x); j=p; } else if(j->get_valor()>x) Insere(x,j->get_esq()); else if(j->get_valor()<x) Insere(x,j->get_dir()); }
Onde que estou errando? Você tem uma dica?
Obrigado, agradeço mais uma vez a resposta!

O seu método insere deve atualizar a raiz, caso ela seja vazia. Note que nele vc insere um nó, mas se a raiz tiver nula, esse nó não passa a ser a raiz.

Insira um if lá, antes de terminar o método:

if (raiz == NULL) raiz = j;

Certo, eu fiz isso. Mas quando eu vou imprimir, só aparece a raíz na tela…
Será que fiz algo de errado?

[code]void Tree::Insere(int x, No* j){
No p;
if(j==NULL){
p=new No();
p->set_valor(x);
j=p;
}
else if(j->get_valor()>x)
Insere(x,j->get_esq());
else if(j->get_valor()<x)
Insere(x,j->get_dir());
if(raiz==NULL)
raiz=j;
}
void Tree::Imprime(No
p){

if (p->get_esq()!=NULL)
	Imprime(p->get_esq());
cout << p->get_valor()<< " ";
	if (p->get_dir()!=NULL)
		Imprime(p->get_dir());

}
[/code]
e a main :[code]
#include "header.h"
int main(){
Tree teste;
teste.Insere(10,teste.root());
teste.Insere(9,teste.root());
teste.Insere(2,teste.root());
teste.Imprime(teste.root()); // Soh imprime o 10 na tela… O 9 e o 2 são ignorados.

fflush(stdin);
getchar();
return 0;

}[/code]

Obrigado novamente!

Há um problema no seu método de inserção. Você não define o novo nó como esquerda ou direita do último nó encontrado.

Revise aquela linha que diz
j = p;

Ela deveria ser um pouco diferente. Em algum local, você terá que ter um

j->set_dir§;

ou

j->set_esq§;

Não vou passar o código corrigido pra que vc tente achar o problema.

Nossa… Que cabeça a minha… Eu me perdi nos detalhes e não me toquei que estava me esquecendo de dizer pra onde aponta esq ou dir… Deve ser muito chocolate!! Hhehe obrigado, chegando em casa eu farei as alterações e volto para postar o resultado.

Então, na verdade meu código é recursivo, entao não precisa mudar o esq ou o dir, já que não importa pois os dois sào ponteiros… O meu erro estava em passar um valor NULL como parâmetro da função.

Segue o código corrigido:

//MAIN
#include "header.h"
int main(){
	Tree teste;
	teste.Insere(10);
	teste.Insere(5);
	teste.Insere(9);
	teste.Insere(1);
	teste.Insere(2);
	teste.Insere(15);
	teste.Insere(14);
	teste.Insere(13);
	teste.Insere(32);
	teste.Insere(113);
	teste.Insere(11);
	teste.Imprime();
	if (teste.Busca(32))
		cout << "\n\n32 dentro. TAM= "<< teste.get_tam();
	teste.Lumberjacker();// apaga todos os elementos da arvore (lumberjack eh lenhador em ingles)
	teste.Insere(13);
	teste.Insere(32);
	teste.Insere(113);
	teste.Insere(11);
	teste.Imprime();// Testa se apagou os elementos.
	
	

	fflush(stdin);
	getchar();
	return 0;
}
//HEADER.h
#include <iostream>
#include <cstdlib>
using namespace std;

class No{
private:
	No *esq,*dir;
	int valor;
public:
	No():valor(0),esq(NULL),dir(NULL){
	}
	void set_esq(No* p){esq=p;}
	void set_dir(No* p){dir=p;}
	No* get_esq(){return esq;}
	No* get_dir(){return dir;}
	int get_valor(){return valor;}
	void set_valor(int x){valor=x;}
};
class Tree{
private:
	No* raiz;
	unsigned tam;
	void Insere_d(int,No*);
	bool Busca_d(int,No*);
	void Imprime_d(No*);
	void Tamanho(unsigned x,No* p){
		x++;
		if(tam < x)
			tam=x;
		if(p->get_esq()!=NULL)
			Tamanho(x,p->get_esq());
		if(p->get_dir()!=NULL)
			Tamanho(x,p->get_dir());
	}
	void Lumberjacker_d(No*);
public:
	Tree():raiz(NULL),tam(0){	
	}
	~Tree(){
		if (raiz!=NULL)
			Lumberjacker();
	}
	unsigned get_tam(){
		return tam;
	}
	bool Busca(int x){
		if(Busca_d(x,raiz)==true)
			return true;
		return false;
	}
	void Imprime(){
		if (raiz!=NULL)
		Imprime_d(raiz);
	}
	void Insere(int);
	void Lumberjacker(){
		Lumberjacker_d(raiz);
		raiz=NULL;
	}
};
//classes.cpp
#include "header.h"
bool Tree::Busca_d(int x, No* j){
	if(j->get_valor()==x)
		return true;
		if(j->get_valor()>x && j->get_esq()!=NULL)
			Busca_d(x,j->get_esq());
			else if(j->get_valor()<x && j->get_dir()!=NULL)
				Busca_d(x,j->get_dir());
				else
					return false;
}
void Tree::Insere_d(int x, No* j){
	No *p;
	if (x < j->get_valor() && j->get_esq() == NULL){
		p=new No;
		j->set_esq(p);
		p->set_valor(x);
	}
		else if (x < j->get_valor() && j->get_esq() != NULL)
			Insere_d(x,j->get_esq());
	
	if (x > j->get_valor() && j->get_dir() == NULL){
		p=new No;
		j->set_dir(p);
		p->set_valor(x);
	}
		else if (x > j->get_valor() && j->get_dir() != NULL)
			Insere_d(x,j->get_dir());
}
void Tree::Imprime_d(No* p){
	
	if (p->get_esq()!=NULL)
		Imprime_d(p->get_esq());
	cout << p->get_valor()<< " ";
		if (p->get_dir()!=NULL)
			Imprime_d(p->get_dir());
}
void Tree::Insere(int x){
	No *p;
	if (raiz==NULL){
		p=new No;
		raiz=p;
		raiz->set_valor(x);
	}
		else
			Insere_d(x,raiz);
	Tamanho(0,raiz);
}
void Tree::Lumberjacker_d(No* p){
	if(p->get_esq()!=NULL)
		Lumberjacker_d(p->get_esq());
	if(p->get_dir()!=NULL)
		Lumberjacker_d(p->get_dir());
	delete p;
}

Eu adicionei uma funcao chamada Lumberjacker que apaga os elementos da arvore. E tambem adicionei um destrutor da classe Tree.
Obrigado! Agora vou pensar em um modo de remover os elementos ( na verdade ja tenho em mente o algoritmo , so falta implementa-lo recursivamente).