HW1: 线性表 > 1-4 一元多项式的相加和相乘

原题

描述

一元多项式是有序线性表的典型应用,用一个长度为m且每个元素有两个数据项(系数项和指数项)的线性表((p1,e1),(p2,e2),...,(pm,em))可以唯一地表示一个多项式。 本题实现多项式的相加和相乘运算。本题输入不保证有序。

输入

第1行:
一个整数m,表示第一个一元多项式的长度

第2行:
2m项,p1 e1 p2 e2 ...,中间以空格分割,表示第1个多项式系数和指数

第3行:
一个整数n,表示第二个一元多项式的项数

第4行:
有2n项,p1 e1 p2 e2 ...,中间以空格分割,表示第2个多项式系数和指数

第5行:
1个整数,若为0,执行加法运算并输出结果,若为1,执行乘法运算并输出结果;若为2,输出一行加法结果和一行乘法的结果。

输出

运算后的多项式链表,要求按指数从小到大排列
当运算结果为0 0时,不输出。

样例输入

4
7 0 3 1 9 8 5 17
3
8 1 22 7 -9 8
0

样例输出

7 0 11 1 22 7 5 17

解题

分析

既然题目要求使用线性表完成本题,那我们就要用线性表做题(而不是链表)。
多项式的每个项,包含指数底数部分。所以,线性表的每个节点应该涵盖这两个内容。
按照比较好的设计,每个节点内容应如下:

struct Node {
    int expo; // 指数
    int base; // 底数
};

但是,如果真的按照这种方式设计,在进行乘法计算时会涉及一些困难。有兴趣的读者可以自行尝试。考虑到笔者水平受限,采用另一种相对简单的方式。

考虑到底数千变万化,且在不同项中可以重复,而每个项的指数都是独一无二的,我们可以创建一个数组,以指数为下标,每元素存储指数对应的底数。
如:a[5] = 4 表示 45 这项。对于底数为0的项,依旧存在数组里,只是值为0而已。这样做会额外消耗内存,但对如笔者一样的萌新很友好。

如此,每个多项式中只需要存储一个数组,并记录数组长度即可。
为什么要记录数组长度?因为我们不知道指数最大为多少,所以涉及动态内存申请内容。

对于一个完整的多项式,散装似乎不太好。我们采用面向对象的思想,将多项式的内容及功能(乘法、加法、打印等)打包成一个,后续对整体对象进行操作,会方便很多。

设计

多项式类基本成员设计

设计多项式类如下(虽说是,此处实际采用结构体存储。区别不大,但在工程中似乎还是用更好):

struct Polynomial {
    long long* baseVal = NULL;
    size_t arrMax = -1;
    void functions(...) {
        ...
    }
};

Polynomial意为多项式。
baseVal即上述数组,负责存储底数。如果你不知道什么是 size_t,可以先用 int 代替。
arrMax用于标记数组最后一个元素的位置。即:多项式中指数最大值
初始状态,多项式没有内容,所以 baseVal 应指向一个空地址。多项式中没有元素,所以指数最大值比0还小。我们人为设定这种情况下最大指数为 -1.

功能函数设计

思考多项式类需要具有哪些能力?
首先,它需要具有被赋值的能力。不然,它不就永远是0多项式了么。
然后,需要有一个输出能力。似乎可以设计一个一项项读取的功能,然后从外部控制输出。但是我们完全可以为它设计一个能力,直接把自己完整输出,这样岂不是更方便?
按照题目要求,该多项式还应有加法乘法能力。
由于类内涉及动态内存申请,所以需要设计一个清理功能,用于释放申请到的内存。(当然,你可以不做这一步,毕竟现在的操作系统会自动回收内存。但是,万一出现内存泄漏(如Android Lollipop),后果自负)

关于赋值,读者可能会问:谁给谁赋值?对此,我们会想到用一个多项式给另一个多项式赋值,即多项式拷贝。但是,第一个多项式怎么来(鸡生蛋,蛋生鸡,那么第一只鸡谁来生)?所以,我们可以加入一个用整数给多项式赋值的功能。
想法很好!但,如何用整数给多项式赋值?如果多项式有一百项,设计一个带二百个参数的函数?
聪明的你一定想到了传递数组!可以,不过此处采用另一种方式:
为多项式添加更新功能,传入一个底数和一个指数,将它添加到多项式中。
功能设计完毕,这就开写!

