Pilas en Java

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 17

Pilas en Java

1. CLASE STACK EN JAVA

A continuación vamos a explicar la clase Stack de Java, que implementa la


interface List. Stack se traduce por “pila” y para recordar su significado podemos
pensar en una pila de libros. También veremos las características más
importantes de esta nueva implementación y haremos un ejemplo a modo de
ejercicio.

STACK

La clase Stack es una clase de las llamadas de tipo LIFO (Last In - First Out, o
último en entrar - primero en salir). Esta clase hereda de la clase que ya hemos
estudiado anteriormente en el curso Vector y con 5 operaciones permite tratar
un vector a modo de pila o stack.

Las operaciones básicas son push (que introduce un elemento en la pila), pop
(que saca un elemento de la pila), peek (consulta el primer elemento de la cima
de la pila), empty (que comprueba si la pila está vacía) y search (que busca un
determinado elemento dentro de la pila y devuelve su posición dentro de ella).

Esta clase es muy sencilla y al crear un objeto de tipo Stack con el constructor
básico evidentemente no contendrá ningún elemento.

Un conjunto mucho más completo y consistente para operaciones de stack LIFO


es proporcionado en la interface Deque y sus implementaciones, pero nosotros
de momento vamos a limitarnos al estudio de la clase Stack.

EJEMPLO USO CLASE STACK

Realizaremos un ejemplo a modo de uso de pila. Uno de los casos más usados
en informática de una pila es el de querer verificar si una determinada sentencia
o instrucción está equilibrada en cuanto a número de paréntesis, corchetes o
llaves de apertura y cierre. Cuando se escribe código de programación si no
existe equilibrio entre signos de apertura (por ejemplo un paréntesis de
apertura) y cierre (por ejemplo un paréntesis de cierre) ni siquiera debería
procesarse la sentencia ya que no estaría formalmente bien construida. De esto
se encargan los analizadores léxicos de los compiladores.

