arboles en java

16
Arboles en Java: Recorrido Preorden, Inorden y Postorden El recorrido de árboles refiere al proceso de visitar de una manera sistemática, exactamente una vez, cada nodo en una estructura de datos de árbol (examinando y/o actualizando los datos en los nodos). Preorden: (raíz, izquierdo, derecho). Para recorrer un árbol binario no vacío en preorden, hay que realizar las siguientes operaciones recursivamente en cada nodo, comenzando con el nodo de raíz: 1. Visite la raíz 2. Atraviese el sub-árbol izquierdo 3. Atraviese el sub-árbol derecho Inorden: (izquierdo, raíz, derecho). Para recorrer un árbol binario no vacío en inorden (simétrico), hay que realizar las siguientes operaciones recursivamente en cada nodo: 1. Atraviese el sub-árbol izquierdo 2. Visite la raíz 3. Atraviese el sub-árbol derecho Postorden: (izquierdo, derecho, raíz). Para recorrer un árbol binario no vacío en postorden, hay que realizar las siguientes operaciones recursivamente en cada nodo: 1. Atraviese el sub-árbol izquierdo 2. Atraviese el sub-árbol derecho 3. Visite la raíz En general, la diferencia entre preorden, inorden y postorden es cuándo se recorre la raíz. En los tres, se recorre primero el sub-árbol

Upload: carlos-velazquez

Post on 19-Feb-2016

215 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Arboles en Java

Arboles en Java: Recorrido Preorden, Inorden y Postorden

El recorrido de árboles refiere al proceso de visitar de una manera sistemática,

exactamente una vez, cada nodo en una estructura de datos de árbol (examinando y/o

actualizando los datos en los nodos).

Preorden: (raíz, izquierdo, derecho). Para recorrer un árbol binario no vacío en

preorden, hay que realizar las siguientes operaciones recursivamente en cada nodo,

comenzando con el nodo de raíz:

1. Visite la raíz2. Atraviese el sub-árbol izquierdo3. Atraviese el sub-árbol derecho

Inorden: (izquierdo, raíz, derecho). Para recorrer un árbol binario no vacío en

inorden (simétrico), hay que realizar las siguientes operaciones recursivamente en

cada nodo:

1. Atraviese el sub-árbol izquierdo2. Visite la raíz3. Atraviese el sub-árbol derecho

Postorden: (izquierdo, derecho, raíz). Para recorrer un árbol binario no vacío en

postorden, hay que realizar las siguientes operaciones recursivamente en cada nodo:

1. Atraviese el sub-árbol izquierdo2. Atraviese el sub-árbol derecho3. Visite la raíz

En general, la diferencia entre preorden, inorden y postorden es cuándo se recorre la

raíz. En los tres, se recorre primero el sub-árbol izquierdo y luego el derecho.

Page 2: Arboles en Java

En preorden, la raíz se recorre antes que los recorridos de los subárboles izquierdo y derecho

En inorden, la raíz se recorre entre los recorridos de los árboles izquierdo y derecho, y

En postorden, la raíz se recorre después de los recorridos por el subárbol izquierdo y el derecho

¿Para que sirve un árbol binario?Como todos sabemos un árbol binario es una estructura de datos, y como todas, este sirve para organizar datos para facilitar su manipulación, ya sea el ingreso, borrado o búsqueda de datos, y precisamente una de las principales ventajas de los árboles binarios es la búsqueda, ya que como en muchos algoritmos de búsqueda necesitamos tener la información ordenada y en nuestros árboles binarios precisamente los datos van ingresando de forma ordenada.

Recorridos con los conocidos métodos recursivos:

Inorden Postorden Preorden¿Cómo se ingresa la información?Como dije anteriormente, la información se ingresa de forma ordenada esto se resuelve de forma muy sencilla con estos pasos:1. Se toma el dato a ingresar X2. Partiendo de la raíz preguntamos: Nodo == null ( o no existe ) ?3. En caso afirmativo X pasa a ocupar el lugar del nodo y ya hemos ingresado

