Está en la página 1de 68

Capítulo 5.

Árboles
5 Árboles
5.1 Descripción y terminología fundamental.
5.2 Especificación del TDA Árbol.
5.3 Ejemplos de uso del TDA Árbol.
5.4 Implementaciones del TDA Árbol.
5.5 Especificación del TDA Árbol Binario.
5.6 Ejemplos de uso del TDA Árbol Binario.
5.7 Implementaciones del TDA Árbol Binario.
5.8 Árboles Parcialmente Ordenados: Colas de Prioridad.
5.9 Árboles Binarios de Búsqueda.
5.1 Descripción y terminología fundamental

❏ Un árbol es una colección de elementos entre los cuales existe una


estructura jerárquica definida mediante una relación de paternidad
entre los elementos.
❏ Entre los elementos, que llamaremos nodos, se distingue uno que es
el nodo raíz.
❏ Matemáticamente, un árbol es un grafo no orientado, conexo y
acíclico en el que existe un vértice destacado llamado raíz.
❏ Un árbol A n-ario (en adelante sólo árbol), siendo n≥ 1, es un
conjunto de elementos del mismo tipo tal que:
❏ O bien es el conjunto vacío, en cuyo caso se le denomina árbol vacío.
❏ O bien no es vacío, en cuyo caso existe un elemento distinguido llamado
raíz, y el resto de los elementos se distribuyen en en m subconjuntos
disjuntos A1 ,..., Am, con 0≤ m≤ n, cada uno de los cuales es un árbol n-
ario, llamados subárboles del árbol original.
5.1 Descripción y terminología fundamental

❏ Cuando el orden de los subárboles importa, el árbol se dice


ordenado. Se dice que la raíz del árbol A es padre de la raíz de los
subárboles A1 ,..., Am y que las raices de los subárboles A1 ,..., Am son
hijos de la raíz de A.

❏ Un árbol binario A es un conjunto de elementos del mismo tipo tal


que:
❏ O bien es el conjunto vacío, en cuyo caso se le denomina árbol vacío.
❏ O bien no es vacío, en cuyo caso existe un elemento distinguido llamado
raíz, y el resto de los elementos se distribuyen en dos subconjuntos
disjuntos A1, A2, cada uno de los cuales es un árbol binario, llamados
respectivamente subárboles izquierdo y derecho del árbol original.
5.1 Descripción y terminología fundamental
❏ No es lo mismo un árbol 2-ario que un árbol binario, ya que en el árbol
binario se distingue entre súbárboles izquierdo y derecho. Los términos
padre e hijo descritos para árboles n-arios son también utilizados para
árboles binarios. Otros términos comunes son los siguientes:
❏ Camino: Si existe una secuencia de nodos nl ,..., nk tal que ni es padre de ni+1 ,
para 1≤ i ≤ k, entonces la secuencia se denomina camino del nodo nl al nodo
nk. La longitud de un camino es el número de nodos del camino menos 1.
Existe un camino de longitud 0 de todo nodo a sí mismo.
❏ Ascendiente/Descendiente: Un nodo a es ascendiente de un nodo b (y b
descendiente de a), si existe un camino del nodo a al nodo b. Por tanto todo
nodo es ascendiente (y descendiente) de sí mismo. Los ascendientes (y
descendientes) de un nodo, excluido el propio nodo, se denominan
ascendientes (y descendientes) propios.
❏ Hoja: Nodo sin descendientes propios.
❏ Altura: La altura de un nodo en un árbol es la longitud del camino más largo
de ese nodo a una hoja. La altura de un árbol es la altura de la raíz.
❏ Profundidad: La profundidad de un nodo es la longitud del camino único
desde ese nodo a la raíz.
5.2 Especificación del TDA Árbol

Especificación informal del TDA Arbol

❏ Arbol = TDA con operaciones crea, vacio,  raiz, padre,  hijoIzquierdo,   


hermanoDerecho, info,  insertaHijo,  insertaHermano,  suprimeHijo, 
suprimeHermano, modifica, nodoNulo.

❏ DESCRIPCIÓN:
❏ Los valores del TDA Arbol son arboles n-arios donde cada nodo contiene
un dato del tipo Elemento. Las posiciones de los nodos en el árbol son del
tipo Posicion. Los árboles son mutables: insertaHijo, 
insertaHermano,  suprimeHijo,  suprimeHermano y modifica
añaden, eliminan y modifican respectivamente elementos de un árbol.

❏ OPERACIONES:
❏ crea() devuelve (A:Arbol)
❏ efecto: Devuelve el árbol vacío A.
5.2 Especificación del TDA Árbol

❏ vacio(A:Arbol) devuelve (booleano)
❏ efecto: Devuelve cierto si A es el árbol vacío, y falso en caso contrario.

❏ raiz(A:Arbol) devuelve (Posicion)
❏ efecto: Devuelve la posición del nodo raíz en el árbol A. Si el árbol A está vacío,
devuelve nulo.

❏ padre(A:Arbol; P:Posicion) devuelve (Posicion)
❏ requerimientos: El árbol A es no vacío y P no es nodo nulo
❏ efecto: Devuelve la posición del nodo padre del nodo P en el árbol A. Si P es la raiz
devuelve nulo

