01
题目信息
题目地址:
https://leetcode-cn.com/problems/valid-palindrome/
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例:
输入: "A man, a plan, a canal: Panama"
输出: true
输入: "race a car"
输出: false
02
解法一:双指针
首先判断两个字符串是否是回文串,就是原串与倒序是否相等,那不就是前面写的反转字符串么。只不过就是首尾交换,换成首尾是否相等。但在此之前我们先要从原串中获取数字字母串并忽略大小写
public boolean isPalindrome(String s) {
//空字符串直接算回文串
if(s == null || s.length() == 0) return true;
//拿出原数组优化遍历取值
//新建新容器填充只含数字字母的串
char[] arr = s.toCharArray();
int n = arr.length;
StringBuilder cs = new StringBuilder();
//遍历填值
for (int i = 0; i < n; i++) {
char c = arr[i];
if (check(c)) {
cs.append(change(c));
}
}
//首尾比较是否回文
n = cs.length();
for(int i = 0; i < n/2; i++){
if(cs.charAt(i) != cs.charAt(n-i-1)){
return false;
}
}
return true;
}
//是否是字母或数字
public boolean check(char c){
return c>=48&&c<=57 || c>=65&&c<=90 || c>=97&&c<=122;
}
//大写统一转小写
public char change(char c){
return c>=65&&c<=90 ? c+=32 : c;
}
解法二:细节优化(解一)
我们刚刚是用了两个循环,第一个循环筛选了属于字母数字的。第二次循环再判断字母数字的串有木有重复。其实是冗余的,我们直接在第一个循环完成即可,最终的目的是看字母数字是否回文但没必要取出来再做。那样减少了一个容器和一次遍历。
public boolean isPalindrome(String s) {
//空字符串是回文串
if(s == null || s.length() == 0) return true;
//拿出数组优化取值
char[] arr = s.toCharArray();
int n = arr.length;
//定义双指针
int start = 0;
int end = n - 1;
//首尾比较,若不是字母数字继续移动
while(start < end){
while(start < end && !check(arr[start])){
start++;
}
while(start < end && !check(arr[end])){
end--;
}
if(change(arr[start])!=change(arr[end])){
return false;
}
start++;
end--;
}
return true;
}
//是否是字母或数字
public boolean check(char c){
return c>=48&&c<=57 || c>=65&&c<=90 || c>=97&&c<=122;
}
//大写统一转小写
public char change(char c){
return c>=65&&c<=90 ? c+=32 : c;
}
解法三:栈
虽然这题双指针就是比较优的解法,但是前段时间学习了栈,因此在这题上用上温习一下不过空间和效率应该是最低的,我们都知道栈是后进先出(LIFO)一种数据结构。这里我们可以用数组实现一下栈完成一下基本的四个方法
class Stack<E>{
List<E> arr = new ArrayList();
int top = -1;
void push(E c){
arr.add(++top,c);
}
E pop(){
E c = peek();
arr.remove(top--);
return c;
}
E peek(){
return arr.get(top);
}
boolean isEmpty(){
return top < 0;
}
}
public boolean isPalindrome(String s) {
if(s == null || s.length() == 0) return true;
char[] arr = s.toCharArray();
int n = arr.length;
Stack<Character> stack = new Stack<>();
List<Character> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
char c = arr[i];
if (check(c)){
stack.push(change(c));
list.add(change(c));
}
}
int index = 0;
while (!stack.isEmpty()){
if (stack.pop() != list.get(index++))
return false;
}
return true;
}
05
总结
回文串处理的大体方式还是双指针、反转或者栈。对于此题Character类中提供了判断是否为数字或字母的方法和转大小写的方法。我上面是单独写的check(char c)与change(char c)方法。
isLetterOrDigit(char c)
toLowerCase(char c)
利用Java类库的话直接有反转方法当然它里面也是一次遍历处理,反转再比较。相当于三次遍历比解法一还多一次当然效率是比较低的不过可以参考
//cs为取出来的只含字母数字的字符串
String fz = cs.reverse().toString();
return cs.equals(fz);
总体来说呢可以实现的方式有很多,关注实现本身效率来说双指针是较优的。
本文分享自微信公众号 - IT那个小笔记(qq1839646816)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。