常考算法面试题:字符串(大数)相加、字符串(大数)相减、字符串(大数)相乘
笔者今年秋招,最近投了一些大厂的实习,某为的手撕代码题目为 字符串相乘,某讯的手撕代码题目为字符串相减。
于是去LeetCode和网上搜索了一下,发现这类题目是面试的常考题。只要会四则运算这道题的思路就很清晰,主要字符串的处理,难点也在于字符串处理。 以下代码本人均在GCC编译器下运行过,没有问题。自己写上主函数、输入、调用算法函数即可编译运行验证,或者去leetCode找到原题直接粘贴上去。
1、字符串相加
LeetCode上的字符串相加题目如下所示:
LeetCode原题链接
解法思路:
定义两个指针 i和 j分别指向num1和 num 2的末尾,即最低位,同时定义一个变量add 维护当前是否有进位,然后从末尾到开头逐位相加即可。你可能会想两个数字位数不同怎么处理,这里我们统一在指针当前下标处于负数的时候返回 0,等价于对位数较短的数字进行了补零操作,这样就可以除去两个数字位数不同情况的处理。
Cpp代码如下:
string addStrings(string num1, string num2)
{
int i=num1.length()-1;
int j=num2.length()-1;
int add = 0; // 保存进位的值
string ans = ""; // 保存结果的字符串
while(i>=0 || j>=0 || add != 0)
{
int x = i>=0 ? num1[i]-'0':0; // 指针当前下标为非负数的时候返回当前整数num[i]-'0'(此处减‘0’因为字符要转换为整数),处于负数的时候返回 0
int y = j>=0 ? num2[j]-'0':0;
int result = x + y + add;
ans.push_back('0'+result%10); // 将每一位的计算结果压入字符串ans,因为压入的是result的个位 字符数字,所以压入的是 '0'+result%10
add = result/10; // 计算当前进位的值
i-=1;
j-=1;
}
reverse(ans.begin(), ans.end()); // 将压入ans中的字符串反转,即为正确结果
return ans;
}
2、字符串相减
该题LeetCode上没有找到对应的题目
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的差。
提示:
num1 和num2 的长度都小于 5100
num1 和num2 都只包含数字 0-9
num1 和num2 都不包含任何前导零
你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式
解法思路:
字符串相减解法的思路与字符串加法的思路类似,不同点在于加法有进位,减法需要向高位借位,借位可以通过if来判断,然后通过索引将高一位减1。
还需要注意消除结果的前导零。比如 123-120 = 003 就不是正确答案。
Cpp代码如下:
string subStrings(string num1, string num2)
{
int i = num1.length()-1;
int j = num2.length()-1;
string res = "";
while(i>=0 || j>=0)
{
int x=i >= 0 ? num1[i]-'0': 0;
int y=j >= 0 ? num2[j]-'0': 0;
int result = 0; // 保存每一位的运算结果
// 判断被减数某位的值与减数某位的值的大小,如果小于则借位,大于则直接相减
if(x < y)
{
result = x+10-y;
num1[i-1] -= 1;
}
else
{
result = x-y;
}
res.push_back('0'+result); // 将运算结果转换为字符压入 字符串res
i-=1;
j-=1;
}
// 删除前导的 0
int k = res.length()-1;
while(res[k] == '0' && k > 0) // 判断 字符串的末尾(不能超出k范围)是否为 '0', 为 '0'则压出
{
res.pop_back();
k--;
}
// 反转字符串 res, 即为要求的字符串的差
reverse(res.begin(), res.end());
return res;
}
3、字符串乘法
LeetCode上的字符串相乘题目如下所示:
LeetCode原题链接
解题思路1:
如果num1 和 num2之一是 0,则直接将 0 作为结果返回即可。
如果num1 和 num2都不是0,则可以通过模拟「竖式乘法」的方法计算乘积。从右往左遍历乘数,将乘数的每一位与被乘数相乘得到对应的结果,再将每次得到的结果累加(利用上面的字符串相加的函数即可)。这道题中,被乘数是 num1,乘数是 num2。
需要注意的是,num2除了最低位以外,其余的每一位的运算结果都需要补 0,第 i位即补 n-i个 0。
Cpp代码如下:
string multiply(string& num1, string& num2)
{
// 如果 num1或 num2为0,则返回结果 0
if(num1 == "0" || num2 == "0")
return "0";
string ans = "0";
int m=num1.size()-1;
int n =num2.size()-1;
for(int i=n; i>=0; i--)
{
string curr;
int add = 0;
// 乘数为第n-i位(按字符串索引的顺序),则再栈底添加 i个0
for(int j=n; j>i; j--)
{
curr.push_back(0);
}
int y = num2[i] - '0'; // y 为乘数
// 遍历 被乘数的每一位,分别与乘数y 相乘, 并将相乘所得每一位的结果压入 curr中
for(int j=m; j>=0; j--)
{
int x = num1[j] - '0'; // x 为被乘数的某一位
int product = x*y + add;
curr.push_back(product%10);
add = product/10;
}
// 若一轮乘法运算存在进位
while(add != 0)
{
curr.push_back(add%10);
add /= 10;
}
// 每个元素加 '0', 即为对的字符串
for(auto& c : curr)
{
c += '0';
}
reverse(curr.begin(), curr.end()); // curr字符串,即为每次所求字符串
ans = addStrings(ans, curr); // 将 每一轮curr的结果相加
}
return ans;
}
string addStrings(string& num1, string& num2)
{
int i=num1.length()-1;
int j=num2.length()-1;
int add=0;
string ans = "";
while(i>=0 || j>=0 || add != 0)
{
int x=i >= 0 ? num1[i]-'0':0;
int y=j >= 0 ? num2[j]-'0':0;
int result = x+y+add;
ans.push_back('0'+result%10);
add = result/10;
i-=1;
j-=1;
}
reverse(ans.begin(), ans.end());
return ans;
}