❏ hijoIzquierdo(A:Arbol; P:Posicion) devuelve (Posicion)
❏ requerimientos: El árbol A es no vacío y P no es nodo nulo.
❏ efecto: Devuelve la posición del nodo hijo más a la izquierda del nodo P en el árbol
A. Si el nodo P no tiene hijos, devuelve nulo
5.2 Especificación del TDA Árbol

❏ hermanoDerecho(A:Arbol; P:Posicion) devuelve (Posicion)
❏ requerimientos: El árbol A es no vacío. P no es nodo nulo.
❏ efecto: Devuelve la posición del nodo hermano derecho del nodo P en el árbol A. Si
el nodo P no tiene hermano derecho, devuelve nulo

❏ info(A:Arbol; P:Posicion) devuelve (E:Elemento)
❏ requerimientos: El árbol A es no vacío. P no es nodo nulo.
❏ efecto: Devuelve en E} el contenido del nodo P en el árbol A.

❏ insertaHijo(A:Arbol; P:Posicion; E:Elemento)
❏ requerimientos: Si el árbol A es no vacío, entonces P no es nodo nulo.
❏ modifica: A.
❏ efecto: Añade un nodo, con contenido E, como hijo más a la izquierda del nodo P
en el árbol A. Si el árbol A es vacío, entonces añade un nodo con contenido E como
raíz del árbol A.
5.2 Especificación del TDA Árbol
❏ insertaHermano(A:Arbol; P:Posicion; E:Elemento)
❏ requerimientos: El árbol A es no vacío. P no es nodo nulo. P no es la raiz.
❏ modifica: A.
❏ efecto: Añade un nodo, con contenido E, como hermano derecho del nodo P en el
árbol A.

❏ suprimeHijo(A:Arbol; P:Posicion)
❏ requerimientos: El árbol A es no vacío. El nodo P no es nulo.
❏ modifica: A.
❏ efecto: Suprime el hijo más a la izquierda del nodo P en el árbol A y todos sus
descendientes.

❏ suprimeHermano(A:Arbol; P:Posicion)
❏ requerimientos: El árbol A es no vacío. El nodo P no es nulo.
❏ modifica: A.
❏ efecto: Suprime el hermano derecho del nodo P en el árbol A y todos sus
descendientes.
5.2 Especificación del TDA Árbol

❏ modifica(A:Arbol; P:Posicion; E:Elemento)
❏ requerimientos: El árbol A es no vacío. El nodo P no es nodo nulo.
❏ modifica: A.
❏ efecto: Modifica el contenido del nodo P en el árbol A, cambiándolo por el nuevo
contenido E.

❏ nodoNulo(P:Posicion) devuelve (booleano)
❏ efecto: Devuelve cierto si P es nulo, y falso en caso contrario.
5.2 Especificación del TDA Árbol
La interface Java del TDA Árbol de acuerdo a esta especificación, y sus excepciones, es
la siguiente:

package arbolInterface;
import arbolException.*;
public interface Arbol {
    public boolean vacio();
    public Object raiz();
    public Object padre(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public Object hijoIzquierdo(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public Object hermanoDerecho(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public Object info(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public void insertaHijo(Object posicion, Object elemento) throws 
      NodoNuloException;     
    public void insertaHermano(Object posicion, Object elemento) throws 
      ArbolVacioException, NodoNuloException, RaizException;
5.2 Especificación del TDA Árbol
    public void suprimeHijo(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public void suprimeHermano(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public void modifica(Object posicion, Object elemento) throws
      ArbolVacioException, NodoNuloException;
    public boolean nodoNulo(Object posicion);
}  // fin interface Arbol

package arbolException;
public class ArbolException extends Exception {
    public ArbolException() { super(); };
    public ArbolException(String s) { super(s); };
}
package arbolException;
public class ArbolVacioException extends ArbolException {
    public ArbolVacioException() { super(); };
    public ArbolVacioException(String s) { super(s); };
}
5.2 Especificación del TDA Árbol

package arbolException;
public class NodoNuloException extends ArbolException {
    public NodoNuloException() { super(); };
    public NodoNuloException(String s) { super(s); };
}
package arbolException;
public class RaizException extends ArbolException {
    public RaizException() { super(); };
    public RaizException(String s) { super(s); };
}
package arbolException;
public class NodoNoNuloException extends ArbolException {
    public NodoNoNuloException() { super(); };
    public NodoNoNuloException(String s) { super(s); };   
}
5.3 Ejemplos de uso del TDA Árbol
❏ Veremos en esta sección algunos ejemplos de uso del TDA Árbol. Uno de los
aspectos fundamentales en árboles son los esquemas de recorrido. Aprovecharemos
esta sección para describir las distintas formas de recorrido en árboles haciendo
uso de las operaciones del TDA, es decir, los procedimientos de recorrido se
mostrarán como operaciones a un nivel de abstracción mayor, y por tanto serán
independientes de la implementación del TDA.

❏ Los tres esquemas fundamentales de recorrido en árboles se denominan:


❏ orden previo o preorden,
❏ orden simétrico o inorden,
❏ orden posterior o postorden.

❏ Orden previo o Preorden


❏ Si el árbol es vacío, entonces la lista vacía es el listado de los nodos del árbol en
preorden.
❏ Si el árbol no es vacío, el listado en preorden de sus nodos está formado, primero,
por la raíz del árbol, seguido por los nodos del primer subárbol en preorden, luego
por los nodos del segundo subárbol en preorden, y así sucesivamente hasta los
nodos del último subárbol en preorden.
5.3 Ejemplos de uso del TDA Árbol

❏ Orden simétrico o Inorden


❏ Si el árbol es vacío, entonces la lista vacía es el listado de los nodos del árbol en
inorden.
❏ Si el árbol no es vacío, el listado en inorden de sus nodos está formado, primero, por
los nodos del primer subárbol en inorden, seguido por la raíz del árbol, luego por
los nodos del segundo subárbol en inorden, y así sucesivamente hasta los nodos del
último subárbol en inorden.

❏ Orden posterior o Postorden


❏ Si el árbol es vacío, entonces la lista vacía es el listado de los nodos del árbol en
postorden.
❏ Si el árbol no es vacío, el listado en postorden de sus nodos está formado, primero,
por los nodos del primer subárbol en postorden, luego por los nodos del segundo
subárbol en postorden, y así sucesivamente hasta los nodos del último subárbol en
postorden, y finalmente, por la raíz del árbol.
5.3 Ejemplos de uso del TDA Árbol
import arbolInterface.*;
import arbolException.*;
import java.io.*;
class ArbolUtil {
    static public void preOrden(Arbol arbol, Object p) {
        try {
            if (arbol.nodoNulo(p)) return;
            Object e = arbol.info(p);
            System.out.println(e);
            Object q = arbol. hijoIzquierdo(p);
            while (!arbol.nodoNulo(q)) {
                preOrden(arbol, q);
                q = arbol. hermanoDerecho(q);
            }
        } catch (ArbolException e) {
            System.err.println("Error en el uso del Arbol: e");
        }
    } // fin preOrden
5.3 Ejemplos de uso del TDA Árbol

    static public void inOrden(Arbol arbol, Object p) {
        try {
            if (arbol.nodoNulo(p)) return;
            Object q = arbol.hijoIzquierdo(p);
            inOrden(arbol,q);
            Object e = arbol.info(p);
            System.out.println(e);
            while (!arbol.nodoNulo(q)) {
                q = arbol. hermanoDerecho(q);
                inOrden(arbol,q);
            }
        } catch (ArbolException e) {
            System.err.println("Error en el uso del Arbol: e");
        }
    } // fin inOrden
5.3 Ejemplos de uso del TDA Árbol

    static public void postOrden(Arbol arbol, Object p) {
        try {
            if (arbol.nodoNulo(p)) return;
            Object q = arbol.hijoIzquierdo(p);
            while (!arbol.nodoNulo(q)) {
                postOrden(arbol,q);
                q = arbol.hermanoDerecho(q);
            }
            Object e = arbol.info(p);
            System.out.println(e);
        } catch (ArbolException e) {
            System.err.println("Error en el uso del Arbol: e");
        }
    } // fin postOrden

} // fin class ArbolUtil
5.4 Implementaciones del TDA Árbol

Las clases Arbol y Posicion pueden escribirse


básicamente de la siguiente forma:
class Posicion {
    protected Object info;
    protected Posicion hijoIzquierdo;
    protected Posicion padre;
    protected Posicion hermanoDerecho;

 
class Arbol {
    Posicion arbol;
}
5.4 Implementaciones del TDA Árbol

package arbol;
class Posicion {
    protected Object info;
    protected Posicion hijoIzquierdo;
    protected Posicion padre;
    protected Posicion hermanoDerecho;
    public boolean equals(Object o) {
        if (o==null) return false;
        Posicion p = (Posicion)o;
        return (this==p);
    }
} // fin class Posicion
5.4 Implementaciones del TDA Árbol
package arbol;
import arbolException.*;
public class Arbol implements arbolInterface.Arbol {
    Posicion arbol;
    public Arbol() {
        arbol = null;
    }
    public boolean vacio() {
        return (nodoNulo(arbol));
    }
    public Object raiz(){
        return arbol;
    }
    public Object padre(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.padre;
    }
5.4 Implementaciones del TDA Árbol
    public Object hijoIzquierdo(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.hijoIzquierdo;
    }
    public Object hermanoDerecho(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.hermanoDerecho;
    }
    public Object info(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.info;
    }
5.4 Implementaciones del TDA Árbol

    public void insertaHijo(Object posicion, Object elemento) throws 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if ((!vacio())&&(nodoNulo(p))) throw new NodoNuloException();
        Posicion aux = new Posicion();
        aux.info = elemento;
        aux.hijoIzquierdo = null;
        if (vacio()) {
            aux.padre = null;
            aux.hermanoDerecho = null;
            arbol = aux;
        }
        else {
            aux.padre = p;
            aux.hermanoDerecho = p.hijoIzquierdo;
            p.hijoIzquierdo = aux;
        }
    } 
5.4 Implementaciones del TDA Árbol

public void insertaHermano(Object posicion, Object elemento) throws 
      ArbolVacioException, NodoNuloException, RaizException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        if (p==raiz()) throw new RaizException();    
        Posicion aux = new Posicion();
        aux.info = elemento;
        aux.hijoIzquierdo = null;
        aux.hermanoDerecho = p.hermanoDerecho;
        p.hermanoDerecho = aux;
        aux.padre = p.padre;
    }
5.4 Implementaciones del TDA Árbol

    public void suprimeHijo(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        Posicion aux = (Posicion)hijoIzquierdo(p);
        if (!nodoNulo(aux)) {
            while(!nodoNulo(hijoIzquierdo(aux)))
                suprimeHijo(aux);
            p.hijoIzquierdo = aux.hermanoDerecho;
        }
    }
5.4 Implementaciones del TDA Árbol

    public void suprimeHermano(Object posicion) throws ArbolVacioException, 
      NodoNuloException  {
        Posicion p = (Posicion)posicion;
        if (vacio()) new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        Posicion aux = (Posicion)hermanoDerecho(p);
        if (!nodoNulo(aux)) {
            while(!nodoNulo(hijoIzquierdo(aux)))
                suprimeHijo(aux);
            p.hermanoDerecho = aux.hermanoDerecho;
        }
    }
5.4 Implementaciones del TDA Árbol

    public void modifica(Object posicion, Object elemento) throws 
      ArbolVacioException, NodoNuloException  {
        Posicion p = (Posicion)posicion;
        if (vacio()) new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        p.info = elemento;
    }

    public boolean nodoNulo(Object posicion) {
        return (posicion==null);
    }

}  // fin class Arbol
5.5 Especificación del TDA Árbol Binario

Para el TDA Árbol Binario consideramos un conjunto de operaciones muy


similar al que hemos descrito para el TDA Árbol, con la diferencia de que
las operaciones de posicionamiento, inserción y eliminación van dirigidas
hacia el hijo izquierdo y hacia el hijo derecho de un nodo dado, en lugar
de hacia el hijo izquierdo y hacia el hermano como se definían en el TDA
Árbol.
Es importante notar la diferencia entre árboles n-arios y árboles binarios, ya
que son TDA's distintos. En el árbol binario, los subárboles se contituyen
desde su creación como subárboles izquierdos o derechos mientras que en
un árbol, un subárbol puede ser creado como subárbol de más la
izquierda y después dejar de serlo.
5.5 Especificación del TDA Árbol Binario
Especificación informal del TDA Árbol Binario

❏ ArbolBinario = TDA con operaciones crea, vacio,  raiz,  padre, 


hijoIzquierdo,  hijoDerecho, info,  insertaIzquierdo, 
insertaDerecho,  suprimeIzquierdo,  suprimeDerecho, 
modifica, nodoNulo.

❏ DESCRIPCIÓN:
❏ Los valores del TDA ArbolBinario son arboles binarios donde cada nodo contiene un
dato del tipo Elemento. Las posiciones de los nodos en el árbol son del tipo Posicion.
Los árboles son mutables: insertaIzquierdo, insertaDerecho, suprimeIzqquierdo,
suprimeDerecho} y modifica añaden, eliminan y modifican respectivamente
elementos de un árbol.

❏ OPERACIONES:
❏ crea() devuelve (A:ArbolBinario)
❏ efecto: Devuelve el árbol vacío A.
5.5 Especificación del TDA Árbol Binario
❏ vacio(A:ArbolBinario) devuelve (booleano)
❏ efecto: Devuelve cierto si A es el árbol vacío, y falso en caso contrario.

❏ raiz(A:ArbolBinario) devuelve (Posicion)
❏ efecto: Devuelve la posición del nodo raíz en el árbol A. Si el árbol A está vacío,
devuelve nulo

❏ padre(A:ArbolBinario; P:Posicion) devuelve (Posicion)
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo.
❏ efecto: Devuelve la posición del nodo padre del nodo P en el árbol A. Si P es la
raiz, devuelve nulo.

❏ hijoIzquierdo(A:ArbolBinario;  P:Posicion)  devuelve 


(Posicion)}
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo.
❏ efecto: Devuelve la posición del nodo hijo izquierdo del nodo P en el árbol A. Si
el nodo P no tiene hijo izquierdo, devuelve nulo.
5.5 Especificación del TDA Árbol Binario

❏ hijoDerecho(A:ArbolBinario; P:Posicion) devuelve (Posicion)
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo.
❏ efecto: Devuelve la posición del nodo hijo derecho del nodo P en el árbol A. Si el nodo P no
tiene hijo derecho, devuelve nulo

❏ info(A:ArbolBinario; P:Posicion) devuelve (E:Elemento)
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo.
❏ efecto: Devuelve en E el contenido (etiqueta) del nodo P en el árbol A.

❏ insertaIzquierdo(A:ArbolBinario; P:Posicion; E:Elemento)
❏ requerimientos: Si el árbol A es no vacío, entonces el nodo P es no nulo y el nodo hijo izquierdo
de P es nulo
❏ modifica: A.
❏ efecto: Añade un nodo, con contenido E, como hijo izquierdo del nodo P en el árbol A. Si el
árbol A es vacío, entonces añade un nodo con contenido E como raíz del árbol A.
5.5 Especificación del TDA Árbol Binario
❏ insertaDerecho(A:ArbolBinario; P:Posicion; E:Elemento)
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo. El nodo hijo derecho de P
es nulo
❏ modifica: A.
❏ efecto: Añade un nodo, con contenido E, como hijo derecho del nodo P en el árbol A.

❏ suprimeIzquierdo(A:ArbolBinario; P:Posicion)
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo.
❏ modifica: A.
❏ efecto: Suprime el hijo izquierdo del nodo P en el árbol A y todos sus descendientes.

❏ suprimeDerecho(A:ArbolBinario; P:Posicion)
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo.
❏ modifica: A.
❏ efecto: Suprime el hijo derecho del nodo P en el árbol A y todos sus descendientes.
5.5 Especificación del TDA Árbol Binario

❏ modifica(A:ArbolBinario; P:Posicion; E:Elemento)
❏ requerimientos: El árbol A es no vacío. El nodo P es no nulo.
❏ modifica: A.
❏ efecto: Modifica el contenido del nodo P en el árbol A, cambiándolo por el nuevo
contenido E.

❏ nodoNulo(P:Posicion) devuelve (booleano)
❏ efecto: Devuelve cierto si P es nulo, y falso en caso contrario.
5.5 Especificación del TDA Árbol Binario

La interface Java del TDA Árbol de acuerdo a esta especificación es


la siguiente (las excepciones utilizadas son las mismas que las del
TDA Árbol):

package arbolBinarioInterface;
import arbolException.*;
public interface ArbolBinario {
 public boolean vacio();
    public Object raiz();
    public Object padre(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public Object hijoIzquierdo(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public Object hijoDerecho(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
    public Object info(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
5.5 Especificación del TDA Árbol Binario

   public void insertaHijoIzquierdo(Object posicion, Object elemento) throws 
      NodoNuloException, NodoNoNuloException;
   public void insertaHijoDerecho(Object posicion, Object elemento) throws 
      ArbolVacioException, NodoNuloException, NodoNoNuloException;
   public void suprimeHijoIzquierdo(Object posicion) throws 
      ArbolVacioException, NodoNuloException;
   public void suprimeHijoDerecho(Object posicion) throws ArbolVacioException, 
      NodoNuloException;
   public void modifica(Object posicion, Object elemento) throws 
      ArbolVacioException, NodoNuloException;
   public boolean nodoNulo(Object posicion);

}  // fin class ArbolBinario
5.6 Ejemplos de uso del TDA Árbol Binario

❏ Como ejemplos de uso de árboles binarios veremos también los


esquemas de recorrido para estos, los cuales difieren con respecto a
los recorridos para árboles generales ya que ambos tipos de datos y
sus conjuntos de opraciones son diferentes. Para árboles binarios,
se definen los recorridos en preorden, inorden y postorden de la
siguiente forma:

❏ Preorden:
❏ Si el árbol binario es vacío, entonces la lista vacía es el listado de los
nodos del árbol binario en preorden.
❏ Si el árbol binario no es vacío, el listado en preorden de sus nodos está
formado, primero, por la raíz del árbol, seguido por los nodos del
subárbol izquierdo en preorden, y finalmente, por los nodos del
subárbol derecho en preorden.
5.6 Ejemplos de uso del TDA Árbol Binario

❏ Inorden:
❏ Si el árbol binario es vacío, entonces la lista vacía es el listado de los
nodos del árbol binario en inorden.
❏ Si el árbol binario no es vacío, el listado en inorden de sus nodos está
formado, primero, por los nodos del subárbol izquierdo en inorden,
seguido por la raíz del árbol, y finalmente, por los nodos del subárbol
derecho en inorden.

❏ Postorden:
❏ Si el árbol binario es vacío, entonces la lista vacía es el listado de los
nodos del árbol binario en postorden.
❏ Si el árbol binario no es vacío, el listado en postorden de sus nodos está
formado, primero, por los nodos del subárbol izquierdo en postorden,
luego por los nodos del subárbol derecho en postorden, y finalmente,
por la raíz del árbol.
5.6 Ejemplos de uso del TDA Árbol Binario
import arbolBinarioInterface.*;
import arbolException.*;
import java.io.*;

class ArbolBinarioUtil {

    static public void preOrden(ArbolBinario arbol, Object p) {
        try {
            if (arbol.nodoNulo(p)) return;
            Object e = arbol.info(p);
            System.out.println(e);
            preOrden(arbol,arbol.hijoIzquierdo(p));
            preOrden(arbol,arbol.hijoDerecho(p));
        } catch (ArbolException e) {
            System.err.println("Error en el uso del Arbol: e");
        }
    } // fin preOrden
5.6 Ejemplos de uso del TDA Árbol Binario

    static public void inOrden(ArbolBinario arbol, Object p) {
        try {
            if (arbol.nodoNulo(p)) return;
            inOrden(arbol,arbol.hijoIzquierdo(p));
            Object e = arbol.info(p);
            System.out.println(e);
            inOrden(arbol,arbol.hijoDerecho(p));
        } catch (ArbolException e) {
            System.err.println("Error en el uso del Arbol: e");
        }
    } // fin inOrden
5.6 Ejemplos de uso del TDA Árbol Binario

    static public void postOrden(ArbolBinario arbol, Object p) {
        try {
            if (arbol.nodoNulo(p)) return;
            postOrden(arbol,arbol.hijoIzquierdo(p));
            postOrden(arbol,arbol.hijoDerecho(p));
            Object e = arbol.info(p);
            System.out.println(e);
        } catch (ArbolException e) {
            System.err.println("Error en el uso del Arbol: e");
        }
    } // fin postOrden

} // fin class ArbolBinarioUtil
5.7 Implementaciones del TDA Árbol
Binario

Las clases ArbolBinario y Posicion pueden escribirse


básicamente de la siguiente forma:

class Posicion {
    protected Object info;
    protected Posicion hijoIzquierdo;
    protected Posicion padre;
    protected Posicion hijoDerecho;

class ArbolBinario {
    Posicion arbol;
}
5.7 Implementaciones del TDA Árbol
Binario

package arbolBinario;

class Posicion {
    protected Object info;
    protected Posicion hijoIzquierdo;
    protected Posicion padre;
    protected Posicion hijoDerecho;
    public boolean equals(Object o) {
        if (o==null) return false;
        Posicion p = (Posicion)o;
        return (this==p);
    }
} // fin class Posicion
5.7 Implementaciones del TDA Árbol
Binario
package arbolBinario;
import arbolException.*;
public class ArbolBinario implements arbolBinarioInterface.ArbolBinario{
    Posicion arbol;
    public ArbolBinario() {
        arbol = null;
    }
    public boolean vacio() {
        return (nodoNulo(arbol));
    }
    public Object raiz(){
        return arbol;
    }
    public Object padre(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.padre;
    }
5.7 Implementaciones del TDA Árbol
Binario
   public Object hijoIzquierdo(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.hijoIzquierdo;
    }
    public Object hijoDerecho(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.hijoDerecho;
    }
    public Object info(Object posicion) throws ArbolVacioException, 
      NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        return p.info;
    }
5.7 Implementaciones del TDA Árbol
Binario

    public void insertaHijoIzquierdo(Object posicion, Object elemento) throws 
      NodoNuloException, NodoNoNuloException {
        Posicion p = (Posicion)posicion;
        if (!vacio()) {
            if (nodoNulo(p)) throw new NodoNuloException();
            if (!nodoNulo(hijoIzquierdo(p))) throw new 
                  NodoNoNuloException();
        }
        Posicion aux = new Posicion();
        aux.info = elemento;
        aux.hijoIzquierdo = null;
        aux.hijoDerecho = null;
        if (vacio()) { aux.padre = null; arbol = aux; }
        else { aux.padre = p; p.hijoIzquierdo = aux; }
    }
5.7 Implementaciones del TDA Árbol
Binario

    public void insertaHijoDerecho(Object posicion, Object elemento) throws 
      ArbolVacioException, NodoNuloException, NodoNoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) throw new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        if (!nodoNulo(hijoDerecho(p))) throw new NodoNoNuloException();
        Posicion aux = new Posicion();
        aux.info = elemento;
        aux.hijoIzquierdo = null;
        aux.hijoDerecho = null;
        aux.padre = p;
        p.hijoDerecho = aux;
    }
5.7 Implementaciones del TDA Árbol
Binario

    public void suprimeHijoIzquierdo(Object posicion) throws 
      ArbolVacioException, NodoNuloException {
        Posicion p = (Posicion)posicion;
        if (vacio()) new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        Posicion aux = (Posicion)hijoIzquierdo(p);
        if (!nodoNulo(aux)) {
            suprimeHijoIzquierdo(aux);
            suprimeHijoDerecho(aux);
            p.hijoIzquierdo = null;
        }
    }
5.7 Implementaciones del TDA Árbol
Binario

    public void suprimeHijoDerecho(Object posicion) throws ArbolVacioException, 
      NodoNuloException  {
        Posicion p = (Posicion)posicion;
        if (vacio()) new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        Posicion aux = (Posicion)hijoDerecho(p);
        if (!nodoNulo(aux)) {
            suprimeHijoIzquierdo(aux);
            suprimeHijoDerecho(aux);
            p.hijoDerecho = null;
        }
    }
5.7 Implementaciones del TDA Árbol
Binario

    public void modifica(Object posicion, Object elemento) throws 
      ArbolVacioException, NodoNuloException  {
        Posicion p = (Posicion)posicion;
        if (vacio()) new ArbolVacioException();
        if (nodoNulo(p)) throw new NodoNuloException();
        p.info = elemento;
    }

    public boolean nodoNulo(Object posicion) {
        return (posicion==null);
    }

}  // fin class ArbolBinario
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

❏ En un capítulo anterior vimos una implementación de colas de


prioridad mediante estructuras de datos lineales, en concreto,
mediante representación enlazada con apuntadores. En esa
implementación, la operación inserta gastaba un tiempo de
ejecución O(n) en el peor caso, si bien la operación suprime se
realiza en un tiempo de ejecución constante O(1).
❏ La implementación que veremos en esta sección para colas de
prioridad requiere un tiempo de ejecución O(logn) para ambas
operaciones, lo que supone una mejora sustancial para valores
grandes de n.
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

❏ En esta implementación, los elementos de la cola de prioridad se


organizan en un árbol binario que esté lo más balanceado
posible.
❏ En el nivel más bajo, en el cual pueden faltar algunas nodos (lo
cual no puede producirse en niveles superiores), se exige además
que los nodos que falten sean los de más a la derecha posible.
❏ El árbol binario es parcialmente ordenado, es decir, la prioridad
de un nodo no es menor que la de sus hijos, por lo que en la raíz
estará siempre el elemento con mayor prioridad
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

10

7 9

6 7 7 8

3 1 4 6 5

Árbol binario parcialmente ordenado balanceado


5.8 Árboles Parcialmente Ordenados: Colas de prioridad

Podemos usar como representación un vector A en el cual:


 si hay n nodos en el árbol, se usan las n primeras posiciones del
vector,
 A[1] contiene el elemento situado en la raíz del arbol,
 el hijo izquierdo del nodo A[i] está, si existe, está en A[2i],
 el hijo derecho de A[i] está, si existe, está en A[2i+1]
 el padre del elemento representado en A[i] está en A[idiv2], para i>1.
Esta representación de árboles parcialmente ordenados se denomina
comúnmente representación por montículos.
La ventaja está en que se aprovecha la posibilidad de operar
aritméticamente con los índices del vector para obtener, con
tiempos de ejecución constantes, las posiciones de los padres e
hijos izquierdo y derecho.
La desventaja es que se requiere determinar a priori el número
máximo de elementos en el árbol.
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

La interface del TDA Cola de Prioridad se corresponde con el que


presentamos en el capítulo de colas para colas de prioridad. No
obstante, la especificación del TDA Cola de Prioridad que estamos
considerando en esta sección es diferente a la que consideramos
anteriormente. La diferencia radica en que ahora no se tiene en
cuenta la propiedad FIFO para elementos de igual prioridad. Por
tanto, no suponemos ahora ningún orden en el procesamiento de
dos elementos con igual prioridad (si queremos imponer un orden
será necesario dar mayor prioridad a uno de ellos)
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

La implementación completa en Java es la siguiente:


package colaPrioridadBasadaArbol;
import colaException.*;

public class ColaPrioridad implements colaPrioridadInterface.ColaPrioridad {

    private static int max = 100;
    private Object elementos[];
    private int prioridades[];
    private int ultimo;

    public ColaPrioridad() {
        elementos = new Object[max+1];
        prioridades = new int[max+1];
        ultimo = 0;
    }
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

    public boolean vacia() {
        return (ultimo==0);
    }

    public Object primero() throws ColaVaciaException {
        if (vacia()) throw new ColaVaciaException();        
        return elementos[1];
    }

    public int primero_prioridad() throws ColaVaciaException {
        if (vacia()) throw new ColaVaciaException();        
        return prioridades[1];
    }
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

    public void inserta(Object elemento, int prioridad) {
        ultimo++;
        int i = ultimo;
        boolean encontrado = false;
        while((i>1)&&(!encontrado)) {
            int padre = i/2;
            if (prioridad<=prioridades[padre])
                encontrado = true;
            else {
                elementos[i] = elementos[padre];
                prioridades[i] = prioridades[padre];
                i = padre;
            }
        }
        elementos[i] = elemento;
        prioridades[i] = prioridad;
    }
5.8 Árboles Parcialmente Ordenados: Colas de prioridad

    public void suprime() throws ColaVaciaException {
    public void suprime() throws ColaVaciaException {
        if (vacia()) throw new ColaVaciaException();   
        if (vacia()) throw new ColaVaciaException();   
        Object elemento = elementos[ultimo];
        Object elemento = elementos[ultimo];
        int prioridad = prioridades[ultimo];
        int prioridad = prioridades[ultimo];
        ultimo--;
        ultimo--;
        int i=1; int j=2*i;
        int i=1; int j=2*i;
        boolean encontrado=false;
        boolean encontrado=false;
        while((j<=ultimo)&&(!encontrado)) {
        while((j<=ultimo)&&(!encontrado)) {
            if ((j<ultimo)&&(prioridades[j]<prioridades[j+1]))
            if ((j<ultimo)&&(prioridades[j]<prioridades[j+1])) 
 j++;
            if (prioridad>=prioridades[j]) encontrado = true;
            if (prioridad>=prioridades[j]) encontrado = true;
            else { elementos[i] = elementos[j];
            else { elementos[i] = elementos[j];
                   
                   prioridades[i] = prioridades[j];
prioridades[i] = prioridades[j];
                   
                   i=j; j=2*i;
i=j; j=2*i;
            }
            }
        }
        }
        elementos[i] = elemento;
        elementos[i] = elemento;
        prioridades[i] = prioridad;
        prioridades[i] = prioridad;
    }
    }
}  // fin class ColaPrioridad 
5.9 Árboles Binarios de Búsqueda
 Un árbol binario de búsqueda es un árbol binario formado por elementos en
los cuales se asume un orden lineal, y éstos se organizan en el árbol de la
forma:
 todos los elementos almacenados en el subárbol izquierdo de cualquier nodo son
menores que el elemento almacenado en dicho nodo
 todos los elementos almacenados en el subárbol derecho de cualquier nodo son
mayores que el elemento almacenado en ese nodo
 Los árboles binarios de búsqueda se utilizan típicamente como estructura de
datos para la representación de conjuntos con operaciones:
 inserta, para insertar un elemento dado en un conjunto
 suprime, para suprimir un elemento determinado de un conjunto
 miembro, para saber si un elemento dado pertenece o no a un conjunto.
 Todas estas operaciones pueden realizarse en un tiempo de ejecución
promedio O(logn) para un conjunto de n elementos.
5.9 Árboles Binarios de Búsqueda
 Otras posibles operaciones que puede realizarse con el mismo tiempo
de ejecución promedio usando árboles binarios de búsqueda son:
 min, que devuelve el elemento mínimo de un árbol
 suprime_min, que elimina el elemento mínimo de un árbol
 max, que devuelve el elemento máximo de un árbol
 suprime_max, que elimina el elemento máximo de un árbol
 Podríamos usar también árboles binarios de búsqueda para
representar colas de prioridad (en este caso permitiríamos almacenar
elementos repetidos en el árbol, asumiendo, por ejemplo, que los
elementos almacenados en el subárbol derecho de cualquier nodo son
mayores o iguales que el elemento almacenado en ese nodo).
5.9 Árboles Binarios de Búsqueda
Especificación Informal
 ArbolBinarioBusqueda = TDA con operaciones crea, destruye, vacio, 
inserta, suprime,  miembro.

 DESCRIPCIÓN:
 Los valores del TDA ArbolBinarioBusqueda son árboles binarios de búsqueda
de elementos del tipo Elemento. Los árboles binarios de búsqueda son mutables:
inserta} y suprime añaden y eliminan elementos en el árbol respectivamente.

 OPERACIONES:
 crea() devuelve (A:ArbolBinarioBusqueda)
 efecto: Devuelve el árbol binario de búsqueda vacío A.
5.9 Árboles Binarios de Búsqueda
 vacio(A:ArbolBinarioBusqueda) devuelve (booleano)
 efecto: Devuelve cierto si A es el árbol vacío, y falso en caso contrario.

 inserta(A:ArbolBinarioBusqueda; E:Elemento)
 modifica: A.
 efecto: Añade el elemento E al árbol binario de búsqueda A.

 suprime(A:ArbolBinarioBusqueda; E:Elemento)
 modifica: A.
 efecto: Suprime el elemento E, si existe, del árbol binario de búsqueda A.

 miembro(A:ArbolBinarioBusqueda; E:Elemento) devuelve 
(booleano)
 efecto: Devuelve cierto si E es un elemento del árbol binario de búsqueda A, y falso
en caso contrario.
5.9 Árboles Binarios de Búsqueda
La interface en Java del TDA Árbol Binario de Búsqueda es la siguiente:

package arbolBinarioBusquedaInterface;
import Comparable;
public interface ArbolBinarioBusqueda {
    public boolean vacio();
    public void inserta(Comparable elemento);
    public void suprime(Comparable elemento);
    public boolean miembro(Comparable elemento);
}  // fin interface ArbolBinarioBusqueda

Los elementos deben implementar la interface Comparable porque así lo


requiere el TDA Árbol Binario de Búsqueda al asumirse un órden
lineal entre sus elementos.
5.9 Árboles Binarios de Búsqueda
La interface en Java del TDA Árbol Binario de Búsqueda es la siguiente:

package arbolBinarioBusquedaInterface;
import Comparable;
public interface ArbolBinarioBusqueda {
    public boolean vacio();
    public void inserta(Comparable elemento);
    public void suprime(Comparable elemento);
    public boolean miembro(Comparable elemento);
}  // fin interface ArbolBinarioBusqueda

Los elementos deben implementar la interface Comparable porque así lo


requiere el TDA Árbol Binario de Búsqueda al asumirse un órden
lineal entre sus elementos.
5.9 Árboles Binarios de Búsqueda
package arbolBinarioBusqueda;
import Comparable;

public class ArbolBinarioBusqueda implements 
arbolBinarioBusquedaInterface.ArbolBinarioBusqueda {

    private Comparable info;
    private ArbolBinarioBusqueda izquierdo, derecho;

    public ArbolBinarioBusqueda() {
        info = null;
        izquierdo = null;
        derecho = null;
    }
    public boolean vacio() {
        return ((info==null)&&(izquierdo==null)&&(derecho==null));
    }
5.9 Árboles Binarios de Búsqueda
    public void inserta(Comparable elemento) {
        if (vacio()) {
            info = elemento;
            izquierdo = new ArbolBinarioBusqueda();
            derecho = new ArbolBinarioBusqueda();
        }
        else if (elemento.menorQue(info)) {
            izquierdo.inserta(elemento);
        }
        else {
            derecho.inserta(elemento);
        }
    }
5.9 Árboles Binarios de Búsqueda
    public void suprime(Comparable elemento) {
        if (vacio()) return;
        if (elemento.menorQue(info)) {
            izquierdo.suprime(elemento);
        }
        else if (elemento.mayorQue(info)) {
            derecho.suprime(elemento);
        }
        else if ((izquierdo.vacio())&&(derecho.vacio())) {
            info = null;
            derecho = null;
            izquierdo = null;
        }
        else if (izquierdo.vacio()) {
            info = derecho.info;
            izquierdo = derecho.izquierdo;
            derecho = derecho.derecho;
        }
5.9 Árboles Binarios de Búsqueda
        else if (derecho.vacio()) {
            info = izquierdo.info;
            derecho = izquierdo.derecho;
            izquierdo = izquierdo.izquierdo;
        }
        else {
            ArbolBinarioBusqueda min = derecho;
            while (!min.izquierdo.vacio()) min = min.izquierdo;
            info = min.info;
            min.info = min.derecho.info;
            min.izquierdo = min.derecho.izquierdo;
            min.derecho = min.derecho.derecho;
        }
    } 
5.9 Árboles Binarios de Búsqueda
    public boolean miembro(Comparable elemento) {
        if (vacio()) return false;
        if (elemento.equals(info)) return true;
        if (elemento.menorQue(info)) return izquierdo.miembro(elemento);
        return derecho.miembro(elemento);
    }

}  // fin class ArbolBinarioBusqueda

También podría gustarte