nuestro primer dato.4. En caso negativo preguntamos: X < Nodo5. En caso de ser menor pasamos al Nodo de la IZQUIERDA del que acabamos

de preguntar y repetimos desde el paso 2 partiendo del Nodo al que acabamos de visitar

6. En caso de ser mayor pasamos al Nodo de la DERECHA y tal cual hicimos con el caso anterior repetimos desde el paso 2 partiendo de este nuevo Nodo.

Nos daremos cuenta de que es un proceso RECURSIVO en el cual al final por más grande que sea el árbol el dato a entrar ocupará un lugar, vamos a ejemplificar lo ya mencionado con una imagen:

Page 3: Arboles en Java

Si observan con detenimiento la imagen aunque se vea algo “complejo” entenderán bien el funcionamiento.

Vamos a programarloTodo lo dicho anteriormente, vamos a programarlo ahora usando POO con java (para que sea más fácil de entender).

Comenzamos con la abstracción de la información, tenemos que un árbol binario está compuesto por la raíz y sus nodos hijos, de la misma forma que la misma raíz no es más que otro nodo, partiendo de esto entonces crearemos 2 clases:

(Quiero comentar antes de esto que, aunque no soy muy partidario de escribir los nombres de los métodos y clases en español, por cuestiones de entendimiento para muchas personas que lleguen a esta información, los pondré de esa forma). Arbol NodoPara comenzar con esas clases tenemos, vamos ahora a definirlas:

Nodo.java

Page 4: Arboles en Java

12345678910111213141516171819202

public class Nodo {     /* Declaraciones de variables */    private int valor;     private Nodo padre;    private Nodo hojaIzquierda;    private Nodo hojaDerecha;     /* Constructor */    public Nodo(int valor) {        this.valor = valor;    }     /* Setters y Getters */    public void setValor(int valor) {        this.valor = valor;    }     public int getValor() {        return valor;    }     public Nodo getPadre() {        return padre;    } 

Page 5: Arboles en Java

12223242526272829303132333435363738394

    public void setPadre(Nodo padre) {        this.padre = padre;    }     public Nodo getHojaIzquierda() {        return hojaIzquierda;    }     public void setHojaIzquierda(Nodo hojaIzquierda) {        this.hojaIzquierda = hojaIzquierda;    }     public Nodo getHojaDerecha() {        return hojaDerecha;    }     public void setHojaDerecha(Nodo hojaDerecha) {        this.hojaDerecha = hojaDerecha;    } }

Page 6: Arboles en Java

04142434445464748

Vamos a revisar un poco más de cerca el código.

Es una clase muy sencilla, como podemos ver únicamente tiene 4 atributos (variables) fáciles de entender:

valor hojaIzquierda hojaDerechavalor es el dato almacenado del propio nodo, mientras que como recordaremos cada nodo puede tener máximo 2 hijos uno a la izquierda y otro a la derecha, donde todos los nodos a su izquierda son menores a el mismo y todos los nodos a su derecha son mayores a el mismo, pero que comienzan como nulos ya que al crear un nuevo nodo, este no tiene hijos.

Hay que tener cuidado con setValor( int valor ) ¿Porque? pues bien, no podemos actualizar el valor al aire ya que perderíamos el orden, por lo que tenemos un contructor:

1     /* Constructor */

Page 7: Arboles en Java

234

    public Nodo(int valor) {        this.valor = valor;    }

a partir del cual se ingresa el valor del nodo al crear una instancia del mismo:

1 Nodo nodo = new Nodo( 1 );

Ya tenemos nuestra clase nodo lista, ahora pasemos con la clase Arbol.java que es aún más sencilla ( en definición ) que Nodo:

Arbol.java

123456789101112

public class Arbol {     /* Atributos */    private Nodo raiz;     /* Contructories */        public Arbol( int valor ) {        this.raiz = new Nodo( valor );    }     public Arbol( Nodo raiz ) {        this.raiz = raiz;    }

Page 8: Arboles en Java

131415161718192021222324

