1.两数相加
一、问题描述
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
二、问题简化
这个问题实际上是为了解决大数计算问题,即比较大的数字的计算(众所周知int型的表示范围有限)。
很明显题目说的是十进制,实际其他进制也是一样的。
十进制一位只有10个值,用4位2进制就足够表示了,也就是32位可以表示8个十进制。不过int型的计算是快于其他类型的(由于硬件的架构),所以先不考虑这么多,使用int型的链表表示大数类即可:
typedef list<int> BigInteger;
在定义大数类之后,求和函数传入两个值,返回一个值,如下::
BigInteger addTwoNumber(const BigInteger& a, const BigInteger& b)
三、功能实现
基本算法
1.首先区分出长链和短链,也就是哪个数字更短。
2.遍历短链,同时遍历长链,之间相加的结果插入到结果链,并记录是否进位(很明显,进位只能进1)。
3.将长链剩余部分添加到结果链,同时也可能发生进位。
4.最后还可能进位,最后单独判断一次
BigInteger addTwoNumber(const BigInteger& a, const BigInteger& b)
{
BigInteger ret;
const BigInteger* bi_s;
const BigInteger* bi_l;
//区分长短链
if (a.size() > b.size())
{
bi_l = &a;
bi_s = &b;
}
else
{
bi_l = &b;
bi_s = &a;
}
//取较小长度
int length = bi_s->size();
//0为短链
auto iter0 = bi_s->begin();
auto iter1 = bi_l->begin();
//是否进位标记
bool carry = false;
//遍历完短链
for (int i = 0; i != length; ++i)
{
//求和
int sum = *iter0 + *iter1;
//如果上次遍历结果进位,则加1
if (carry) ++sum;
if (sum < 10)
carry = false;//否则不进位
else
{//如果结果大于等于10,则进位(标记进位,下次循环使用)
sum %= 10;
carry = true;
}
ret.push_back(sum);
++iter0;
++iter1;
}
//再添加较长数字的剩余高位(考虑进位)
for (; iter1 != bi_l->end(); ++iter1)
{
if (carry)
{//进位则加1,同时还可能发生继续进位
int num = *iter1 + 1;
if (num < 10)
carry = false;
else
num %= 10;
ret.push_back(num);
}
else
{
ret.push_back(*iter1);
carry = false;
}
}
//最后进位可能
if (carry)
{
ret.push_back(1);
}
return ret;
}
打印功能
需要直观的观察数字,所以我们用逆序迭代器打印每一个节点,以及直接打印出a加b的结果:
void PrintNumber(const BigInteger& bi)
{
//逆序打印
for (auto iter = bi.rbegin(); iter != bi.rend(); ++iter)
{
cout << *iter;
}
}
void PrintNumberAdd(const BigInteger& a, const BigInteger& b)
{
auto ret = addTwoNumber(a, b);
PrintNumber(a);
cout << " add ";
PrintNumber(b);
cout << " is equal ";
PrintNumber(ret);
cout << "." << endl;
}
四、测试结果
代码如下(网上的代码看上去比较简洁,但是并没有拆分步骤,而是在循环内多次进行判断和求和,我的代码效率必然会更快一些,不过不是数量级的变化):
//2.两数之和
#include <list>
#include <iostream>
using namespace std;
typedef list<int> BigInteger;
BigInteger addTwoNumber(const BigInteger& a, const BigInteger& b)
{
BigInteger ret;
const BigInteger* bi_s;
const BigInteger* bi_l;
//区分长短链
if (a.size() > b.size())
{
bi_l = &a;
bi_s = &b;
}
else
{
bi_l = &b;
bi_s = &a;
}
//取较小长度
int length = bi_s->size();
//0为短链
auto iter0 = bi_s->begin();
auto iter1 = bi_l->begin();
//是否进位标记
bool carry = false;
//遍历完短链
for (int i = 0; i != length; ++i)
{
//求和
int sum = *iter0 + *iter1;
//如果上次遍历结果进位,则加1
if (carry) ++sum;
if (sum < 10)
carry = false;//否则不进位
else
{//如果结果大于等于10,则进位(标记进位,下次循环使用)
sum %= 10;
carry = true;
}
ret.push_back(sum);
++iter0;
++iter1;
}
//再添加较长数字的剩余高位(考虑进位)
for (; iter1 != bi_l->end(); ++iter1)
{
if (carry)
{//进位则加1,同时还可能发生继续进位
int num = *iter1 + 1;
if (num < 10)
carry = false;
else
num %= 10;
ret.push_back(num);
}
else
{
ret.push_back(*iter1);
carry = false;
}
}
//最后进位可能
if (carry)
{
ret.push_back(1);
}
return ret;
}
void PrintNumber(const BigInteger& bi)
{
//逆序打印
for (auto iter = bi.rbegin(); iter != bi.rend(); ++iter)
{
cout << *iter;
}
}
void PrintNumberAdd(const BigInteger& a, const BigInteger& b)
{
auto ret = addTwoNumber(a, b);
PrintNumber(a);
cout << " add ";
PrintNumber(b);
cout << " is equal ";
PrintNumber(ret);
cout << "." << endl;
}
int main()
{
//test0
list<int> a = { 1,1,2,4,0,0,0,0 };//4211
list<int> b = { 0,4,8,5,1,7 };//715840
//求和并打印
PrintNumberAdd(a, b);
//test1
a = { 1,2,3,4,5,6,7,8,9 };//987654321
b = { 4,4,4,4 };//4444
//求和并打印
PrintNumberAdd(a, b);
//test2
a = { 2,5,9,0,0,0,1 };//1000925
b = { 6,7 };//76
//求和并打印
PrintNumberAdd(a, b);
//test3
a = { 9,9,9 };//999
b = { 1,1,1 };//111
//求和并打印
PrintNumberAdd(a, b);
//test4
a = {};//
b = {};//
//求和并打印
PrintNumberAdd(a, b);
}