0

I have a method that's supposed to validate accurate opening and closing parenthesis in a string using java. This method will be used to parse mathematical expressions so it's important for the parenthesis to be balanced. For some reason, it is returning false in both of these runs:

System.out.println(parChecker("(()"));  // returns false :-)
System.out.println(parChecker("((()))")); // returns false :-(  WHY??

Here is the method that uses a stack to solve the problem. Something is wrong here becuase it's returning false for a balanced set of parenthesis as well. What's the problem? Thank you in advance.

public static boolean parChecker(String str) {
    String[] tokens = str.split("");
    int size = tokens.length;
    Stack theStack = new Stack(size);
    int index = 0;
    boolean balanced = true;
    while ((index < size) && balanced) {
        String symbol = tokens[index];
        if (symbol.equals("(")) {
            theStack.push(symbol);
        } else {
            if (theStack.isEmpty()) {
                balanced = false;
            } else {
                theStack.pop();
            }
        }
        index++;
    }

    if (balanced && theStack.isEmpty()) {
        return true;
    } else {
        return false;
    }

}

Here is my stack class that I'm using:

public class Stack {

    private Object [] stack;
    private int maxSize = 0;
    private int top;

    public Stack(int size){
        maxSize = size;
        stack = new Object[maxSize];
        top = -1;
    }

    public void push(Object obj){
        top++;
        stack[top] = obj;
    }

    public Object pop(){
        return stack[top--];
    }

    public Object peek(){
        return stack[top];
    }

    public boolean isEmpty(){
        return (top == -1);
    }

    public boolean isFull(){
        return (top == maxSize -1);
    }
}
Doublespeed
  • 1,153
  • 4
  • 16
  • 29

2 Answers2

6

The immediate problem is this

String[] tokens = str.split("");

Gives you first char = "" if you use java 1.7 or less, so you will exit your loop since stack is empty...

Note: this has been changed in java 1.8 split difference between java 1.7 and 1.8

change to:

char[] tokens = str.toCharArray();

