How to save and load a binary tree in a file?

2

I have a question about how to save and upload a file to a tree. As I understand a tree is that it can not be saved only by traveling both sides separately (Left and Right) if not that it has to travel in a special way. As I understand how to save this well.

 template <class T>
void ArbolBinario<T>::Guardar(NodoArbol<T> *p, ofstream &salida)
{
    if(p){

        salida + p->Info;
       Guardar(p->HijoIzq,salida);
       Guardar(p->HijoDer,salida);

    }
    cout<<endl;

}

But when loading is when I have many doubts about how it should be done. Implement this form that is discriminating a response to know where to load the nodes, but this obviously does not work since the program stops at the beginning. I have the feeling that is the operator overload the problem but I can not think How to solve it? Could you help me?

template <class T>
void ArbolBinario<T>::Cargar(NodoArbol<T> *p, ifstream &entrada,ifstream &resp)
{
char res;

        if(!entrada.eof())
            {
            entrada>>p->Info;
         cout<<p->Info;


       if(!entrada.eof())
       {

       resp>>res;

       if(res=='s' || res=='S')
       Cargar(p->HijoIzq,entrada,resp);
       }
       if(!entrada.eof())

       {


       resp>>res;

       if(res=='s' || res=='S')
       Cargar(p->HijoDer,entrada,resp);

       }

    }
}

This is the complete code

#include<iostream>
#include<stdlib.h>
#include<fstream>
#include"71.h"
using namespace std;

template<class T>

class ArbolBinario;

template <class T>

class NodoArbol
{
private:
    T Info;
    NodoArbol<T>*HijoIzq;
    NodoArbol<T>*HijoDer;
public:
    NodoArbol();
    T RegresaInfo();
    void ActualizarInfo(T);
    friend class ArbolBinario<T>;
};

char nomres[]="respuestas.txt";
template<class T >
NodoArbol<T>::NodoArbol()
{

    HijoIzq = NULL;
    HijoDer = NULL;
}
template <class T>

T NodoArbol<T>::RegresaInfo(){

    return Info;

}
template <class T>
void NodoArbol<T>::ActualizarInfo(T Dato){

    Info = Dato;

}
template <class T >
class ArbolBinario{

private:
    NodoArbol<T>*Raiz;
public:
    ArbolBinario();
    NodoArbol<T>*RegresaRaiz();
    void CrearArbol(NodoArbol<T> *);
    void ImprimeIzq(NodoArbol<T> *);
    void Guardar(NodoArbol<T> * ,ofstream &);
    void ImprimeDer(NodoArbol<T> *);
    void Cargar(NodoArbol<T> *, ifstream &,ifstream &);



};
template <class T>
ArbolBinario<T>::ArbolBinario(){

    Raiz = NULL;
}
template <class T >
NodoArbol<T> *ArbolBinario<T>::RegresaRaiz(){

    return Raiz;
}
template <class T >
void ArbolBinario<T>::CrearArbol(NodoArbol<T> *Apunt){

    ofstream salidaRes;
    salidaRes.open(nomres, ios::app);
    char Resp;
    Apunt = new NodoArbol<T>;
    cout<<"\n\nIngrese la informacion a almacenar:";
    cin>>Apunt->Info;
    cout<<"\n\n"<<Apunt->Info<<"¿Tiene hijo izquierdo (S/N)?";
    cin>>Resp;
    salidaRes<<Resp<<endl;
    if(Resp == 's'){

        CrearArbol(Apunt->HijoIzq);
        Apunt->HijoIzq = Raiz;
    }
    cout<<"\n\n"<<Apunt->Info<<"Tiene hijo derecho (S/N)?";
    cin>>Resp;
    salidaRes<<Resp<<endl;
    if(Resp == 's'){

        CrearArbol(Apunt->HijoDer);
        Apunt->HijoDer = Raiz;
    }
    Raiz = Apunt;
    salidaRes.close();
}
template <class T>
void ArbolBinario<T>::ImprimeIzq(NodoArbol<T> *Apunt){

    if(Apunt){


        if(Apunt->HijoIzq){

            cout<<Apunt->HijoIzq->Info;
            ImprimeIzq(Apunt->HijoIzq);
        }
        ImprimeIzq(Apunt->HijoDer);

    }

}
template <class T>
void ArbolBinario<T>::ImprimeDer(NodoArbol<T> *Apunt){
    if(Apunt){
        if(Apunt->HijoDer){


            cout<<Apunt->HijoDer->Info;
            ImprimeDer(Apunt->HijoDer);

        }
        ImprimeDer(Apunt->HijoIzq);



    }

}
template <class T>
void ArbolBinario<T>::Guardar(NodoArbol<T> *p, ofstream &salida)
{
    if(p){

        salida + p->Info;
       Guardar(p->HijoIzq,salida);
       Guardar(p->HijoDer,salida);

    }
    cout<<endl;

}