功能函数实现

更新

更新过程,传入参数为一个项的指数和底数。判断,如果baseVal数组长度足以包含这个指数,就直接赋值。否则,要重新申请空间。

/**
 * 项更新。
 * 
 * @param base 底数。
 * @param expo 指数。
 * 
 * @return (暂无。可考虑返回处理结果等)
 * @throw (暂无)
 */
void Polynomial::update(int base, int expo) {
    // 判断是否需要重新申请内存
    if (expo > arrMax) {
        // 需要重新申请内存
        long long* newArray;
        newArray = (long long*) realloc(baseVal, (expo + 1) * sizeof(long long));

        if (newArray == NULL) {
            // 如果申请失败...
            // 进行一些处理,如抛出异常或结束程序。
        } else {
            // 申请成功
            baseVal = newArray;
        }
           
        for (size_t i = arrMax + 1; i < expo; i++) {
            baseVal[i] = 0; // 将新申请到的内容设为0.
        }
            
        arrMax = expo; // 记录新的 arrMax 值
    }

    baseVal[expo] = base; // 单项赋值
}

上方代码未作异常处理。这样做是不对的,但考虑到笔者水平有限,暂不设计这些内容。

多项式拷贝

该功能较简单。

/**
 * 多项式拷贝。
 * 
 * @param source 源多项式。
 * 
 * @return (暂无。可考虑返回处理结果等)
 * @throw (暂无)
 */
void Polynomial::set(Polynomial* source) {
    // 如果目标的数组大小小于源多项式数组大小,则要申请更多空间存储内容。
    if (arrMax < source->arrMax) {
        long long* newArray;
        newArray = (long long*)realloc(baseVal, (source->arrMax + 1) * sizeof(long long));
        if (newArray == NULL) {
            // 如果申请失败...
            // 进行一些处理,如抛出异常或结束程序。
        }
        else {
            // 申请成功
            baseVal = newArray;
        }

        arrMax = source->arrMax;
    }

    // 逐项复制
    for (size_t i = 0; i <= arrMax; i++) {
        if (i > source->arrMax) {
            baseVal[i] = 0; // 如果这项超出源多项式范围,则赋为0.
        }
        else {
            baseVal[i] = source->baseVal[i]; // 拷贝。
        }
    }
}

加法

逐项相加即可。

/**
 * 多项式加法。
 * 
 * @param pn 加“数”。
 * 
 * @return (暂无。可考虑返回处理结果等)
 * @throw (暂无)
 */
void Polynomial::add(Polynomial* pn) {
    if (arrMax < pn->arrMax) {

        long long* newArray = (long long*)realloc(baseVal, (pn->arrMax + 1) * sizeof(long long));
    
        if (newArray == NULL) {
            // 如果申请失败...
            // 进行一些处理,如抛出异常或结束程序。
        }
        else {
            // 申请成功
            baseVal = newArray;
        }
     
        for (size_t i = arrMax + 1; i <= pn->arrMax; i++) {
            baseVal[i] = 0;
        }

        arrMax = pn->arrMax;
    }
    for (size_t i = 0; i <= arrMax && i <= pn->arrMax; i++) {
        baseVal[i] += pn->baseVal[i]; // 逐项加。
    }
}

乘法

本部分稍微复杂一些。
我们有多项式 f 和 g 如下:
f =
g =
做乘法,得到多项式 h = f · g, 如下:
h =
其中:

据此,便可设计多项式乘法功能。

/**
 * 多项式乘法。
 *
 * @param pn 乘“数”。
 *
 * @return (暂无。可考虑返回处理结果等)
 * @throw (暂无)
 */
