.jpg) Árboles
    
    Árboles
  Tabla de contenidos
.png) 1. 
      Sesión 2 (laboratorio): Árboles (I)
1. 
      Sesión 2 (laboratorio): Árboles (I)
      
     1.1. 
		Árbol binario: implementación
1.1. 
		Árbol binario: implementación
		
Instruir al alumno en la implementación y utilización de árboles binarios.
En este ejercicio se utilizará la definición recursiva de árbol para diseñar una clase Java que implemente un árbol binario. De acuerdo a la definición, un árbol binario es, o bien vacío o consta de un elemento raíz y, a lo sumo, dos árboles hijo (izquierdo y/o derecho).
Recuerda que se necesitan dos clases, BTree y BNode, para poder modelar adecuadamente el árbol vacío.
			  Crea la clase BTree basándote en la definición recursiva anterior. Programa un constructor sin parámetros que inicialice un árbol vacío. Programa un constructor adicional que reciba como parámetro el elemento (de la clase Object) a almacenar en el nodo raíz.
			
			  Crea la clase BNode basándote en la definición recursiva anterior, que encapsule la información a almacenar en el nodo y las referencias a los hijos izquierdo y derecho. Programa un constructor que reciba como parámetro el elemento (de la clase Object) a almacenar en el nodo (e inicializa sus hijos a null). Programa también los correspondientes métodos accesores (get y set).
			
			  Programa la clase excepción BTreeException, con un constructor que reciba un mensaje, de tipo String.
			
Las soluciones están al final de los ejercicios.
 1.2. 
		Inserción y borrado de elementos en un árbol binario
1.2. 
		Inserción y borrado de elementos en un árbol binario
		
Entrenar al alumno en los métodos de inserción y borrado en árboles binarios.
Dado un árbol binario implementar los métodos que permitan insertar un subárbol en el árbol y extraer un subárbol del mismo.
			 Programa el método void insert(BNode tree, int side) throws BTreeException en la clase BNode, que permite añadir el subárbol que se pasa por parámetro en el lado correspondiente. Si ya existe el subárbol, el método lanzará una BTreeException con el mensaje de eror correspondiente.
			
Declara, en la clase BNode, las constantes de clase LEFT_SIDE y RIGHT_SIDE necesarias. Si se introduce un lado incorrecto, o hay un subárbol no vacío en el lado indicado, debe lanzarse la excepción BTreeException. 
			
			 Programa el método BNode extract(int side) throws BTreeException, que extrae y elimina el subárbol del lado que se le pasa como parámetro. En caso de que el lado no sea correcto, el método lanzará una BTreeException con el mensaje de error correspondiente.
			
Las soluciones están al final de los ejercicios.
 1.3. 
		Recorridos en árboles binarios.
1.3. 
		Recorridos en árboles binarios.
		
Practicar los recorridos sobre árboles binarios.
	Dado el árbol binario BTree del ejercicio anterior, programa los recorridos (Pre-Orden, In-Orden, Post-Orden) que permitan recorrer, de forma recursiva, el árbol.
	
			 Programa el método void printPreOrder() que recorra el árbol en pre-orden, imprimiendo en pantalla el elemento contenido en cada nodo. 
			
			 Programa el método void printInOrder() que recorra el árbol en in-orden, imprimiendo en pantalla el elemento contenido en cada nodo. 
			
			 Programa el método void printPostOrder() que recorra el árbol en post-orden, imprimiendo en pantalla el elemento contenido en cada nodo. 
			
Las soluciones están al final de los ejercicios.
 1.4. 
		Altura de un árbol
1.4. 
		Altura de un árbol
		
Profundizar en el manejo de estructuras de datos árboles.
	Dado un árbol binario, BTree, implementar un método en éste que devuelva la altura del mismo.
	
