C++之用链表实现大数的加减乘除

1.前言

实现大数的加减乘除是今年寒假C++的大作业,原本我是用字符串写的,但是后来看作业要求要用链表实现,于是我不情不愿的用链表的实现了一遍

2.Num类

2.1Node类

首先是内部的一个Node类用于建立单向链表,size用于计算大小方便Num对象之间做比较

    class Node
    {
    public:
        int val;
        Node*next;
        Node(int v, Node*n)
        {
            val = v;
            next = n;
        }
    };
    Node*head;
    int size;

2.2构造函数,赋值函数以及析构函数

首先是默认构造函数,其实可能不会使用到,但为了方便,还是写了

    Num()
    {
        head = NULL;
        size = 0;
        D = false;
    }

然后是主要的一个构造函数,当遇到'.'的时候,我们用-100来作为标记

该链表为逆向构造的一个数,方便进行加减操作

    Num(const string&s)
    {
        D = false;
        head = NULL;
        size = 0;
        for (int i = 0; i < s.size(); i++)
        {
            if (s[i] == ','||s[i]=='-')
                continue;
            size++;
            if (s[i] == '.')
                head = new Node(-100, head);
            else
                head = new Node(s[i] - '0', head);
        }
    }

接下来是拷贝构造函数以及赋值函数,两者基本是一样的

值得注意的是,赋值函数要首先确认赋值对象是否为本身,若为本身,则返回

    Num(const Num& num)
    {
        D = num.D;
        size = num.size;
        head = NULL;
        Node*cur = num.head, *temp=head;
        while (cur)
        {
            if (!head)
            {
                head = new Node(cur->val, NULL);
                temp = head;
            }
            else
            {
                temp->next = new Node(cur->val, NULL);
                temp = temp->next;
            }
            cur = cur->next;
        }
    }
    Num& operator=(const Num&num)
    {
        if (this == &num)
            return *this;
        free();
        size = num.size;
        head = NULL;
        D = num.D;
        Node*cur = num.head, *temp=head;
        while (cur)
        {
            if (!head)
            {
                head = new Node(cur->val, NULL);
                temp = head;
            }
            else
            {
                temp->next = new Node(cur->val, NULL);
                temp = temp->next;
            }
            cur = cur->next;
        }
        return *this;
    }

最后是析构函数,我们析构函数只需要回收掉链表的内存即可

为了方便函数内部回收链表内存,将回收操作写为free函数

    ~Num()
    {
        free();
    }
    void free()
    {
        size = 0;
        while (head)
        {
            Node*temp = head->next;
            delete head;
            head = temp;
        }
        head = NULL;
    }

上述即为Num类的基本组成函数

2.3加法操作与减法操作

为方便操作,加减时大数在前,小数在后,直接对本身对象进行操作,所以使用+=,-=以及返回对象本身引用

加减操作没什么好说的,从最低位开始依次相加

在进位或借位时进行了一个判断是否为小数点的操作,以防操作错误

    Num& operator+=(const Num&num)
    {
        Node*pa = head, *pb = num.head;
        while (pa||pb)
        {
            int b = 0;
            if (pb)
            {
                b = pb->val;
                pb = pb->next;
            }
            if (pa->val != -100)
            {
                pa->val += b;
                if (pa->val > 9)
                {
                    if (pa->next)
                    {
                        if (pa->next->val == -100)
                            pa->next->next->val++;
                        else
                            pa->next->val++;
                    }
                    else
                    {
                        pa->next = new Node(1, NULL);
                        size++;
                    }
                    pa->val -= 10;
                }
            }
            pa = pa->next;
        }
        return *this;
    }
    Num& operator-=(const Num&num)
    {
        Node*pa = head, *pb = num.head;
        while (pa || pb)
        {
            int b = 0;
            if (pb)
            {
                b = pb->val;
                pb = pb->next;
            }
            if (pa->val != -100)
            {
                pa->val -= b;
                if (pa->val < 0)
                {
                    if (pa->next->val == -100)
                        pa->next->next->val--;
                    else
                        pa->next->val--;
                    pa->val += 10;
                }
            }
            pa = pa->next;
        }
        return *this;
    }

2.4乘法操作