     /* Setters y Getters */    public Nodo getRaiz() {        return raiz;    }     public void setRaiz(Nodo raiz) {        this.raiz = raiz;    } }

Prácticamente por ahora lo importante es su atributo “raiz” del tipo Nodo, el cual representará a todo nuestro árbol, adicionalmente he creado constructores que pueden ser de ayuda al momento de inicializar el árbol para crear la raíz de paso.

pffff que nos hemos alargado con el post, pero aún no terminamos, acabamos apenas de definir entidades faltan todas las funciones del mismo. vamos con ellas.

Hay que agregar métodos a nuestra clase de Arbol para poder ingresar nuevos nodos:

addNodo removeNodo recorridoPreorden recorridoInorden recorridoPostordenProgramando las funciones del árbol

Page 9: Arboles en Java

En esta primera parte quiero abarcar al menos hasta el ingreso de nuevos nodos, comencemos entonces.

La modificación la haremos en nuestra clase Arbol que es la que llevará estas funciones de control, recordemos nuestro algoritmo de inserción que planteamos anteriormente:

1. Se toma el dato a ingresar X2. Partiendo de la raíz preguntamos: Nodo == null ( o no existe ) ?3. En caso afirmativo X pasa a ocupar el lugar del nodo y ya hemos

ingresado nuestro primer dato.4. En caso negativo preguntamos: X < Nodo5. En caso de ser menor pasamos al Nodo de la IZQUIERDA del que

acabamos de preguntar y repetimos desde el paso 2 partiendo del Nodo al que acabamos de visitar

6. En caso de ser mayor pasamos al Nodo de la DERECHA y tal cual hicimos con el caso anterior repetimos desde el paso 2 partiendo de este nuevo Nodo.

 

Entonces definamos nuestra función:

12345678910111

    private void private void addNodo( Nodo nodo, Nodo raiz ) {        /* 2.- Partiendo de la raíz preguntamos: Nodo == null ( o no existe ) ? */        if ( raiz == null ) {            /*              * 3.- En caso afirmativo X pasa a ocupar el lugar del nodo y ya              * hemos ingresado nuestro primer dato.              * ==== EDITO =====             * Muchas gracias a @Espectro por la corrección de esta línea             */            this.setRaiz(nodo);        }        else {

Page 10: Arboles en Java

213141516171819202122232425262728293031

            /* 4.- En caso negativo preguntamos: X < Nodo */            if ( nodo.getValor() <= raiz.getValor() ) {                /*                  * 5.- En caso de ser menor pasamos al Nodo de la IZQUIERDA del                 * que acabamos de preguntar y repetimos desde el paso 2                  * partiendo del Nodo al que acabamos de visitar                  */                if (raiz.getHojaIzquierda() == null) {                    raiz.setHojaIzquierda(nodo);                }                else {                    addNodo( nodo , raiz.getHojaIzquierda() );                }            }            else {                /*                  * 6.- En caso de ser mayor pasamos al Nodo de la DERECHA y tal                 * cual hicimos con el caso anterior repetimos desde el paso 2                 * partiendo de este nuevo Nodo.                 */                if (raiz.getHojaDerecha() == null) {                    raiz.setHojaDerecha(nodo);                }                else {                    addNodo( nodo, raiz.getHojaDerecha() );                }            }        }    }     public void addNodo( Nodo nodo ) {

Page 11: Arboles en Java

3233343536373839404142434445

        this.addNodo( nodo , this.raiz );    }

Podemos ver que he agregado 2 funciones para insertar un nuevo nodo, pero solo 1 de ellas es vista desde fuera, la que nosotros llamaremos, esto es porque la función que  usamos realmente para insertar (llamada dentro del método que vemos desde fuera de la clase [el método público, para quien ande perdido]) necesita una referencia de la “raíz” a partir de que nodo va a hacer las comprobaciones, para que al usar la recursividad esta raíz pueda ir cambiando (de acuerdo al algoritmo) y seguir probando y comparando nodos.