Las soluciones se pueden ver en el siguiente listado:
public class BTree {
  /* ********************************************** *
   * CLASS BNode * **********************************************
   */
  static class BNode {
    private Object info;
    private BNode left;
    private BNode right;
    BNode(Object info) {
      this(info, null, null);
    }
    BNode(Object info, BNode l, BNode r) {
      this.info = info;
      left = l;
      right = r;
    }
    Object getInfo() {
      return info;
    }
    BNode getLeft() {
      return left;
    }
    BNode getRight() {
      return right;
    }
    void setInfo(Object info) {
      this.info = info;
    }
    void setLeft(BNode left) {
      this.left = left;
    }
    void setRight(BNode right) {
      this.right = right;
    }
    void insert(BNode tree, int side) throws BTreeException {
      if (side == LEFT_SIDE) {
        if (left == null) {
          left = tree;
        } else {
          throw new BTreeException("A non-empty tree already exists");
        }
      } else if (side == RIGHT_SIDE) {
        if (right == null) {
          right = tree;
        } else {
          throw new BTreeException("A non-empty tree already exists");
        }
      } else {
        throw new BTreeException("Incorrect side");
      }
    }
    BNode extract(int side) throws BTreeException {
      BNode subtree;
      if (side == LEFT_SIDE) {
        subtree = left;
        left = null;
      } else if (side == RIGHT_SIDE) {
        subtree = right;
        right = null;
      } else {
        throw new BTreeException("Incorrect side");
      }
      return subtree;
    }
    int size() {
      int size = 1;
      if (left != null) {
        size += left.size();
      }
      if (right != null) {
        size += right.size();
      }
      return size;
    }
    int height() {
      int hl = -1;
      int hr = -1;
      if (left != null) {
        hl = left.height();
      }
      if (right != null) {
        hr = right.height();
      }
      return 1 + Math.max(hl, hr);
    }
    void preorder() {
      System.out.println(info);
      if (left != null) {
        left.preorder();
      }
      if (right != null) {
        right.preorder();
      }
    }
    void inorder() {
      if (left != null) {
        left.inorder();
      }
      System.out.println(info);
      if (right != null) {
        right.inorder();
      }
    }
    void postorder() {
      if (left != null) {
        left.postorder();
      }
      if (right != null) {
        right.postorder();
      }
      System.out.println(info);
    }
  }
  /* ********************************************** */
  public static final int LEFT_SIDE = 0;
  public static final int RIGHT_SIDE = 1;
  protected BNode root;
  public BTree() {
    root = null;
  }
  public BTree(Object info) {
    root = new BNode(info);
  }
  public BNode getRoot() {
    return root;
  }
  public void setRoot(BNode root) {
    this.root = root;
  }
  public void insert(BTree tree, int side) throws BTreeException {
    root.insert(tree.getRoot(), side);
  }
  public int size() {
    int size = 0;
    if (root != null) {
      size = root.size();
    }
    return size;
  }
  public int height() {
    int h = -1;
    if (root != null) {
      h = root.height();
    }
    return h;
  }
  public void preorder() {
    if (root != null) {
      root.preorder();
    }
  }
  public void inorder() {
    if (root != null) {
      root.inorder();
    }
  }
  public void postorder() {
    if (root != null) {
      root.postorder();
    }
  }
}
	  
                
                public class BTreeException extends Exception {
  public BTreeException(String mensaje) {
    super(mensaje);
  }
}
	  
               1.5. 
		Tamaño de un árbol
1.5. 
		Tamaño de un árbol
		
Profundizar en el manejo de estructuras de datos árboles.
	Dado un árbol binario, BTree, implementar un método en éste que devuelva el número de nodos que tiene.
	
Las soluciones están aquí y al final de los ejercicios:
public class TestNumNodes {
  public TestNumNodes() {
  }
  public static void main(String args[]) {
    BTree tree;
    try {
      tree = new BTree("Tres");
      BTree subarbol = new BTree("tristes");
      tree.insert(subarbol, BTree.LEFT_SIDE);
      BTree temporal = new BTree("tigres");
      tree.insert(temporal, BTree.RIGHT_SIDE);
      temporal = new BTree("com\355an");
      subarbol.insert(temporal, BTree.LEFT_SIDE);
      temporal = new BTree("trigo");
      subarbol.insert(temporal, BTree.RIGHT_SIDE);
    } catch (BTreeException ex) {
      System.out.println(ex.getMessage());
      return;
    }
    System.out.println("------------------");
    System.out.println((new StringBuilder())
        .append("Num Nodos Arbol Binario = ").append(tree.size()).toString());
    System.out.println("------------------");
  }
}
	  
               1.6. 
		Utilización de un árbol binario
1.6. 
		Utilización de un árbol binario
		
