目录
一、前言
最近春招已经开始,看周围的同学都在投递一些大厂的实习,某为的手撕代码 --- 字符串相乘,某讯的手撕代码 --- 字符串相减等。
于是专门去 Leetcode 上搜索了一下,发现这类题目是面试常考的题目。只要我们熟练的 掌握四则运算,熟练的掌握字符串的处理,就可以迎刃而解啦!
常考算法面试题分为:字符串(大数)相加、字符串(大数)相乘、字符串(大数)相减。本次博客主要通过图例和双指针来讲解 ---- 字符串(大数)相减
如果你还没有做过这道题,可以先去看看 : 字符串相加 、 字符串相乘,这两到题目,减法稍微麻烦一点,但核心的思路相似。
链接:字符串相加 、 字符串相乘
二、字符串相减
✨ 题目描述
题目描述:给定两个字符串形式的非负整数 num1 和num2 ,计算它们的差。
注意:
-
num1 和num2 都只会包含数字 0-9
-
num1 和num2 都不包含任何前导零
-
你不能使用任何內建 BigInteger 库
✨ 题目分析
题目分析:
- 我们先假设给定的不是字符串形式的数字,而是正常的非负整数,则两数相减遵循正常的减法运算,个位数与个位数相减,十位数与十位数相减,如果该位计算结果<0,则向前借位。
- 既然我们对加法的运算非常熟悉,代码也非常好写,那我们现在的任务就是将字符串形式的数字转换为正常的数字进行算术运算。
- 那么我们应该怎样取字符串中的每一位数字呢?我们定义两个指针,分别指向两个字符串的尾,然后取出该位置的字符,并将其转换为整数形式参与运算,并将结果再次转换为字符串形式,依次进行计算。 (双指针算法)
上图中就是我们计算字符串相减的一个大概思路,但是这里需要注意几点情况:
- a:两个非负整数相减的结果可能为负;
- b:注意边界问题
针对情况a:
- 因此,首先比较两个数的大小。
如何比较两个大数的大小呢?
- 由于是大数,肯定不能直接转成int比较。
- 我们可以比较两个字符串的长度。
- 长度更长的字符串,数一定更大;当长度一样的就去比较字典序。
// 防止字符串是 小减大
// 正常来说 是 a-b 且 a>b
bool isLess(string a, string b)
{
if (a.size() == b.size())
{
//当长度一样的就去比较字典序。
return a < b;
}
// 长度更长的字符串,数一定更大
return a.size() < b.size();
}
- 如代码所示,当小减大时,需将两个参数调换一下位置执行减法,在结果前填上
负号
即可 - 注意:结果为0时不加负号。
// 保证 大数减法 正常进行
string substrings(string num1, string num2)
{
string res;
// 发现 num1 < num2
if (isLess(num1, num2))
{
// 转换顺序 ,加上 负号
res = sub(num2, num1);
// 如果不等于 0 将其反过来即可
if (res != "0")
{
res.insert(0, "-");
}
}
else // num1 > num2
{
res = sub(num1, num2);
}
return res;
}
大体框架写完了,接下来根据上述的图例来,实现关键的减法
函数
。
// 减法
string sub(string a, string b)
{
string s;
// 借位
int next = 0;
int len1 = a.size() - 1 , len2 = b.size() - 1;
// 两个字符串都从 最后一位开始减
while (len1 >= 0 || len2 >= 0)
{
int val1 = 0;
if (len1 >= 0)
{
val1 = a[len1--] - '0';
}
int val2 = 0;
if (len2 >= 0)
{
val2 = b[len2--] - '0';
}
// 每次都假设借位
// %10 可以满足 借位和不借位两种情况
int ret = (val1 - val2 - next + 10) % 10;
// 判断是否借位
if ((val1 - val2 - next) < 0)
{
next = 1;
}
else
{
next = 0;
}
s += ret + '0';
}
reverse(s.begin(), s.end());
// 删除前导 0 ,注意边界是 s.size()-1 防止当s为 "0000"
int pos;
for (pos = 0; pos < s.size() - 1; pos++)
{
if (s[pos] != '0')
{
break;
}
}
return s.substr(pos);
}
针对问题b:边界问题
例如,当121-120=001
,需要将前面的0删除,得到最终结果1
。注意121-121=000
这种情况,不要把所有0都删了!
✨完整代码展示
【C++】完整代码如下:
// 防止字符串是 小减大
// 正常来说 是 a-b 且 a>b
bool isLess(string a, string b)
{
if (a.size() == b.size())
{
//当长度一样的就去比较字典序。
return a < b;
}
// 长度更长的字符串,数一定更大
return a.size() < b.size();
}
// 减法
string sub(string a, string b)
{
string s;
// 借位
int next = 0;
int len1 = a.size() - 1 , len2 = b.size() - 1;
// 两个字符串都从 最后一位开始减
while (len1 >= 0 || len2 >= 0)
{
int val1 = 0;
if (len1 >= 0)
{
val1 = a[len1--] - '0';
}
int val2 = 0;
if (len2 >= 0)
{
val2 = b[len2--] - '0';
}
// 每次都假设借位
// %10 可以满足 借位和不借位两种情况
int ret = (val1 - val2 - next + 10) % 10;
// 判断是否借位
if ((val1 - val2 - next) < 0)
{
next = 1;
}
else
{
next = 0;
}
s += ret + '0';
}
reverse(s.begin(), s.end());
// 删除前导 0 ,注意边界是 s.size()-1 防止当s为 "0000"
int pos;
for (pos = 0; pos < s.size() - 1; pos++)
{
if (s[pos] != '0')
{
break;
}
}
return s.substr(pos);
}
// 保证 大数减法 正常进行
string substrings(string num1, string num2)
{
string res;
// 发现 num1 < num2
if (isLess(num1, num2))
{
// 转换顺序 ,加上 负号
res = sub(num2, num1);
// 如果不等于 0 将其反过来即可
if (res != "0")
{
res.insert(0, "-");
}
}
else // num1 > num2
{
res = sub(num1, num2);
}
return res;
}
int main()
{
string a, b, c;
cin >> a >> b;
cout << substrings(a, b) << endl;
return 0;
}
三、共勉
以下就是我对 字符串相减 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对 双指针专题 的更新,请持续关注我哦!!!