template <class T>
void ArbolBinario<T>::Cargar(NodoArbol<T> *p, ifstream &entrada,ifstream &resp)
{
char res;

        if(!entrada.eof())
            {
            entrada>>p->Info;
         cout<<p->Info;


       if(!entrada.eof())
       {

       resp>>res;

       if(res=='s' || res=='S')
       Cargar(p->HijoIzq,entrada,resp);
       }
       if(!entrada.eof())

       {


       resp>>res;

       if(res=='s' || res=='S')
       Cargar(p->HijoDer,entrada,resp);

       }

    }
}
void menu(){


    ArbolBinario<Persona> Genealogico;
    Persona Individuo;
    NodoArbol<Persona> *Ap;

    ifstream entrada;
    ifstream resp;

    resp.open(nomres);
    entrada.open("arb_genealogico.txt");


if(entrada)
    Genealogico.Cargar(Genealogico.RegresaRaiz(),entrada,resp);


    bool salir = false;

    int opc;
    while(!salir){
        cout<<"Menu - Arbol genealogico"<<endl;
        cout<<"1.- Crear un arbol"<<endl;
        cout<<"2.- Imprimir ascendientes femeninos"<<endl;
        cout<<"3.- Imprimir ascendientes masculinos"<<endl;
        cout<<"4.- Salir"<<endl;
        cin>>opc;
        switch(opc){
        case 1:
            cout<<"Creando arbol"<<endl;
             Ap = Genealogico.RegresaRaiz();
            Genealogico.CrearArbol(Ap);
            Ap = Genealogico.RegresaRaiz();
        break;
        case 2:
            Individuo = Ap->RegresaInfo();
            cout<<"Los desendientes femeninos son: "<<Individuo;
            Genealogico.ImprimeIzq(Ap);
            break;
        case 3:
            Individuo = Ap->RegresaInfo();
            cout<<"Los desendientes masculinos son: "<<Individuo;
            Genealogico.ImprimeDer(Ap);
            break;
        case 4:
            {


                        remove("arb_genealogico.txt");

            ofstream salida2;
            salida2.open("arb_genealogico.txt", ios::app);
            Genealogico.Guardar(Genealogico.RegresaRaiz(),salida2);
            salida2.close();
            salir = true;
            }
        break;
        default:
            cout<<"Ingrese una opcion valida"<<endl;


        }

    }

   /* cout<<"\n\n\n_____________________________\n\n";
    cout<<"Los ascendientes femeninos de : \n"<<Individuo;
    cout<<"\n\n_____________________________\n";

    Genealogico.ImprimeIzq(Ap);*/

}
int main(){

    menu();

}

And the header

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
class Persona
{

private:
    int AnioNac,Vive;
    char NomPers[64],LugNac[64];

public:
    Persona();
    Persona( const char[],const char [],int, int);
    char hi,hd;
    friend istream & operator>> (istream &, Persona &);
    friend ostream & operator<< (ostream & ,Persona &);
    friend ostream & operator +(ostream &, Persona &);


};