Hasta ahora hemos comprendido el funcionamiento de los árboles binarios, hemos definido las clases a usar y ya comenzamos con las funciones básicas del mismo, en la próxima entrada veremos como hacer la eliminación de un nodo para así continuar hasta terminar este

Page 12: Arboles en Java

tema (que tanto dolor de cabeza le da a algunas personas) tan interesante.Igual que la lista, el árbol es una estructura de datos. Son muy eficientes para la búsqueda de información. Los árboles soportan estructuras no lineales.

Algunos conceptos de la estructura de datos tipo árbol:Nodo hoja: Es un nodo sin descendientes (Nodo terminal)Ej. Nodos E ? F ? C y D.Nodo interior: Es un nodo que no es hoja.Ej. Nodos A y B.Nivel de un árbol: El nodo A está en el nivel 1 sus descendientes directos están en el nivel 2 y así sucesivamente.El nivel del árbol está dado por el nodo de máximo nivel.Ej. Este árbol es de nivel 3.Grado de un nodo: es el número de nodos hijos que tiene dicho nodo (solo se tiene en cuenta los nodos interiores)Ej. El nodo A tiene grado 3.El nodo B tiene grado 2.Los otros nodos no tienen grado porque no tienen descendientes.Grado de un árbol: Es el máximo de los grados de todos los nodos de un árbol.Ej. El grado del árbol es 3.Longitud de camino del nodo x: Al número de arcos que deben ser recorridos para llegar a un nodo x, partiendo de la raiz.La raiz tiene longitud de camino 1, sus descendientes directos tienen longitud de camino 2, etc. En forma general un nodo en el nivel i tiene longitud de camino i.Árbol binario: Un árbol es binario si cada nodo tiene como máximo 2 descendientes.

Page 13: Arboles en Java

Para cada nodo está definido el subárbol izquierdo y el derecho.Para el nodo A el subárbol izquierdo está constituido por los nodos B, D y E. Y el subárbol derecho está formado por los nodos C y F.Lo mismo para el nodo B tiene el subárbol izquierdo con un nodo (D) y un nodo en el subárbol derecho (E).El nodo D tiene ambos subárboles vacíos.El nodo C tiene el subárbol izquierdo vacío y el subárbol derecho con un nodo (F).Árbol binario perfectamente equilibrado: Si para cada nodo el número de nodos en el subárbol izquierdo y el número de nodos en el subárbol derecho, difiere como mucho en una unidad.Hay que tener en cuenta todos los nodos del árbol.El árbol de más arriba es perfectamente equilibrado.Ej. árbol que no es perfectamente equilibrado:El nodo A tiene 3 nodos en el subárbol izquierdo y solo uno en el subárbol derecho, por lo que no es perfectamente equilibrado.

Page 14: Arboles en Java

Árbol binario completo: Es un árbol binario con hojas como máximo en los niveles n-1 y n (Siendo n el nivel del árbol)Los dos árboles graficados son completos porque son árboles de nivel 3 y hay nodos hoja en el nivel 3 en el primer caso, y hay nodos hoja en los niveles 3 y 2 en el segundo caso.Ej. Árbol binario no completo:

Page 15: Arboles en Java

Hay nodos hoja en los niveles 4, 3 y 2. No debería haber nodos hojas en el nivel 2.Árbol binario ordenado: Si para cada nodo del árbol, los nodos ubicados a la izquierda son inferiores al que consideramos raíz para ese momento y los nodos ubicados a la derecha son mayores que la raíz.

Page 16: Arboles en Java

Ej. Analicemos si se trata de un árbol binario ordenado:Para el nodo que tiene el 50:Los nodos del subárbol izquierdo son todos menores a 50? 8, 25, 30 SiLos nodos del subárbol derecho son todos mayores a 50? 70 Si.Para el nodo que tiene el 25:Los nodos del subárbol izquierdo son todos menores a 25? 8 SiLos nodos del subárbol derecho son todos mayores a 25? 30 Si.No hace falta analizar los nodos hoja. Si todas las respuestas son afirmativas podemos luego decir que se trata de un árbol binario ordenado.