链表多项式大整数-BigInt

非负大整数类 - 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;
    }

};
相关推荐
kyle~2 小时前
BFS(广度优先搜索)与 DFS (深度优先搜索)
c++·算法·深度优先·宽度优先
汉克老师2 小时前
GESP2024年3月认证C++三级( 第二部分判断题(1-10))
c++·位运算·string·gesp三级·gesp3级
段一凡-华北理工大学2 小时前
【大模型+知识图谱+工业智能体技术架构】~系列文章02:工业知识图谱的构建与知识表示学习方法!!!
数据结构·python·神经网络·知识图谱·物理系统·神经逆向渲染
jinyishu_2 小时前
链表经典算法题(2)
c语言·数据结构·链表
y = xⁿ2 小时前
MySQL为什么抛弃了B树,选择B+树?(含面试回答)
数据结构·b树·面试
kyle~3 小时前
FANUC机械臂---PR位置寄存器(Position Register)
c++·机器人·fanuc
自我意识的多元宇宙3 小时前
二叉树遍历方式代码解读(1递归)
java·数据结构·算法
_日拱一卒3 小时前
LeetCode:142环形链表Ⅱ
算法·leetcode·链表
John.Lewis3 小时前
C++加餐课-继承和多态:扩展学习
开发语言·c++·笔记