Problem overview
A palindrome reads the same forward and backward. The method isPalindrome(String input) takes a string and returns true if it is one, false otherwise. Two examples:
"madam racecar madam"returnstrue. Reversing the whole string, spaces included, gives the same string back."abcbx"returnsfalse. The first character isaand the last isx.
The comparison is literal: every character counts, including spaces, punctuation and upper versus lower case. The lesson requires O(n) time and O(1) extra space.
Approach: compare from both ends with two pointers
A string is a palindrome exactly when its first character equals its last, its second equals its second-to-last, and so on. In index terms, input.charAt(k) must equal input.charAt(n - 1 - k) for every k, where n is the length.
The simplest way to check that is two index variables. left starts at the first character and right at the last. While they have not met, compare the two characters, then move both one step toward the middle. A single mismatch settles the answer as false immediately. If the pointers meet without a mismatch, every pair matched.
The common alternative is to build the reversed string and compare it with the original:
static boolean isPalindromeReversed(String input) {
return new StringBuilder(input).reverse().toString().equals(input);
}
It is correct and short, but it creates a second string as long as the first, so it uses O(n) extra space and always looks at every character. The two-pointer version allocates nothing and can stop at the first mismatch, which is why the lesson asks for it.
Step by step
- Create
leftas anintset to 0. It will move, so it is a variable and not a constant. - Create
rightas anintset toinput.length() - 1. Indices start at 0, so the last valid index is one less than the length. Note thatlength()has parentheses for aString; for an array it would be the fieldlength. - Loop while
left < right. When the pointers meet or cross, there is no pair left to compare. For a string of odd length, the middle character is never compared, which is fine because it is its own mirror image. - Return
falseon a mismatch. Compareinput.charAt(left)withinput.charAt(right). - Move the pointers. Increment
leftand decrementrightat the end of each pass. Without this the loop would compare the same pair forever. - Return
trueafter the loop. The code can only get there if no pair differed.
The solution in Java
class Solution {
public boolean isPalindrome(String input) {
int left = 0;
int right = input.length() - 1;
while (left < right) {
if (input.charAt(left) != input.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
}
Java details that matter here:
charAtreturns achar, a primitive. So!=compares the characters themselves, which is exactly what is wanted. This is different from strings and other objects, where==and!=compare references and you must callequals.charAtis O(1). A JavaStringis backed by an array, so reading a position does not walk the string. That is what keeps the loop linear.- The empty string is a palindrome. Its length is 0, so
rightstarts at -1, the loop condition is false at once, and the method returnstrue. A one-character string behaves the same way, becauseleftandrightare both 0.
Time and space complexity
Each pass of the loop moves the pointers one step closer, so the loop runs at most n / 2 times. That is O(n) time in the worst case, which happens when the string is a palindrome or differs only near the middle. When the first and last characters differ, the method returns after one comparison. The extra memory is two int variables, which is O(1) space.
Edge cases and common mistakes
- Upper and lower case. This solution is case-sensitive, so
"Madam"returnsfalsebecauseMandmare different characters. To ignore case, compareCharacter.toLowerCase(input.charAt(left))with the lower-cased right character, or callinput.toLowerCase()first. - Spaces and punctuation. They are compared like any other character. A phrase such as
"A man, a plan"is not a palindrome here. The version that skips non-letters moves each pointer past characters thatCharacter.isLetterOrDigitrejects before comparing. - Looping to the end of the string. A loop like
for (int i = 0; i < input.length(); i++)that compares with the mirror position works, but does every comparison twice. Stopping at the middle is enough. - Off-by-one on the right pointer. Starting
rightatinput.length()makes the firstcharAtthrowStringIndexOutOfBoundsException. null. Callinginput.length()on anullreference throwsNullPointerException. The lesson passes real strings, but production code would check fornullfirst.
Checking a number
The same method checks whether an integer is a palindrome: convert it with String.valueOf and reuse the check. For example 121 gives "121", which is a palindrome, and 123 is not. A negative number such as -121 becomes "-121", which reads 121- backward, so it is not one.
Testing
Good inputs to try: "" and "a" (both true), "ab" (false), "abba" (even length, true), "abcba" (odd length, true) and "abca" (the ends match but the middle does not, false).
