非负大整数类 - BigInt
大整数ADT - 链表实现
为了进行大整数乘法时好进位,我们把幂次低的项靠近链表头部,即反着存放
将大整数封装为一个
BigInt类(singly-linked-list)
BigInt类内部有一个节点类Node,一个节点代表一项
BigInt类可以看做是一个节点按幂次从小到大排列的有序链表
cpp
class BigInt
{
private:
struct Node;
typdef Node* PtrToNode;
PtrToNode bigint;
PtrToNode clone() const;//返回自身的深拷贝
void swap(BigInt & other);//交换两个对象的指针(copy-and-swap)
void Carry();//对链表进行统一进位
public:
//==================//
/* 三/五法则 */
//=================//
BigInt(); //无参构造
~BigInt();//析构函数
BigInt(int num);//将一个整型变量转换为大整数
BigInt(const BigInt & other);//深拷贝构造
BigInt & operator= (BigInt other);//赋值重载:copy-and-swap
BigInt(BigInt && other) noexcept; //移动构造
BigInt & operator= (BigInt && other) noexcept;//移动赋值
//===========================//
/* 大整数的输入输出 */
//==========================//
friend std::istream & operator>> (std::istream & is, BigInt & B);
friend std::ostream & operator<< (std::ostream & os, const BigInt & B);
//============================//
/* 大整数的加法 */
//===========================//
BigInt operator+ (const BigInt & B) const;
//===========================//
/* 大整数的乘法 */
//==========================//
BigInt operator* (const BigInt & B) const;
在做运算(加减)或输入输出时,一定要注意抵消(val = 0)的情况
大整数的加法
函数声明
cpp
BigInt operator+ (const BigInt & B) const ;
因为加法会产生临时对象,所以返回值只能是
BigInt对象, 不能加引用,避免悬空引用注意一定要声明为
const, 因为const对象和临时对象只能调用const成员函数要实现 BigInt + BigInt + BigInt ····+····+BigInt 必须加const
cpp
BigInt A, B, C;
//std::cout << A + B + C;
std::cout << (A.operator+(B)).operator+(C);
//(A.operator+(B))为临时对象,C++默认临时对象只能调用const成员函数
//如果operator+不声明为const,那么临时对象(A.operator+(B))无法调用operator+
//无法实现连加的操作
维护一个变量
carry代表进位维护两个指针
ptr1、ptr2分别指向this->bigint、B.bigint如果两个指针都不为空,一定要保持同时前进(保证位次相同),
如果一个指针为空,将其值定义为0,继续后移另一个指针
直到
carry == 0 && ptr1 == nullptr && ptr2 == nullptr
cpp
//=================
// 大整数的加法
//=================
BigInt operator+ (const BigInt & B) const
{
BigInt newBigInt;
int carry = 0, curSum = 0;
PtrToNode ptr1 = bigint;
PtrToNode ptr2 = B.bigint;
PtrToNode cur = newBigInt.bigint;//用于延长链表
while (ptr1 || ptr2 || carry)
{
int val1 = ptr1 ? ptr1->val : 0;
int val2 = ptr2 ? ptr2->val : 0;
curSum = carry + val1 + val2;
carry = curSum / 10;
cur->val = curSum % 10;
if (ptr1)
ptr1 = ptr1->Next;
if (ptr2)
ptr2 = ptr2->Next;
if (ptr1 || ptr2 || carry)//判断要不要延长链表
{
cur->Next = new Node();
cur = cur->Next;
}
}
return newBigInt;//返回临时对象
}
大整数的乘法
模拟竖式乘法,最后再统一进位
第i位与第j位相乘的结果直接放到第i+j位上(i,j 从0开始算),不用先考虑进位,后面再统一进位
cpp
//统一向后进位
void Carry()//调用: A.Carry();
{
PtrToNode ptr = bigint;
for(;;)
{
if (ptr)
{
int carry = ptr->val / 10;
ptr->val %= 10;
if (carry > 0 && !ptr->Next)
ptr->Next = new Node();
else if (!carry && !ptr->Next)
return;
ptr = ptr->Next;
ptr->val += carry;
}
}
}
函数声明
cpp
BigInt operator* (const BigInt & B) const;
定义变量
cpp
int i, j, newSize;//第i位与第j位相乘
int size1 = 0, size2 = 0, preIdx = 0;
BigInt newBigInt;//相乘的结果
PtrToNode cur = nullptr;//用于遍历链表
PtrToNode ptr1 = bigint;
PtrToNode ptr2 = B.bigint;
提前计算两个相乘链表的长度
方便新开一个链表存放相乘的结果
cpp
//计算两个相乘链表的长度
for (cur = ptr1; cur; cur = cur->Next)
size1++;
for (cur = ptr2; cur; cur = cur->Next)
size2++;
//将结果链表的长度加长,每个节点的值初始化为0
newSize = size1 + size2 - 2;
cur = newBigInt.bigint;
for (i = 0; i < newSize; ++i)
{
cur->Next = new Node();
cur = cur->Next;
}
模拟运算,两重循环,用指针
ptr1、ptr2遍历链表,用变量i, j记录链表的位次每一次用
cur指针找第i + j个节点,将ptr1->val * ptr2->val加到该节点小优化:不用每次都让
cur从头开始遍历链表,我们可以用一个preIdx记录上一次cur停在第几个节点了,如果这一次的i + j在preIdx的后面或相等,那就直接往后就行了,如果cur在后面了,在从头遍历
cpp
//================
// 大整数的乘法
//================
BigInt operator* (const BigInt & B) const
{
int i, j, newSize;//第i位与第j位相乘
int size1 = 0, size2 = 0, preIdx = 0;
BigInt newBigInt;//相乘的结果
PtrToNode cur = nullptr;//用于遍历链表
PtrToNode ptr1 = bigint;
PtrToNode ptr2 = B.bigint;
for (cur = ptr1; cur; cur = cur->Next)
size1++;
for (cur = ptr2; cur; cur = cur->Next)
size2++;
newSize = size1 + size2 - 2;
cur = newBigInt.bigint;
for (i = 0; i < newSize; ++i)
{
cur->Next = new Node();
cur = cur->Next;
}
cur = newBigInt.bigint;
for (i = 0, ptr1 = bigint; ptr1; ptr1 = ptr1->Next, ++i)
{
for (j = 0, ptr2 = B.bigint; ptr2; ptr2 = ptr2->Next, ++j)
{
int curSum = ptr1->val * ptr2->val;
int idx = i + j;
int k = 0;//cur需要后移的步数
if (idx >= preIdx)
for (; k < idx - preIdx; ++k)
cur = cur->Next;
else
for (cur = newBigInt.bigint; k < idx; ++k)
cur = cur->Next;
cur->val += curSum;
preIdx = i + j;
}
}
newBigInt.Carry();//统一进位
return newBigInt;
}
完整代码
cpp
#include<iostream>
#include<string>
#include<stack>
class BigInt
{
private:
struct Node;
typedef Node* PtrToNode;
struct Node
{
int val;
PtrToNode Next;
Node(): val(0), Next(nullptr) {}
Node(int v, PtrToNode p = nullptr): val(v), Next(p) {}
~Node() {delete Next;}
Node(const Node & node) = delete;
Node & operator= (const Node & node) = delete;
};
PtrToNode bigint;
//==================
// 工具函数
//==================
PtrToNode clone() const
{
if (!bigint)
return nullptr;
PtrToNode newHead = new Node(bigint->val);
PtrToNode cur = newHead;
for (PtrToNode ptr = bigint->Next; ptr; ptr = ptr->Next)
{
cur->Next = new Node(ptr->val);
cur = cur->Next;
}
return newHead;
}
void swap(BigInt & other) noexcept
{
PtrToNode ptr = bigint;
bigint = other.bigint;
other.bigint = ptr;
}
void Carry()
{
PtrToNode ptr = bigint;
for(;;)
{
if (ptr)
{
int carry = ptr->val / 10;
ptr->val %= 10;
if (carry > 0 && !ptr->Next)
ptr->Next = new Node();
else if (!carry && !ptr->Next)
return;
ptr = ptr->Next;
ptr->val += carry;
}
}
}
public:
//================
// 三/五法则
//================
BigInt(): bigint(new Node(0)) {}
BigInt(int num) : bigint(new Node(num % 10))
{
PtrToNode cur = bigint;
while (num / 10)
{
cur->Next = new Node(num % 10);
cur = cur->Next;
}
}
~BigInt(){delete bigint;}//封闭类的析构
BigInt(const BigInt & other): bigint(other.clone()) {}
BigInt & operator= (BigInt other)
{
swap(other);
return *this;
}
BigInt(BigInt && other) noexcept : bigint(other.bigint)
{
other.bigint = nullptr;
}
BigInt & operator= (BigInt && other) noexcept
{
delete bigint;
bigint = other.bigint;
other.bigint = nullptr;
return *this;
}
//=================
// 大整数的输入输出
//=================
friend std::istream & operator>> (std::istream & is, BigInt & B)
{
std::string num;
is >> num;
int len = num.size();
delete B.bigint;
B.bigint = new Node(num[len - 1] - '0');
PtrToNode cur = B.bigint;
for (int i = len - 2; i >= 0; --i)
{
int curVal = num[i] - '0';
cur->Next = new Node(curVal);
cur = cur->Next;
}
return is;
}
friend std::ostream & operator<< (std::ostream & os, const BigInt & B)
{
std::stack<int> st;
PtrToNode ptr = B.bigint;
while (ptr)
{
st.push(ptr->val);
ptr = ptr->Next;
}
while (!st.empty())
{
os << st.top();
st.pop();
}
return os;
}
//=================
// 大整数的加法
//=================
BigInt operator+ (const BigInt & B) const
{
BigInt newBigInt;
int carry = 0, curSum = 0;
PtrToNode ptr1 = bigint;
PtrToNode ptr2 = B.bigint;
PtrToNode cur = newBigInt.bigint;
while (ptr1 || ptr2 || carry)
{
int val1 = ptr1 ? ptr1->val : 0;
int val2 = ptr2 ? ptr2->val : 0;
curSum = carry + val1 + val2;
carry = curSum / 10;
cur->val = curSum % 10;
if (ptr1)
ptr1 = ptr1->Next;
if (ptr2)
ptr2 = ptr2->Next;
if (ptr1 || ptr2 || carry)
{
cur->Next = new Node();
cur = cur->Next;
}
}
return newBigInt;
}
//================
// 大整数的乘法
//================
BigInt operator* (const BigInt & B) const
{
int i, j, newSize;//第i位与第j位相乘
int size1 = 0, size2 = 0, preIdx = 0;
BigInt newBigInt;//相乘的结果
PtrToNode cur = nullptr;//用于遍历链表
PtrToNode ptr1 = bigint;
PtrToNode ptr2 = B.bigint;
for (cur = ptr1; cur; cur = cur->Next)
size1++;
for (cur = ptr2; cur; cur = cur->Next)
size2++;
newSize = size1 + size2 - 2;
cur = newBigInt.bigint;
for (i = 0; i < newSize; ++i)
{
cur->Next = new Node();
cur = cur->Next;
}
cur = newBigInt.bigint;
for (i = 0, ptr1 = bigint; ptr1; ptr1 = ptr1->Next, ++i)
{
for (j = 0, ptr2 = B.bigint; ptr2; ptr2 = ptr2->Next, ++j)
{
int curSum = ptr1->val * ptr2->val;
int idx = i + j;
int k = 0;
if (idx >= preIdx)
for (; k < idx - preIdx; ++k)
cur = cur->Next;
else
for (cur = newBigInt.bigint; k < idx; ++k)
cur = cur->Next;
cur->val += curSum;
preIdx = i + j;
}
}
newBigInt.Carry();
return newBigInt;
}
};