zoukankan      html  css  js  c++  java
  • 多项式运算

    编译环境说明,以下代码能够在 codeblocks 16.01 自带编译器版本中, C语言 项目中正确运行,没有警告,没有错误,如果,更换为其它环境,例如 DEV 请自行修改编译器不同造成的差异。

    前言

    一元多项式计算器没有用链表写这个多项式之前,哇,觉得这个好难啊!!!链表是人干出来的事情??写完了才发现,“真香警告”。(︶^︶)我才不会承认的。

    基本变量

    变量名描述
    c 系数
    n 幂值

    程序模块

    函数名描述
    input() 返回一个多项式升序链表
    insert(int c, int n, LIST head) 改良的插入方法,升序插入和幂值相同控制
    add(LIST A, LIST B) 两个多项式相加
    sub(LIST A, LIST B) 多项式相减 A - B
    mul(LIST A, LIST B) 多项式相乘 A * B
    output(LIST head) 按照一定形式输出多项式
    reversed(LIST head) 链表反转(降幂)
    uptate(LIST head) 更新链表的信息(删除系数为0的结点)
    show(LIST head) 对输出方法(降幂,升幂,return)封装
    init() 对运算函数(add,sub,mul)封装

    构建输入函数

    不管会不会,输入是必须的,先把数据读入再说。既然选用了链表存储,用链表来实现,那么我们肯定要考虑到链表的两种形式:带头结点的链表不带头结点的链表,这两种有什么区别呢?

    同时我们还需要了解下面三个概念:(不想看直接跳过,我已经整理成图片的形式了)

    名称解释说明
    首元节点 就是指链表中存储第一个数据元素a1的结点。
    头结点 它是在首元结点之前附设的一个节点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可以存储与数据元素类型的其他附加信息,例如,当数据元素为整数型时,头结点的数据域中可存放该线性表的长度。如上表中的数据域和指针域(在链表和线性中一个结点包含两个域:一个是数据域,另外一个是指针域,指针域它本有自己的存储地址,同时又指向下一个节点的数据域,这就形成了一个逻辑关系)就是一个头结点。
    头指针 它是指向链表中的第一个结点的指针。若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。

    下面是图片辅助理解:

    带头结点

    不带头结点

    那么看到这个地方,我的选择是带头结点。原因是一般的链表都带头结点,另外就是,带头结点,插入,查找,删除都比较方便。

    输入函数的写法

    首先看到这个地方,我们需要有一个头指针,暂时标记为head,这个指针这这个地方不存储人任何元素(实际操作这个头结点的数据域可以存储元素,比如链表长度)。

    其次,我们应该考虑输入的多项式的格式问题:

    特征提取得到两个关键的元素,系数指数。这样考虑的原因是因为,X是固定不变的,没有逻辑上的意义,所以不考虑,那么我们得到这些,就可以每次申请一块地址空间,来存放多项式中的某一项,链表的一般方法是插入。我们假定有这样一个方法,那么我们如何存储链呢?

    第一种就是尾插法,节点一直插入到链表的尾部:

    这种方式存储我们的链表当然可以。

    我们假定我们已经写了 insert 函数

    LIST input()
    {
        printf("输入系数和指数,以0 0结束 ");
        //输入系数和指数
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        int n, c;
        while(scanf("%d%d", &c, &n), n || c)
            insert(c, n, head);
        return head;
    }

    可以看到返回的是链表类型。

    那么我们可以直接调用。

    printf("请先输入两个多项式
    ");
    LIST A = input();
    LIST B = input();

    构建插入函数`insert`

    刚刚呢我们假设了有一个这样的 insert 函数,把所有的结点都插入在链表的尾部 ,那么会造成什么样的情况呢?

    如果是按照这样升幂输入的数据当然没问题,但也有可能输入的数据是这样的:

    类似于我们这样的无序输入,那么我们下一步,肯定需要进行排序的操作,把这个变成升幂或者降幂的形式。那既然这样,为什么我们不直接就让这个链表有序插入呢?我们知道,链表的优势就是易拓展,也就是可以在中间直接插入某元素。

    这就是关键的地方!!!插入的时候就维持有序状态。

    那么是不是还有别的情况?

    比如说这两个值相同,换句话说,这两个幂值相同,根据运算的原理,系数肯定是要相加的。

    这两个在实际计算过程中是等价的。

    所以,我们直接在插入的时候就考虑这两种情况!!

    演示:

    找不到的情况:

    代码如下:

    void insert(int c, int n, LIST head)
    {
        //头结点性质
        LIST tmp = (LIST)malloc(sizeof(struct node));
        tmp->n = n, tmp->c = c, tmp->NEXT = NULL;
        LIST P = head;
        while(P->NEXT)
        {
            //相等
            if(P->NEXT->n == tmp->n)
            {
                P->NEXT->c += tmp->c;
                return;
            }
            if(P->NEXT->n > tmp->n)
                break;
            P = P->NEXT;
        }
        //找到最后还没有找到
        if(!P->NEXT)
        {
            P->NEXT = tmp;
            return;
        }
        tmp->NEXT = P->NEXT;
        P->NEXT = tmp;
    }

    关于加法

    有了上面的铺垫是不是问题就非常简单啦!!!

    insert 实现了升序和处理相同值的情况,那么加法怎么加呢?

    第一种就是A链为母链,B链的所有结点都添加到(insert)A链中。

    第二种就是B链为母链,A链的所有结点都添加到(insert)B链中。

    第三种就是新增一条链,A链和B链的所有结点都添加到(insert)新链中。

    代码如下(使用的第三种方法):

    void add(LIST A, LIST B)
    {
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        LIST P = A->NEXT;
        while(P)
            insert(P->c, P->n, head), P = P->NEXT;
        P = B->NEXT;
        while(P)
            insert(P->c, P->n, head), P = P->NEXT;
        uptate(head);//暂时忽略
        show(head);//暂时忽略
    }

    P从A的首元节点到最末尾,然后从B的首元节点到末尾,也就是把A链和B链的所有结点都添加到(insert)新链中。

    关于减法

    有了加法,减法还会远吗?

    这两个形式在值上面是等价的。

    那么我们直接在加法的基础上面变形。

    第一种就是A链为母链,B链的所有结点的系数的相反数都添加到(insert)A链中。

    第二种就是B链为母链,A链的所有结点的系数的相反数都添加到(insert)B链中。

    第三种就是新增一条链,A链和B链的所有结点的系数的相反数都添加到(insert)新链中。

    P从A的首元节点到最末尾,然后从B的首元节点到末尾,也就是把A链和B链的所有结点的系数的相反数添加到(insert)新链中。

    代码如下(使用的第三种方法):

    void sub(LIST A, LIST B)
    {
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        LIST P = A->NEXT;
        while(P)
            insert(P->c, P->n, head), P = P->NEXT;
        P = B->NEXT;
        while(P)
            insert(-P->c, P->n, head), P = P->NEXT;
        uptate(head);//暂时忽略
        show(head);//暂时忽略
    }

    关于乘法

    有了之前加法和乘法的思路,乘法就不用我说了。

    遍历A链的所有结点,遍历B链的所有结点,也就是双重循环,AXB的所有结点添加(insert)到新链中。

    因为我们规定的 insert 就是有序,不重复幂值。

    所以直接丢进去就行,不用考虑。

    void mul(LIST A, LIST B)
    {
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        LIST i = A->NEXT;
        //if A NULL
        LIST j = B->NEXT;
        if(!i)
        {
            while(j)
            {
                insert(j->c, j->n, head);
                j = j->NEXT;
            }
            uptate(head);
            show(head);
            return;
        }
        //if B NULL
        if(!j)
        {
            while(i)
            {
                insert(i->c, i->n, head);
                i = i->NEXT;
            }
            uptate(head);
            show(head);
            return;
        }
        while(i){
            j = B->NEXT;
            while(j)
            {
                insert(i->c * j->c, i->n + j->n, head);
                j = j->NEXT;
            }
            i = i->NEXT;
        }
        uptate(head);
        show(head);
    }

    非法值处理

    什么样的值是特殊的?非法的?肯定是不符合我们规定的形式的。

    那么我们可以得到,系数为0,其实这一项就是没有了,所以,剔除。

    这里就体现了 头结点 的方便之处,本质上来说,这段代码属于,链表的删除。

    基本思想就是:

    怎么剔除呢?

    P->NEXT = P->NEXT->NEXT;

    这样可能会说,有bug,真的有么?

    就算我是最后一个结点,也没有问题。

    void uptate(LIST head)
    {
        LIST P = head;
        while(P->NEXT)
        {
            if(P->NEXT->c == 0){
                P->NEXT = P->NEXT->NEXT;
                continue;
            }
            P = P->NEXT;
        }
    }

    关于输出

    输出就是考虑特殊值,比如说正好,负号,首次输出的符号。

    考虑到这些,应该就没有问题了。

    首个值,那就让指针动一下,然后 P=P->NEXT 就ok了;

    代码如下:

    void output(LIST head)
    {
        LIST P = head->NEXT;
        if(!p) return;
        //输出第一个
        if(P->c < 0)
            printf("-");
        if(P->n == 0)
            printf("%d"abs(P->c));
        else
            printf("%dx^%d"abs(P->c), P->n);
        P = P->NEXT;
        //继续输出
        while(P)
        {
            if(P->n == 0)
                printf("%c%d", P->c > 0 ? '+' : '-',abs(P->c));
            else
                printf("%c%dx^%d", P->c > 0 ? '+' : '-'abs(P->c), P->n);
            P = P->NEXT;
        }
        printf(" ");
    }

    两次封装

    说白了,就是别人看着舒服。( ╯□╰ )

    主要运用 switch 语句。

    第一层封装是在main函数。主要以下功能:

    名称编号功能
    add(A, B) 1 A + B
    sub(A, B) 2 A - B
    sub(B, A) 3 B - A
    mul(A, B) 4 A * B
    return 5 返回上一级(结束)

    第二层封装主要是三种运算关系调用:

    名称编号功能
    升幂 1 升幂展示多项式
    降幂 2 降幂展示多项式
    all 3 升序降幂
    return 4 返回上一级

    执行结果

    测试数据

    A,B都由常数构成

    A = 5; B = 7

    A, B常数和多项式

    A = 6; B = 7X^2 + 5 X^ 3 + 3

    A, B 为多项式

    A = 3X^2 + 5X^4 + 3 + 4X; B = 7X^7 + 2X + 3X^4

    A, B运算后系数为0

    A = 2X^2; B = -2X^2

    A,B其中不全存在

    A = ; B = 2X^2 + 3X^3 + 2

    综合方案

    A = 3X^4-5X^2+6X^1-2X^0

    B = 5X^20-7X^4+3^1

    完整代码

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    typedef struct node * LIST;
    struct node
    {

        int c;//系数
        int n;//指数
        LIST NEXT;
    };
    LIST input();
    void insert(int c, int n, LIST head);//插入和维护升幂
    void add(LIST A, LIST B);//A链节点和B链节点直接insert里面丢,进去就有序
    void output(LIST head);//
    LIST reversed(LIST head);//反转->降幂
    void sub(LIST A, LIST B);//A链节点和B链负节点直接insert里面丢,进去就有序
    void mul(LIST A, LIST B);//A链节点 * B链节点(for for)直接insert(c*c, n+n, head)里面丢,进去就有序
    void uptate(LIST head);//A链节点B链节点删除c=0节点
    void show(LIST head);//展示 ,switch,
    void init();//初始化
    int main(int argc, char const *argv[])
    {
        init();
        return 0;
    }
    void init()//初始化
    {
        printf("请先输入两个多项式 ");
        LIST A = input();
        LIST B = input();
        printf("A = ");output(A);
        printf("B = ");output(B);
        while(1){
            int flag;
            printf("请选择操作命令->    1:加法; 2:A - B; 3:B - A; 4:乘法; 5:return ");
            scanf("%d", &flag);
            switch (flag){
                case 1:
                    add(A, B);
                    break;
                case 2:
                    sub(A, B);
                    break;
                case 3:
                    sub(B, A);
                    break;
                case 4:
                    mul(A, B);
                    break;
                case 5:
                    return;
            }
        }
    }
    void insert(int c, int n, LIST head)
    {
        //头结点性质
        LIST tmp = (LIST)malloc(sizeof(struct node));
        tmp->n = n, tmp->c = c, tmp->NEXT = NULL;
        LIST P = head;
        while(P->NEXT)
        {
            //相等
            if(P->NEXT->n == tmp->n)
            {
                P->NEXT->c += tmp->c;
                return;
            }
            if(P->NEXT->n > tmp->n)
                break;
            P = P->NEXT;
        }
        //找到最后还没有找到
        if(!P->NEXT)
        {
            P->NEXT = tmp;
            return;
        }
        tmp->NEXT = P->NEXT;
        P->NEXT = tmp;
    }
    LIST input()
    {
        printf("输入系数和指数,以0 0结束 ");
        //输入系数和指数
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        int n, c;
        while(scanf("%d%d", &c, &n), n || c)
            insert(c, n, head);
        return head;
    }
    void add(LIST A, LIST B)
    {
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        LIST P = A->NEXT;
        while(P)
            insert(P->c, P->n, head), P = P->NEXT;
        P = B->NEXT;
        while(P)
            insert(P->c, P->n, head), P = P->NEXT;
        uptate(head);
        show(head);
    }
    void sub(LIST A, LIST B)
    {
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        LIST P = A->NEXT;
        while(P)
            insert(P->c, P->n, head), P = P->NEXT;
        P = B->NEXT;
        while(P)
            insert(-P->c, P->n, head), P = P->NEXT;
        uptate(head);
        show(head);
    }
    void mul(LIST A, LIST B)
    {
        LIST head = (LIST)malloc(sizeof(struct node));
        head->NEXT = NULL;
        LIST i = A->NEXT;
        //if A NULL
        LIST j = B->NEXT;
        if(!i)
        {
            while(j)
            {
                insert(j->c, j->n, head);
                j = j->NEXT;
            }
            uptate(head);
            show(head);
            return;
        }
        //if B NULL
        if(!j)
        {
            while(i)
            {
                insert(i->c, i->n, head);
                i = i->NEXT;
            }
            uptate(head);
            show(head);
            return;
        }
        while(i){
            j = B->NEXT;
            while(j)
            {
                insert(i->c * j->c, i->n + j->n, head);
                j = j->NEXT;
            }
            i = i->NEXT;
        }
        uptate(head);
        show(head);
    }
    void uptate(LIST head)
    {
        LIST P = head;
        while(P->NEXT)
        {
            if(P->NEXT->c == 0){
                P->NEXT = P->NEXT->NEXT;
                continue;
            }
            P = P->NEXT;
        }
    }
    void output(LIST head)
    {
        LIST P = head->NEXT;
        if(!P) return;
        //输出第一个
        if(P->c < 0)
            printf("-");
        if(P->n == 0)
            printf("%d"abs(P->c));
        else
            printf("%dx^%d"abs(P->c), P->n);
        P = P->NEXT;
        //继续输出
        while(P)
        {
            if(P->n == 0)
                printf("%c%d", P->c > 0 ? '+' : '-',abs(P->c));
            else
                printf("%c%dx^%d", P->c > 0 ? '+' : '-'abs(P->c), P->n);
            P = P->NEXT;
        }
        printf(" ");
    }
    LIST reversed(LIST head)
    {
        LIST rear, P = head->NEXT;
        rear = (LIST)malloc(sizeof(struct node));
        rear->NEXT = NULL;
        while(P)
        {
            LIST tmp = (LIST)malloc(sizeof(struct node));
            tmp->NEXT = NULL, tmp->c = P->c, tmp->n = P->n;
            tmp->NEXT = rear->NEXT;
            rear->NEXT = tmp;
            P = P->NEXT;
        }
        return rear;
    }
    void show(LIST head)
    {
        while(1){
            int flag;
            printf("请问你想升幂输出还是降幂输出->  1:升幂; 2:降幂; 3:all; 4:return ");
            scanf("%d", &flag);
            switch (flag){
                case 1:
                    printf("升幂排列是 ");
                    output(head);
                    break;
                case 2:
                    printf("降幂排列是 ");
                    output(reversed(head));
                    break;
                case 3:
                    printf("升幂排列是 ");
                    output(head);
                    printf("降幂排列是 ");
                    output(reversed(head));
                    break;
                case 4:
                    return;
            }
        }
    }
  • 相关阅读:
    从0开始搭建Element项目
    Git的上传步骤
    从JVM的角度解析String
    两个字符串相加究竟发生了什么
    简单工厂搭配策略模式
    java返回集合为null还是空集合
    idea常用快捷键
    (7)Stream简介
    (6)函数式接口的简单使用
    (4)函数式接口
  • 原文地址:https://www.cnblogs.com/wfybeliefs/p/12748405.html
Copyright © 2011-2022 走看看