乘法操作其实和列式计算一样,涉及到进位,但很容易理解

    Num operator*(const Num&num)
    {
        Num res(string(size + num.size, '0'));
        Node *pr = res.head, *temp;
        for (Node*pa = head; pa != NULL; pa = pa->next)
        {
            int carry = 0;
            temp = pr;
            for (Node*pb = num.head; pb != NULL; pb = pb->next,pr=pr->next)
            {
                int temp = pa->val*pb->val + carry + pr->val;
                pr->val = temp % 10;
                carry = temp / 10;
            }
            pr->val += carry;
            pr = temp->next;
        }
        return res;
    }

2.5除法操作

除法操作我们使用被除数减去除数乘以若个干10进行

比如58除以5 首先用58-50=8 然后商加10 然后用8-5=3 商加1 由于3比5小,就不进行操作了 最终结果为11

但是题目要求除法操作要保留十位小数四舍五入,我们只需要事先对被除数乘以10^11即可,最终再加上小数点进行四舍五入操作

我们首先实现了两个与10有关的函数

    void Mul10(const int& x)
    {
        for (int i = 0; i < x; ++i)
        {
            head = new Node(0, head);
            size++;
        }
    }
    void Div10(const int&x)
    {
        for (int i = 0; i < x; ++i)
        {
            Node*temp = head->next;
            delete head;
            head = temp;
            size--;
        }
    }

然后是除法操作,res用于返回结果,p用于与res相加

d用于与被除数相减

    Num operator/(const Num&num)
    {
        Num res(string(1, '0')), p(string(1, '1'));
        res.D = true;
        p.D = true;
        Num d = num;
        while (num<=*this)
        {
            int len = size - num.size;
            d.Mul10(len);
            p.Mul10(len);
            int z = 0;
            if (*this < d)
            {
                d.Div10(1);
                p.Div10(1);
                z = -1;
            }
            if (res.head->val == 0&&res.size==1)
                res = p;
            else
                res += p;
            *this -= d;
            balance();
            d.Div10(len + z);
            p.Div10(len + z);
        }
        return res;
    }

我们注意到了除法操作中有比较<的操作,接下来便是重载<函数

暂时写成单向链表转化为字符串string比较的方法

    bool operator<(const Num&num)const
    {
        if (size < num.size)
            return true;
        else if (size > num.size)
            return false;
        string A(size, '0'), B(num.size, '0');
        Node*temp = head;
        for (int i = size - 1; i >= 0; --i)
        {
            if (temp->val == -100)
                A[i] = '.';
            else
                A[i] = temp->val + '0';
            temp = temp->next;
        }
        temp = num.head;
        for (int i = num.size - 1; i >= 0; --i)
        {
            if (temp->val == -100)
                B[i] = '.';
            else
                B[i] = temp->val + '0';
            temp = temp->next;
        }
        return cmp(A, B);
    }
    bool cmp(const string&a, const string&b)const
    {
        if (a.size() < b.size())
            return true;
        else if (a.size() > b.size())
            return false;
        else
            return a < b;
    }
    bool operator==(const Num&num)const
    {
        if (size != num.size)
            return false;
        Node*pa = head, *pb = num.head;
        while (pa&&pb)
        {
            if (pa->val != pb->val)
                return false;
            pa = pa->next;
            pb = pb->next;
        }
        return true;
    }
    bool operator<=(const Num&num)const
    {
        return *this == num || *this < num;
    }

2.6打印数字

我们用一个bool值D来标记是否为除法结果,若D为真,则为商,需要进行小数点处理

