51、构建乘积数组
题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0]
= A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];) 对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。
可知:
left[i+1] = A[0]*...A[i-1]*A[i]
right[i+1] = A{i+2]*...*A[n-1]
于是,
left[i+1] = left[i] * A[i]
right[i] = right[i+1] * A[i+1]
所以,我们可以先把所有的left[i]求出,right[i]求出。
class Solution {
public int[] constructArr(int[] a) {
if(a==null||a.length==0) return new int[0];
int[] b = new int[a.length];
b[0] = 1;
for(int i=1;i<a.length;i++){
b[i] = b[i-1]*a[i-1];
}
//b[i] = b[i+1]*a[i+1]这个不能直接使用,因为现在b里面已经有值了,不但单纯是b[i+1]了
//计算的应该是b[i]*[a[i+1]*a[i+2]*...a[n]]
int temp = 1;
for(int i=a.length-2;i>=0;i--){
temp*=a[i+1];
b[i] = b[i]*temp;
}
return b;
}
}
52、正则表达式
题目描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
class Solution {
public boolean isMatch(String s, String p) {
if(s==null&&p==null) return true;
return matchString(s.toCharArray(),0,p.toCharArray(),0);
}
private boolean matchString(char[] str,int i, char[] pattern,int j){
if(i == str.length&& j == pattern.length) return true;
if(i<str.length&&j==pattern.length) return false;
/**
如果p的下一个位置是*
如果模式匹配字符的下一个字符是‘*’,且pttern当前字符和str的当前字符匹配,:有以下三种可能情况
(1)pttern当前字符能匹配 str 中的 0 个字符:match(str, pattern+2)(跳到*的下一个,指*前面的字符出现了0次,则str不动,只移动pattern)
(2)pttern当前字符能匹配 str 中的 1 个字符:match(str+1, pattern+2)(指*前面的字符出现了1次)
(3)pttern当前字符能匹配 str 中的 多 个字符:match(str+1, pattern)(pattern不动,可以当做是当做重复前一个字符)
*/
if(j+1<pattern.length&&pattern[j+1]=='*'){
//当前点匹配
if((i<str.length&&pattern[j]=='.')||i<str.length&&str[i]==pattern[j]){
return matchString(str,i,pattern,j+2)||matchString(str,i+1,pattern,j+2)||matchString(str,i+1,pattern,j);
}else{
//当前字符不匹配,则当做*前的出现了0次,pattern后移,继续匹配
//如 bbc a*b*c--> 下一个是*,b a不匹配,则继续比较 bbc b*c
return matchString(str,i,pattern,j+2);
}
}
//pattern下一个不是*,且和str匹配,就依次向后匹配
if(i<str.length&&(str[i]==pattern[j]||pattern[j]=='.')){
return matchString(str,i+1,pattern,j+1);
}
return false;
}
}
53、表示数值的字符串
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。
但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
public boolean isNumber(String s) {
// s为空对象或 s长度为0(空字符串)时, 不能表示数值
if(s == null || s.length() == 0) return false;
// 标记是否遇到数位、小数点、‘e’或'E'
boolean isNum = false, isPoint = false, ise_or_E = false;
// 删除字符串头尾的空格,转为字符数组,方便遍历判断每个字符
char[] str = s.trim().toCharArray();
for(int i=0; i<str.length; i++) {
// 判断当前字符是否为 0~9 的数位
if(str[i] >= '0' && str[i] <= '9') isNum = true;
// 遇到小数点
else if(str[i] == '.') {
// 小数点之前可以没有整数,但是不能重复出现小数点、或出现‘e’、'E'
if(isPoint || ise_or_E) return false;
isPoint = true; // 标记已经遇到小数点
}
// 遇到‘e’或'E'
else if(str[i] == 'e' || str[i] == 'E') {
// ‘e’或'E'前面必须有整数,且前面不能重复出现‘e’或'E'
if(!isNum || ise_or_E) return false;
// 标记已经遇到‘e’或'E'
ise_or_E = true;
// 重置isNum,因为‘e’或'E'之后也必须接上整数,防止出现 123e或者123e+的非法情况
isNum = false;
}
else if(str[i] == '-' ||str[i] == '+') {
// 正负号只可能出现在第一个位置,或者出现在‘e’或'E'的后面一个位置
if(i!=0 && str[i-1] != 'e' && str[i-1] != 'E') return false;
}
// 其它情况均为不合法字符
else{
return false;
}
}
return isNum;
}
直接用正则:
import java.util.regex.Pattern;
public class Solution {
public static boolean isNumeric(char[] str) {
String pattern = "^[-+]?\\d*(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?$";
String s = new String(str);
return Pattern.matches(pattern,s);
}
}
54、字符流中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
用Hashmap记录次数,然后用一个变长字符串(StringBuilder
)记录插入的元素
package M51TO55;
import java.util.HashMap;
public class SoluInsert {
//Insert one char from stringstream
HashMap<Character,Integer> map = new HashMap<>();
StringBuilder str = new StringBuilder();
public void Insert(char ch) {
str.append(ch);
if (map.containsKey(ch)){
map.put(ch,map.get(ch)+1);
}else{
map.put(ch,1);
}
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce(){
for (int i=0;i< str.length();i++){
if (map.get(str.charAt(i))==1){
return str.charAt(i);
}
}
return '#';
}
}
55、链表中环的入口结点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
使用HashSet,依次添加节点,如果节点中出现重复的点,则存在环:
注意:HashSet不能添加重复的元素,当调用add(o)方法时候,会判断o是否已经存在,如果o已经存在则返回false
,不存在则插入返回true
public class EntryNodeOfLoop {
public ListNode EntryNodeOfLoop(ListNode pHead) {
// 想法1:
//a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c)(在慢指针走完一圈前快指针就会追上他,极限情况是循环链表,刚好走完一圈相遇)
//有了 a=c+(n−1)(b+c) 的等量关系,而b+c是环长。我们会发现:
// 从相遇点到入环点的距离再加上n−1 圈的环长,恰好等于从链表头部到入环点的距离。
//当发现 slow 与 fast 相遇时,我们再额外使用一个指针 ptr。
// 0起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。
//想法二:用容器储存每一个节点,当遍历到第一个重复的节点时,这个节点就是重复的、
//这里实现想法一:
if(pHead == null || pHead.next == null){
return null;
}
ListNode fast = pHead;
ListNode slow = pHead;
while(fast!=null&&fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if(fast==slow) break;
}
slow = pHead;
while(fast!=slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
二:
package M51TO55;
import M10TO15.ListNode;
import java.util.HashSet;
public class SoluEntryNodeOfLoop {
public ListNode EntryNodeOfLoop(ListNode pHead){
HashSet<ListNode> set = new HashSet<>();
while (pHead!=null){
if (set.add(pHead)==false){
return pHead;
}else {
pHead=pHead.next;
}
}
return null;
}
}