[Java]剑指offer51-55题_day11

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"均不匹配

思路:

当模式中第二个字符不是"*"时:

  1. 如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的.
  2. 如果字符串第一个字符和模式中的第一个字符不匹配,直接返回false.

而当模式中的第二个字符是"*"时:
如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配.如果字符串第一个字符跟模式第一个字符匹配,可以有三种匹配模式:

  1. 模式后移2字符,相当于X被忽略(例如aaaba)
  2. 字符串后移一字符,模式后移2字符(aaaa*a)
  3. 字符串后移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;
         }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值