Palindromes称之为回文。在中文文当中是指倒着念和顺着念都是相同的,前后对称,例如“上海自来水来自海上”。在英文文当中是指正着看和反着看都相同的单词,例如“madam”。而对于数字,又称之为回文数,是指一个像“16461”这样的对称的数,即这个数的数字按相反的顺序重新排列后得到的数和原来的数一样。
在JavaScript中Palindromes也常出现在一些算法题中,这篇文章主要介绍如何使用JavaScript判断一个字符是不是Palindromes。
算法
- 判断给定的字符串,如果字符串是一个Palindromes,那么返回
true
,反之返回false
- Palindromes是一个词或一个名子,在忽略标点符号和间距之下,前后指写方式相同
测试用例
palindrome("race car")
返回true
palindrome("not a palindrome")
返回false
palindrome("A man, a plan, a canal. Panama")
返回true
palindrome("never odd or even")
返回true
palindrome("nope")
返回false
palindrome("almostomla")
返回false
palindrome("My age is 0, 0 si ega ym.")
返回true
palindrome("1 eye for of 1 eye.")
返回false
palindrome("0_0 (: /-\ :) 0–0")
返回true
palindrome("我爱妈妈,妈妈爱我")
返回true
其中palindrome()
是一个我们将要定义的函数,并且给这个函数传入一个str
参数,如果str
是一个Palindromes,将返回的是true
,反之返回的是false
。
知识点
在后面的介绍中,将会用到的一些JavaScript知识点:
- 正则表达式
- String.prototype.toLowerCase()
- String.prototype.replace()
- String.prototype.split()
- Array.prototype.reverse()
- Array.prototype.join()
- String.length
- for
需要用到的正则表达式
正则表达式是被用来匹配字符串中的字符组合的模式。在JavaScript中,正则表达式也是对象。这种模式可以被用于
RegExp
的exec
和test
方法以及String
的match
、replace
、search
和split
方法。
当你需要搜索一个比直接匹配需要更多条件的匹配时,比如寻找一个或多个b's
,或者寻找空格,那么这时模式将要包含特殊字符。
在这篇文章中主要用到下面两个正则表达式:
/[^A-Za-z0–9]/g
// 或
/[\W_]/g
\W
删除所有非常字母数字字符:
\W
匹配一个非单字字符- 等价于
[^A-Za-z0-9_]
那么\W
的意思就是:
[^A-Z]
匹配非26个大写字母中的任意一个[^a-z]
匹配非26个小写字母中的任意一个[^0-9]
匹配非0
到9
中的任意一个数字[^_]
匹配非下划线
因为我们测试用例中的最后一个palindrome(“0_0 (: /-\ :) 0–0”)
将返回的是true
,也就是说_(: /-\ :)–
必须是相匹配的。所以需要添加_
这个符号来通过这个特定的测试用例。
最后还需要添加g
,表示全局搜索。我们最后需要的正则表达式是/[\W_]/g
。
实现方法
检测一个字符串是不是Palindrome,方法不仅仅是一种,接下来看看网上介绍的几种方法:
方法一
function palindrome(str) {
var re = /[\W_]/g; // 或者 var re = /[^A-Za-z0-9]/g;
var lowRegStr = str.toLowerCase().replace(re,'');
var reverseStr = lowRegStr.split('').reverse().join('');
return reverseStr === lowRegStr;
}
也可以写成:
function palindrome(str) {
return str.replace(/[\W_]/g, '').toLowerCase() ===
str.replace(/[\W_]/g, '').toLowerCase().split('').reverse().join('');
}
这个方案,我们使用了下面几个方法:
- 通过正则表达式
/[\W_]/g
(或者/[^A-Za-z0-9]/g
)删除不必要的字符 - 通过
toLowerCase()
方法将传入的字符串str
转换为小写字母。比如str.toLowerCase()
,当str
的值为"A man, a plan, a canal. Panama"
时,str.toLowerCase()
就相当于"A man, a plan, a canal. Panama".toLowerCase()
,其值将是"a man, a plan, a canal. panama"
- 通过
replace()
方法和前面定义好的正则表达式/[\W_]/g
匹配出需要的字符(删除不必要的字符)。比如上例中str.replace(/[\W_]/g, '')
就相当于"a man, a plan, a canal. panama".replace(/[\W_]/g, '')
,得到的值将是"amanaplanacanalpanama"
- 通过
split()
方法将字符串转换成数组。如"amanaplanacanalpanama".split('')
,得到的值["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"]
- 使用
reverse()
方法将数组做一个反转处理["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"].reverse
,此时得到一个新数组["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"]
- 通过
join()
方法,将得到的新数组的每个数组项连接在一起变成一个新的字符串,如["a", "m", "a", "n", "a", "p", "l", "a", "n", "a", "c", "a", "n", "a", "l", "p", "a", "n", "a", "m", "a"].join('')
,其值将变成一个新字符串"amanaplanacanalpanama"
通过上面的几个方法,我们得到两个不同的字符串,其中第一个是str.replace(/[\W_]/g, '').toLowerCase()
,可以将这个字符串赋值给一个变量,比如lowRegStr
,另外一个是经过处理后得到一个反转字符串str.replace(/[\W_]/g, '').toLowerCase().split('').reverse().join('')
,同样可以将其赋值给一个变量reverseStr
,最后给这两个值做全等比较lowRegStr === reverseStr
,如果他们全等,返回的是true
,也就是说字符串str
是一个真正的Palindromes,反之返回的将是false
,那么字符串str
就不是一个Palindromes。
方法二
这个方法是使用for
循环来处理的。
function palindrome (str) {
var re = /[\W_]/g;
var lowRegStr = str.toLowerCase().replace(re, '');
var len = lowRegStr.length;
for (var i = 0, halfLen = len / 2; i < halfLen; i++){
if (lowRegStr[i] !== lowRegStr[len - 1 - i]) {
return false;
}
}
return true;
}
这个方法主要分为以下几个步骤:
- 通过正则表达式,删除字符串中不必要的字符,并且将字符串转换成小写字符
- 创建一个
for
循环,同时声明一个变量halfLen
,其值是字符串长度的一半len / 2
,并且将其当作循环的终点值 - 判断
lowRegStr[i]
和lowRegStr[len - 1 - i]
是否相同,如果不相同,返回false
,反之返回true
假设str
的值为"A man, a plan, a canal. Panama"
,其length
值为30
, 对应的len / 2 = 15
。下面,通过下表来看看整个遍历中,lowRegStr[i]
和lowRegStr[len - 1 - i]
对应的值:
遍历的次数 | i = ? | i < len / 2 | i++ | lowRegStr[i] | lowRegStr[len - 1 - i] | if(lowRegStr[i] !== lowRegStr[len - 1 - i]) |
---|---|---|---|---|---|---|
第一次遍历 | 0 | yes | 1 | lowRegStr[0] = "a" | lowRegStr[15 - 1 - 0] = "a" | false |
第二次遍历 | 1 | yes | 2 | lowRegStr[1] = "m" | lowRegStr[15 - 1 - 1] = "m" | false |
第三次遍历 | 2 | yes | 3 | lowRegStr[2] = "a" | lowRegStr[15 - 1 - 2] = "a" | false |
第四次遍历 | 3 | yes | 4 | lowRegStr[3] = "n" | lowRegStr[15 - 1 - 3] = "n" | false |
第五次遍历 | 4 | yes | 5 | lowRegStr[4] = "a" | lowRegStr[15 - 1 - 4] = "a" | false |
第六次遍历 | 5 | yes | 6 | lowRegStr[5] = "p" | lowRegStr[15 - 1 - 5] = "p" | false |
第七次遍历 | 6 | yes | 7 | lowRegStr[6] = "l" | lowRegStr[15 - 1 - 6] = "l" | false |
第八次遍历 | 7 | yes | 8 | lowRegStr[7] = "a" | lowRegStr[15 - 1 - 7] = "a" | false |
第九次遍历 | 8 | yes | 9 | lowRegStr[8] = "n" | lowRegStr[15 - 1 - 8] = "n" | false |
第十次遍历 | 9 | yes | 10 | lowRegStr[9] = "a" | lowRegStr[15 - 1 - 9] = "a" | false |
第十一次遍历 | 10 | yes | 11 | lowRegStr[10] = "c" | lowRegStr[15 - 1 - 10] = "c" | false |
第十二次遍历 | 11 | yes | 12 | lowRegStr[11] = "a" | lowRegStr[15 - 1 - 11] = "a" | false |
第十三次遍历 | 12 | yes | 13 | lowRegStr[12] = "n" | lowRegStr[15 - 1 - 12] = "n" | false |
第十四次遍历 | 13 | yes | 14 | lowRegStr[13] = "a" | lowRegStr[15 - 1 - 13] = "a" | false |
第十五次遍历 | 14 | yes | 15 | lowRegStr[14] = "l" | lowRegStr[15 - 1 - 14] = "l" | false |
第十六次遍历 | 15 | no |
方法三
function palindrome (str) {
// 删除字符串中不必要的字符
var re = /[\W_]/g;
// 将字符串变成小写字符
var lowRegStr = str.toLowerCase().replace(re, '');
// 如果字符串lowRegStr的length长度为0时,字符串即是palindrome
if (lowRegStr.length === 0) {
return true;
}
// 如果字符串的第一个和最后一个字符不相同,那么字符串就不是palindrome
if (lowRegStr[0] !== lowRegStr[lowRegStr.length - 1]) {
return false;
} else {
return palindrome(lowRegStr.slice(1, lowRegStr.length - 1));
}
}
方法四
function palindrome (str) {
// 删除字符串中不必要的字符
var re = /[\W_]/g;
// 将字符串变成小写字符
var lowRegStr = str.toLowerCase().replace(re, '');
var count = 0;
// 检查字符串是不是空字符串
if (lowRegStr === "") {
return false;
}
// 检查字符串长度是单数还是双数
if (lowRegStr.length % 2 === 0) {
count = lowRegStr.length / 2;
} else {
// 如果字符串长度值等于1,那么它是palindrome
if (lowRegStr.length === 1) {
return true;
} else {
// 如果字符串长度是奇数,忽略字符串中最中间的字符
count = (lowRegStr.length - 1) / 2;
}
}
// 添加for循环,遍历字符串,检测字符串第一个字符和最后一个字符,然后依次类推
for (var i = 0; i < count; i++) {
// 如果不匹配字符串就不是一个palindrome
if (lowRegStr[i] != lowRegStr.slice(-1 - i)[0]) {
return false;
}
}
return true;
}
不过这种方法测试过之后,检测中文类型的回文不生效,比如palindrome("我爱妈妈,妈妈爱我")
返回false
。
总结
文中通过不同的方法简单阐述了JavaScript中如何检测一个字符串是不是一个回文。这也是JavaScript中算法练习之一。文章中主要运用到了JavaScript中的正则表达式,toLowerCase()
、split()
、reverse()
和join()
等基础知识。如果您在这方面有更好的方案,欢迎在下面的评论中分享。
扩展阅读
- Two Ways to Check for Palindromes in JavaScript
- Algorithm Check for Palindromes
- JavaScript function: Check whether a passed string is palindrome or not
- Palindrome check in Javascript
如需转载,烦请注明出处:http://www.w3cplus.com/javascript/palindrome-check-in-javascript.html