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()];
}
}