
示例工程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载导读本文基于 LeetCode 125. 验证回文串 的官方题解文档结合 doocs/leetcode 仓库中该题的多语言源码系统讲解「忽略大小写与非字母数字字符后的回文判断」这一经典双指针问题。读完本文你将掌握准确理解题目的过滤规则与边界条件、双指针 O(n) 时间 / O(1) 空间的通用解法以及 Python、Java、C、Go、TypeScript、Rust、JavaScript、C#、PHP 九种语言的逐行实现与 API 差异。一、题目理解先「归一化」再判回文1.1 题目定义如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后短语正着读和反着读都一样则可以认为该短语是一个回文串。字母和数字都属于字母数字字符alphanumeric characters。给你一个字符串s如果它是回文串返回true否则返回false。示例 1输入: s A man, a plan, a canal: Panama 输出: true 解释: amanaplanacanalpanama 是回文串。示例 2输入: s race a car 输出: false 解释: raceacar 不是回文串。示例 3输入: s 输出: true 解释: 在移除非字母数字字符之后s 是一个空字符串 。 由于空字符串正着反着读都一样所以是回文串。提示1 s.length 2 * 10^5s仅由可打印的 ASCII 字符组成1.2 关键语义拆解解题前需要先厘清三个隐含规则这也是本题最常见的失分点大小写不敏感所有大写字符需要先转换为小写字符再比较非字母数字一律剔除空格、标点逗号、冒号、感叹号等、其他符号都不参与比较空串视为回文如果过滤后字符串为空按定义「正着读反着读都一样」应返回true——这正是示例 3 的核心考点。由于s仅由可打印 ASCII 字符组成过滤规则可以用「字母A-Z / a-z 数字0-9」的集合来精确描述无需考虑 Unicode 的复杂情况这让各语言的标准库 API 都能直接派上用场。二、解法一双指针推荐2.1 从「过滤 反转」到「双指针」的思维推导最容易想到的朴素思路是先扫描一遍字符串把合法字符字母数字筛出来组成新串并统一转小写再用反转后与原串比较。在n 2 * 10^5的数据规模下这种做法时间上可行但需要额外的O(n)空间。进一步思考会发现判断回文其实只需要对称位置上的字符两两相等。既然如此完全不必构造新串——让两个指针分别从字符串两端向中间移动遇到非法字符就跳过遇到合法字符就忽略大小写比较。一次扫描即可完成判断空间开销降为常数。这正是文档中给出的核心结论回文只需对称位置相等。两端指针同时向中间走遇到非法字符就跳过合法字符则忽略大小写比较。一次扫描、常数空间即可。2.2 算法流程用双指针i和j分别指向字符串s的两端循环以下过程直至i j如果s[i]不是字母或数字指针i右移一位继续下一次循环如果s[j]不是字母或数字指针j左移一位继续下一次循环如果s[i]和s[j]的小写形式不相等返回false否则指针i右移一位指针j左移一位继续下一次循环。循环结束返回true。2.3 复杂度分析时间复杂度O(n)其中n是字符串s的长度。每个字符最多被访问一次双指针整体走一遍空间复杂度O(1)只使用两个指针变量不依赖输入规模。这也是本题作为「简单」难度、双指针入门题的标志性价值在字符串长度高达2 * 10^5时依然能以常数空间线性完成。三、九种语言实现详解以下代码与仓库 solution/0100-0199/0125.Valid Palindrome 目录下各Solution.*文件完全一致README 文档与源码一一对应可直接复制运行。Python3class Solution: def isPalindrome(self, s: str) - bool: i, j 0, len(s) - 1 while i j: if not s[i].isalnum(): i 1 elif not s[j].isalnum(): j - 1 elif s[i].lower() ! s[j].lower(): return False else: i, j i 1, j - 1 return True实现要点str.isalnum()同时覆盖字母与数字str.lower()做忽略大小写比较指针跳过非法字符时只移动单侧指针i、j相遇时循环自然终止。Javaclass Solution { public boolean isPalindrome(String s) { int i 0, j s.length() - 1; while (i j) { if (!Character.isLetterOrDigit(s.charAt(i))) { i; } else if (!Character.isLetterOrDigit(s.charAt(j))) { --j; } else if (Character.toLowerCase(s.charAt(i)) ! Character.toLowerCase(s.charAt(j))) { return false; } else { i; --j; } } return true; } }实现要点Character.isLetterOrDigit(char)对应字母数字判断Character.toLowerCase(char)处理大小写归一注意通过s.charAt(i)逐个取字符避免substring产生额外对象。Cclass Solution { public: bool isPalindrome(string s) { int i 0, j s.size() - 1; while (i j) { if (!isalnum(s[i])) { i; } else if (!isalnum(s[j])) { --j; } else if (tolower(s[i]) ! tolower(s[j])) { return false; } else { i; --j; } } return true; } };实现要点C 标准库的isalnum()与tolower()均以unsigned char为参数这里直接作用于chartolower仅对字母生效对数字和符号返回原值天然契合「忽略大小写」的需求。Gofunc isPalindrome(s string) bool { i, j : 0, len(s)-1 for i j { if !isalnum(s[i]) { i } else if !isalnum(s[j]) { j-- } else if tolower(s[i]) ! tolower(s[j]) { return false } else { i, j i1, j-1 } } return true } func isalnum(ch byte) bool { return (ch A ch Z) || (ch a ch z) || (ch 0 ch 9) } func tolower(ch byte) byte { if ch A ch Z { return ch 32 } return ch }实现要点Go 没有内置isalnum这里手写两个辅助函数用 ASCII 码范围判断字母数字大写转小写利用 ASCII 码表中A(65) 与a(97) 相差 32 的规律直接ch 32逻辑一目了然且零依赖。TypeScriptfunction isPalindrome(s: string): boolean { let i 0; let j s.length - 1; while (i j) { if (!/[a-zA-Z0-9]/.test(s[i])) { i; } else if (!/[a-zA-Z0-9]/.test(s[j])) { --j; } else if (s[i].toLowerCase() ! s[j].toLowerCase()) { return false; } else { i; --j; } } return true; }JavaScript/** * param {string} s * return {boolean} */ var isPalindrome function (s) { let i 0; let j s.length - 1; while (i j) { if (!/[a-zA-Z0-9]/.test(s[i])) { i; } else if (!/[a-zA-Z0-9]/.test(s[j])) { --j; } else if (s[i].toLowerCase() ! s[j].toLowerCase()) { return false; } else { i; --j; } } return true; };实现要点TS/JS 一致用正则/ [a-zA-Z0-9] /的test方法判断字符是否为字母数字toLowerCase()完成大小写归一。注意test每次只针对单个字符不会因全局匹配标志g产生 lastIndex 状态问题。Rustimpl Solution { pub fn is_palindrome(s: String) - bool { let s s.to_lowercase(); let s s.as_bytes(); let n s.len(); let (mut l, mut r) (0, n - 1); while l r { while l r !s[l].is_ascii_alphanumeric() { l 1; } while l r !s[r].is_ascii_alphanumeric() { r - 1; } if s[l] ! s[r] { return false; } l 1; if r ! 0 { r - 1; } } true } }实现要点Rust 版策略略有不同——先对整个字符串执行to_lowercase()一次性完成大小写归一再转为字节切片as_bytes()以便用下标访问内部再用两层 while 循环跳过两侧非字母数字字符is_ascii_alphanumeric。由于题目保证输入为可打印 ASCII字节级操作安全高效。C#public class Solution { public bool IsPalindrome(string s) { int i 0, j s.Length - 1; while (i j) { if (!char.IsLetterOrDigit(s[i])) { i; } else if (!char.IsLetterOrDigit(s[j])) { --j; } else if (char.ToLower(s[i]) ! char.ToLower(s[j--])) { return false; } } return true; } }实现要点C# 版在分支内直接利用s[i]、s[j--]的后置自增/自减将「比较 移动指针」合并到一行代码更紧凑char.IsLetterOrDigit与char.ToLower为框架内置 API。PHPclass Solution { /** * param String $s * return Boolean */ function isPalindrome($s) { $regex /[a-z0-9]/; $s strtolower($s); preg_match_all($regex, $s, $matches); if ($matches[0] null) { return true; } $len floor(count($matches[0]) / 2); for ($i 0; $i $len; $i) { if ($matches[0][$i] ! $matches[0][count($matches[0]) - 1 - $i]) { return false; } } return true; } }实现要点PHP 版走「过滤 逐位比对」路线——先用strtolower转小写再用preg_match_all配合正则/ [a-z0-9] /一次性筛出全部合法字符到$matches[0]随后只比对数组前半段与对称位置。若过滤后为空数组则直接返回true与示例 3 的空串语义一致。该写法空间为O(m)m为合法字符数在n 2 * 10^5下依然可行。四、边界情况与易错点归纳综合题目约束与各语言实现实战中需要重点验证以下场景场景输入示例期望结果易错原因纯符号/空白 ,,.!true过滤后为空串需按回文处理大小写混合Abatrue必须忽略大小写比较含数字0Pfalse0与p均为合法字符需参与比较单字符atrue单字符天然回文双指针循环体不执行奇数/偶数长度abba,abcbatrue双指针i j终止条件需同时覆盖两种情况数字与字母的边界9vsa—ASCII 上9(57) A(65)手写判断时必须按三段区间分别判断其中「空串返回true」是题目的显式定义示例 3也是过滤式写法中容易漏判的细节而「数字也属于合法字符」则要求判断逻辑必须覆盖0-9不能只保留字母。五、复杂度对比与延伸思考5.1 两种思路的取舍方案时间复杂度空间复杂度适用场景过滤 反转比较O(n)O(n)新串思路直观、实现简单双指针原地比较O(n)O(1)内存敏感、面试首选本题n上限为2 * 10^5两种方案都能通过但双指针方案以常数空间完成同等任务是更优解也是 LeetCode 双指针系列题目的入门范式。5.2 延伸练习掌握了本题的「字符过滤 双指针」组合拳后可以进一步挑战仓库中同主题的进阶题剑指 Offer II 018. 有效的回文同一回文判定逻辑在《剑指 Offer专项突击版》中的复现LCCI 02.06. Palindrome Linked List把回文判断从字符串迁移到链表双指针需结合快慢指针与反转链表。六、小结本题的核心价值在于两点一是精确理解题目语义——忽略大小写、剔除非字母数字、空串视为回文二是掌握双指针原地判定范式——从两端向中间收缩跳过非法字符比较合法字符的小写形式以O(n)时间、O(1)空间完成判断。仓库 solution/0100-0199/0125.Valid Palindrome 目录下提供了 9 种语言的独立可运行源码中文与英文题解分别见 README.md 与 README_EN.md可作为面试复习与多语言对照的随身参考。赞分享示例工程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载相关推荐LeetCode 题解仓库实战125 验证回文串的双指针解法与三语言实现LeetCode 题解仓库实战125 验证回文串的双指针解法与三语言实现 本篇基于 LeetCode 解题仓库leetcode中收录的 125 题解文档教程文档知识库LeetCode 125 验证回文串头尾双指针解法全解含 JS/C/Python/Java 多语言实现LeetCode 125 验证回文串头尾双指针解法全解含 JS/C/Python/Java 多语言实现 本篇技术指南以 leetcode 题解仓库中的教程文档知识库LeetCode 125. 验证回文串Valid Palindrome题解双指针一次扫描的 O(n) 实现与多语言实战LeetCode 125. 验证回文串Valid Palindrome题解双指针一次扫描的 O n 实现与多语言实战 本文以 doocs/leetcode示例工程上一篇如何把两小时录音一键变成文字稿免费离线音频转录工具 Buzz 完整上手指南下一篇legit配置迁移最佳实践版本升级时的设置处理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考