Problem overview
The method isPalindrome receives a string input and returns true when the string reads the same forwards and backwards, and false otherwise. The lesson asks for O(n) time and O(1) extra space.
For "madam racecar madam" the answer is true: the spaces are characters like any other, and the whole string is its own mirror image. For "abcbx" the answer is false, because the first character a doesn't match the last one, x.
Approach: compare the two ends and move inward
A string is a palindrome when the first character equals the last, the second equals the second to last, and so on. That is the whole test. A tempting shortcut is to build a reversed copy of the string and compare it with the original. It is correct, but it uses O(n) extra memory, which the lesson rules out.
Instead, keep two indices: left at the first character and right at the last one. Compare the two characters they point to. If they differ, the string cannot be a palindrome, so stop with false. If they match, move left one step right and right one step left, and repeat. When the pointers meet or cross, every pair has been checked, and the answer is true.
Take "abcbx". The first comparison, positions 0 and 4, is a against x, and the method returns false immediately. For "abcba" the pairs are a/a and b/b, then the pointers both reach position 2 and the loop ends: the answer is true.
Step by step
- Create the left pointer as a
varnamedleft, starting at 0. It must be avarbecause it changes as the loop runs. - Create the right pointer as a
varnamedright, starting atinput.length - 1. Indices start at 0, so the last one is one less than the length. - Loop while
left < right. As long as the pointers haven't met, there is still a pair to compare. With an odd length, the middle character is never compared; it matches itself, so skipping it is correct. - Compare the pair. Inside the loop, test
input(left) != input(right). On a mismatch,return falseat once: one bad pair is enough. - Move the pointers. After the
if, add 1 toleftand subtract 1 fromright. Without this, the loop would compare the same pair forever. - Return
trueafter the loop. Reaching this line means no pair failed.
The solution in Scala
class Solution {
def isPalindrome(input: String): Boolean = {
var left = 0
var right = input.length - 1
while (left < right) {
if (input(left) != input(right)) {
return false
}
left += 1
right -= 1
}
return true
}
}
The Scala details worth a closer look:
- Indexing a string. Scala reads the character at a position with parentheses:
input(left). Square brackets are used for type parameters in Scala, not for indexing. Charcomparison.input(left)is aChar, and!=compares the two characters by value. Uppercase and lowercase letters are different characters, so"Madam"is not a palindrome here.returninside awhileloop. Awhileloop is part of the method body, soreturn falseexits the method directly.returnis used rarely in Scala, but it is fine in this position.left += 1is shorthand forleft = left + 1. It works becauseleftis avar.- Parameters are fixed.
inputis a method parameter and can't be reassigned, and a ScalaStringis immutable. Nothing is copied or changed; the loop only reads characters.
Other ways to write it
Scala can compare a string with its reversed copy directly:
val input = "madam racecar madam"
println(input == input.reverse) // true
This is short and correct, but it creates a second string of the same length, so it uses O(n) extra space. The two-pointer loop avoids that, which is the point of this lesson.
If a version of the problem ignored case, spaces and punctuation, the string would have to be cleaned before the check:
val text = "A man, a plan, a canal: Panama"
val cleaned = text.filter(_.isLetterOrDigit).toLowerCase
println(cleaned) // amanaplanacanalpanama
println(cleaned == cleaned.reverse) // true
println(text == text.reverse) // false
This lesson compares characters exactly as they are.
Complexity
Time: O(n). Each iteration moves the pointers closer by two positions, so the loop runs at most n / 2 times, with a constant amount of work in each.
Space: O(1). The method uses two integers and never builds a new string.
Edge cases and common mistakes
- Empty string.
rightstarts at-1, soleft < rightis false and the method returnstrue. An empty string reads the same in both directions. - One character. Both pointers are 0, the loop never runs, and the result is
true. - Even length.
"abba"comparesa/aandb/b, then the pointers cross. The result istrue;"ab"isfalse. - Spaces and case. The method compares characters exactly.
"a man a plan"isfalse, and"Madam"isfalsebecauseMandmdiffer. The lesson's tests need no special handling for either. - Using
left <= right. It also works, since the last comparison would be a character with itself, but it does one needless step. - Forgetting to move a pointer. The loop would never end for a palindrome, because the same pair would match again and again.
- Starting
rightatinput.length. That index is one past the end, and reading it throws aStringIndexOutOfBoundsException.
Testing
"madam racecar madam"givestrue."abcbx"givesfalse.""givestrue."a"givestrue."ab"givesfalse."abba"givestrue.