Instruir al alumno en la utilización de árboles binarios.
	  En este ejercicio debes utilizar las clases BTree y BNode  programadas en los ejercicios anteriores. Para ello, debes programar una clase TongueTwister (Trabalenguas), que en su método main cree un árbol con la siguiente estructura:  
      
               Tres
                |
       --------------------
       |                  |
    tristes             tigres
       |
  ------------
  |          |
  |          |
comían     trigo
              Imprime el árbol binario usando los métodos programados en el anterior ejercicio. Comprueba que el orden en que aparecen los elementos en pantalla sea el correcto.
			 Haz los cambios que necesites, en la estructura del árbol binario, para que, imprimiendo en pre-orden, aparezca en pantalla "Tres tristes tigres comían trigo".  
			
Las soluciones están aquí y al final de los ejercicios:
public class TongueTwister {
  public static void main(String args[]) {
    BTree tongueTwister;
    BTree.BNode subtree, tmp;
    try {
      tongueTwister = new BTree("Tres");
      subtree = new BTree.BNode("tristes");
      tongueTwister.getRoot().insert(subtree, BTree.LEFT_SIDE);
      tmp = new BTree.BNode("tigres");
      tongueTwister.getRoot().insert(tmp, BTree.RIGHT_SIDE);
      tmp = new BTree.BNode("comian");
      subtree.insert(tmp, BTree.LEFT_SIDE);
      tmp = new BTree.BNode("trigo");
      subtree.insert(tmp, BTree.RIGHT_SIDE);
    } catch (BTreeException ex) {
      System.out.println(ex.getMessage());
      return;
    }
    tongueTwister.preorder();
    System.out.println("------------------");
    tongueTwister.inorder();
    System.out.println("------------------");
    tongueTwister.postorder();
    System.out.println("------------------");
    System.out.println("------------------");
    try {
      subtree = tongueTwister.getRoot().getLeft().extract(BTree.LEFT_SIDE);
      tongueTwister.getRoot().getRight().insert(subtree, BTree.LEFT_SIDE);
      subtree = tongueTwister.getRoot().getLeft().extract(BTree.RIGHT_SIDE);
      tongueTwister.getRoot().getRight().insert(subtree, BTree.RIGHT_SIDE);
      tongueTwister.preorder();
    } catch (BTreeException ex) {
      System.out.println(ex.getMessage());
    }
  }
}
	  
              .png) 2. 
	
	Actividades para casa
2. 
	
	Actividades para casa
       2.1. 
		Árboles N-arios y sistemas de ficheros
2.1. 
		Árboles N-arios y sistemas de ficheros
		
	Estudiar otro tipo de árboles más genéricos que los binarios, proponiéndo un ejemplo concreto de aplicación: los sistemas de ficheros. Se pretende que los alumnos aprendan a generalizar las operaciones que han visto en el caso de árboles binarios para aplicarlas al caso de árboles N-arios.
			Hasta ahora hemos trabajado con árboles binarios, pero no son el
			único tipo
			de árbol existente. En algunas ocasiones necesitamos
			árboles
			más flexibles que nos permitan tener, por cada nodo, un número
			N de
			nodos hijo
			que no tiene por qué ser exactamente dos, y que puede
			ser diferente para
			cada
			nodo. Esta estructura de datos es lo que se
			conoce como árbol N-ario y se
			muestra en la
			figura 1
			. Cada nodo del árbol contiene una referencia a la información que se
			quiere almacenar (
			info
			), y un conjunto de referencias a los nodos hijo (
			children
			). Para acceder a todos los nodos del árbol tan sólo necesitamos una
			referencia a su nodo raíz (
			root
			), tal y como ocurría en el caso de árboles binarios.
		
