【LeetCode】2.两数相加

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值