如果 else 和 switch 使用堆栈逻辑,它们有什么区别?

4
我正在通过编写一个验证括号语法的程序来学习堆栈。如果我输入(Baller),它应该给我一个正结果。如果我有(Baller(),它应该给我一个负结果。本质上,该应用程序检查用户是否正确使用了(),{}和[]。
  • 如果我遇到(,{或[字符,我将把它添加到堆栈上的字符。
  • 如果我遇到)} ]标志,我将从堆栈中删除一个字符。
  • 如果文本包含奇数个括号或括号不连续(例如,(和]不是连续的),则会打印错误消息。

所以我已经在if else语句中完成了一半,但我认为用switch语句应该更容易,并且也是一个很好的学习体验。

所以我在switch语句中做的是:

public class Input {
    public static void main(String[] args) {
        Stack stack = new Stack();
        String str;
        str = JOptionPane.showInputDialog("Text to parse: ");
        char arr[] = str.toCharArray();
        System.out.print(str);
        System.out.println();
        System.out.println();

        for(char c : arr) {
            switch(c) {

            case '{':
                stack.Push(c);
                System.out.print(stack.firstNode.getData());
                break;
            case '(':
                stack.Push(c);
                System.out.print(stack.firstNode.getData());
                break;
            case '[':
                stack.Push(c);
                System.out.print(stack.firstNode.getData());
                break;

            case '}':
                c = (Character) stack.Peek(); //<-- Edited for @Jimmy
                if( c != '{') {
                    System.out.println("  Syntax ERROR");
                }
            case ']':
                if( c != '[') {
                    System.out.println("  Syntax ERROR");
                }   
            case ')':
                if( c != '(') {
                    System.out.println("  Syntax ERROR");
                }
            }
        }
    }
}

但是我现在遇到的问题是,如果我只添加一个右括号,它就会被删除,因为我有一个 pop。我尝试用 if-else 语句来解决这个问题,最终以 if-语句结束,如下所示:

if(first == '(' && (current == '}' || current == ']')) {
if first == '{' && (current == ']' || current == ')')) {
//and so on

我该如何将这个代码转换为switch case结构?这样做是否不好?

我知道左侧括号没有问题,但右侧的有问题。

编辑:目前代码的样子是怎样的?

import javax.swing.JOptionPane;



public class Input {
    public static void main(String[] args) {
        Stack stack = new Stack();
        String str;
        str = JOptionPane.showInputDialog("Text to parse: ");
        char arr[] = str.toCharArray();
        System.out.print(str);
        System.out.println();
        System.out.println();


        for(char c : arr) {

            switch(c) {

            case '{':
                stack.Push(c);
                break;
            case '(':
                stack.Push(c);
                break;
            case '[':
                stack.Push(c);
                break;

            case '}':
                if(stack.isEmpty() || (Character) stack.Pop() != '{') {
                    System.out.println("  Syntax ERROR");
                }
                break;
            case ']':
                if(stack.isEmpty() || (Character) stack.Pop() != '[') {
                    System.out.println("  Syntax ERROR");
                }
                break;
            case ')':
                if(stack.isEmpty() || (Character) stack.Pop() != '(') {
                    System.out.println("  Syntax ERROR");
                }
                break;
            }
        } if(!stack.isEmpty()) {
            System.out.println("  Syntax ERROR");
        }
    }
}

你可以使用 peek() 函数来查看栈中的最后一个元素,如果没有错误,则可以调用 pop() 函数进行出栈操作,否则不要调用 pop() 以免移除错误的元素。 - Jimmy
在你的第一个移除情况下,类似这样:当 case 为 '}' 时,c = (Character) stack.Peek(); if (c != '{') { System.out.println("语法错误"); } else { stack.Pop(); } - Jimmy
@Jimmy 好的,我把它改成了Peek(),得到了相同的结果。我在编辑中进行了更改,以便向您展示它被编辑的位置。 - Thrillofit123
如果闭合字符与栈顶的最后一个字符匹配,您需要将其从栈中移除。如果不匹配,您希望做什么?是打印错误信息并忽略该输入,还是打印消息并从栈中移除最后一个条目? - Jimmy
另外,在末尾添加额外的结束字符时,您可以捕获EmptyStackException()并以此方式处理,或在操作堆栈之前检查堆栈是否为空(isEmpty())。 - Jimmy
显示剩余3条评论
3个回答

4

好问题!很好,你已经有一个可行的解决方案并试图改进它。

你仍然可以使用你的switch语句,但你需要先验证你的堆栈不为空,然后再尝试弹出下一个值。在我的实现中,我首先通过检查stack.isEmpty() 并使用短路OR条件||来进行该检查。短路意味着如果OR条件的左侧为真,则右侧甚至不会被评估。

这是更新后的for循环。我不确定你正在使用哪个Stack类,所以我的代码使用的是java.util.Stack

for(char c : arr) {

    switch(c) {

    case '{':
        stack.push(c);
        System.out.print(stack.peek());
        break;
    case '(':
        stack.push(c);
        System.out.print(stack.peek());
        break;
    case '[':
        stack.push(c);
        System.out.print(stack.peek());
        break;

    case '}':
        if(stack.isEmpty() || (Character) stack.pop() != '{') {
            System.out.println("  Syntax ERROR");
        }
        break;
    case ']':
        if(stack.isEmpty() || (Character) stack.pop() != '[') {
            System.out.println("  Syntax ERROR");
        }
        break;
    case ')':
        if(stack.isEmpty() || (Character) stack.pop() != '(') {
            System.out.println("  Syntax ERROR");
        }
        break;
    }
}

编辑:我添加了缺少的break;语句。如果没有这些语句,系统将执行多个case:条件,因为它会“穿过”每一个条件。

编辑2:Zong Zheng Li在他们的答案中提出了一个很好的观点。你应该验证当你完成时堆栈上是否还有任何剩余字符。在你的循环之后,你应该像这样做:

if(!stack.isEmpty()) {
    System.out.println("  Syntax ERROR");
}

编辑3:将stack.firstElement()更改为stack.peek()


这看起来有点惊讶!但是是的,我已经创建了自己的类,在其中初始化了我的Push、Pop、Peek和Count方法。这使我们没有了isEmpty方法。但我猜它几乎与我的Count()方法相同,因为它返回元素数量。所以如果我对它进行一些更改,能帮助它吗? - Thrillofit123
@Thillofit123 是的,如果你愿意,你可以使用Count()方法,并进行等于零的检查,例如stack.Count() == 0。不过你应该考虑创建自己的isEmpty()方法,因为这会使你的代码更易读(而且不重复自己通常是一个好习惯)。 - Ben M.
公共整型 Count() { 返回计数; }公共布尔值 isEmpty() { 如果 (计数 == 0) { 返回真; } else { 返回假; } } 这样的代码是否足够? - Thrillofit123
1
@Thrillofit123 这几乎是完美的!你可以将其简化为这样:public boolean isEmpty() { return count == 0; } - Ben M.
太棒了,它起作用了!哇呼!真的!我非常感谢您的时间,Ben. M和其他所有人!就我所看到的,它运行正常。现在我要试着让它更酷一些,但我现在看到的是一切都往好的方向发展!我无话可说!!! - Thrillofit123

2
除了检查特定字符是否匹配之外,有两种情况需要关注栈本身:
1. 当中间状态下方括号的数量不匹配时。这种情况通常发生在尝试弹出空栈时。 2. 当字符串结尾处存在孤立的括号时。这时栈会保持非空状态。
为了解决第一种情况,您应该在弹出元素之前检查栈是否为空:
```python if stack and stack[-1] == bracket_pair[char]: stack.pop() else: return False ```
(请注意保留HTML标签,此处为代码段)
case '}':
    if (stack.empty()) {
        System.out.println("  Syntax ERROR");
    }
    else {
        c = (Character) stack.Pop();
        if( c != '{') {
            System.out.println("  Syntax ERROR");
        }
    }
    break;

或者等价地说,
case '}':
    if (stack.empty() || ((Character)stack.Pop()) != '{') {
        System.out.println("  Syntax ERROR");
    }
    break;

为了解决问题2,在程序结尾处,您应该检查堆栈是否为空。这可以捕获未处理的(bal)ler)情况。

if (!stack.empty()) 
    System.out.println("  Syntax ERROR");
}