I guess however that you need to consider the fact that there can be chars before your first ( and that you may have other chars then ( and )

Community
  • 1
  • 1
Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
  • Huh? No that's not the issue. Split string is going to return an array of tokens in String. – Doublespeed Nov 05 '15 at 20:44
  • Also you cannot assign a `char[]` to a `String[]`. – Kenney Nov 05 '15 at 20:44
  • 1
    @Doublespeed Please use `System.out.println(Arrays.toString(tokens))`) to see the result of the split. Peter Friberg is right about that. – RealSkeptic Nov 05 '15 at 20:45
  • Yea. this answer is irrelevant and inaccurate. – Doublespeed Nov 05 '15 at 20:45
  • Why do you need to split into tokens? Just iterate through each character in the string and increment the counter for '(' and decrement for ')'. Whenever the countre goes negative there is a mismatch. and if the counter is non-zero at the end you also have a problem. What do you want to do with escaped parentheses, or parentheses inside a comment or inside quotes? – FredK Nov 05 '15 at 20:47
  • ... You just officially lost the possibility of gaining help on this site. We're here to help you. The fact is, the first string will be an empty string. – jgitter Nov 05 '15 at 20:47
  • Still this is NOT the problem. – Doublespeed Nov 05 '15 at 20:48
  • trust me @Doublespeed with this change his example will give true as long as first char is ( – Petter Friberg Nov 05 '15 at 20:50
  • @Doublespeed The answer is accurate. Including its last line which means that your `else` condition is wrong as you are assuming that everything that is not a `(` is automatically a `)` which is not going to happen in a real expression. – RealSkeptic Nov 05 '15 at 20:51
  • Doesn't matter, I'm referring to just this example: (()))())()()(((())))))( – Doublespeed Nov 05 '15 at 20:52
  • First char is empty, try with System.out.println(Arrays.toString(tokens)) you get 7 chars with first empty,,,, you exit loop since stack is empty – Petter Friberg Nov 05 '15 at 20:52
  • OHH Sh*&.. Okay. Java issue. Python doesn't screw you like this. So I should split using chars? Thank you! – Doublespeed Nov 05 '15 at 20:57
  • 1
    If you have the possibilty use an compiler like eclipse, that way its easy to debug your code.. it will save you alot of time... Have fun – Petter Friberg Nov 05 '15 at 20:59
3

As far as I can tell, there is no problem with the code (turns out it's a Java 7 specific issue..).

I would like to offer a replacement method though, for educational purposes, that is shorter, and and is tolerant of other characters being present:

public static boolean parChecker(String str) {
    Stack stack = new Stack(str.length());
    for (char c : str.toCharArray())
        switch (c) {
        case '(':
            stack.push(c);
            break;
        case ')':
            if (stack.isEmpty() || stack.pop() != Character.valueOf('('))
                return false;
        }
    return stack.isEmpty();
}

As requested, here's another solution that doesn't use a stack:

public static boolean parChecker(String str) {
    str = str.replaceAll("[^()]", "");
    while (str.contains("()"))
        str = str.replace("()", "");
    return str.isEmpty();
}

And one more for the road: @FredK's algorithm:

public static boolean parChecker(String str) {
    int depth = 0;
    for ( char c : str.toCharArray() )      
        if ( ( depth += c == '(' ? 1 : c == ')' ? -1 : 0 ) < 0 )
            return false;
    return depth == 0;
}
Kenney
  • 9,003
  • 15
  • 21
  • Vote for Kenney.. mine was mostly a debug answer, even if some problems where present ; ) – Petter Friberg Nov 05 '15 at 21:32
  • Thank you! I upvoted this. But @Petter you pointed me to exactly what was wrong with my code and shed light onto the screwness of string.split. So I had to accept your answer. But this answer is a great way to handle the code. – Doublespeed Nov 05 '15 at 21:35
  • @PetterFriberg Thanks! You deserve your votes too, for your efforts! Although, I haven't been able to reproduce getting an empty string: `for ( String s : "()".split("") ) System.out.println(s);` – Kenney Nov 05 '15 at 21:36
  • Kenny it turns out you are using java 8. In which they have fixed the issue. I'm on 7 and am seeing an empty string leading and ending the split – Doublespeed Nov 05 '15 at 21:37
  • @Kenny Because you are not seeing it!... the first is empty!, use your try System.out.println(" " + s) – Petter Friberg Nov 05 '15 at 21:38
  • @PetterFriberg Ahh, allright. I didn't want to go through the trouble of installing Java 7 so I used the ideone with Java 7, but apparently they run it on Java 8 and just compile with `-source 1.7`.. And I tried with `...println("FOO: " + s )` but it still only prints 2 lines... So you're right, it's fixed in Java 8 (or, 1.8 really ;-)) If you mention this is a Java 7 issue I'll give your answer an upvote. – Kenney Nov 05 '15 at 21:40
  • I also learned something new.... had no clue that split was changed in java 1.8 (need to edit my answer) – Petter Friberg Nov 05 '15 at 21:50
  • If you want to be educational, perhaps you should add a solution without a stack, because one is really not necessary. – RealSkeptic Nov 05 '15 at 21:50
  • @RealSkeptic its OP class, not the java Stack – Petter Friberg Nov 05 '15 at 21:51
  • @PetterFriberg Me neither. But to be honest, I haven't done much Java since Java 6 ;-) Anyway you got the upvote, because now you addressed the real issue. Btw, I've seen this weird split behaviour in Perl and PHP too. – Kenney Nov 05 '15 at 21:51
  • Yes, I know, but you said you are offering this solution to be educational. Remember that StackOverflow is not just for the OP. Of course, since your answer is accurate and solves the issues in the OP, you don't have to. I'm just suggesting it since you talked about educating. – RealSkeptic Nov 05 '15 at 21:53
  • @RealSkeptic there you go. – Kenney Nov 05 '15 at 21:56
  • @Kenney, hmmm you need (), same strange code as OP (correctly validate balanced parenthesis) i guess you use it with numbers normally... es (1+3)*8 – Petter Friberg Nov 05 '15 at 21:59
  • I know that he is only asking about example which only includes () but I guess that someone arriving on this question whats to check expressions like (1+3)*(1+(8) to see if correctly validate balanced parenthesis your str.replace("()", "" ) will not work. Your fault that you like to educational : ) – Petter Friberg Nov 05 '15 at 22:05