66. 加一(简单)
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入:digits = [1,2,3] 输出:[1,2,4] 解释:输入数组表示数字 123。示例 2:
输入:digits = [4,3,2,1] 输出:[4,3,2,2] 解释:输入数组表示数字 4321。示例 3:
输入:digits = [0] 输出:[1]提示:
1 <= digits.length <= 100
0 <= digits[i] <= 9
解法一、模拟逐位
首先一百位数组上限,肯定不能转int或者long(转了再转回数组工作量也挺大的。。)
class Solution {
public int[] plusOne(int[] digits) {
boolean isNine = false;
if(digits[0] == 9)isNine = true;//如果最高位是9
int[] res = new int[digits.length + 1];//提前备好新的数组
int count = 0;
if(digits[digits.length-1] + 1 < 10){//最后一位提前处理
digits[digits.length - 1] += 1;
return digits;
}
for(int i = digits.length - 1;i >= 0;i--){
if(count == 1 && digits[i] + 1 < 10){//如果上一位有进位且可以马上处理
digits[i] += 1;
return digits;
}else{//有进位
digits[i] = 0;
count = 1;
if(i == 0){
res[0] = 1;
return res;
}
}
if(isNine)res[i+1] = digits[I];//新的数组存存
}
return digits;
}
}
解法二、解法一优化
不是,哥们jpg你这样显得我很蠢
不进直接return。一旦进入下个for,那么一定是进位,省略了count判断。
只要不是连串9,中途一定可以成功return。如果是连串9,只需要一个空数组+第一位是1,如{9,9,9}进位是{1,0,0,0}
class Solution {
public int[] plusOne(int[] digits) {
for (int i = digits.length - 1; i >= 0; i--) {
digits[i]++;
digits[i] = digits[i] % 10;
if (digits[i] != 0) return digits;
}
digits = new int[digits.length + 1];
digits[0] = 1;
return digits;
}
}
作者:YHHZW
链接:https://leetcode.cn/problems/plus-one/solutions/4481/java-shu-xue-jie-ti-by-yhhzw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解法三、找到最长的后缀9
如{1,2,3,9,9}第一个不是9的数字是3,3加1,后面全部置0
如果循环结束还没return,那如下
class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;
for (int i = n - 1; i >= 0; --i) {
if (digits[i] != 9) {
++digits[i];
for (int j = i + 1; j < n; ++j) {
digits[j] = 0;
}
return digits;
}
}
// digits 中所有的元素均为 9
int[] ans = new int[n + 1];
ans[0] = 1;
return ans;
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/plus-one/solutions/1057162/jia-yi-by-leetcode-solution-2hor/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
67. 二进制求和(简单)
给你两个二进制字符串
a
和b
,以二进制字符串的形式返回它们的和。示例 1:
输入:a = "11", b = "1" 输出:"100"示例 2:
输入:a = "1010", b = "1011" 输出:"10101"提示:
1 <= a.length, b.length <= 104
a
和b
仅由字符'0'
或'1'
组成- 字符串如果不是
"0"
,就不含前导零
解法一、 模拟遍历
遍历最长的那个字符串,然后逐位加减。好繁琐,下标纠结了半天,把一道简单题做得挺复杂的。。是真不怎么长考虑细节的那部分脑子
class Solution {
public static String addBinary(String a, String b) {
StringBuffer sb = new StringBuffer();
int count = 0;
int lenA = a.length();
int lenB = b.length();
int lenMax = Math.max(lenA,lenB);
for(int i = 1;i <= lenMax;i++){
int A = 0;
int B = 0;
if(i <= lenA)A = a.charAt(lenA - i)-'0';
if(i <= lenB)B = b.charAt(lenB - i)-'0';
sb.insert(0,(A + B + count) % 2);
count = (A + B + count)/2;
}
if(count == 1)sb.insert(0,count);
return sb.toString();
}
}
解法二、解法一优化
其实和我的类似,但是用了三目,省略了两个变量新建与赋值。注意这里的append后面的处理是多余的,sb.append()本身是带int的处理的
class Solution {
public String addBinary(String a, String b) {
StringBuffer ans = new StringBuffer();
int n = Math.max(a.length(), b.length()), carry = 0;
for (int i = 0; i < n; ++i) {
carry += i < a.length() ? (a.charAt(a.length() - 1 - i) - '0') : 0;
carry += i < b.length() ? (b.charAt(b.length() - 1 - i) - '0') : 0;
ans.append((char) (carry % 2 + '0'));
carry /= 2;
}
if (carry > 0) {
ans.append('1');
}
ans.reverse();
return ans.toString();
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/add-binary/solutions/299667/er-jin-zhi-qiu-he-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
415. 字符串相加(简单)
给定两个字符串形式的非负整数
num1
和num2
,计算它们的和并同样以字符串形式返回。你不能使用任何內建的用于处理大整数的库(比如
BigInteger
), 也不能直接将输入的字符串转换为整数形式。示例 1:
输入:num1 = "11", num2 = "123" 输出:"134"示例 2:
输入:num1 = "456", num2 = "77" 输出:"533"示例 3:
输入:num1 = "0", num2 = "0" 输出:"0"提示:
1 <= num1.length, num2.length <= 104
num1
和num2
都只包含数字0-9
num1
和num2
都不包含任何前导零
解法一、模拟遍历
乌乌 学聪明惹(指把前面几道题的优化部分搬过来优化了,但时间复杂度还是惨不忍睹(
public String addStrings(String num1, String num2) {
StringBuffer sb = new StringBuffer();
int carry = 0;
int t = 0;
int lenA = num1.length();
int lenB = num2.length();
int lenMax = Math.max(lenA,lenB);
for(int i = 1;i <= lenMax;i++){
t = carry;
t += i<= lenA? num1.charAt(lenA-i)-'0':0;
t += i<= lenB? num2.charAt(lenB-i)-'0':0;
sb.insert(0,t % 10);
carry = t >= 10 ? 1:0;
}
if(carry!=0)sb.insert(0,carry);
return sb.toString();
}
解法二、解法一优化
双指针遍历,for改while(确实判断>=0比写一堆方便,但我不太习惯while)
顺便一提这是1ms答案,而2ms答案是把||carry==1放在while里,相当于每次判断多判断一个。。确实只是最后一次需要多判断的事,真是一个细节不注意就会拖沓啊。
class Solution {
public String addStrings(String num1, String num2) {
int i=num1.length()-1;
int j=num2.length()-1;
int addSum;
int carry=0;
StringBuilder res=new StringBuilder();
while(i>=0||j>=0){
int n1=i<0?0:num1.charAt(i--)-'0';
int n2=j<0?0:num2.charAt(j--)-'0';
addSum=n1+n2+carry;//!!记得加上carry
res.append(addSum%10);
carry=addSum/10;
}
if(carry==1){
res.append(1);//!!!!!!如果最后还有进位,高位补1
}
return res.reverse().toString();
}
}
43. 字符串相乘(中等)
给定两个以字符串形式表示的非负整数
num1
和num2
,返回num1
和num2
的乘积,它们的乘积也表示为字符串形式。注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
示例 1:
输入: num1 = "2", num2 = "3" 输出: "6"示例 2:
输入: num1 = "123", num2 = "456" 输出: "56088"提示:
1 <= num1.length, num2.length <= 200
num1
和num2
只能由数字组成。num1
和num2
都不包含任何前导零,除了数字0本身。
解法一、按顺序相乘模拟
num1作为基准,num2作为拆分,从头字符一直乘到尾字符然后相加,每加一次左移一位。计组学过乘法器,但是没好好学。。
class Solution {
public static String multiply(String num1, String num2) {
String res = "";
if(num1.equals("0") || num2.equals("0"))return "0";
for(int i = 0;i < num2.length();i++){
String a = multy(num1,num2.charAt(i));
res = addStrings(res,a);
if(i!=num2.length()-1)res = res + "0";
}
return res;
}
public static String multy(String num1,char c){
int carry = 0;
StringBuffer sb = new StringBuffer();
for(int i = num1.length()-1;i>=0;i--){
int t = (num1.charAt(i)-'0') * (c -'0') + carry;
sb.insert(0,t%10);
carry = t / 10;
}
if(carry != 0)sb.insert(0,carry);
return sb.toString();
}
public static String addStrings(String num1, String num2) {
int i=num1.length()-1;
int j=num2.length()-1;
int addSum;
int carry=0;
StringBuilder res=new StringBuilder();
while(i>=0||j>=0){
int n1=i<0?0:num1.charAt(i--)-'0';
int n2=j<0?0:num2.charAt(j--)-'0';
addSum=n1+n2+carry;//!!记得加上carry
res.append(addSum%10);
carry=addSum/10;
}
if(carry==1){
res.append(1);//!!!!!!如果最后还有进位,高位补1
}
return res.reverse().toString();
}
}
解法二、数组存储,按位相乘
对于len1,从最右侧开始往左遍历,拆解数字。内扣循环len2,从右往左拆解,存入ansArr[i+j+1]位,这里下标是人脑提前预算好的。然后对于ansArr进行处理,把进位塞进去。
对于m长度的num1和n长度的num2相乘,最后的结果可能是m+n或者m+n+1。因此最后开始处理时,需要判断,去掉前导0。
这是1ms的击败99%的做法。
class Solution {
public String multiply(String num1, String num2) {
if(num1.equals("0")||num2.equals("0")){
return "0";
}
int m=num1.length(),n=num2.length();
int[] ansArr=new int[m+n];
for(int i=m-1;i>=0;i--){
int x=num1.charAt(i)-'0';
for(int j=n-1;j>=0;j--){
int y=num2.charAt(j)-'0';
ansArr[i+j+1]+=x*y;//先乘后变
}
}
for(int i=m+n-1;i>0;i--){
ansArr[i-1]+=ansArr[i]/10;
ansArr[i]%=10;
}
int index=ansArr[0]==0?1:0;
StringBuilder ans=new StringBuilder();
while(m+n>index){
ans.append(ansArr[index]);//为1则从1开始
index++;
}
return ans.toString();
}
}
306. 累加数(中等)☆
累加数 是一个字符串,组成它的数字可以形成累加序列。
一个有效的 累加序列 必须 至少 包含 3 个数。除了最开始的两个数以外,序列中的每个后续数字必须是它之前两个数字之和。
给你一个只包含数字
'0'-'9'
的字符串,编写一个算法来判断给定输入是否是 累加数 。如果是,返回true
;否则,返回false
。说明:累加序列里的数,除数字 0 之外,不会 以 0 开头,所以不会出现
1, 2, 03
或者1, 02, 3
的情况。示例 1:
输入:"112358"
输出:true 解释:累加序列为:1, 1, 2, 3, 5, 8
。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8示例 2:
输入:
"199100199"
输出:true 解释:累加序列为:1, 99, 100, 199。
1 + 99 = 100, 99 + 100 = 199提示:
1 <= num.length <= 35
num
仅由数字(0
-9
)组成进阶:你计划如何处理由过大的整数输入导致的溢出?
解法一、DFS
其实没法做,用例到一半的时候数字溢出···就是递归然后不断切分,切出一个前缀,往后分割
public class Solution {
public boolean isAdditiveNumber(String num) {
return dfs(num, new ArrayList<>());
}
private boolean dfs(String num, List<Integer> path) {
// 如果路径长度 >= 3,且当前数字不等于路径中倒数第二个和倒数第三个数字之和,则返回 false
if (path.size() >= 3 && path.get(path.size() - 1) != path.get(path.size() - 2) + path.get(path.size() - 3)) {
return false;
}
// 如果路径长度 >= 3,并且没有剩余的字符串,返回 true
if (path.size() >= 3 && num.isEmpty()) {
return true;
}
// 遍历所有可能的前缀
for (int i = 1; i <= num.length(); i++) {
String curr = num.substring(0, i);
// 如果前缀长度大于 1 且以 0 开头,跳过
if (curr.length() > 1 && curr.charAt(0) == '0') {
continue;
}
// 将当前前缀转换为整数,并递归处理剩余字符串
int currNum = Integer.parseInt(curr);
if (dfs(num.substring(i), new ArrayList<>(path) {{add(currNum);}})) {
return true;
}
}
return false;
}
}
解法二、也是分裂(双百解法)
第一个函数负责分裂,边界情况讨论。i是第二个数最后一个下标,j是分裂的位置,j+1是第二个数的首下标。backtrack是一个递归函数,num是原字符串,ij意义同上,idx是第三个数首下标。
isAdditiveNumber
方法
外层循环(
for (int i = 1; i < num.length(); i++)
):
i
是第二个数的最后一个下标。- 循环控制第二个数的结束位置。
内层循环(
for (int j = 0; j < i; j++)
):
j
是第一个数的最后一个下标。- 用于计算第一个数
k
的值。计算第一个数(
k = k * 10 + (num.charAt(j) - '0')
):
- 将
num
的前j
位字符转换为第一个数k
。处理前导零:
- 如果第一个数的长度大于1并且以0开头,则直接跳出内层循环。
- 如果第二个数的长度大于1并且以0开头,则跳过此次分裂。
计算第二个数(
for (int m = j + 1; m <= i; m++)
):
- 将
num
从j+1
位到i
位的字符转换为第二个数l
。递归检查(
if (backtrack(num, k, l, i + 1)) return true
):
- 调用
backtrack
方法检查从i + 1
位置开始是否能形成累加数列。
backtrack
方法
计算累加数:
- 将从
idx
开始的字符转换为一个数n
,并与前两个数i
和j
的和进行比较。检查条件:
- 如果
i + j == n
,且已经到达字符串末尾或递归调用返回true
,则返回true
。- 如果
num
从idx
位置开始以0
开头或n
大于或等于i + j
,则跳出循环。递归调用:
- 如果
i + j == n
,则递归调用backtrack
方法,更新前两个数为j
和n
,并从k + 1
位置继续检查。
class Solution {
public boolean isAdditiveNumber(String num) {
for (int i = 1; i < num.length(); i++) {
long k = 0;
for (int j = 0; j < i; j++) { // k, l 分别代表两加数,j为分裂的位置
k = k * 10 + (num.charAt(j) - '0'); // 计算第一个数的值
long l = 0;
if (j > 0 && num.charAt(0) == '0') break; // 如果第一个数开头是0并且此时长度大于1,则直接结束循环
if (i - j - 1 > 0 && num.charAt(j + 1) == '0') continue; // 如果第二个数开头是0且长度大于1,则跳过此次分裂
for (int m = j + 1; m <= i; m++) l = l * 10 + (num.charAt(m) - '0'); // 计算第二个数的值
if (backtrack(num, k, l, i + 1)) return true; // 如果组成累加数,返回true
}
}
return false;
}
public boolean backtrack(String num, long i, long j, int idx) {
long n = 0;
for (int k = idx; k < num.length(); k++) {
n = n * 10 + (num.charAt(k) - '0'); // 计算两数可能的和
if (i + j == n) {
if (k == num.length() - 1 || backtrack(num, j, n, k + 1)) return true;
}
if (num.charAt(idx) == '0' || n >= i + j) break; // 如果第一个位置上为0或此时和大于两数之和,则退出循环,不用再继续累加和
}
return false;
}
}
解法三、官方题解,通过1/2推后面,判断和原字符串是否相等(其实是暴力
第一个函数:负责分割出第一个、第二个数字,其中包含先导零则零。第二个函数:负责验证是否正确,根据一和二来创造一整个字符串,和原字符串比对。第三个函数:字符串加法,处理大型整数溢出。
class Solution {
public boolean isAdditiveNumber(String num) {
int n = num.length();
for (int secondStart = 1; secondStart < n - 1; ++secondStart) {
if (num.charAt(0) == '0' && secondStart != 1) {
break;
}
for (int secondEnd = secondStart; secondEnd < n - 1; ++secondEnd) {
if (num.charAt(secondStart) == '0' && secondStart != secondEnd) {
break;
}
if (valid(secondStart, secondEnd, num)) {
return true;
}
}
}
return false;
}
public boolean valid(int secondStart, int secondEnd, String num) {
int n = num.length();
int firstStart = 0, firstEnd = secondStart - 1;
while (secondEnd <= n - 1) {
String third = stringAdd(num, firstStart, firstEnd, secondStart, secondEnd);
int thirdStart = secondEnd + 1;
int thirdEnd = secondEnd + third.length();
if (thirdEnd >= n || !num.substring(thirdStart, thirdEnd + 1).equals(third)) {
break;
}
if (thirdEnd == n - 1) {
return true;
}
firstStart = secondStart;
firstEnd = secondEnd;
secondStart = thirdStart;
secondEnd = thirdEnd;
}
return false;
}
public String stringAdd(String s, int firstStart, int firstEnd, int secondStart, int secondEnd) {
StringBuffer third = new StringBuffer();
int carry = 0, cur = 0;
while (firstEnd >= firstStart || secondEnd >= secondStart || carry != 0) {
cur = carry;
if (firstEnd >= firstStart) {
cur += s.charAt(firstEnd) - '0';
--firstEnd;
}
if (secondEnd >= secondStart) {
cur += s.charAt(secondEnd) - '0';
--secondEnd;
}
carry = cur / 10;
cur %= 10;
third.append((char) (cur + '0'));
}
third.reverse();
return third.toString();
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/additive-number/solutions/1200446/lei-jia-shu-by-leetcode-solution-cadc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
碎碎念:
- 实现了加法器,造了几个小破轮子。字符串加加加乘乘乘,略有复杂。。三目和边界判断很好玩
- 66、67、415难度逐步递加。造成了43一个解法的基础。306想不到暴力的话是真有点难。415其实是后面两道题的一个基础。
- 累加数的DFS解法也是真的挺神奇的,除了大数溢出了