2

您说您想学习如何使用switch语句。有一个巧妙的技巧,称为“穿透”:

switch (c) {
    case '{':
    case '(':
    case '[':
        stack.Push(c);
        System.out.print(stack.firstNode.getData());
        break;

    case '}':
    case ']':
    case ')':
        if (stack.isEmpty() || stack.pop() != c) {
            System.out.println("Syntax ERROR");
        }
        break;
}

你也可以使用预先实现的Stack类,这个类也是泛型的。


1
这看起来非常不错!我创建了自己的类,在其中初始化了我的Push、Pop、Peek和Count方法。这让我们没有一个isEmpty方法。但我猜它几乎与我的Count()方法相同,因为它返回计数中的元素。所以如果我对它做一些改变,能帮助它吗?因为如果我导入Java堆栈,它会给我其他错误,因为我已经创建了自己的类,其中包括Pop、Push等等。 - Thrillofit123
使用自己的 Stack 类是完全可以的,除非你计划将代码作为库发布并在其 API 中使用自己的 Stack。您可以通过 stack.count() == 0 检查堆栈是否为空,因此您不需要创建一个 isEmpty() 方法,这完全取决于您。 - kajacx
太好了,它成功了!哇呼!真的!我非常感激你在这里花费的时间,Kajacx和其他所有人也一样!就我所看到的,它已经可以工作了。现在我要尝试让它更酷炫,但就目前而言,一切都很顺利!我无话可说!!! - Thrillofit123

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接