今天是《剑指offer》算法题系列的最后一天了,但是这个系列并没有包括书上的所有题目,因为正如第一天所说,这些代码是在牛客网上写并且测试的,但是牛客网上并没有涵盖书上所有的题目。
今日题目:
- 正则表达式匹配
- 表示数值的字符
- 把字符串转换成整数
- 删除连表中重复的节点
- 按之字形顺序打印二叉树
- 将二叉树打印成多行
其中第5,6题是比较典型的二叉树层次遍历的题目,比较简单,这边就不在阐述,但是大家对它们还是得非常熟悉。
1. 正则表达式匹配
题目描述:
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配 思路:
这道题目之前在leetcode上有遇到过,思路比较简单,但是对于新手来说是比较难想到的。
它本质上是一个递归的问题,当前的匹配只关注当前的字符和一下个字符是‘*’的情况,代码比较直观。但在用java实现的时候要时刻注意数组是否越界。
代码如下:
public class Solution {
public boolean match(char[] str, char[] pattern) {
if (str == null || pattern == null) {
return false;
}
int strIndex = 0;
int patternIndex = 0;
return matchCore(str, strIndex, pattern, patternIndex);
}
public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
//有效性检验:str到尾,pattern到尾,匹配成功
if (strIndex == str.length && patternIndex == pattern.length) {
return true;
}
//pattern先到尾,匹配失败
if (strIndex != str.length && patternIndex == pattern.length) {
return false;
}
//模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符
|| matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符
|| matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个
} else {
return matchCore(str, strIndex, pattern, patternIndex + 2);
}
}
//模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
}
return false;
}
}
2. 表示数值的字符串
题目描述:
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。 思路:
参考《剑指》上的讲解,将字符串根据'.'和'E'分成若干段,每一段都有不同的约束条件,根据这个来判断是否符合数字的要求。同样地,要注意java中数组越界的问题。
代码如下:
public class Solution {
int ind = 0;
public boolean isNumeric(char[] str) {
if(str.length < 1) return false;
boolean num = scanInteger(str);
if(ind < str.length && str[ind] == '.'){
ind++;
num = scanUnsignedInt(str) || num;
}
if(ind < str.length && (str[ind] == 'e' || str[ind] == 'E')){
ind++; num = scanInteger(str) && num;
} return num && (ind == str.length);
} public boolean scanInteger(char[] str){
if(ind < str.length && (str[ind] == '+' || str[ind] == '-'))
ind ++;
return scanUnsignedInt(str);
} public boolean scanUnsignedInt(char[] str){
int before = ind;
while(ind < str.length && str[ind] >= '0'
&& str[ind] <= '9')
ind ++;
return ind > before;
}
}
3. 把字符串转换成整数
题目描述:
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。 思路:
这个题目就是atoi(),是面试中经典得不能再经典的题目了,与之对应的是itoa(),请参考:http://www.cnblogs.com/cobbliu/archive/2012/08/25/2656176.html
atoi()中,关键是要考虑到各种可能的输入,以及对字符串的操作。
代码如下:
public class Solution {
public int StrToInt(String str) {
if(str == null || str.length() == 0)
return 0;
int res = 0;
boolean isNeg = false;
if(str.charAt(0) == '-'){
isNeg = true;
}else if(str.charAt(0) != '+'){
res = (str.charAt(0) - '0');
}
for(int i = 1; i < str.length(); i++){
char ch = str.charAt(i);
if(ch >= '0' && ch <= '9')
res = res*10 + (ch - '0');
else
return 0;
} return isNeg? -res:res;
}
}
4. 删除链表中重复的节点
题目描述:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 思路:
比较简单,熟悉链表操作的朋友能很快地写出来,关键是一定要熟悉!
博主这边加入了一个头结点来处理连表中第一个节点是重复节点的情况。
代码如下:
public class Solution {
public ListNode deleteDuplication(ListNode pHead){
if(pHead == null || pHead.next == null)
return pHead;
ListNode dummy = new ListNode(0);
dummy.next = pHead;
ListNode prev = dummy,cur = pHead;
while(cur != null){
boolean duplicate = false;
while(cur.next != null && cur.val == cur.next.val){
duplicate = true;
cur.next = cur.next.next;
}
if(duplicate){
prev.next = cur.next;
cur = cur.next;
}else{
prev = prev.next;
cur = prev.next;
}
}
return dummy.next;
}
}