void Polynomial::mul(Polynomial* pn) {

    // 如果两个多项式中有一个为0多项式,则结果为0多项式。
    if (pn->arrMax < 0 || arrMax < 0) {
        for (size_t i = 0; i <= arrMax; i++) {
            baseVal[i] = 0;
        }
        return;
    }

    // 将目标多项式原内容备份。
    long long* tmp = (long long*) malloc((arrMax + 1) * sizeof(long long));
    // 此处应对内存申请失败的情况做处理。本例暂认为该申请一定能成功。
    
    for (size_t i = 0; i <= arrMax; i++) {
        tmp[i] = baseVal[i];
    }

    size_t prevArrMax = arrMax; // 目标多项式原始数据大小。

    if (arrMax < arrMax + pn->arrMax) {
        long long* newArray;
        newArray = (long long*) realloc(baseVal, (pn->arrMax + arrMax + 1) * sizeof(long long));
        if (newArray == NULL) {
            // 如果申请失败...
            // 进行一些处理,如抛出异常或结束程序。
        }
        else {
            // 申请成功
            baseVal = newArray;
        }
    
        arrMax = pn->arrMax + arrMax;
    }

    for (size_t i = 0; i <= arrMax; i++) {
        baseVal[i] = 0; // 清空目标多项式的内容,以备后续写入。
        // 注意:此处虽清空相乘的两个多项式之一的数据,但前方已将它备份到tmp中,故未造成数据丢失。
    }

    size_t i, j;
    for (i = 0; i <= arrMax; i++) {
        for (j = 0; j <= i; j++) {
            if (j > prevArrMax || i - j > pn->arrMax) {
                // 若超出两多项式之一的范围,则跳过。
                continue;
            }

            baseVal[i] += tmp[j] * pn->baseVal[i - j];
        }
    }
}

输出

按照题意,如果多项式为空,则不输出。否则,按照规则输出多项式。
输出过程很简单,只需要循环读取baseVal数组元素,对非0元素进行输出即可。

void Polynomial::print() {
    for (int i = 0; i <= arrMax; i++) {
        if (baseVal[i] != 0) {
            printf("%lld %d ", baseVal[i], i);
        }
    }
}

清理

由于只有baseVal涉及动态内存申请,所以清理部分释放baseVal指向的内存即可。

void Polynomial::cleanup() {
    free(baseVal);
}

多项式类组装

完成功能函数编写,即可获得完整的多项式类。如下:

/**
 * 多项式类。
 */
struct Polynomial {
    long long* baseVal = NULL;
    size_t arrMax = -1;

    /**
     * 项更新。
     *
     * @param base 底数。
     * @param expo 指数。
     *
     * @return (暂无。可考虑返回处理结果等)
     * @throw (暂无)
     */
    void update(int base, int expo) {
        // 判断是否需要重新申请内存
        if (expo > arrMax) {
            // 需要重新申请内存
            long long* newArray;
            newArray = (long long*)realloc(baseVal, (expo + 1) * sizeof(long long));

            if (newArray == NULL) {
                // 如果申请失败...
                // 进行一些处理,如抛出异常或结束程序。
            }
            else {
                // 申请成功
                baseVal = newArray;
            }

            for (size_t i = arrMax + 1; i < expo; i++) {
                baseVal[i] = 0; // 将新申请到的内容设为0.
            }

            arrMax = expo; // 记录新的 arrMax 值
        }

        baseVal[expo] = base; // 单项赋值
    }

    /**
     * 多项式拷贝。
     *
     * @param source 源多项式。
     *
     * @return (暂无。可考虑返回处理结果等)
     * @throw (暂无)
     */
    void set(Polynomial* source) {
        // 如果目标的数组大小小于源多项式数组大小,则要申请更多空间存储内容。
        if (arrMax < source->arrMax) {
            long long* newArray;
            newArray = (long long*)realloc(baseVal, (source->arrMax + 1) * sizeof(long long));
            if (newArray == NULL) {
                // 如果申请失败...
                // 进行一些处理,如抛出异常或结束程序。
            }
            else {
                // 申请成功
                baseVal = newArray;
            }

            arrMax = source->arrMax;
        }

        // 逐项复制
        for (size_t i = 0; i <= arrMax; i++) {
            if (i > source->arrMax) {
                baseVal[i] = 0; // 如果这项超出源多项式范围,则赋为0.
            }
            else {
                baseVal[i] = source->baseVal[i]; // 拷贝。
            }
        }
    }