Persona::Persona(){}
Persona::Persona(const char NomP[],const char LugN[],int ANac,int Vi)
{
    AnioNac = ANac;
    Vive=Vi;
    strcpy(NomPers,NomP);
    strcpy(LugNac,LugN);

}
istream & operator >>(istream & Lee, Persona & ObjPers){
    cout<<"\n\nIngrese nombre de la Persona:";
    Lee>>ObjPers.NomPers;
    cout<<"\n\nIngrese anio de nacimiento:";
    Lee>>ObjPers.AnioNac;
    cout<<"\n\nIngrese lugar de nacimiento:";
    Lee>>ObjPers.LugNac;
    cout<<"\n\n¿Esta viva?:";
    Lee>>ObjPers.Vive;
    return Lee;
}

ostream & operator <<(ostream & Escribe, Persona & ObjPers){
    Escribe<<"\n\nDatos de la Persona\n";
    Escribe<<"\nNombre: "<<ObjPers.NomPers;
    Escribe<<"\nLugar de nacimiento: "<<ObjPers.LugNac;
    Escribe<<"\nAnio de nacimiento: "<<ObjPers.AnioNac;
    if(ObjPers.Vive == 1)
        Escribe<<"\nEsta viva.\n";
    else
        Escribe<<"\nNo esta viva.\n";
    return Escribe;

}
ostream & operator +(ostream &escribe ,Persona &obj ){
    escribe<<obj.NomPers<<" "<<obj.LugNac<<" "<<obj.AnioNac<<" "<<obj.Vive<<endl;
    return escribe;

}
    
asked by Marco Leslie 16.11.2017 в 20:33
source

1 answer

2
  

Since I understand a tree, it can not be saved by only going through both sides separately (Left and Right), but you have to travel in a special way

This statement is, in principle, false.

To save an object (tree, vector, list, ...) what you have to try is to save all the necessary information and then rebuild it. How you collect that information and how the guard is indifferent as long as you are able to reconstruct the object at any time.

  

But when loading is when I have many doubts about how to do it

You can start by doing it roughly, that is: you read a value and you add it to the user as if you were a user. If the tree is balanced and the number of elements is especially large then you may find certain performance problems.

In this case it is when it starts to make sense to think about less expensive methods to perform the reading.

If the tree is balanced what is interesting is that the nodes are saved following a specific order and this order is to fill the tree from the nearest closest to the root to the most distant (to avoid the tree has to rearrange spending time on the road). Depending on the rules that determine the balance the save sequence will change.

Now, given that any data outside the program can be manipulated manually by a user (even if they are binary data), you will not be able to avoid validating the data you are reading, otherwise it will create an inconsistent tree.

An example. Imagine you have the following tree:

   A
  / \
 B   C
/\   /\
D E  F G

And you decide to save it generating the following sequence:

A B C D E F G

You prepare the corresponding routine to reconstruct the tree (assuming that the sequence is going to be good), the tests and you actually get the original tree ... but ... what happens if you now take a malicious user (say example the teacher) and exchange a couple of nodes?

B A C D E F G

If you do not validate the data and insert them without checking, your new tree will not be consistent (note that according to the original tree, B is less than A ):

   B
  / \
 A   C
/\   /\
D E  F G

Thus, the regeneration of the tree has to treat the read values in the same way as if they were random data entered by a user. Unless it is a requirement or you run a system that practically makes it impossible for the user to touch the file without you knowing and can undo the changes or invalidate the data, the reading should be treated the same as the user's entries.

In short ... you do not have to create a special routine to rebuild the tree ... just reuse the ones you already have for the user to fill in the tree. The only thing you have to program is a routine that understands the format of the file, the rest you already have done.

All this has its nuances. Of course the format in which you save the data and the way in which you collect the state of the object depend on how the structure of the object is, but beyond that the process is quite mechanical.

    
answered by 17.11.2017 в 14:14