En las líneas El método insertarNodo de la clase

Anuncio
Árboles
Las listas enlazadas, pilas y colas son estructuras de datos lineales (es decir, secuencias). Un árbol es una estructura de datos
bidimensional no lineal, con propiedades especiales. Los nodos de un árbol contienen dos o más enlaces. En esta sustentación
hablaremos sobre los árboles binarios cuyos nodos contienen dos enlaces (uno de los cuales puede ser null). El nodo raíz es el
primer nodo en un árbol. Cada enlace en el nodo raíz hace referencia a un hijo. El hijo izquierdo es el primer nodo en el subárbol
izquierdo (también conocido como el nodo raíz del subárbol izquierdo), y el hijo derecho es el primer nodo en el subárbol
derecho (también conocido como el nodo raíz del subárbol derecho). Los hijos de un nodo específico se llaman hermanos. Un
nodo sin hijos se llama nodo hoja. Generalmente, los científicos computacionales dibujan árboles desde el nodo raíz hacia abajo;
exactamente lo opuesto a la manera en que crecen los árboles naturales.
En nuestro ejemplo de árbol binario crearemos un árbol binario especial, conocido como árbol de búsqueda binaria. Un árbol de
búsqueda binaria (sin valores de nodo duplicados) cuenta con la característica de que los valores en cualquier subárbol izquierdo
son menores que el valor del nodo padre de ese subárbol, y los valores en cualquier subárbol derecho son mayores que el valor
del nodo padre de ese subárbol. La forma del árbol de búsqueda binaria que corresponde a un conjunto de datos puede variar,
dependiendo del orden en el que se inserten los valores en el árbol.
La aplicación crea un árbol de búsqueda binaria compuesto por valores enteros, y lo recorre (es decir, avanza a través de todos
sus nodos) de tres maneras: usando los recorridos inorden, preorden y postorden recursivos. El programa genera 10 números
aleatorios e inserta a cada uno de ellos en el árbol.
Analicemos el programa del árbol binario.
El método main de la clase PruebaArbol, empieza creando una instancia de un objeto Árbol vacío y asigna su referencia a la
variable árbol
Arbol arbol = new Arbol();
En las líneas
for ( int i = 1; i <= 10; i++ ){
valor = numeroAleatorio.nextInt( 100 );
System.out.print( valor + " " );
arbol.insertarNodo( valor );
} // fin de for
se generan 10 enteros al azar, cada uno de los cuales se inserta en el árbol binario mediante una llamada al método
insertarNodo.Después el programa realiza recorridos preorden, inorden, postorden y demás metodos
El método insertarNodo de la clase Arbol
public void insertarNodo( int valorInsertar ) {
if ( raiz == null )
raiz = new NodoArbol( valorInsertar ); // crea el nodo raíz aquí
else
raiz.insertar( valorInsertar ); // llama al método insertar
} // fin del método insertarNodo
Determina primero si el árbol está vacío. De ser así, en la línea raiz = new NodoArbol( valorInsertar ); se asigna un nuevo
objeto NodoArbol, se inicializa el nodo con el entero que se insertará en el árbol y se asigna el nuevo nodo a la
referencia raíz. Si el árbol no está vacío, en la línea raiz.insertar( valorInsertar ); se hace una llamada al método insertar
de NodoArbol.
public void insertar( int valorInsertar ){
// inserta en el subárbol izquierdo
if ( valorInsertar < datos ) {
// inserta nuevo NodoArbol
if ( nodoIzq == null )
nodoIzq = new NodoArbol( valorInsertar );
else // continúa recorriendo el subárbol izquierdo
nodoIzq.insertar( valorInsertar );
} // fin de if
else if ( valorInsertar > datos ) {// inserta en el subárbol derecho
// inserta nuevo NodoArbol
if ( nodoDer == null )
nodoDer = new NodoArbol( valorInsertar );
else // continúa recorriendo el subárbol derecho
nodoDer.insertar( valorInsertar );
} // fin de else if
} // fin del método insertar
} // fin de la clase NodoArbol
Este método utiliza la recursividad para determinar la posición del nuevo nodo en el árbol e inserta el nodo en esa
posición. En un árbol de búsqueda binaria, un nodo puede insertarse solamente como nodo hoja.
El método insertar de NodoArbol compara el valor a insertar con el valor de datos en el nodo raíz. Si el valor a
insertar es menor que los datos del nodo raíz, el programa determina si el subárbol izquierdo está vacío. De ser así,
se asigna un nuevo objeto NodoArbol, se inicializa con el entero que se insertará y se asigna el nuevo nodo a la
referencia nodoIzquierdo. En caso contrario, se hace una llamada recursiva a insertar para que se inserte el valor en
el subárbol izquierdo. Si el valor a insertar es mayor que los datos del nodo raíz, el programa determina si el subárbol
derecho está vacío. De ser así, se asigna un nuevo objeto NodoArbol, se inicializa con el entero que se insertará y se
asigna el nuevo nodo a la referencia nodoDerecho. En caso contrario, se hace una llamada recursiva a insertar para
que se inserte el valor en el subárbol derecho. Si el valorInsertar ya se encuentra en el árbol, simplemente se ignora.
Los métodos recorridoInorden, recorridoPreorden y recorridoPostorden llaman a los métodos ayudantes de Árbol
llamados ayudanteInorden, ayudantePreorden y ayudantePostorden, respectivamente, para recorrer el árbol e
imprimir los valores de los nodos. Los métodos ayudantes en la clase Arbol permiten iniciar un recorrido sin tener
que pasar el nodo raíz al método. La referencia raíz es un detalle de implementación que no debe ser accesible para
el programador.
Los métodos recorridoInorden, recorridoPreorden y recorridoPostorden simplemente toman la referencia privada
raiz y la pasan al método ayudante apropiado para iniciar un recorrido del árbol. El caso base para cada método
ayudante determina si la referencia que recibe es null y, de ser así, regresa inmediatamente.
El método ayudanteInorden define los pasos para un recorrido inorden:
1. Recorrer el subárbol izquierdo con una llamada a ayudanteInorden
2. Procesar el valor en el nodo.
3. Recorrer el subárbol derecho con una llamada a ayudanteInorden.
El recorrido inorden no procesa el valor en un nodo sino hasta que se procesan los valores en el subárbol izquierdo
de ese nodo. El recorrido inorden de un árbol de búsqueda binaria imprime los valores de los nodos en orden
ascendente. El proceso de crear un árbol de búsqueda binaria ordena los datos de antemano; por lo tanto, a este
proceso se le conoce como ordenamiento de árbol binario.
El método ayudantePreorden define los pasos para un recorrido preorden:
1. Procesar el valor en el nodo
2. Recorrer el subárbol izquierdo con una llamada a ayudantePreorden
3. Recorrer el subárbol derecho con una llamada a ayudantePreorden
El recorrido preorden procesa el valor en cada uno de los nodos, a medida que se van visitando. Después de
procesar el valor en un nodo dado, el recorrido preorden procesa los valores en el subárbol izquierdo y después los
valores en el subárbol derecho.
El método ayudantePostorden define los pasos para un recorrido postorden:
1. Recorrer el subárbol izquierdo con una llamada a ayudantePostorden
2. Recorrer el subárbol derecho con una llamada a ayudantePostorden
3. Procesar el valor en el nodo
El recorrido postorden procesa el valor en cada nodo después de procesar los valores de todos los hijos de ese nodo.
El árbol de búsqueda binaria facilita la eliminación de valores duplicados. Al crear un árbol, la operación de inserción
reconoce los intentos de insertar un valor duplicado, ya que éste sigue las mismas decisiones de “ir a la izquierda” o
“ir a la derecha” en cada comparación, al igual que el valor original. Por lo tanto, la operación de inserción
eventualmente comparará el valor duplicado con un nodo que contenga el mismo valor. En este punto, la operación
de inserción puede decidir descartar el valor duplicado (como lo hicimos en esta sustentacion).
/**
* Universidad del Quindío – Estructuras de Datos – Árbol de búsqueda binaria
* @authores
* Mauricio Duque
* German Ramirez
*/
public class PruebaArbol {
public static void main(String[] args) {
Arbol arbol = new Arbol();
int valor;
Random numeroAleatorio = new Random();
System.out.println( "Insertando los siguientes valores: " );
// inserta 10 enteros aleatorios de 0 a 99 en arbol
for ( int i = 1; i <= 10; i++ )
{
valor = numeroAleatorio.nextInt( 100 );
System.out.print( valor + " " );
arbol.insertarNodo( valor );
} // fin de for
System.out.println ( "\n\nRecorrido preorden" );
arbol.recorridoPreorden(); // realiza recorrido preorden de arbol
System.out.println ( "\n\nRecorrido inorden" );
arbol.recorridoInorden(); // realiza recorrido inorden de arbol
System.out.println ( "\n\nRecorrido postorden" );
arbol.recorridoPostorden(); // realiza recorrido postorden de arbol
System.out.println ( "\n\nContar Nodos" );
System.out.println( arbol.contarNodos() ); // realiza recorrido Contar nodos
System.out.println ( "\n\nContar Hijos" );
System.out.println( arbol.contarHijos() ); // realiza recorrido Contar hijos
System.out.println ( "\n\nContar Hermanos" );
System.out.println( arbol.contarHermanos() ); // realiza recorrido Contar hermanos
System.out.println ( "\n\nContar Nivel" );
System.out.println( arbol.contarNivel() ); // realiza recorrido Contar nivel
System.out.println ( "\n\nContar Raices" );
System.out.println( arbol.contarRaices() ); // realiza recorrido Contar raices
System.out.println ( "\n\nContar Hojas" );
System.out.println( arbol.contarHojas() ); // realiza recorrido Contar hojas
System.out.println();
} // fin de main
} // fin de la clase PruebaArbol
-------------------------------------------------------------///////////////////////////////////////////////////////////////////////-----------------------------------------------------
public class Arbol {
// Atributos
private NodoArbol raiz;
// el constructor inicializa un Arbol vacio de enteros
public Arbol()
{
raiz = null;
} // fin del constructor de Arbol sin argumentos
// inserta un nuevo nodo en el arbol de busqueda binaria
public void insertarNodo( int valorInsertar ) {
if ( raiz == null )
raiz = new NodoArbol( valorInsertar ); // crea el nodo raiz aqui
else
raiz.insertar( valorInsertar ); // llama al metodo insertar
} // fin del metodo insertarNodo
// Comienza el recorrido preorden
public void recorridoPreorden()
{
ayudantePreorden( raiz );
} // fin del metodo recorridoPreorden
// metodo recursivo para realizar el recorrido preorden
private void ayudantePreorden( NodoArbol nodo )
{
if ( nodo == null ) //caso base del metodo
return;
System.out.printf( "%d ", nodo.datos ); // imprime los datos del nodo
ayudantePreorden( nodo.nodoIzq ); // recorre el subarbol izquierdo
ayudantePreorden( nodo.nodoDer ); // recorre el subarbol derecho
} // fin del metodo ayudantePreorden
// Comienza recorrido inorden
public void recorridoInorden()
{
ayudanteInorden( raiz );
} // fin del metodo recorridoInorden
// metodo recursivo para realizar el recorrido inorden
private void ayudanteInorden( NodoArbol nodo )
{
if ( nodo == null )
return;
ayudanteInorden( nodo.nodoIzq ); // recorre el subarbol izquierdo
System.out.printf( "%d ", nodo.datos ); // imprime los datos del nodo
ayudanteInorden( nodo.nodoDer ); // recorre el subarbol derecho
} // fin del metodo ayudanteInorden
// Comienza recorrido postorden
public void recorridoPostorden()
{
ayudantePostorden( raiz );
} // fin del metodo recorridoPostorden
// metodo recursivo para realizar el recorrido postorden
private void ayudantePostorden( NodoArbol nodo )
{
if ( nodo == null )
return;
ayudantePostorden( nodo.nodoIzq ); // recorre el subarbol izquierdo
ayudantePostorden( nodo.nodoDer ); // recorre el subarbol derecho
System.out.printf( "%d ", nodo.datos ); // imprime los datos del nodo
} // fin del metodo ayudantePostorden
//metodo que permite contar nodos del arbol
public int contarNodos()
{
return ayudanteContarNodos( raiz );
} // fin del metodo contarNodos
// metodo recursivo para contar todos los nodos del arbol
private int ayudanteContarNodos( NodoArbol nodo )
{
if ( nodo == null )
return 0;
int hijosIzquierdo = ayudanteContarNodos( nodo.nodoIzq );
int hijosDerecho = ayudanteContarNodos( nodo.nodoDer );
return hijosIzquierdo+hijosDerecho+1;
} // fin del metodo ayudanteContarNodos
//metodo que permite contar nodos hijos del arbol
public int contarHijos()
{
int nodos = ayudanteContarNodos( raiz );
if( nodos <= 1 ) {
return 0;
}
else {
return nodos-1;
}
} // fin del metodo contarHijos
// metodo para contar todos los nodos hermanos del arbol
public int contarHermanos()
{
return ayudanteContarHermanos( raiz );
} // fin del metodo contarHermanos
// metodo recursivo que permite contar todos los nodos hermanos del arbol
private int ayudanteContarHermanos( NodoArbol nodo )
{
if ( nodo == null )
return 0;
// recorre el subarbol izquierdo
// recorre el subarbol derecho
int hermanos = 0;
if( nodo.nodoIzq != null && nodo.nodoDer != null )
hermanos += 2;
hermanos += ayudanteContarHermanos( nodo.nodoIzq );
hermanos += ayudanteContarHermanos( nodo.nodoDer );
// recorre el subarbol izquierdo
// recorre el subarbol derecho
return hermanos;
} // fin del metodo ayudanteContarHermanos
// metodo para contar los niveles del arbol
public int contarNivel()
{
return ayudanteContarNivel( raiz )-1;
} // fin del metodo recorridoPostorden
// metodo recursivo que permite contar todos los niveles del arbol
private int ayudanteContarNivel( NodoArbol nodo )
{
if ( nodo == null )
return 0;
int nivelIzquierdo = ayudanteContarNivel( nodo.nodoIzq );
int nivelDerecho = ayudanteContarNivel( nodo.nodoDer );
// recorre el subarbol izquierdo
// recorre el subarbol derecho
if( nivelIzquierdo > nivelDerecho )
return nivelIzquierdo+1;
else if( nivelIzquierdo < nivelDerecho )
return nivelDerecho+1;
else
return nivelDerecho+1;
} // fin del metodo ayudanteContarNivel
// metodo para contar todos los nodos raiz del arbol
public int contarRaices()
{
return ayudanteContarRaices( raiz );
} // fin del metodo contarRaices
// metodo recursivo que permite contar todos los nodos raiz del arbol
private int ayudanteContarRaices( NodoArbol nodo )
{
if ( nodo == null )
return 0;
int raices = 0;
if( nodo.nodoIzq != null || nodo.nodoDer != null )
raices ++;
raices += ayudanteContarRaices( nodo.nodoIzq );
raices += ayudanteContarRaices( nodo.nodoDer );
return raices;
} // fin del metodo ayudanteContarRaices
// metodo para contar todos los nodos hojas del arbol
public int contarHojas()
{
return ayudanteContarHojas( raiz );
} // fin del metodo contarHojas
// recorre el subarbol izquierdo
// recorre el subarbol derecho
// metodo recursivo que permite contar todos los nodos hojas del arbol
private int ayudanteContarHojas( NodoArbol nodo )
{
if ( nodo == null )
return 0;
int hojas = 0;
if( nodo.nodoIzq == null && nodo.nodoDer == null )
hojas ++;
hojas += ayudanteContarHojas( nodo.nodoIzq );
hojas += ayudanteContarHojas( nodo.nodoDer );
// recorre el subarbol izquierdo
// recorre el subarbol derecho
return hojas;
} // fin del metodo ayudanteContarHojas
} // fin de la clase Arbol
-------------------------------------------------------------///////////////////////////////////////////////////////////////////////----------------------------------------------------public class NodoArbol {
// Atributos
NodoArbol nodoIzq; // nodo izquierdo
int datos; // valor del nodo
NodoArbol nodoDer; // nodo derecho
// El constructor inicializa los datos y hace de este nodo un nodo raiz
public NodoArbol( int datosNodo ){
datos = datosNodo;
nodoIzq = nodoDer = null; // el nodo no tiene hijos
} // fin del constructor de NodoArbol
// localiza el punto de insercion e inserta un nuevo nodo; ignora los valores duplicados
public void insertar( int valorInsertar ) {
// inserta en el subarbol izquierdo
if ( valorInsertar < datos ){
// inserta nuevo NodoArbol
if ( nodoIzq == null )
nodoIzq = new NodoArbol( valorInsertar );
else // continua recorriendo el subarbol izquierdo
nodoIzq.insertar( valorInsertar );
} // fin de if
else if ( valorInsertar > datos ){ // inserta en el subarbol derecho
// inserta nuevo NodoArbol
if ( nodoDer == null )
nodoDer = new NodoArbol( valorInsertar );
else // continua recorriendo el subarbol derecho
nodoDer.insertar( valorInsertar );
} // fin de else if
} // fin del metodo insertar
} // fin de la clase NodoArbol
Descargar