LeetCode 热题100 -29、41、44

94 阅读4分钟

29. 两数相除

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。 返回被除数 dividend 除以除数 divisor 得到的商。 整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2

输入: dividend = 10, divisor = 3 输出: 3

注意🚀:

  • 不使用乘法、除法和 mod 运算符
  • 除数不为 0。
  • 被除数和除数均为 32 位有符号整数。
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1
class Solution {
    
    //乘法是除法的逆运算
    private  boolean quickMul(int num1,int num2,int com){
        int res = 0;    
        while(num2 != 0){
            if((num2 & 1) == 1){
                //判断除数与商相乘结果值与被除数的大小关系
                if(res < com - num1) {
                    return false;
                }
                res += num1;
            }
            //
            if(num2 != 1){
                if(num1 < com - num1) {
                    return false;
                }
                //num1*2
                num1 = num1 + num1;
            }
            //右移一位
            num2 >>= 1;
        }
        return true;
    }
public  int divide(int dividend, int divisor) {
        //除数不为 0
        if(divisor == 0)
        {     
            return 0;
        }
        //判断是否溢出start 
        if(dividend == Integer.MIN_VALUE){
            if(divisor == 1) {
                return Integer.MIN_VALUE;
            }
            if(divisor == -1){   
                return Integer.MAX_VALUE;
            }
        }
        if (divisor == Integer.MIN_VALUE) {
            return dividend == Integer.MIN_VALUE ? 1 : 0;
        }
        if (dividend == Integer.MAX_VALUE && divisor == 1){
            return dividend;
        }
    	//判断是否溢出 end
    
        //记录符号是否相同
        boolean rev = (dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0);
	
        //都变成负数,为了扩大范围->32位整型能表示的负数要比正数多1
        dividend = -Math.abs(dividend);
        divisor = -Math.abs(divisor);
        //二分
        int lef = 1,rig = Integer.MAX_VALUE;
        int ans = 0;
        while(lef <= rig){
            
            int mid = lef + ((rig - lef) >> 1);
        	//通过除数乘以商小于等于被除数关系推出目标数值
            if(quickMul(divisor, mid, dividend)){
                //相乘大于被除数
                ans = mid;
                lef = mid + 1;
            }
            else{
                //相乘小于被除数     
                rig = mid - 1;
            }
        }
        //确定正负符号
        return rev ? -1 * ans :  ans;
    }
 }

41. 缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

输入:nums = [1,2,0] 输出:3 输入:nums = [3,4,-1,1] 输出:2

注意🚀:

  • 只使用常数级别额外空间(不能使用哈希表)
class Solution {
    public int firstMissingPositive(int[] nums) {
        int n = nums.length;
        // 1. 遍历数组,将所有小于等于 0 的数修改为 n + 1->假定1-n中每个整数都出现过一次每个固定值,
        //则答案是n+1
        for (int i = 0; i < n; i++) {
            if (nums[i] <= 0) {
                nums[i] = n + 1;
            }
        }
        // 2. 遍历数组,将所有在 [1, n] 范围内的数对应的索引位置的数修改为负数
        for (int i = 0; i < n; i++) {
            int num = Math.abs(nums[i]);
            if (num <= n) {
                nums[num - 1] = -Math.abs(nums[num - 1]);
            }
        }
        // 3. 遍历数组,找到第一个大于 0 的数,返回它的索引 + 1
        for (int i = 0; i < n; i++) {
            if (nums[i] > 0) {
                return i + 1;
            }
        }
        // 4. 如果数2组中的数都在 [1, n] 范围内,那么返回 n + 1
        return n + 1;
    }
}

44. 通配符匹配

给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。

输入: s = "aa" p = "a" 输出: false

解释: "a" 无法匹配 "aa" 整个字符串。 输入: s = "aa" p = "" 输出: true 解释: '' 可以匹配任意字符串。

输入: s = "cb" p = "?a" 输出: false 解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。

输入: s = "adceb" p = "ab" 输出: true 解释: 第一个 '' 可以匹配空字符串, 第二个 '' 可以匹配字符串 "dce". 输入: s = "acdcb" p = "a*c?b" 输出: false

注意🚀:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
  • *可以匹配任意字符串,可以为空,也就是匹配或任意多个小写字母。
class Solution{

public boolean isMatch(String s, String p) {
        //二维boolean数组 i,j 代表s的前i个字符和p的前j个字符是否匹配
        boolean[][] f = new boolean[s.length() + 1][p.length() + 1];
        //空串情况
        f[0][0] = true;
        //初始化第一行
        for(int i = 1;i <= p.length();i++){

            if(p.charAt(i-1) == '*') {
                //如果p的第i个字符是*,那么f[0][i] = true,方便后面或运算
                f[0][i] = true;
            } else {
                break;
            }
        }
        //动态规划
        for(int i = 1;i <= s.length();i++) {
            for(int j = 1;j <= p.length();j++){
                if(p.charAt(j - 1) == '*') {
                    //如果 p[i - 1] == '*',这个位置可以匹配 0 到 若干个字符。
                    // 那么 dp[i][j]可以从 dp[i - 1][j] 转移而来(表示当前星号没有匹配字符),
                    // 也可以从 dp[i][j - 1] 转移而来(表示当前星号匹配了当前的位置的字符)。
                    f[i][j] = f[i][j - 1] | f[i - 1][j];

                } else
                    //两个字符相同或者是p.charAt(j)为?,则能够匹配
                    if(p.charAt(j - 1) == s.charAt(i - 1) || p.charAt(j - 1) == '?') {
                    f[i][j] = f[i - 1][j - 1];
                }
            }
        }
        return f[s.length()][p.length()];
    }
    }