graph TD
A["'('"] --> B["'1'"]
B --> C["'+'"]
C --> D["'2'"]
D --> E["')' ✅"]
style A fill:#fefae0,stroke:#dda15e;
style E fill:#d9ed92,stroke:#606c38,stroke-width:2px;
Determinar si una cadena de texto tiene los paréntesis, corchetes o llaves balanceados.
Ejemplo:
✅ "(1 + 2)" → Balanceado
❌ "(1 + 2" → No balanceado
Este problema aparece todo el tiempo en programación: en compiladores, editores de texto o validadores de sintaxis.
Podemos usar una pila (stack) para resolverlo fácilmente, ya que las pilas nos permiten volver hacia atrás y verificar el orden de apertura y cierre.
graph TD
A["'('"] --> B["'1'"]
B --> C["'+'"]
C --> D["'2'"]
D --> E["')' ✅"]
style A fill:#fefae0,stroke:#dda15e;
style E fill:#d9ed92,stroke:#606c38,stroke-width:2px;
📌 La expresión comienza con un paréntesis que se abre, y termina con un paréntesis que se cierra correctamente → balanceado ✔️
Vamos a ver cómo la pila cambia en cada paso al analizar la expresión:
graph BT
base["⬜ (vacía)"]:::vacia
classDef vacia fill:#edf2fb,stroke:#4361ee,stroke-width:2px;
📌 Al inicio, la pila está vacía. Comenzamos a leer la expresión carácter por carácter.
{ (símbolo de apertura)graph BT
A["{"]:::abrir
base["⬜ (vacía)"]:::vacia
A --> base
classDef abrir fill:#fefae0,stroke:#dda15e,stroke-width:2px;
classDef vacia fill:#edf2fb,stroke:#4361ee,stroke-width:1px;
📥 Vemos una llave de apertura {, así que la apilamos.
[graph BT
B["["]:::abrir
A["{"]:::abrir
base["⬜"]:::vacia
B --> A --> base
classDef abrir fill:#fefae0,stroke:#dda15e,stroke-width:2px;
classDef vacia fill:#edf2fb,stroke:#4361ee,stroke-width:1px;
📥 Otro símbolo de apertura, lo apilamos sobre la llave anterior. La pila crece hacia arriba ⬆️
(graph BT
C["("]:::abrir
B["["]:::abrir
A["{"]:::abrir
base["⬜"]:::vacia
C --> B --> A --> base
classDef abrir fill:#fefae0,stroke:#dda15e,stroke-width:2px;
classDef vacia fill:#edf2fb,stroke:#4361ee,stroke-width:1px;
📥 Otro símbolo de apertura → lo apilamos. Ahora la pila tiene tres símbolos: { [ (
) (símbolo de cierre)graph BT
B["["]:::abrir
A["{"]:::abrir
base["⬜"]:::vacia
B --> A --> base
classDef abrir fill:#d9ed92,stroke:#606c38,stroke-width:2px;
classDef vacia fill:#edf2fb,stroke:#4361ee,stroke-width:1px;
📤 Al ver ), quitamos el último símbolo apilado → (. Coinciden, así que seguimos ✅
] (símbolo de cierre)graph BT
A["{"]:::abrir
base["⬜"]:::vacia
A --> base
classDef abrir fill:#d9ed92,stroke:#606c38,stroke-width:2px;
classDef vacia fill:#edf2fb,stroke:#4361ee,stroke-width:1px;
📤 Cerramos el corchete. El último símbolo abierto era [ → coincide ✅
} (último cierre)graph BT
base["⬜ (vacía)"]:::vacia
classDef vacia fill:#d9ed92,stroke:#606c38,stroke-width:2px;
📤 Quitamos la llave { → coincide ✅ La pila ahora está vacía, lo que significa que toda la expresión está balanceada ✔️
✅ Expresión balanceada:
🧠 Cada vez que se encontró un símbolo de cierre, se eliminó el que estaba en el tope de la pila. Como al final no quedaron símbolos pendientes → está correctamente balanceada.
(, [, {, <) → lo apilamos.), ], }, >) → desapilamos el último símbolo y comprobamos si coinciden.import java.util.*;
public class VerificadorBalanceo {
private final List<Character> apertura = Arrays.asList('(', '[', '{', '<');
private final List<Character> cierre = Arrays.asList(')', ']', '}', '>');
public boolean estaBalanceado(String expresion) {
Stack<Character> pila = new Stack<>();
for (char c : expresion.toCharArray()) {
if (apertura.contains(c))
pila.push(c);
else if (cierre.contains(c)) {
if (pila.isEmpty()) return false;
char tope = pila.pop();
if (apertura.indexOf(tope) != cierre.indexOf(c))
return false;
}
}
return pila.isEmpty();
}
}📦 Importamos las clases necesarias del paquete java.util, porque vamos a usar Stack y List para manejar los símbolos.
📋 Creamos dos listas paralelas:
apertura: contiene los símbolos que abren.
cierre: contiene los símbolos que cierran.
👉 Así podemos comparar sus posiciones: por ejemplo, '(' está en la posición 0 y su par ')' también.
🧱 La pila guardará los símbolos de apertura que vayamos encontrando. Cada vez que abrimos algo, lo apilamos (push); cuando cerramos algo, lo desapilamos (pop).
🔹 Convertimos la cadena en un arreglo de caracteres.
🔹 Si el carácter c es un símbolo de apertura, lo colocamos encima de la pila.
📥 Ejemplo: Expresión: { [ ( 1 + 2 ) ] } → al leer {, [, ( → se apilan en orden.
🔹 Si encontramos un símbolo de cierre, debemos verificar tres cosas:
❌ Si la pila está vacía → no hay nada que cerrar → falla.
📤 Quitamos el último símbolo de apertura (pop).
🔍 Comparamos las posiciones:
'(' está en la misma posición que ')', coinciden ✅[ con )).📌 Al final, si la pila queda vacía, significa que todos los símbolos abiertos se cerraron correctamente → ✅ balanceado.
Si aún hay elementos → ❌ no balanceado.
public class Principal {
public static void main(String[] args) {
VerificadorBalanceo verificador = new VerificadorBalanceo();
System.out.println(verificador.estaBalanceado("(1 + 2)")); // ✅
System.out.println(verificador.estaBalanceado("((3 + 2]")); // ❌
System.out.println(verificador.estaBalanceado("{[()]}")); // ✅
System.out.println(verificador.estaBalanceado("(1 + 2")); // ❌
}
}🖥️ Salida esperada:
true
false
true
false
graph TD
A["Expresión: (1 + 2)"]
A --> B["push('(')"]
B --> C["ignora '1', '+', '2'"]
C --> D["pop('(') al ver ')' ✅"]
D --> E["Pila vacía → Balanceado ✅"]
classDef good fill:#d9ed92,stroke:#606c38,stroke-width:2px;
classDef mid fill:#fefae0,stroke:#dda15e;
class A,B,C,D,E good;
graph TD
A["Expresión: ((3 + 2]"]
A --> B["push('(')"]
B --> C["push('(')"]
C --> D["pop('(') al ver ']' ❌"]
D --> E["Paréntesis abierto ≠ corchete cerrado → Error"]
classDef bad fill:#f7cad0,stroke:#e63946,stroke-width:2px;
class A,B,C,D,E bad;
| Situación | Acción | Resultado |
|---|---|---|
push() cuando hay apertura |
guarda símbolo | pila crece |
pop() cuando hay cierre |
compara símbolos | deben coincidir |
| Pila vacía al final | ✅ balanceado | |
| Error de coincidencia | ❌ no balanceado |
push, pop, isEmpty) tienen complejidad O(1).