    /**
     * 多项式加法。
     *
     * @param pn 加“数”。
     *
     * @return (暂无。可考虑返回处理结果等)
     * @throw (暂无)
     */
    void add(Polynomial* pn) {
        if (arrMax < pn->arrMax) {

            long long* newArray = (long long*)realloc(baseVal, (pn->arrMax + 1) * sizeof(long long));

            if (newArray == NULL) {
                // 如果申请失败...
                // 进行一些处理,如抛出异常或结束程序。
            }
            else {
                // 申请成功
                baseVal = newArray;
            }

            for (size_t i = arrMax + 1; i <= pn->arrMax; i++) {
                baseVal[i] = 0;
            }

            arrMax = pn->arrMax;
        }
        for (size_t i = 0; i <= arrMax && i <= pn->arrMax; i++) {
            baseVal[i] += pn->baseVal[i]; // 逐项加。
        }
    }

    /**
     * 多项式乘法。
     *
     * @param pn 乘“数”。
     *
     * @return (暂无。可考虑返回处理结果等)
     * @throw (暂无)
     */
    void mul(Polynomial* pn) {

        // 如果两个多项式中有一个为0多项式,则结果为0多项式。
        if (pn->arrMax < 0 || arrMax < 0) {
            for (size_t i = 0; i <= arrMax; i++) {
                baseVal[i] = 0;
            }
            return;
        }

        // 将目标多项式原内容备份。
        long long* tmp = (long long*)malloc((arrMax + 1) * sizeof(long long));
        // 此处应对内存申请失败的情况做处理。本例暂认为该申请一定能成功。

        for (size_t i = 0; i <= arrMax; i++) {
            tmp[i] = baseVal[i];
        }

        size_t prevArrMax = arrMax; // 目标多项式原始数据大小。

        if (arrMax < arrMax + pn->arrMax) {
            long long* newArray;
            newArray = (long long*)realloc(baseVal, (pn->arrMax + arrMax + 1) * sizeof(long long));
            if (newArray == NULL) {
                // 如果申请失败...
                // 进行一些处理,如抛出异常或结束程序。
            }
            else {
                // 申请成功
                baseVal = newArray;
            }

            arrMax = pn->arrMax + arrMax;
        }

        for (size_t i = 0; i <= arrMax; i++) {
            baseVal[i] = 0; // 清空目标多项式的内容,以备后续写入。
            // 注意:此处虽清空相乘的两个多项式之一的数据,但前方已将它备份到tmp中,故未造成数据丢失。
        }

        size_t i, j;
        for (i = 0; i <= arrMax; i++) {
            for (j = 0; j <= i; j++) {
                if (j > prevArrMax || i - j > pn->arrMax) {
                    // 若超出两多项式之一的范围,则跳过。
                    continue;
                }

                baseVal[i] += tmp[j] * pn->baseVal[i - j];
            }
        }
    }

    void print() {
        for (int i = 0; i <= arrMax; i++) {
            if (baseVal[i] != 0) {
                printf("%lld %d ", baseVal[i], i);
            }
        }
    }

    void cleanup() {
        free(baseVal);
    }
};

使用多项式类完成题目

拥有多项式类,即可轻松完成本题。main函数如下:

int main() {
    Polynomial pn[2], pnM;
    int m, n, p, e;
    int i, j;

    for (int _k = 0; _k < 2; _k++) {
        scanf("%d", &m);
        for (i = 0; i < m; i++) {
            scanf("%d%d", &p, &e);
            pn[_k].update(p, e);
        }
    }
    // 输入完毕。

    int operationCode;
    scanf("%d", &operationCode);
    pnM.set(&pn[0]);
    if (operationCode == 0) {
        // add
        pnM.add(&pn[1]);
        pnM.print();
    }
    else if (operationCode == 1) {
        // mul
        pnM.mul(&pn[1]);
        pnM.print();
    }
    else {
        // add and mul
        pnM.add(&pn[1]);
        pnM.print();
        pnM.set(&pn[0]);

        pnM.mul(&pn[1]);
        pnM.print();
    }
}

总结

本题难度较低,对线性表的操作考验强度很弱。运算只需模拟。其中,执行乘法时,先创建缓存数组,将目标原始数据复制,然后清空目标数据,再模拟多项式相乘手算过程。

你可能感兴趣的:(HW1: 线性表 > 1-4 一元多项式的相加和相乘)