双链表求500位Π

题目描述:

 

思路分析:

  根据上面给出的公式,我们可以知道每一项都是前一项乘

  但是考虑到会出现的大数,不能直接用long long来进行存储,绝对会溢出。又题目限定了使用双链表,因此我们使用双链表。

  最主要需要解决的问题是大数的加法、乘法除法。解决方案见下。

  另外,可以给第一项直接乘6,这样之后每一项都是原来的6倍,得出来的结果直接位Π,不必再继续乘。

  并且我们数字低位在前,高位在后。

 解题过程:

  ①完成一个双链表,题目中要求前500位,那么我们定义一个有600个节点的双链表,并将其初始化。

struct Node
{
    int data = 0;
    Node* next = nullptr;
    Node* prev = nullptr;
};
void insertNode(Node* head)
{
    Node* newNode = new Node;       //直接插入一个元素,这样就不用多次判断head->next是否为NULL了
    newNode->next = head->next;
    newNode->prev = head;
    head->next = newNode;
    for (int i = 1; i < 600; i++)
    {
        Node* newNode = new Node;
        newNode->next = head->next;
        newNode->prev = head;
        head->next->prev = newNode;
        head->next = newNode;
    }
}
Node* createAList()
{
    return new Node;
}

  ②大数乘法

  让程序模仿我们手算的过程即可。

 

void multiple(Node* source, Node* target, int n)
{//source指乘数,而结果将存在source中
    int temp = 0, ret = 0, left = 0;    //temp指产生的临时变量,ret指进位,left指留下来的数
    while (source)
    {
        temp = source->data * n + ret;
        left = temp % 10;
        ret = temp / 10;
        target->data = left;
        source = source->next;
        target = target->next;
    }
}

 

  ③大数加法

  主要用来求这次结果与上次的和,模拟手算。

void add(Node* sum, Node* num)
{
    //num为这次计算结果,相加的和存于sum中,依然需要传刚开始的节点
    int temp = 0, ret = 0;
    while (sum && num)
    {
        temp = sum->data + num->data + ret;
        sum->data = temp % 10;
        ret = temp / 10;
        sum = sum->next;
        num = num->next;
    } 
}

  ④大数除法

  仍然是模拟手算。

void divison(Node *source, int n)
{
    //值得注意的是,此次传入的应是尾节点
    int temp = 0, ret = 0;
    while (source)
    {
        int dividend = temp + source->data; //计算过程中的被除数
        int now = dividend / n;             //这里应该是列式计算时,计算符号上面的数
        if (!now)
        {
            temp = dividend * 10;
        }
        else
        {
            temp = (dividend % n) * 10;
        }
        source->data = now;
        source = source->prev;
    }
} 

 

  ⑤开始计算

int main()
{
    //必要初始化
    Node *sum = createAList();
    Node *num = createAList();
    insertNode(num);
    insertNode(sum);
    //获得尾结点
    Node *sumTail = sum, *numTail = num;
    while (sumTail->next != nullptr)
    {
        sumTail = sumTail->next;
    }
    while (numTail->next != nullptr)
    {
        numTail = numTail->next;
    }
    //从3开始,简化运算
    numTail->data = 3;
    sumTail->data = 3;
    for (int i = 1; i < 1000; i++)
    {
        int dividend = (2 * i) * (2 * i + 1) * 4;
        int mult = (2 * i - 1) * (2 * i - 1);
        //先做除法,否则会溢出
        divison(numTail, dividend);
        //再做乘法,注意从头结点开始
        multiple(num, mult);
        //相加
        add(sum, num);
    }
    //指定输出位数输出
    int n;
    std::cin >> n;
    std::cout << sumTail->data;
    sumTail = sumTail->prev;
    std::cout << ".";
    for (int i = 0; i < n - 1; i++)
    {
        std::cout << sumTail->data;
        sumTail = sumTail->prev;
    }
}

 

输出结果:

 

 成功!

完整代码:

#include <iostream>

struct Node
{
    int data = 0;
    Node *next = nullptr;
    Node *prev = nullptr;
};
void insertNode(Node *head)
{
    Node *newNode = new Node; //直接插入一个元素,这样就不用多次判断head->next是否为NULL了
    newNode->next = head->next;
    newNode->prev = head;
    head->next = newNode;
    for (int i = 1; i < 600; i++)
    {
        Node *newNode = new Node;
        newNode->next = head->next;
        newNode->prev = head;
        head->next->prev = newNode;
        head->next = newNode;
    }
}
Node *createAList()
{
    return new Node;
}

void multiple(Node *source, int n)
{
    int temp = 0, ret = 0; // temp指产生的临时变量,ret指进位,left指留下来的数
    while (source)
    {
        temp = source->data * n + ret;
        ret = temp / 10;
        source->data = temp % 10;
        source = source->next;
    }
}

void add(Node *sum, Node *num)
{
    // num为这次计算结果,相加的和存于sum中,依然需要传刚开始的节点
    int temp = 0, ret = 0;
    while (sum && num)
    {
        temp = sum->data + num->data + ret;
        sum->data = temp % 10;
        ret = temp / 10;
        sum = sum->next;
        num = num->next;
    }
}

void divison(Node *source, int n)
{
    //值得注意的是,此次传入的应都是尾节点
    int temp = 0, ret = 0;
    while (source)
    {
        int dividend = temp + source->data; //计算过程中的被除数
        int now = dividend / n;             //这里应该是列式计算时,计算符号上面的数
        if (!now)
        {
            temp = dividend * 10;
        }
        else
        {
            temp = (dividend % n) * 10;
        }
        source->data = now;
        source = source->prev;
    }
}

int main()
{
    //必要初始化
    Node *sum = createAList();
    Node *num = createAList();
    insertNode(num);
    insertNode(sum);
    //获得尾结点
    Node *sumTail = sum, *numTail = num;
    while (sumTail->next != nullptr)
    {
        sumTail = sumTail->next;
    }
    while (numTail->next != nullptr)
    {
        numTail = numTail->next;
    }
    //从3开始,简化运算
    numTail->data = 3;
    sumTail->data = 3;
    for (int i = 1; i < 1000; i++)
    {
        int dividend = (2 * i) * (2 * i + 1) * 4;
        int mult = (2 * i - 1) * (2 * i - 1);
        //先做除法,否则会溢出
        divison(numTail, dividend);
        //再做乘法,注意从头结点开始
        multiple(num, mult);
        //相加
        add(sum, num);
    }
    //指定输出位数输出
    int n;
    std::cin >> n;
    std::cout << sumTail->data;
    sumTail = sumTail->prev;
    std::cout << ".";
    for (int i = 0; i < n - 1; i++)
    {
        std::cout << sumTail->data;
        sumTail = sumTail->prev;
    }
}

 

 更新:才发现此题是NOJ数据结构实验第二题

如果你一点没变地复制粘贴上去,肯定会WA,因为我输出的不是小数点后几位

如果你输入了n输出的是3.1415,所以你只要把倒数第六行n-1改成n就行了。

posted @ 2022-03-09 17:08  帝皇の惊  阅读(237)  评论(0)    收藏  举报