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[i]的值可以看做如图矩阵中每行的乘积,下三角用连称可以很容易求得,上三角从上向下也是连乘,所以我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律把另一部分也乘进去
package com.matajie;
/**
* 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]。
* 不能使用除法。
*
* 我的程序才不会有bug!
* author:年仅18岁的天才少年程序员丶mata杰
**/
public class Multiply {
public int[] multiply(int[] A) {
int length = A.length;
int[] B = new int[length];
if(length != 0){
B[0] = 1;
//计算下三角连乘
for(int i = 1;i<length;i++){
B[i] = B[i-1] *A[i-1];
}
int temp = 1;
//计算上三角连乘
for(int j = length - 2;j >= 0;j--){
temp *= A[j+1];
B[j] *= temp;
}
}
return B;
}
}
52.正则表达式匹配
题目描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
思路:
当模式中第二个字符不是"*"时:
- 如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的.
- 如果字符串第一个字符和模式中的第一个字符不匹配,直接返回false.
而当模式中的第二个字符是"*"时:
如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配.如果字符串第一个字符跟模式第一个字符匹配,可以有三种匹配模式:
- 模式后移2字符,相当于X被忽略(例如aaaba)
- 字符串后移一字符,模式后移2字符(aaaa*a)
- 字符串后移1字符,模式不变,即继续匹配字符下一位,因为可以匹配多位.(例如baab bab,*前可能有多个a)
再观察一下,我们发现情况2可以被情况1和情况3包含,执行一次情况3,在执行一次情况1,就相当于情况2.
package com.matajie;
/**
* 52.正则表达式匹配
* 题目描述
*
* 请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,
* 而'*'表示它前面的字符可以出现任意次(包含0次)。
* 在本题中,匹配是指字符串的所有字符匹配整个模式。
* 例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
*
* 我的程序才不会有bug!
* author:年仅18岁的天才少年程序员丶mata杰
**/
public class Match {
public boolean match(char[] str, char[] pattern){
if(str == null || pattern == null) return false;
int strIndex = 0;
int patterIndex = 0;
return matchCore(str,strIndex,pattern,patterIndex);
}
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种匹配模式
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);//*匹配一个,再匹配str中的下一个.
}else {
return matchCore(str,strIndex,pattern,patternIndex+2);
}
}
//模式第二个不是*,且字符串第一个跟模式第一个匹配,则都后移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;
}
}
53.表示数值的字符串(正则表达式匹配)
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
思路一:
package com.matajie;
/**
* 53.表示数值的字符串(正则表达式匹配)
* 题目描述
*
*
* 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
* 例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。
* 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
*
* 我的程序才不会有bug!
* author:年仅18岁的天才少年程序员丶mata杰
**/
public class IsNumeric {
public boolean isNumeric(char[] str) {
//标记符号,小数点,e是否出现过
boolean sign = false;
boolean decimal = false;
boolean hsaE = false;
for(int i = 0;i<str.length;i++){
if(str[i] == 'e' || str[i] == 'E') {
if(i == str.length-1)return false;//e后面一定要接数字
if(hsaE) return false;//不能同时存在两个e
hsaE = true;
} else if (str[i] == '+' || str[i] == '-') {
//第二次出现+-符号,则必须紧接在e后面
if(sign&&str[i-1]!='e' && str[i-1] != 'E')return false;
//第一次出现+-符号,且不是在字符串开头,则也必须紧接在e之后
if(!sign && i>0 && str[i-1] !='e' &&str[i-1] !='E')return false;
sign =true;
}else if(str[i] == '.'){
//e后面不能接小数点,小数点不能出现两次
if( hsaE || decimal) return false;
decimal = true;
}else if(str[i] < '0' ||str[i] > '9'){
return false;
}
}
return true;
}
}
思路二:正则表达式
package com.matajie;
/**
* 53.表示数值的字符串(正则表达式匹配)
* 题目描述
*
*
* 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
* 例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。
* 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
*
* 我的程序才不会有bug!
* author:年仅18岁的天才少年程序员丶mata杰
**/
public class IsNumeric {
public boolean isNumeric(char[] str) {
String string = String.valueOf(str);
return string.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");
}
}
[\+\-]? : 正或负号出现与否
\d* : 整数部分是否出现,如-3.14或3.14均符合
\d*(\.\d+)? : 如果出现小数点,那么小数点后面必须有数字,否则一起不出现
([eE][\+\-]?\d+)? : 如果存在指数部分,那么e或E肯定出现,+或-可以不出现,紧接着必须跟着整数,或者整个部分都不出现
思路三: 编译原理自动机
54.字符流中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
思路:
使用一个HashMap来统计字符出现的次数,同时用一个ArrayList来记录输入流,每次返回第一个出现一次的字符都是在这个ArrayList(输入流)中的字符作为key去map中查找.
package com.matajie;
import java.util.ArrayList;
import java.util.HashMap;
/**
* 54.字符流中第一个不重复的字符
* 题目描述
*
* 请实现一个函数用来找出字符流中第一个只出现一次的字符。
* 例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。
* 当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
*
* 我的程序才不会有bug!
* author:年仅18岁的天才少年程序员丶mata杰
**/
public class FirstAppearingOnce {
HashMap<Character,Integer> map = new HashMap<>();
ArrayList<Character> list = new ArrayList<>();
public void Insert(char ch)
{
if(map.containsKey(ch)){
map.put(ch,map.get(ch)+1);
}else {
map.put(ch,1);
}
list.add(ch);
}
public char FirstAppearingOnce()
{
char c = '#';
for(char key : list){
if(map.get(key) == 1){
c = key;
break;
}
}
return c;
}
}
55.链表中环的入口节点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路一:
假设X为环前面的路程(绿),a为环入口到相遇点的路程(蓝)c为环的长度(蓝+紫)
当快慢指针相遇时:
此时慢指针走的路程为Sslow = X + m * c + a.
快指针走的路程为Sfast = X + n * c + a.
2Sslow = Sfast.
2*(X + mc +a) = (X + nc + a)
=>X = (n-2*m)c-a = (n-2m-1)c+c-a(因为m = 0,n>0,慢指针是不可能绕圈的,走不完一圈就会被追上,所以n-2m-1 >= 0).
即环前面的路程= 数个环的长度 + c-a
c-a是什么?
这是相遇点后,换后面部分的路程(紫),所以我们可以让一个指针从起点A开始走,让一个指针从相遇点B开始继续往后走,2个指针速度相同,那么当从原点的指针走到环入口点时(此时当好走了X),从相遇点开始走的那个指针也一定刚好到达环入口点,所以2者会相遇,且恰好相遇在环的入口点.
由上述证明可这样解:
package com.matajie;
/**
* 55.链表中环的入口节点
* 题目描述
*
* 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
*
* 我的程序才不会有bug!
* author:年仅18岁的天才少年程序员丶mata杰
**/
public class EntryNodeOfLoop {
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null || pHead.next == null || pHead.next.next == null)
return null;
ListNode fast = pHead.next.next;
ListNode slow = pHead.next;
//先判断有没有环
while (fast != slow){
if(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
}else {
return null;
}
}
//循环出来说明有环,此时fast == slow
fast = pHead;
while (fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
思路二:
set不能有重复元素(比较的是ListNode这个指针是否指向同一个对象,地向的地址是否相同,和所指对象无关)
package com.matajie;
import java.util.HashSet;
/**
* 55.链表中环的入口节点
* 题目描述
*
* 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
*
* 我的程序才不会有bug!
* author:年仅18岁的天才少年程序员丶mata杰
**/
public class EntryNodeOfLoop {
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
public ListNode EntryNodeOfLoop(ListNode pHead)
{
HashSet<ListNode> set = new HashSet<>();
while (pHead != null){
if(!set.add(pHead)){
return pHead;
}
pHead = pHead.next;
}
return null;
}
}