LeetCode 面试经典 150 题之验证回文串:双指针解题思路详解
在 LeetCode 面试经典 150 题中,“验证回文串” 是一道极具代表性的字符串处理题目。它不仅考察我们对字符串中有效字符(字母、数字)的筛选能力,还考验对 “回文” 这一对称特性的理解,而双指针法则是解决这类对称匹配问题的高效方案。掌握这道题的解题逻辑,能帮助我们在面对类似 “对称校验” 类题目时快速找到突破口,同时提升对边界条件的处理意识。
一、题目概览与链接
首先明确题目核心要求:给定字符串 s,判断它是否为回文串。这里的回文串需满足两个特殊规则:一是仅考虑字符串中的字母和数字字符,忽略空格、标点、符号等非有效字符;二是忽略字母的大小写差异(例如 'P' 和 'p' 视为相同字符)。若满足上述条件且正读、反读一致,则返回 true,否则返回 false。
题目链接:LeetCode 125. 验证LeetCode 125.验证回文串(点击链接可直接跳转至题目页面,建议先查看题目中的示例,如 "A man, a plan, a canal: Panama" 是回文串、"race a car" 不是回文串,提前熟悉规则细节)
二、双指针解题思路深度拆解
双指针思路是验证回文串的最优解法之一,核心是 “从字符串两端向中间靠拢,动态筛选有效字符并对比对称性”。
1. 核心逻辑:双指针的 “对称校验” 原理
回文串的本质是 “对称”—— 从左到右读和从右到左读的有效字符序列完全一致。基于这一特性,我们设置两个指针:
- 左指针 left:从字符串的起始位置(索引 0)开始,向右移动;
- 右指针 right:从字符串的末尾位置(索引 s.length() - 1)开始,向左移动。
通过两个指针向中间靠拢,每一轮循环完成 “筛选有效字符→对比对称字符→移动指针” 的流程,直到 left >= right(此时所有对称位置的有效字符已对比完毕)。这种思路无需额外存储字符串,仅通过指针移动和字符判断即可完成验证,效率高且空间消耗低。
2. 循环步骤详解
步骤 1:筛选左指针的有效字符(跳过非字母 / 数字)
“如果 s[left] 不是字母或数字,指针 left 右移,直到 s[left] 是字母或数字”
- 核心目的:排除非有效字符对回文判断的干扰。例如字符串 "A man, a plan" 中,左指针初始指向 'A'(有效字符),无需移动;若左指针指向 ',' 或空格(非有效字符),则需持续右移,直到找到下一个字母或数字。
- 关键注意点:必须在循环中加入 left < right 的判断。若字符串全是非有效字符,左指针持续右移可能超过右指针,导致索引越界。加入 left < right 可确保指针移动在安全范围内,避免程序报错。
步骤 2:筛选右指针的有效字符(跳过非字母 / 数字)
“如果 s[right] 不是字母或数字,指针 right 左移,直到 s[right] 是字母或数字”
- 逻辑与步骤 1 对称:右指针从字符串末尾开始,向左跳过非有效字符,最终指向当前最右侧的有效字符。例如字符串 "canal: Panama",右指针初始指向 'a'(Panama 的最后一个字母,有效字符),无需移动;若右指针指向 ':' 或空格,则持续左移,直到找到有效字符。
- 示例场景:对于字符串 " hello world! "(首尾均有空格),左指针需跳过开头的空格,最终指向 'h';右指针需跳过末尾的 '!' 和空格,最终指向 'd',确保后续对比的是真正的有效字符。
步骤 3:对比对称字符,移动指针
“如果 s[left] 和 s[right] 的小写形式不相等,返回 false;指针 left 右移一位,指针 right 左移一位,继续下一次循环”
- 大小写统一:题目要求忽略字母大小写,因此需将两个指针指向的有效字符转为同一大小写(这里选择小写,转为大写也可)。例如 'M' 转为小写是 'm','p' 转为小写仍是 'p',确保 'M' 和 'm' 能被判定为相等。
- 早停逻辑:一旦发现某一对对称字符不相等,立即返回 false,无需继续对比后续字符,提升算法效率。例如字符串 "race a car",当左指针指向 'e'、右指针指向 'a' 时,两者小写形式不相等,直接返回 false,避免多余的循环操作。
- 指针移动:若字符对比相等,左指针右移、右指针左移,向中间靠拢,进入下一轮循环,继续验证下一对对称字符。
3. 循环终止条件与结果判定
“循环以下过程,直至 left >= right;循环结束,返回 true”
- 终止条件的意义:当 left == right 时,说明指针指向字符串的中间位置(单个字符),单个字符本身具有对称性,无需对比;当 left > right 时,说明所有对称位置的有效字符都已对比完毕,且全部相等,此时循环终止。
- 返回 true 的合理性:循环能正常结束,意味着没有任何一对对称字符不相等,且所有非有效字符都已被跳过,满足回文串的定义,因此返回 true。
4. 典型示例演示
为了更直观地掌握双指针的运行过程,我们以两道典型题目示例为例,完整演示解题步骤:
示例 1:有效回文串(含特殊字符)
s = "A man, a plan, a canal: Panama"(长度为 30)
- 初始化指针:left = 0(指向 'A'),right = 29(指向 'a')。
- 第一轮循环:
-
- 步骤 1:'A' 是字母(有效),left 不移动;
-
- 步骤 2:'a' 是字母(有效),right 不移动;
-
- 步骤 3:'A' 小写为 'a','a' 小写为 'a',相等;left = 1,right = 28。
- 后续循环:
-
- left = 1 指向空格(非有效),右移至 2('m');right = 28 指向空格(非有效),左移至 27('m');
-
- 对比 'm' 和 'm'(相等),指针继续移动;
-
- 持续重复步骤,直到 left >= right,循环终止,返回 true。
示例 2:非回文串(中间字符不匹配)
s = "race a car"(长度为 7)
- 初始化指针:left = 0(指向 'r'),right = 6(指向 'r')。
- 前几轮循环:
-
- 对比 'r' 和 'r'(相等),left = 1,right = 5;
-
- 对比 'a' 和 'a'(相等),left = 2,right = 4;
-
- 对比 'c' 和 'c'(相等),left = 3,right = 3(此时 left == right,本应终止,但需继续处理非有效字符)。
- 关键轮次:
-
- left = 3 指向 'e'(有效),right = 3 指向 'e'(有效),对比相等;left = 4,right = 2(此时 left > right,但实际因中间有空格,需重新筛选);
-
- 修正:left = 3 指向 'e',right = 4 指向空格(非有效),左移至 3(指向 'a');
-
- 对比 'e'(小写 'e')和 'a'(小写 'a'),不相等,返回 false。
三、复杂度分析
1. 时间复杂度:O (n)
- 其中 n 是字符串 s 的长度。
- 双指针 left 和 right 总共遍历字符串一次:左指针从起始位置到中间,右指针从末尾位置到中间,每个字符最多被访问一次(筛选非有效字符时,每个字符仅被判断一次,不会重复处理)。每一轮循环中的字符判断、大小写转换、指针移动均为常数时间操作(O (1)),因此总时间复杂度为 O (n),是最优的时间复杂度。
2. 空间复杂度:O (1)
- 整个算法仅使用了 left 和 right 两个指针变量,未使用任何额外的数组、字符串或其他数据结构,空间占用不随字符串长度的变化而变化,始终保持常数级别。因此空间复杂度为 O (1),属于 “原地算法”,内存使用效率极高。
四、代码实现
根据上述双指针思路,我们提供 Java、Python两种主流编程语言的完整代码:
1. Java 代码
class Solution {public boolean isPalindrome(String s) {int left = 0,right = s.length()-1;while (left < right){while (left < right && !Character.isLetterOrDigit(s.charAt(left))){left++;}while (left < right && !Character.isLetterOrDigit(s.charAt(right))){right--;}if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))){return false;}left++;right--;}return true;}
}
2. Python 代码
class Solution:def isPalindrome(self, s: str) -> bool:left,right = 0,len(s) - 1while left < right:while left < right and not s[left].isalnum():left += 1while left < right and not s[right].isalnum():right -= 1if s[left].lower() != s[right].lower():return Falseleft += 1right -= 1return True
五、总结与拓展
“验证回文串” 这道题的核心是利用双指针的 “对称特性”,动态筛选有效字符并对比,既保证了效率,又降低了空间消耗。通过这道题,我们可以提炼出解决 “对称匹配” 类问题的通用思路:
- 确定对称的起点(通常是两端或中心);
- 筛选有效元素,排除干扰项;
- 对比对称位置的元素,发现不匹配立即终止;
- 移动指针 / 索引,直至遍历完所有对称位置。
这道题虽然难度中等,但能很好地锻炼我们的 “问题拆解能力” 和 “边界处理意识”。希望通过本文的分析,你能彻底掌握双指针法在验证回文串中的应用,并且能将该思想运用到其他题目中。