En este ejercicio vamos a ver un ejemplo en el que se necesitan árboles N-arios: los sistemas de ficheros. Supongamos que tenemos el siguiente sistema de ficheros (también conocido como árbol de directorios):
C:
 |_Archivos de programa
   |_Eclipse
   |_Java
 |_Mis documentos
   |_Imagenes
   |_Musica
   |_Videos
 |_ProgSis
   |_Proyecto
     |_Modulo1
     |_Modulo2
              
			Tal y como su nombre indica, todos los
			directorios o carpetas (en
			nuestro caso, para simplificar, vamos a ignorar los
			archivos) se
			organizan en forma de árbol: existe un nodo raíz (C:)
			que contiene
			varias carpetas, cada una de las cuales contiene a su
			vez más
			carpetas, y así sucesivamente. Para crear y manejar este sitema de ficheros,
			vamos a
			tomar la estructura genérica mostrada en la
			figura 1
			, y la vamos a particularizar para adaptarla al caso que nos ocupa.
			Los nodos de la imagen serán nuestras carpetas. Cada carpeta está
			representada mediante un objeto de tipo
			Folder
			. Cada uno de estos objetos posee dos atributos:
		
					name
					es un atributo de tipo
					String
					que almacena el nombre de la carpeta
				
					subdirectories
					es un atributo de tipo
					Vector
					que almacena los subdirectorios (objetos de tipo
					Folder
					) que contiene la carpeta.
				
			Para representar el sistema de ficheros, haremos uso de una clase
			FileSystem
			que desempeña el papel de árbol, puesto que es la que contiene una
			referencia al nodo raíz (atributo
			root
			de tipo
			Folder
			), desde el cual se puede acceder al resto de carpetas (nodos) del
			sistema de ficheros.
		
La figura 2 representa el sistema de ficheros del ejemplo previo representado mediante los objetos Java que vamos a manejar.
		
			Para facilitar la realización de este ejercicio, se proporciona parte
			de la
			clase
			Folder
			, así como la estructura de la clase
			FileSystem, que se pueden descargar en los siguientes enlaces
			:
		
			En primer lugar, se va a practicar el uso de objetos de la clase
			Vector (si no sabes cómo usarla, consulta el API)
			,
			para lo que se pide implementar los siguientes métodos de la clase
			Folder
			:
		
					public Folder addSubdirectory(String folderName)
					: añade una nueva
					carpeta, con nombre
					folderName
					, a la colección de subdirectorios y devuelve una
					referencia a la misma.
				
					public void printSubdirectories()
					: imprime en pantalla el nombre de
					todos los subdirectorios de la
					carpeta en el formato: subdirectorio1
					subdirectorio2 ...
					subdirectorioN. Sólo se imprimirán los
					subdirectorios, sin
					tener en
					cuenta los subdirectorios que pudiera tener cada uno de éstos.
				
			Antes de seguir avanzando asegúrate de que los métodos funcionan
			correctamente. Para ello, crea una clase
			FileSystemTest.java
			para comprobar el funcionamiento.
		
			Una vez tenemos claro cómo funcionan los objetos de tipo
			Folder
			y cómo
			recorrer el objeto de la clase
			Vector
			que contiene los subdirectorios
			(nodos hijo) de cada carpeta, nos
			centraremos en el sistema de ficheros en
			sí. Para
			ello se pide
			implementar los siguientes métodos de la clase
			FileSystem
			(además de realizar las pruebas necesarias para comprobar su correcto
			funcionamiento con la clase
			FileSystemTest
			que se creaste previamente):
		
					public Folder searchFolder(Folder root, String folderName)
					
					: busca
					la carpeta cuyo nombre es
					folderName
					en todo el árbol. Si la carpeta existe, se
					devuelve una referencia a la misma, o
					null
					en caso contrario.
					Pista: este método es recursivo ya que, para
					cada carpeta, hay que
					recorrer
					todos sus subdirectorios, los
					subdirectorios de éstos y así
					sucesivamente
					hasta cubrir todo el
					árbol. Tal vez sea útil utilizar un método auxiliar
					private Folder searchFolder(Folder root, String folderName)
					, donde root será el nodo raíz de cada
					subárbol sobre el que se vaya a hacer la búsqueda de la
					carpeta especificada.
				
					public Folder addNewFolder(String parentFolderName, String
						newFolderName): crea una nueva carpeta de nombre
					newFolderName
					, y la añade como
					subdirectorio de la carpeta cuyo nombre es
					parentFolderName
					. Si ya
					existe una carpeta en el sistema de ficheros con el nombre
					newFolderName
					,
					o si la carpeta
					parentFolderName
					no existe, no se debe hacer nada y se
					devuelve
					null
					. En caso contrario, se crea y añade la nueva carpeta
					y se devuelve
					una referencia a la misma.
				
					public void printFileSystem()
					: imprime la estructura del sistema de
					ficheros con el siguiente
					formato:
C:
 |_Archivos de programa
   |_Eclipse
   |_Java
 |_Mis documentos
   |_Imagenes
   |_Musica
   |_Videos
 |_ProgSis
   |_Proyecto
     |_Modulo1
     |_Modulo2
                    
					
					Pista: este método es también recursivo. Además, el número de
					espacios en blanco
					antes de cada nombre tiene mucho que ver con el
					nivel del árbol en el
					que se
					encuentra la carpeta. Tal vez pueda ser
					útil crear un método auxiliar
					private void printFileSystem(Folder root, int level)
					con el que se harán las llamadas recursivas.
					
				
 2.2. Problema de examen para trabajar con Árboles
  Binarios.
2.2. Problema de examen para trabajar con Árboles
  Binarios. Estudiar una posible aplicación de árboles binarios para resolver expresiones sencillas.
Una expresión es una determinada combinación de operadores y operandos que se evalúan para obtener un resultado particular. La forma más habitual de escribir una expresión es la conocida como "infija" en la que el orden de la combinación de los anteriores es: primer operando, operador y segundo operando. A continuación, se muestra un ejemplo de expresión infija (sin uso de paréntesis para mayor sencillez):
      
        a-b*c+d
      
    
Sin embargo, existe otro tipo de notación que la anterior expresión, más conocida como "postfija", en la que cambia el orden de la combinación a: primer operando, segundo operando y operador, es decir, que el operador pasa al final de la combinación. Según esta regla, la expresión infija inicial se transforma en la siguiente expresión postfija equivalente:
      
        abc*-d+
      
    
Las expresiones postfijas tienen muchas ventajas a la hora de su posterior tratamiento informático ya que, por ejemplo, no requieren del uso de paréntesis para ser evaluadas y, además, dicha evaluación se puede realizar mediante algoritmos sencillos como se comprobará en este problema. Teniendo en cuenta que cualquier expresión infija tiene su equivalente en notación postfija, esto proporciona una gran versatilidad y agilidad a la hora de plantear y resolver el problema de la evaluación de expresiones.
Otra de las grandes ventajas de las expresiones postfijas es que pueden representarse en forma de un Arbol de Sintaxis Abstracta (AST). Un AST es un árbol binario cuyas hojas contienen los caracteres asociados a los operandos (a,b,c,d...) y el resto de nodos contienen los caracteres asociados a los operadores (+,-,*,/). El árbol binario AST de la anterior expresión postfija se muestra a continuación:
Mediante el uso de esta representación de la expresión
    postfija en forma de árbol binario AST, la evaluación de la misma se
    reduce a realizar un sencillo algoritmo de recorrido recursivo en
    POSTORDEN del mismo y usando una estructura auxiliar de datos para
    almacenar los resultados intermedios del procesamiento de los nodos en el
    recorrido del mismo. Tanto para la creación como para la evaluación del
    árbol binario AST, las estructuras necesarias serán DOS PILAS AUXILIARES
    respectivas de datos: una pila de nodos binarios y
    una pila de valores decimales(float) que
    permitirán crear la estructura de los nodos del árbol así como almacenar
    los resultados de las operaciones intermedias de la evaluación del mismo.
    Por último, para evaluar numéricamente la expresión, será necesaria una
    tabla adicional que almacene los valores numéricos de los operandos de la
    expresión, para lo que se usará una tabla HASH auxiliar.
El interfaz Operator contiene todos los
    operadores posibles que intervienen en las expresiones (para simplificar
    el problema, solo se consideran operadores binarios):
public interface Operator {
  public static final char SUM = '+';
  public static final char SUBTRACTION = '-';
  public static final char PRODUCT = '*';
  public static final char DIVISION = '/';
}
              La clase PostfixExprEvaluator implementa el
    anterior interfaz y contiene el árbol binario AST asociado a la expresión,
    además de las estructuras de datos necesarias para crear el árbol y
    evaluar la expresión postfija. En este caso, serán dos PILAS de datos
    auxiliares para almacenar tanto nodos como valores intermedios. La
    declaración de la clase es la siguiente:
public class PostfixExprEvaluator implements Operator {
  Node postfixASTTree = null; // The root node of the AST postfix expression tree
  Stack stackNodes = null; // Auxiliary stack to store the nodes of the AST tree
  Stack stackResults = null; //Auxiliary stack to store the intermediate results
              El objeto que implementa una pila es el que se
      proporciona en el JDK(java.util.Stack) y cuyos métodos para
      apilar y des-apilar datos se declaran a continuación:
public Object push(Object item); // Stack data on top of the stack public Object pop(); // Unstack data from the top of the stack
La clase Node representa el nodo binario del
    árbol AST y almacena cada carácter(char) de la expresión
    postfija que puede ser tanto un operador(+,-,*,/) o un operando(a, b, c,
    d...):
class Node {
    char value = '\0'; // The character (either an operand or a operator)
    
    Node left = null;  // The reference to the left child node of the binary tree
    Node der = null; // The reference to the right child node of the binary tree
              Para crear el árbol binario AST que guardará la
      expresión postfija se necesita una PILA de Nodos
      binarios(Node) que servirá para construir el árbol binario
      a medida que se procesan sus elementos. La siguiente figura muestra el
      proceso general:
El procedimiento para crear el árbol binario AST es el siguiente:
Para cada uno de los caracteres de la expresión postfija hacer lo siguiente:
Si el carácter analizado es un operando (a,b,c,d...), crea un nuevo nodo binario sin hijos que contenga el carácter y apílalo en la PILA.
Si el elemento analizado es un operador(+,-,*,/) , crea un nuevo nodo binario de la siguiente manera:
Extrae dos nodos binarios de la PILA.
El primer elemento extraído de la PILA será el hijo DERECHO del nuevo nodo binario.
El segundo elemento extraído de la PILA es el hijo IZQUIERDO del nuevo nodo binario.
El dato que almacenará el nuevo nodo binario será el carácter que representa el operador.
Una vez creado el nuevo nodo binario, apílalo en la PILA.
Por último, devuelve el nodo raíz del árbol binario AST creado y que es precisamente el que se obtiene de desapilar el único nodo binario restante que queda en la PILA.
Implementa el siguiente método de la clase
      PostfixExprEvaluator que permite crear el árbol binario AST
      a partir de una expresión postfija de acuerdo al anterior
      procedimiento:
        Node createPostfixTree( String sPostfix ) throws
        Exception
      
Una expresión postfija se puede evaluar fácilmente mediante el uso de un recorrido en POSTORDEN del arbol binario AST asociado e implementado en el ejercicio anterior. Para ello, también será necesario tener dos estructuras de datos auxiliares: una PILA y un DICCIONARIO de VALORES, tal y como se muestra en la siguiente figura:
El DICCIONARIO de valores contiene los valores enteros
        correspondientes de los operandos(a,b,c,d....). Para ello, se ha
        decidido usar un objeto de tipo Hashtable que proporciona
        el JDK (java.util.Hashtable) y que permite mapear una
        serie de claves con sus valores correspondientes. En este caso, la
        clave será el carácter asociado al operando. Para obtener el valor
        entero asociado a un operando, usa el siguiente método de la clase,
        donde key es el carácter(char) asociado al
        mismo.
public Object get(Object key) //Returns the value to which the specified key is mapped in this hashtable.
El procedimento para conseguir la evaluación del árbol binario AST se basa en hacer un recorrido en POSTORDEN del mismo realizando el siguiente proceso en cada nodo binario del árbol:
Si el nodo a evaluar es un operando (a,b,c,d....)
Apilar en la PILA de resultados el valor entero (int) del operando que se obtiene previamente del DICCIONARIO de VALORES auxiliar.
Si el nodo a evaluar es un operador binario (+,-,*,/)
Des-apilar dos valores de la PILA de resultados, calcular el resultado de aplicar el operador a dichos valores y, por último, apilar dicho resultado de nuevo en la PILA de Resultados.
Implementa el siguiente método RECURSIVO de la clase
      Node que implementa el recorrido en POSTORDEN del árbol
      binario AST de acuerdo al anterior procedimiento:
        void processNode( Hashtable values, Stack stackResults ) throws
        Exception
      
La soluciones se pueden ver en los siguientes listados:
public interface Operator {
  public static final char SUM = '+';
  public static final char SUBTRACTION = '-';
  public static final char PRODUCT = '*';
  public static final char DIVISION = '/';
}
	  
                
                import java.util.Hashtable;
import java.util.Stack;
class Node {
  char value = '\0'; // The character (either an operand or a operator)
  Node left = null; // The reference to the left child node of the binary tree
  Node right = null; // The reference to the right child node of the binary tree
  public Node() {
  }
  public Node(char value, Node left, Node right) {
    this.value = value;
    this.left = left;
    this.right = right;
  }
  public Node(char value) {
    this(value, null, null);
  }
  public void processNode(Hashtable values, Stack stackResults)
      throws Exception {
    // This recursive method realizes a postorder tree traversal to process the
    // node
    // Firstly, we process the left node recursively
    if (left != null) {
      left.processNode(values, stackResults);
    }
    // Secondly, we process the right node recursively
    if (right != null) {
      right.processNode(values, stackResults);
    }
    // In the other case, we process the actual node
    float result = 0;
    // Depending on the Node's value that indicates the corresponding opperation
    // the result value is calculated.
    switch (value) {
    case Operator.SUM:
      result = (Float) stackResults.pop() + (Float) stackResults.pop();
      break;
    case Operator.SUBTRACTION:
      float subtrahend = (Float) stackResults.pop();
      float minuend = (Float) stackResults.pop();
      result = minuend - subtrahend;
      break;
    case Operator.PRODUCT:
      result = (Float) stackResults.pop() * (Float) stackResults.pop();
      break;
    case Operator.DIVISION:
      float divisor = (Float) stackResults.pop();
      float dividend = (Float) stackResults.pop();
      result = dividend / divisor;
      break;
    default:
      result = (Integer) (values.get(value));
    }
    // And finally, we store the result at the results' stack
    stackResults.push(result);
  }
}
	  
                
                import java.util.Stack;
import java.util.Hashtable;
public class PostfixExprEvaluator implements Operator {
  Node postfixASTTree = null; // The node of the AST postfix expression tree.
  Stack stackNodes = null; // Auxiliary stack to store the nodes of the AST tree
  Stack stackResults = null; // Auxiliary stack to store the intermediate
                             // results
  public PostfixExprEvaluator(String sPostfix) throws Exception {
    // Create the AST Tree from the postfix expression
    postfixASTTree = createPostfixTree(sPostfix);
  }
  private Node createPostfixTree(String sPostfix) throws Exception {
    int i = 0;
    stackNodes = new Stack();
    // while there are tokens(char) left to analyze
    while (i < sPostfix.length()) {
      // Evaluate the token (char)
      char token = sPostfix.charAt(i++);
      switch (token) {
      // If it is an operator
      case Operator.SUM:
      case Operator.PRODUCT:
      case Operator.SUBTRACTION:
      case Operator.DIVISION:
        // Popping the two operands
        Node leftOperand = (Node) stackNodes.pop();
        Node rightOperand = (Node) stackNodes.pop();
        // Create a new Binary Tree Node
        Node binaryOperation = new Node(token, rightOperand, leftOperand);
        // Push it on the stack
        stackNodes.push(binaryOperation);
        break;
      // If it is an operand
      default:
        // Push the new node on the stack
        Node operand = new Node(token);
        stackNodes.push(operand);
      }
    }
    // Finally, we return the first Node of the stack.
    return (Node) stackNodes.pop();
  }
  public float evaluateExpression(String expression, Hashtable values)
      throws Exception {
    // Expression's result
    float result = 0;
    // The results' stack
    stackResults = new Stack();
    // Create a Binary Tree with the postfix expression
    postfixASTTree = createPostfixTree(expression);
    // Traverse the tree to calculate the intermediate results
    postfixASTTree.processNode(values, stackResults);
    // The result value to be returned is located on top of the results' stack
    result = (Float) stackResults.pop();
    return result;
  }
  public static void main(String args[]) throws Exception {
    Hashtable values = new Hashtable();
    values.put('a', 2);
    values.put('b', 5);
    values.put('c', 3);
    values.put('d', 1);
    String sPostfix = "abc*-d+";
    PostfixExprEvaluator myExpression = new PostfixExprEvaluator(sPostfix);
    System.out.println(" Evaluating postfix expression '" + sPostfix
        + "' with values: " + values);
    System.out.println(" Result = "
        + myExpression.evaluateExpression(sPostfix, values));
  }
}