我们需要考虑下列情况
1.若为小数点,将小数后面0清空,若全为0,则去掉小数点
2.将前导0去掉,若只剩小数点,则加0,或全为0,则为0

    friend ostream& operator<<(ostream&os, const Num&num)
    {
        bool z = false;
        Num::Node* temp = num.head;
        string s(num.size, '0');
        for (int i = num.size - 1; i >= 0; --i)
        {
            if (temp->val == -100)
            {
                s[i] = '.';
                z = true;
            }
            else
                s[i] = temp->val + '0';
            temp = temp->next;
        }
        if (num.D)
        {
            if (s.size() <= 11)
                s = "0" + string(11 - s.size(), '0') + s;
            if (s[s.size() - 1] > '4')
                s[s.size() - 2]++;
            s.erase(s.end() - 1);
            for (int j = s.size() - 1; j > 0; --j)
            {
                if (s[j] > '9')
                {
                    s[j - 1]++;
                    s[j] -= 10;
                }
                else
                    break;
            }
            if (s[0] > '9')
            {
                s[0] -= 10;
                s = "1" + s;
            }
            s.insert(s.size() - 10, ".");
            z = true;
        }
        int n = 0;
        while (s[n] == '0')
            n++;
        if (n == s.size())
            s = "0";
        else
            s = s.substr(n);
        if (s[0] == '.')
            s = '0' + s;
        bool t = true;
        int q;
        for (q = 0; q < s.size(); ++q)
            if (s[q] == '.')
                break;
        int j = s.size() - 1;
        if (z)
        {
            while (j > q&&s[j] == '0')
                j--;
            if (j == q)
                j--;
        }
        for (int i = 0; i <= j; ++i)
        {
            if (s[i] == '.')
                t = false;
            if (t && (q - i) % 3 == 0 && i != 0)
                os << ",";
            os << s[i];
        }
        os << endl;
        return os;
    }

2.7操作前处理

加减法将小数点对其,除法则将除数小数点去掉并将被除数扩大相应的倍数

void convert_Float_Plus(string&a, string&b)
{
    int x = 0, y = 0;
    while (x<a.size() && a[x] != '.')
        x++;
    while (y<b.size() && b[y] != '.')
        y++;
    if (x != a.size() || y != b.size())
    {
        int a_point = a.size() - x - 1, b_point = b.size() - y - 1;
        if (a_point > b_point)
            b += string(a_point - b_point, '0');
        else if (a_point < b_point)
            a += string(b_point - a_point, '0');
        a[x] = '.';
        b[y] = '.';
    }
}
void convet_Float_Divide(string&a, string&b)
{
    int x, y;
    for (x = 0; x < a.size(); ++x)
        if (a[x] == '.')
            break;
    for (y = 0; y < b.size(); ++y)
        if (b[y] == '.')
            break;
    if (y < b.size())
    {
        if (x < a.size())
            for (int i = 0; i < b.size() - y - 1; ++i)
                swap(a[x + i], a[x + 1 + i]);
        else
            a = a + string(b.size() - y - 1, '0');
        b.erase(b.begin() + y);
        int n = 0;
        while (b[n] == '0')
            ++n;
        b = b.substr(n);
    }
    for (x = 0; x < a.size(); ++x)
        if (a[x] == '.')
            break;
    if (x < a.size())
    {
        if (a.size() - x - 1 <11)
            a = a + string(11 - (a.size() - x - 1), '0');
        else
            a = a.substr(0, x + 12);
        a.erase(a.begin() + x);
    }
    else
        a = a + string(11, '0');
}

转载于:https://www.cnblogs.com/vhyz/p/8469905.html

  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大数加减实现可以通过链表实现,具体步骤如下: 1. 定义链表节点结构体,包括节点值(val)和指向下一个节点的指针(next)。 2. 将两个大数分别存储在两个链表中,每个节点的值表示该位的数字。 3. 从链表头开始依次相加或相减对应节点的值,注意进位或借位的处理,将结果存储在新的链表中。 4. 如果链表长度不同,需要在短链表前面补0来使两个链表长度一致。 5. 最终得到的链表即为计算结果,将其转换为整数或字符串形式即可。 下面是一个 C++实现示例: ```cpp #include <iostream> using namespace std; struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode* dummy = new ListNode(0); ListNode* cur = dummy; int carry = 0; while (l1 || l2) { int x = l1 ? l1->val : 0; int y = l2 ? l2->val : 0; int sum = x + y + carry; carry = sum / 10; cur->next = new ListNode(sum % 10); cur = cur->next; if (l1) l1 = l1->next; if (l2) l2 = l2->next; } if (carry) { cur->next = new ListNode(carry); } return dummy->next; } int main() { // 构造链表1: 342 ListNode* l1 = new ListNode(2); l1->next = new ListNode(4); l1->next->next = new ListNode(3); // 构造链表2: 465 ListNode* l2 = new ListNode(5); l2->next = new ListNode(6); l2->next->next = new ListNode(4); // 相加 ListNode* res = addTwoNumbers(l1, l2); // 输出结果: 807 while (res) { cout << res->val; res = res->next; } cout << endl; return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值