1. 字符串数字相加
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
解法一
因为是字符串数字,如果字符串太长,可能直接导致int或long溢出。因此需要用竖式计算,用数组计算每位相乘得到的结果,可以先采用累加的方式,最后再统一进位计算。
例如:789 * 6 = 【42,48,54】,最后再统一进位,【4,7,3,4】
class Solution {
public String multiply(String num1, String num2) {
int len1 = num1.length();
int len2 = num2.length();
if(num1.equals("0") || num2.equals("0")) return "0";
int[] result = new int[len1+len2];
//每个槽中进行累加
for(int i = len1-1; i >= 0; i-- ){
for(int j = len2-1; j >= 0 ; j--){
result[j+i+1] += (num1.charAt(i) - '0')*(num2.charAt(j) - '0');//注意j+i+1才是index
}
}
//进行进位计算
int count = 0;
for(int i = len1+len2-1; i >= 0; i--){
if(result[i] + count >= 10){
result[i] += count;//进位
count = result[i] / 10;
result[i] = result[i] % 10;
}else{
result[i] += count;
count = 0;
}
}
//转换成字符串
StringBuilder sb = new StringBuilder();
boolean flag = false;
for(int i = 0; i < len1+len2; i++){
if( !flag && result[i] > 0){
flag = true;
}
if(flag){
sb.append(result[i]);
}
}
return sb.toString();
}
}
2. Z字形变换
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
解法一
维持一个StringBuilder数组,遍历字符串,首先从上到下依次放入字符到StringBuilder中,当字符串索引到底时(numRows)则开始反向,从下到上放置字符到StringBuilder的尾部,如此反复即可。
abcdefghi 3
a e i
b d f h
c g
class Solution {
public String convert(String s, int numRows) {
if(numRows == 1) return s;
StringBuilder[] sarr = new StringBuilder[numRows];
for(int i = 0; i < numRows ; i++){
sarr[i] = new StringBuilder();
}
int index = 0;
boolean direct = false;
int len = s.length();
for(int i = 0; i < len; i++){
sarr[index].append(s.charAt(i));
if(!direct){//反向
if(index == numRows - 1){
direct = true;
index = numRows - 2;
}else{
index++;
}
}else{//反向
if(index == 0){
direct = false;
index = 1;
}else{
index--;
}
}
}
StringBuilder result = new StringBuilder();
for(StringBuilder sb : sarr){
result.append(sb);
}
return result.toString();
}
}
3. 整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
解法一
利用取余和除法,依次取出最后一位数,然后与新数相加,每次新数都要乘以10。注意溢出问题。
两边都除以10,即可得到新的判断式,且保证中间值不溢出32位数范围
123
3
310 + 2
(310+2)* 10 + 1 = 321
class Solution {
public int reverse(int x) {
int tmp = 0;
int result = 0;
while(x != 0){
if(result < Integer.MIN_VALUE / 10 || result > Integer.MAX_VALUE/10) return 0;
tmp = x % 10;
x = x / 10;
result = result*10 + tmp;
}
return result;
}
}
4. 字符串转换整数 (atoi)
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数。
函数 myAtoi(string s) 的算法如下:
空格:读入字符串并丢弃无用的前导空格(" ")
符号:检查下一个字符(假设还未到字符末尾)为 ‘-’ 还是 ‘+’。如果两者都不存在,则假定结果为正。
转换:通过跳过前置零来读取该整数,直到遇到非数字字符或到达字符串的结尾。如果没有读取数字,则结果为0。
舍入:如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被舍入为 −231 ,大于 231 − 1 的整数应该被舍入为 231 − 1 。
返回整数作为最终结果。
解题一
这题最主要是要读懂题意:遇到空格就跳过,遇到数字就开始计数,遇到-+就开始计数,如果空格-+排除后计数前遇到其他字符,则返回0;否则直接开始计数。计数时需要考虑之前的正负号。如果计数后遇到数字之外的其他字符,则直接返回结果。如果数字超过32位有符号整数的范围,则返回对应的边界值。
class Solution {
public int myAtoi(String s) {
if(s.equals("")) return 0;
//首先去除空格
int index = 0;
while(index < s.length() && s.charAt(index) == ' ') index++;
if(index == s.length()) return 0;
int sign = 1;//正负标志位
if(s.charAt(index) == '-'){
sign = -1;
index++;
}else if(Character.isLetter(s.charAt(index))){
return 0;
}else if(s.charAt(index) == '+'){
index++;
}
long result = 0;
for(int i = index ; i < s.length(); i++){
if(!Character.isDigit(s.charAt(i))){
return (int)result;
}
result = result*10 + sign*(s.charAt(i) - '0');
if(result > Integer.MAX_VALUE ) return Integer.MAX_VALUE;
if(result < Integer.MIN_VALUE) return Integer.MIN_VALUE;
}
return (int)result;
}
}
5. 数字 1 的个数
给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
解法一
如果列举出所有小于等于n的数,求和1的数量时间复杂度太高。需要通过找规律,从低位遍历到高位,计算每一位上会出现1的次数:
下列规律本质上是将数字分为高位低位。计算某一位上出现1的次数时,其实是计算如果当前位为1,其他位有多少种变化。
- 当当前位大于1时,高位可以从0~高位数字,共高位+1种变化,低位上由于当前位大于1,说明之前等于1的情况下,所有低位的可能情况都已经被包括,于是就是0-9 0-9 0-9,也就是10的n-1次方种变化。高位与低位变化相乘则为总共可能的情况
- 当当前位等于1时,高位可以从0-高位-1,之所以减一是因为低位可能并不满足总是0-9 0-9 0-9。。。均可以(因为还没有成功进位,低位有一些情况还不能满足)。于是,比高位小一的数字则肯定能保证满足0-9 0-9 0-9。。。于是总共高位*10的低位-1次方。此外,还需要加上当高位数字不变时,剩下低位上剩余的数+1种变化。
- 当高位小于1,说明当前高位不变的情况下,肯定不存在当前位为1的情况。直接高位*10的低位-1次方。
class Solution {
public int countDigitOne(int n) {
int index = 1;
int top = n;
int low = 0;
int cur = 1;
int result = 0;
while(top > 0){
top = (int) (n / Math.pow(10,index));
low = (int) (n % Math.pow(10,index-1));
cur = (int) (n / Math.pow(10,index-1) % 10);
if(cur > 1){
result += (top + 1)*Math.pow(10,index-1);
}else if( cur == 1){
result += (top)*Math.pow(10,index-1) + low + 1;
}else{
result += (top)*Math.pow(10,index-1);
}
index++;
}
return result;
}
}
6. 两数相除
给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。
整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。
返回被除数 dividend 除以除数 divisor 得到的 商 。
注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−2^31, 2^31 − 1] 。本题中,如果商 严格大于 2^31 − 1 ,则返回 2^31 − 1 ;如果商 严格小于 -2^31 ,则返回 -2^31 。
解法一
类似于之前用快速幂求幂次方,采用二分法,这里是加法,2+2,4+4,8+8,16+16…,这样可以避免除数一个一个累加导致超时。递归地方式如下:需要注意,-231-1如果除以-1会变成231,32位数存不下,需要直接返回int最大值。被除数为0,直接返回0即可。
上述对半相加的方式可能会导致溢出,因此可以将判断x+x是否大于目标值改为,目标值减x是否小于x。此外,有可能
class Solution {
public int divide(int dividend, int divisor) {
if(dividend == Integer.MIN_VALUE && divisor == 1) return Integer.MIN_VALUE;
if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE;
// 考虑除数为最小值的情况
if (divisor == Integer.MIN_VALUE) {
return dividend == Integer.MIN_VALUE ? 1 : 0;
}
if(dividend == 0) return 0;
boolean flag = false;
//被除数和除数全部转换为负数进行计算,如果全部转换为正数可能正数最大值存不下
//之所有进行统一符号转换是为了方便后面采用同样的计算方法,两个数同号只用考虑加即可
if(dividend > 0){
dividend = -dividend;
flag = !flag;
}
if(divisor > 0){
divisor = -divisor;
flag = !flag;
}
return flag ? -newdivide(dividend,divisor) : newdivide(dividend,divisor);
}
public int newdivide(int dividend,int divisor){
if(dividend > divisor) return 0;//重要,不能只凭dividend大于0判断就返回0,因为可能存在dividen和divisor都是负数,但dividend已经不够除了,下面count就又会多加一个1.
int tmp = divisor;
int count = 1;
while(divisor >= dividend - divisor){
count += count;//成倍数增加
divisor += divisor;
}
return count + newdivide(dividend - divisor,tmp);
}
}