Así que vamos a utilizar esta vez tan solo una clase Programa con el método
main, donde vamos a ir analizando una sentencia para verificar si es equilibrada
o no en símbolos de paréntesis, recorriendo todos sus caracteres desde el inicio
hasta el final.
Iremos construyendo nuestra pila apilando un símbolo (cada vez que detectemos
un símbolo de apertura o desapilando de ella cuando detectemos un símbolo de
cierre. Tendremos que ir analizando todos los caracteres de una expresión y
actuar cuando detectemos un paréntesis, operando en función de si el paréntesis
leído es de abrir (“(”) o cerrar (“)”). El equilibrio en la escritura vendrá
determinado al terminar el análisis en función de si la pila está vacía (hay
equilibrio) o contiene algún elemento (no hay equilibrio).

Ejemplo: analizamos la expresión “Hay varios países (México, España) que


comparten el mismo idioma (español o castellano).”

El resultado al finalizar el análisis de la sentencia sería que la pila está vacía, y


esto querrá decir que nuestra sentencia es equilibrada en paréntesis y por tanto
el resultado es correcto.

Si analizáramos la expresión “Hay varios países (México, España) que comparten


el mismo idioma (español o castellano.”

El resultado al finalizar el análisis será que la pila contiene un paréntesis, lo que


quiere decir que la expresión no es equilibrada y no tiene el mismo número de
paréntesis abiertos que cerrados.

Tendremos que tener en cuenta casos especiales como una expresión cuyo
primer elemento sea un paréntesis de cierre. Por ejemplo: “Hay varios países
)México, España(“ la consideraríamos una expresión incorrecta ya que si la pila
está vacía el primer elemento siempre tendrá que ser un paréntesis de apertura
y no uno de cierre. Tendremos en cuenta por tanto que además de equilibrio
exista corrección en la forma de construcción (que no puedan existir cierres de
paréntesis que no se hayan abierto).

Vamos a escribir ahora el siguiente código con el que vamos a trabajar:

import java.util.Stack;
public class Programa {
public static void main(String arg[]) {
String cadenano = "(Cadena no equilibrada en paréntesis(()()()))))";
String cadenasi = "(Cadena equilibrada en parentesis())";
System.out.println("Verificación equilibrado en paréntesis para cadenano:");
System.out.println(verificaParentesis(cadenano));
System.out.println("Verificación equilibrado en paréntesis para cadenasi:");
System.out.println(verificaParentesis(cadenasi));
}

public static boolean verificaParentesis(String cadena) {


Stack<String> pila = new Stack<String>(); int i = 0;
while (i<cadena.length()) { // Recorremos la expresión carácter a carácter
if(cadena.charAt(i)=='(') {pila.push("(");} // Si el paréntesis es de apertura apilamos
siempre
else if (cadena.charAt(i)==')') { // Si el paréntesis es de cierre actuamos según el caso
if (!pila.empty()){ pila.pop(); } // Si la pila no está vacía desapilamos
else { pila.push(")"); break; } // La pila no puede empezar con un cierre, apilamos y
salimos
}
i++;
}
if(pila.empty()){ return true; } else { return false; }
}
}

En este ejemplo hemos creado la función verificaParentesis que nos devuelve un


boolean indicando si dada una cadena, esta está equilibrada y correcta en
paréntesis. Para ello se hace uso internamente en este método de una pila o
stack. Así el programa principal main tan solo llama a esta función con una
cadena de ejemplo (cadenano o cadenasi) para verificar su equilibrado y
corrección en paréntesis.

El diagrama de clases por tanto para BlueJ es muy sencillo y tiene tan solo la
clase Programa:

La salida que obtendremos por consola será similar a esta:


CONCLUSIONES

Hemos visto un claro ejemplo del uso de la clase Stack que aunque muy sencilla,
es muy útil ya que su implementación es muy fácil de aprender con tan solo los
5 métodos comentados anteriormente (push, pop, peek, empty, search).

2. APLICACIÓN DE PILAS EN JAVA

Una pila (stack) es una colección ordenada de elementos a los que sólo se puede
acceder por un único lugar o extremo de la pila. Al primer elemento agregado
se lo denomina como "fondo", mientras que al último se lo conoce como
"cima". Los elementos de la pila se añaden o se borran de la misma sólo por su
parte superior (cima).

Debido a su propiedad específica “último en entrar, primero en salir” se conoce


a las pilas como estructura de datos LIFO (last-in / first-out).

Una pila es una estructura para el almacenamiento de datos, en la cual


la información posee solamente una entrada y salida para su acceso, es
decir mantiene una estructura LIFO(Last in First out) lo que quiere decir
que el último elemento ingresado debe ser el primero al cual accedemos,
por ende el primer elemento ingresado puede solamente ser visto si
pasamos por todos los elementos de la Pila. Dentro de java podemos
acceder a este tipo de clase mediante el uso de la clase Stack.

Una pila se puede implementar de las siguientes formas:


1. Guardando los elementos en un arreglo en cuyo caso su dimensión o
longitud es fija.
2. Utilizando un vector para almacenar los elementos.
3. Construir una lista enlazada, cada elemento de la pila forma un nodo de
la lista; la lista crece o decrece según se añaden o se extraen,
respectivamente, elementos de la pila; ésta es una representación
dinámica y no existe limitación en su tamaño excepto la memoria de la
computadora.
OPERACIONES DE LAS PILAS:

Una pila cuenta con 2 operaciones imprescindibles:

APILAR (PUSH):
Consiste en añadir un elemento a la pila. En la siguiente imagen se explica de
una forma más abstracta en la que se puede ver que se está añadiendo el dato
"7" sobre el dato "8", convirtiéndose éste en la "cima" de la pila.

DESAPILAR (POP):
Es la operación contraria de "apilar", es decir, en lugar de añadir elementos a la
pila, se los va a quitar de la misma. En la siguiente imagen se explica de una
forma más abstracta en la que se puede ver que se está quitando de la pila el
dato "7" establecido anteriormente como la "cima".

EJEMPLO PRÁCTICO:

Clase pilas

package estructurasDeDatos;

public class Pila {


private int[] pila;
private int top;
private int tamanioPila;

public Pila(){
tamanioPila = 1;
top = -1;
pila = new int[tamanioPila];
}

//las operaciones basicas de la pila


public void push(int dato){
if (top == (tamanioPila -1))
redimensionar();
pila[++top] = dato;
}

public Integer pop(){


if(top < 0)
return null;
return pila[top--];
}

private void redimensionar(){


int[] temp = pila;
tamanioPila *= 2;
pila = new int[tamanioPila];

for(int i = 0; i <= top; i++)


pila[i] = temp[i];
}
}

Clase principal

package estructurasDeDatos;

public class PilaPrueba {

public static void main(String[] args) {


Pila pila = new Pila();

pila.push(10);
pila.push(20);
pila.push(30);

System.out.println(pila.pop());
System.out.println(pila.pop());
System.out.println(pila.pop());
System.out.println(pila.pop());
}

Después de las dos operaciones fundamentales en una pila, se pueden establecer


otras, las cuales servirán para obtener información y manipular su contenido:

CrearPila: Inicia la pila


Pila vacía (Empty): Comprueba si la pila no tiene elementos
Pila llena: Comprueba si la pila está llena de elementos
Limpiar pila: Quita todos sus elementos y deja la pila vacía
CimaPila (Peek): Obtiene el elemento cima de la pila
Tamaño de la pila: Número de elementos máximo que puede contener la pila

Buscar (Search): Busca un elemento determinado que esté dentro de la pila y


devuelve su posición.

Una pila es una estructura de datos del tipo LIFO, con dos operaciones imprescindibles:
apilar (push) y desapilar (pop), al primer elemento se lo conoce como "fondo" y al último
como "cima".
Aplicaciones de las Pilas
Cuando un programa llama a un subprograma, internamente, se utilizan pilas
para guardar el lugar desde donde se hizo la llamada, y el estado de las variables
en ese momento.
Otro ejemplo es que todo algoritmo recursivo, puede ser implementado en forma
iterativa utilizando una pila para almacenar los valores de las variables y
parámetros.

En java podemos importar la librería java.util.Stack

Con las Pilas podemos hacer operaciones como Push (apilar)


Push o apilar .-los elementos se introducen en la pila por un extremo
POP .- sacar datos de la pila (desapilar)
EMPTY.- comprueba si la pila está vacía.
PEEK (consulta el primer elemento de la cima de la pila)
SEARCH (que busca un determinado elemento dentro de la pila y devuelve su
posición dentro de ella).

Código Pila

import java.util.Stack;

public class Principal {

public static void main(String[] args) {

Stack pila=new Stack();


for(int x=5;x<=10;x++){
pila.push(Integer.toString(x));
while(!pila.empty()){
System.out.println(pila.pop());
}
}
}

}
EXPRESIONES InFija, PreFija Y PosFija

PreFija:Notación prefija: El orden es operador, primer operando, segundo


operando

InFija: Notación infija: La notación habitual. El orden es primer operando,


operador, segundo operando

PosFija:Notación postfija: El orden es primer operando, segundo operando,


operador

Código de ejemplo de tratamiento de expresiones posfija

import java.util.Stack;
/**
*
* @author Administrador
*/
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
//Entrada (Expresión en Postfija)
String expr = "2 23 6 + * 1 -"; // equivale a 2*(23+6)-1
String[] post = expr.split(" ");

//Declaración de las pilas


Stack < String > E = new Stack < String > (); //Pila entrada
Stack < String > P = new Stack < String > (); //Pila de operandos

//Añadir post (array) a la Pila de entrada (E)


for (int i = post.length - 1; i >= 0; i--) {
E.push(post[i]);
}

//Algoritmo de Evaluación Postfija


String operadores = "+-*/%";
while (!E.isEmpty()) {
if (operadores.contains("" + E.peek())) {
P.push(evaluar(E.pop(), P.pop(), P.pop()) + "");
}else {
P.push(E.pop());
}
}

//Mostrar resultados:
System.out.println("Expresion: " + expr);
System.out.println("Resultado: " + P.peek());

private static int evaluar(String op, String n2, String n1) {


int num1 = Integer.parseInt(n1);
int num2 = Integer.parseInt(n2);
if (op.equals("+")) return (num1 + num2);
if (op.equals("-")) return (num1 - num2);
if (op.equals("*")) return (num1 * num2);
if (op.equals("/")) return (num1 / num2);
if (op.equals("%")) return (num1 % num2);
return 0;
}
}

3. Otros Ejemplos de Pilas


Una pila (stack en inglés) es una estructura de datos lineal que solo tienen un único
punto de acceso fijo por el cual se añaden, eliminan o se consultan elementos. El
modo de acceso a los elementos es de tipo LIFO (del inglés Last In First Out, último
en entrar, primero en salir).
Características:
• La única forma de acceder a los elementos es desde el tope de la pila.
• Su administración es muy sencilla ya que tiene pocas operaciones.
• Si la pila está vacía no tiene sentido referirse a un tope ni a un fondo.
• En caso de querer acceder a un elemento que nos e encuentre en el tope de la
pila se debe realizar un volcado de la pila a una pila auxiliar, una vez realizada la
operación con el elemento se vuelve a volcar los elementos de la pila auxiliar a la
original.
Operaciones básicas:
• apilar (valor): también conocido como push agrega el valor al tope de la pila.
• retirar (): también conocido como pop retira el último elemento apilado.
• cima (): devuelve el valor del elemento que está en la cima de la pila.
• esVacia (): retorna true si la pila no ha sido inicializada.
• buscar (valor): retorna la true si el elemento a buscar existe en la pila.
• eliminar(): elimina la pila
• listar (): imprime en pantalla los elementos de la pila. Programa en Java

Clase Nodo:
package pilas;
/**
* Clase que define los elementos que debe tener un Nodo de
la lista.
* @author xavier
*/
public class Nodo {
// Variable en la cual se va a guardar el valor.
private int valor;
// Variable para enlazar los nodos.
private Nodo siguiente;
/**
* Constructor que inicializamos el valor de las
variables.
*/
public void Nodo(){
this.valor = 0;
this.siguiente = null;
}

// Métodos get y set para los atributos.

public int getValor() {


return valor;
}

public void setValor(int valor) {


this.valor = valor;
}

public Nodo getSiguiente() {


return siguiente;
}

public void setSiguiente(Nodo siguiente) {


this.siguiente = siguiente;
}
}
Clase Pila:

package pilas;
/**
* Clase que define las operaciones básicas que debe tener
una pila.
* @author xavier
*/
public class Pila {
// Puntero que indica el inicio de la pila o tambein
conocida como el
// tope de la pila.
private Nodo inicio;
// Variable para registrar el tamaño de la pila.
private int tamanio;
/**
* Constructor por defecto.
*/
public void Pila(){
inicio = null;
tamanio = 0;
}
/**
* Consulta si la pila esta vacia.
* @return true si el primer nodo (inicio), no apunta a
otro nodo.
*/
public boolean esVacia(){
return inicio == null;
}
/**
* Consulta cuantos elementos (nodos) tiene la pila.
* @return numero entero entre [0,n] donde n es el numero
de elementos
* que contenga la lista.
*/
public int getTamanio(){
return tamanio;
}
/**
* Agrega un nuevo nodo a la pila.
* @param valor a agregar.
*/
public void apilar(int valor){
// Define un nuevo nodo.
Nodo nuevo = new Nodo();
// Agrega al valor al nodo.
nuevo.setValor(valor);
// Consulta si la pila esta vacia.
if (esVacia()) {
// Inicializa la pila con el nuevo valor.
inicio = nuevo;
}
// Caso contrario agrega el nuevo nodo al inicio de
la pila.
else{
nuevo.setSiguiente(inicio);
inicio = nuevo;
}
// Incrementa el contador del tamaño.
tamanio++;
}
/**
* Elimina el elemento que se encuentra en el tope de la
piala.
*/
public void retirar(){
if (!esVacia()) {
// Asigna como primer nodo al siguiente de la
pila.
inicio = inicio.getSiguiente();
// Decrementa el contador del tamaño de la pila
tamanio--;
}
}
/**
* Consulta el valor del nodo que se encuentra en la cima
de la pila
* @return valor del nodo.
* @throws Exception
*/
public int cima() throws Exception{
if(!esVacia()){
return inicio.getValor();
} else {
throw new Exception("La pila se encuentra
vacia.");
}
}
/**
* Busca un elemento en la pila.
* @param referencia valor del nodo a buscar.
* @return true si el valor de referencia existe en la
pila.
*/
public boolean buscar(int referencia){
// Crea una copia de la pila.
Nodo aux = inicio;
// Bandera para verificar si existe el elemento a
buscar.
boolean existe = false;
// Recorre la pila hasta llegar encontrar el nodo o
llegar al final
// de la pila.
while(existe != true && aux != null){
// Compara si el valor del nodo es igual que al
de referencia.
if (referencia == aux.getValor()) {
// Cambia el valor de la bandera.
existe = true;
}
else{
// Avanza al siguiente nodo.
aux = aux.getSiguiente();
}
}
// Retorna el valor de la bandera.
return existe;
}
/**
* Elimina un nodo de la pila ubicado por su valor.
* @param referencia valor de referencia para ubicar el
nodo.
*/
public void remover(int referencia){
// Consulta si el valor existe en la pila.
if (buscar(referencia)) {
// Crea una pila auxiliar para guardar los
valores que se
// vayan desapilando de la pila original.
Nodo pilaAux = null;
// Recoore la pila hasta llegar al nodo que tenga
el valor
// igual que el de referencia.
while(referencia != inicio.getValor()){
// Crea un nodo temporal para agregarlos a la
pila auxiliar.
Nodo temp = new Nodo();
// Ingresa el valor al nodo temporal.
temp.setValor(inicio.getValor());
// Consulta si la pila auxiliar no a sido
inicializada.
if(pilaAux == null){
// Inicializa la pila auxiliar.
pilaAux = temp;
}
// Caso contrario si la pila auxiliar ya
contiene elementos
// los agrega al inicio.
else{
temp.setSiguiente(pilaAux);
pilaAux = temp;
}
// Elimina el nodo del tope de la pila hasta
llegar al nodo
// que se desea eliminar.
retirar();
}
// Elimina el nodo que coincide con el de
referencia.
retirar();
// Regresa los valores de la pila auxiliar a la
pila original
// mientras la pila auxiliar tenga elementos.
while(pilaAux != null){
// Utiliza el metodo apilar para regresar los
elementos a
// la pila original.
apilar(pilaAux.getValor());
// Avansa al siguiente nodo de la pila
auxiliar.
pilaAux = pilaAux.getSiguiente();
}
// Libera la memoria utilizada por la pila
auxiliar.
pilaAux = null;
}
}
/**
* Actualiza el valor de un nodo en la pila.
* @param referencia valor del nodo para ubicar el que se
desea actualizar.
* @param valor por el cual se desea remplazar el valor
del nodo.
*/
public void editar(int referencia, int valor){
// Consulta si el nodo existe en la pila
if (buscar(referencia)) {
// Crea una pila auxiliar.
Nodo pilaAux = null;
// Recoore la pila hasta llegar al nodo que tenga
el valor
// igual que el de referencia.
while(referencia != inicio.getValor()){
// Crea un nodo temporal para agregarlos a la
pila auxiliar.
Nodo temp = new Nodo();
// Ingresa el valor al nodo temporal.
temp.setValor(inicio.getValor());
// Consulta si la pila auxiliar no a sido
inicializada.
if(pilaAux == null){
// Inicializa la pila auxiliar.
pilaAux = temp;
}
// Caso contrario si la pila auxiliar ya
contiene elementos
// los agrega al inicio.
else{
temp.setSiguiente(pilaAux);
pilaAux = temp;
}
// Elimina el nodo del tope de la pila hasta
llegar al nodo
// que se desea eliminar.
retirar();
}
// Actualiza el valor del nodo.
inicio.setValor(valor);
// Regresa los valores de la pila auxiliar a la
pila original
// mientras la pila auxiliar tenga elementos.
while(pilaAux != null){
// Utiliza el metodo apilar para regresar los
elementos a
// la pila original.
apilar(pilaAux.getValor());
// Avansa al siguiente nodo de la pila
auxiliar.
pilaAux = pilaAux.getSiguiente();
}
// Libera la memoria utilizada por la pila
auxiliar.
pilaAux = null;
}
}
/**
* Elimina la pila
*/
public void eliminar(){
// Elimina el valor y la referencia a los demas
nodos.
inicio = null;
// Reinicia el contador a 0.
tamanio = 0;
}
/**
* Despliega en pantalla los elementos de la pìla.
*/
public void listar(){
// Crea una copia de la pila.
Nodo aux = inicio;
// Recorre la pila hasta el ultimo nodo.
while(aux != null){
System.out.println("|\t" + aux.getValor() +
"\t|");
System.out.println("-----------------");
aux = aux.getSiguiente();
}
}
}

Clase Main
package pilas;
/**
* Clase principa que implementa los metodos de la clase
pila.
* @author xavier
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Pila pila = new Pila();

System.out.println("<<-- Ejemplo de Pila -->>\n\n");

pila.apilar(4);
pila.apilar(16);
pila.apilar(12);
pila.apilar(8);
pila.apilar(65);

System.out.println("<<-- Pila -->>");


pila.listar();
System.out.println("\n<<-- Tamaño -->");
System.out.println(pila.getTamanio());

System.out.println("\n<<-- Retirar el elemento del


tope de la pila -->>");
pila.retirar();
pila.listar();
System.out.println("Tamaño: " + pila.getTamanio());

System.out.println("\n<<-- Actualizar el valor del


nodo con el valor 12 por 24 -->>");
pila.editar(12, 44);
pila.listar();
System.out.println("Tamaño: " + pila.getTamanio());

System.out.println("\n<<-- Eliminar el nodo con el


valor 16 -->>");
pila.remover(16);
pila.listar();
System.out.println("Tamaño: " + pila.getTamanio());

System.out.println("\n<<-- Consulta si existe el


valor 65 -->>");
System.out.println(pila.buscar(65));

System.out.println("\n<<-- Elimina la pila -->>");


pila.eliminar();

System.out.println("\n<<-- Consulta si la pila esta


vacia -->>");
System.out.println(pila.esVacia());

System.out.println("\n\n<<-- Fin de ejemplo pila --


>>");
}
}

También podría gustarte