
本文探讨了使用自定义栈检测括号字符串平衡性时常见的逻辑陷阱。通过分析一个初始实现中盲目弹出操作导致的问题,我们揭示了在处理栈数据结构时,循环条件设置的关键性。文章提供了一个修正后的解决方案,利用 `while` 循环确保只有在两个栈都包含元素时才进行弹出操作,从而实现对括号数量平衡的准确判断,并附带了完整的自定义栈和节点实现代码。
括号平衡是编程中一个经典问题,通常用于检查表达式的语法正确性。一个简单的括号平衡定义是:字符串中开括号 ( 的数量必须等于闭括号 ) 的数量。在此教程中,我们将遵循一个特定的双栈策略来检测这种“数量平衡”:将所有开括号压入一个栈,所有闭括号压入另一个栈,然后尝试同时弹出,如果最终两个栈都为空,则认为数量上是平衡的。
为了实现这一目标,我们需要自定义栈数据结构。以下是基于链表实现的 Node 和 Stack 类:
// Node 类:栈中每个元素的载体
public class Node {
    Object info; // 存储元素信息
    Node next;   // 指向下一个节点
    Node(Object info, Node next){
        this.info = info;
        this.next = next;
    }
}
// Stack 类:基于链表实现的栈
public class Stack {
    private Node top; // 栈顶指针
    public Stack() {
        top = null; // 初始化时栈为空
    }
    // 检查栈是否为空
    public boolean isEmpty(){
        return (top == null);
    }
    // 元素入栈
    public void push(Object newItem){
        top = new Node(newItem, top); // 新元素成为栈顶,指向原来的栈顶
    }
    // 元素出栈
    public Object pop(){
        if (isEmpty()){
            System.out.println("Trying to pop when stack is empty"); // 尝试从空栈弹出时的警告
            return null;
        } else {
            Node temp = top; // 暂存当前栈顶
            top = top.next;  // 栈顶下移
            return temp.info; // 返回原栈顶元素
        }
    }
    // 清空栈
    void popAll(){
        top = null;
    }
    // 查看栈顶元素但不弹出
    public Object peek(){
        if (isEmpty()){
           System.out.println("Trying to peek when stack is empty");
           return null;
        } else {
           return top.info;
        }
    }
}在尝试检测括号平衡时,一个常见的错误是未能正确处理栈的弹出逻辑。考虑以下 isBalanced 方法的初始实现:
立即学习“Java免费学习笔记(深入)”;
static boolean isBalanced(String expr){
    // 基本情况:表达式为空或长度为奇数,则不平衡
    if (expr == null || expr.length() % 2 == 1) {
        return false;
    }
    Stack stack1 = new Stack(); // 存储开括号
    Stack stack2 = new Stack(); // 存储闭括号
    // 遍历表达式,将开括号和闭括号分别压入不同的栈
    for (int i = 0; i < expr.length(); i++){
        if (expr.charAt(i) == '(') {
            stack1.push(expr.charAt(i));
        }
        if (expr.charAt(i) == ')') {
           stack2.push(expr.charAt(i));
        }
    }
    // 尝试弹出所有元素
    for(int i = 0; i < expr.length(); i++) {
        stack1.pop(); // 盲目弹出
        stack2.pop(); // 盲目弹出
    }
    // 判断两个栈是否都为空
    return (stack1.isEmpty() && stack2.isEmpty()) ;
}这个实现的主要问题在于第二个 for 循环:for(int i = 0; i < expr.length(); i++) { stack1.pop(); stack2.pop(); }。它尝试从 stack1 和 stack2 中各弹出 expr.length() 次元素。这种“盲目弹出”的问题在于:
正确的逻辑应该是,只有当两个栈都有元素时才进行配对弹出操作,一旦其中一个栈为空,就应该停止弹出,然后检查两个栈的状态。
为了解决上述问题,我们需要修改弹出逻辑,使其在两个栈都非空的情况下才进行弹出操作。这可以通过一个 while 循环来实现。
static boolean isBalanced(String expr){
    // 基本情况:表达式为空或长度为奇数,则不平衡
    if (expr == null || expr.length() % 2 == 1) {
        return false;
    }
    Stack stack1 = new Stack(); // 存储开括号
    Stack stack2 = new Stack(); // 存储闭括号
    // 遍历表达式,将开括号和闭括号分别压入不同的栈
    for (int i = 0; i < expr.length(); i++){
        if (expr.charAt(i) == '(') {
            stack1.push(expr.charAt(i));
        }
        if (expr.charAt(i) == ')') {
           stack2.push(expr.charAt(i));
        }
    }
    // 修正后的弹出逻辑:只有当两个栈都非空时才进行弹出
    while (!stack1.isEmpty() && !stack2.isEmpty()) {
        stack1.pop(); // 弹出 stack1 的一个元素
        stack2.pop(); // 弹出 stack2 的一个元素
    }
    // 此时,如果表达式是平衡的(即开括号和闭括号数量相等),
    // 那么两个栈应该同时变为空
    return (stack1.isEmpty() && stack2.isEmpty());
}这个修正后的 while 循环保证了:
将上述 Node、Stack 和修正后的 isBalanced 方法整合,并添加一个 main 方法进行测试,可以得到一个完整的解决方案:
public class BalanceChecker {
    // Node 类:栈中每个元素的载体
    public static class Node {
        Object info;
        Node next;
        Node(Object info, Node next){
            this.info = info;
            this.next = next;
        }
    }
    // Stack 类:基于链表实现的栈
    public static class Stack {
        private Node top;
        public Stack() {
            top = null;
        }
        public boolean isEmpty(){
            return (top == null);
        }
        public void push(Object newItem){
            top = new Node(newItem, top);
        }
        public Object pop(){
            if (isEmpty()){
                System.out.println("Trying to pop when stack is empty");
                return null;
            } else {
                Node temp = top;
                top = top.next;
                return temp.info;
            }
        }
        void popAll(){
            top = null;
        }
        public Object peek(){
            if (isEmpty()){
               System.out.println("Trying to peek when stack is empty");
               return null;
            } else {
               return top.info;
            }
        }
    }
    // 修正后的括号平衡检测方法
    static boolean isBalanced(String expr){
        // 基本情况:表达式为空或长度为奇数,则不平衡
        if (expr == null || expr.length() % 2 == 1) {
            return false;
        }
        Stack stack1 = new Stack(); // 存储开括号
        Stack stack2 = new Stack(); // 存储闭括号
        // 遍历表达式,将开括号和闭括号分别压入不同的栈
        for (int i = 0; i < expr.length(); i++){
            if (expr.charAt(i) == '(') {
                stack1.push(expr.charAt(i));
            } else if (expr.charAt(i) == ')') { // 使用 else if 确保字符只处理一次
               stack2.push(expr.charAt(i));
            }
        }
        // 只有当两个栈都非空时才进行弹出
        while (!stack1.isEmpty() && !stack2.isEmpty()) {
            stack1.pop();
            stack2.pop();
        }
        // 判断两个栈是否都为空
        return (stack1.isEmpty() && stack2.isEmpty());
    }
    public static void main(String[] args) {
        // 测试用例
        String expr1 = "((()))"; // 期望:true
        String expr2 = "()()";   // 期望:true
        String expr3 = "(()";    // 期望:false (stack1 非空)
        String expr4 = "())";    // 期望:false (stack2 非空)
        String expr5 = ")((";    // 期望:false (stack1 非空, 尽管数量上平衡) - 注意:此方法只检查数量平衡,不检查顺序
        String expr6 = "";       // 期望:false (长度为0,按规则返回false)
        String expr7 = null;     // 期望:false (null,按规则返回false)
        String expr8 = "()(()";  // 期望:false (长度为奇数)
        System.out.println("Expression \"" + expr1 + "\" is balanced: " + isBalanced(expr1));
        System.out.println("Expression \"" + expr2 + "\" is balanced: " + isBalanced(expr2));
        System.out.println("Expression \"" + expr3 + "\" is balanced: " + isBalanced(expr3));
        System.out.println("Expression \"" + expr4 + "\" is balanced: " + isBalanced(expr4));
        System.out.println("Expression \"" + expr5 + "\" is balanced: " + isBalanced(expr5));
        System.out.println("Expression \"" + expr6 + "\" is balanced: " + isBalanced(expr6));
        System.out.println("Expression \"" + expr7 + "\" is balanced: " + isBalanced(expr7));
        System.out.println("Expression \"" + expr8 + "\" is balanced: " + isBalanced(expr8));
        // 示例输出:
        // Expression "((()))" is balanced: true
        // Expression "()()" is balanced: true
        // Expression "(()" is balanced: false
        // Expression "())" is balanced: false
        // Expression ")(((" is balanced: false
        // Expression "" is balanced: false
        // Expression "null" is balanced: false
        // Expression "()(()" is balanced: false
    }
}通过理解和应用这些原则,可以有效地避免在处理栈相关问题时常见的逻辑错误,并编写出更加稳定和正确的代码。
以上就是Java中基于自定义栈的括号平衡检测:问题分析与正确实现的详细内容,更多请关注php中文网其它相关文章!
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号