Let's start with some ideas:
Counting is easy. We could count the number of left
parentheses, and then separately count the number of right parentheses.
Suppose we wrote one method for each task:
At this point, we could go ahead and fill in some detail,
then implement and test the above methods.
But let's pause for a moment and ask ... do we really need two
separate methods?
Here's a more compact approach:
Note:
A1.2 Exercise:
Write the code that should go into the method
countChar()
above. Then, include all the 5 test strings from the previous
page in
main()
and test that the counts are correct.
OK, so now if the counts are correct, what's next?
Let's try and match the parens. For example, with
What do we do when we find it? We can erase both, in effect
marking the pair as matched:
Since we eliminated the matched pair, we simply repeat the
exact same process. If there are no more parens left at the end,
it's balanced. Otherwise, we have mismatched parens.
Let's flesh this out with a method:
A1.3 Exercise:
Write the code that should go into the method above
and test it.
Now, let's outline the approach:
Yes, it's a bit more complex than what we're used to.
To understand it, first work it out by hand for a few examples.
A1.4 Exercise:
In
A1.4.pdf
trace out, step by step, the above approach for this example
string: "(((a(()()))d)(e)f)".
Next, notice that we've inserted some println's. This is
essential defensive programming: add copious println's
as you code so that you can catch an error early. It's much
harder to catch errors after your code has become complex
and gnarly. Once your program is working, go back and comment
out the println's.
A1.5 Exercise:
Put all the pieces together into
ParensTest.java
to complete the exercise.
A1.6 Audio:
First, the easy parts
static int countLeftParens ( ...)
{
}
static int countRightParens ( ...)
{
}
At this point, let's not worry about the parameters (is it a string?
array of chars?).
We could then write code like:
static int countLeftParens ( ...)
{
}
static int countRightParens ( ...)
{
}
Then, we can use this
static boolean checkBalanced (String s)
{
int numLeft = countLeftParens ( ... )
int numRight = countRightParens ( ... )
if (numLeft != numRight) {
// Not balanced
}
}
If there are equal number, then we can set about seeing if they are
actually well-formed.
static boolean checkBalanced (String s)
{
char[] letters = s.toCharArray ();
int numLeft = countChar (letters, '(')
int numRight = countChar (letters, ')')
System.out.println ("numLeft=" + numLeft + " numRight=" + numRight);
if (numLeft != numRight) {
// Not balanced
return false;
}
}
static int countChar (char[] letters, char c)
{
}
We've outlined a more general purpose "look for a particular char and
count its occurences" method that we use twice, one with the left
paren and once with the right paren.
The one hard part
String s = "(a(bc)d)(e)f"
we could try and look for its match:
String s = "(a(bc)d)(e)f"
But how do we know how many parens in between to skip over? What if we
had
String s = "(a(()()bc)d)(e)f"
Now for a key insight: let's find the first right parens
(the leftmost right parens):
String s = "(a(bc)d)(e)f"
and then go leftwards to find its match.
String s = "(a(bc)d)(e)f"
There are no other parens in between!
String s = "(a_bc_d)(e)f"
static int findFirstRight (char[] letters)
{
// return -1 if not found.
}
static boolean checkBalanced (String s)
{
char[] letters = s.toCharArray ();
int numLeft = countChar (letters, '(')
int numRight = countChar (letters, ')')
System.out.println ("numLeft=" + numLeft + " numRight=" + numRight);
if (numLeft != numRight) {
// Not balanced
return false;
}
int k = findFirstRight (letters);
while (k >= 0) {
// k is the location of the leftmost right-paren
//** print k here
// We now go left from here to locate its match:
int n = findFirstLeft (letters, k-1);
//** print n here
if (n < 0) {
// None found, so it's unbalanced.
return false;
}
else {
// Matched pair: left at n, right at k
letters[n] = ' ';
letters[k] = ' ';
}
//** print letters here
// Proceed by repeating.
k = findFirstRight (letters);
}
}
And that's our complete algorithm!
Back to the assignment