实现一元多项式的加减法运算,要求多项式采用链表存储结构。多项式的输入输出格式不做要求,如可以用键盘按系数、指数格式输入,输入(0,0)代表输入结束,也可以用文件数据形式输入。只要把输入输出描述清楚即可。

目录

题前想法:

输入:

修改后的input()源代码:

输出:

修改后的output()源代码:

相加:

plus()源代码:

相减:

minus()源代码:

利用文件进行输入输出:


题前想法:

题目已经表述得很清楚了,而我们关键要做的就是如何实现两个一元多项式的加法。在这个基础上,减法操作相当于为减号后面的多项式各个项的系数乘以 -1 后与减号前面的多项式相加即可。我们给出几个实例(采用降幂输入):

(1)a(x)=3x^5+7x^3+1b(x)=x^5-x^3+2x+8加法运算结果:c(x)=4x^5+6x^3+2x+1(2)a(x)=3x^5+7x^3+1b(x)=9x^6-7x^3+4x^2+5x-1加法运算结果:c(x)=9x^6+3x^5+4x^2+5x(3)a(x)=3x^5+7x^3+1b(x)=-3x^5-7x^3-1加法运算结果:c(x)=0(4)a(x)=0b(x)=1加法运算结果:c(x)=1(5)a(x)=x^4+x^2+1b(x)=x^5+x^3-x^2+1加法运算结果:c(x)=x^5+x^4+x^3+2

为了适当提高难度,同时为了更好解决后面进阶版的多元多项式相加减这一难题,我们现在要从更高的角度来思考解法,并且要求向文件中输入“a(x)=多项式1”“b(x)=多项式2”后,在另外指定的文件中会输出结果“c(x)=多项式3”,就像这样:

如果没学过文件操作的博友也不必畏怯,通过本篇博文你将掌握这个点。当我们向文件main1.txt中写入两个多项式后,通过运行程序,两式相加的结果会出现在main2.txt中:

不过首先,我们得先学习如何在控制台成功地进行多项式的加法,如图:

得到结果:

第一点,也是最重要的一点,我们要将链表的结点设置成什么样子?这一步很重要,因为它会影响到我们该如何确定我们的算法。为了能够清晰地得到一个单项式的全面信息,我们需要知道它的符号、系数、字母以及指数,例如对于一个单项式 -x^3 ,它的符号是'-',系数是 1,字母是 x,指数是3;对于 y^3 这个多项式,它的它的符号是'+',系数是 1,字母是 y,指数是 3。所以,我们可以这样来设置一个结点:

//代码清单1
typedef struct poly
{char sign;//符号int coef;//系数(绝对值)char alph;//字母int exp;//指数struct poly *next;
}poly;

在设置好了节点后,我们就要确定链表的建立方式了。该如何处理“a(x)=多项式1”“b(x)=多项式2”呢?说到底,我们需要对这两个字符串进行操作,例如,对于 x^4+2x^2+3 这个多项式建立相应链表的操作可以简略地表述为:

首先,对于第一个单项式 x^4,我们建立第一个结点node1:

注意,node1表示的结点最后的指针域设置为 ' ^ ' ,表示不指向另外的结点,而非乘方符号。然后,我们依法建立下一个结点node2:

形成了一条链表:

依法炮制建立第三个结点并将它并入链表:

这样,我们便成功地建立起一条代表多项式 x^4+2x^2+3 的链表,而这样的链表我们需要建立两条。

以上的操作说明我们是将一个多项式分割成多个单项式从而建立结点组成链表,在接下来的操作中我们也将延续这种思想。

输入:

利用 line[ ] 数组存储我们输入的字符串,假设它的下标最大值为39;然后我们分别定义一个存储系数和指数的数组 coef[ ] 和 exp[ ] ,下标的最大值均为3。设置 line[ ] 数组是因为我们输入的多项式对于 PC 来说只是一串待处理的字符,所以我们用 line[ ] 来存储这串字符;所以各个单项式的系数与指数也是也是单个或多个字符,我们先将它们存储在数组中,然后通过 atoi() 函数将它们转化为数字,例如对于一个单项式 -23x^34 ,coef[]中存储的字符串为''23\0'',exp[ ]中存储的字符串为''34\0'',然后这个单项式的系数为 atoi(coef)=23 ,指数为 atoi(exp)=34 。

对于输入的字符串,我们一个字符一个字符地进行分析。一个完整的多项式可以是这样的:+2x^3+3x^2+1x^1+3x^0 ,那么,单项式相应节点依次为:+2x^3、+3x^2、+1x^1、+3x^0,我们可以建立起处理处理字符串的函数 input() 的雏形:

//代码清单2
void input(poly* head)
{poly *new = head;poly *temp = NULL;char coef[4];char exp[4];int cnt = 0;//cnt辅助系数和指数的存储与转化char line[40];scanf("%s", line);strcpy(line, line + 5);int line_cnt = 0;//line_cnt辅助输入字符串的字符计数while (1){new->sign = line[line_cnt++];//结点符号while (isdigit(line[line_cnt])){coef[cnt++] = line[line_cnt++];}coef[cnt] = '\0';new->coef = atoi(coef);//结点系数cnt = 0;strcpy(coef, "\0");new->alph = line[line_cnt];//结点字母line_cnt += 2;//跳过'^'while (isdigit(line[line_cnt])){exp[cnt++] = line[line_cnt++];}exp[cnt] = '\0';new->exp = atoi(exp);//结点指数cnt = 0;strcpy(exp, "\0");if (line[line_cnt] == '\0'){new->next = NULL;break;}else{temp = new;new = (poly*)malloc(sizeof(poly));temp->next = new;}}
}

第4、5行,我利用 new 来接收队首结点的地址,temp 协同 new 来创建新的结点,体现在第43和45行的代码间。在 main() 函数中,我们首先需要为链表队首结点开辟空间,然后调用 input() 函数,之后将队首结点的地址作为参数传递给 input() 函数的形参 head ,来为两个多项式分别建立新的链表。代码第12行用于去掉字符串的前缀如 “a(x)=” 和 “b(x)=” ,从而只保留多项式。随着   line_cnt 的增长,我们可以实现对多项式字符串的遍历。第17行代码用于记录一个结点的符号,有两个可能的值即 ‘+’ 和 ‘-’ ,同时 line_cnt 自加,表示将检查下一个字符。第18至23行的代码则将 ‘+’ 或 ‘-’ 后的数字字符按序存进 coef[ ] 数组当中,第22行的代码是必要的,它为存储在 coef[ ] 中的字符串加上了结尾,第23行则将 coef[ ] 中的数字字符串转换为整型数字。第27行存在是因为我们是完整地输入多项式的,故字母字符的两个后的字符才是指数的第一个数字字符,在第28至33行代码继续依法炮制得到该项的指数。注意,第34行的代码不可去掉,因为有可能后一项仍是单项式,exp[ ] 和 coef[ ] 仍然会被使用。第36至46行间的代码通过是否到达多项式字符串地末尾来用于判断是否要继续开辟一个新的结点。

然而,一般我们根本不会输入 “a(x)=+2x^3+3x^2+1x^1+3x^0” ,而更倾向于输入 “a(x)=2x^3+3x^2+x+3” 。所以为了美观起见,我们不能够继续使用上面的 input() 函数建立代表 a(x) 的链表了。我们需要做一些必要的修改:

从分析首字符开始,那么首字符可能是什么?可能是符号、数字,也有可能是字母,例如 “a(x)=-2x^3+3x^2+x+3”、“a(x)=+2x^3+3x^2+x+3”、“a(x)=2x^3+3x^2+x+3” 、“a(x)=x^3+3x^2+x+3”,多项式的开头分别是‘-’、‘ + ’、‘ 2 ’ 、‘ x ’ ,所以,如果首字符是数字或字母(统一输入小写字母),那么单项式的符号默认是 ‘ + ’;否则,单项式的符号直接是line[line_cnt](‘-’或‘ + ’),然后 line_cnt 自增使 line_cnt 指向下一个字符以便对下一个字符进行判断。这一段话转换成代码:

//代码清单3
if (isdigit(line[line_cnt]) || isalpha(line[line_cnt]))new->sign = '+';
elsenew->sign = line[line_cnt++];//结点符号

我们用这个判断结构替换代码清单2中的第17行代码

那么在确定了这个单项式的符号后,它的系数又该如何确定呢?已知 line[line_cnt] 在经过上面这个判断结构后,必然不会再指向单项式的符号了(尽管可能没有符号)。如果它不是一个数字字符,那么必然是字母字符,此时将结点的系数设置为 1 ;如果确实是数字字符,那么我们将利用 line_cnt 来添加数字字符到 coef[ ] 数组中并且使 line_cnt 自增直到下标 line_cnt 指向的字符不再是数字字符,最终通过 atoi() 函数将 coef[ ] 中存储的字符串转化为数字作为结点的系数。但是,在得到系数后,line_cnt 指向的也将不是数字字符了,如果前面判断是字母字符,那么由于 line_cnt 未自增,它指向的仍然是字母字符,此时表明字符串还未到结尾处;但如果前面判断是数字字符,在将这一连串数字转化后, line_cnt 指向的可能就是 '\0' 了,也就是说,此时这个单项式是一个常数,那么这个结点的指数将被设置为0,字母将是首个非常数项的字母,指针也将指向NULL,同时跳出这个while循环。这一段话转换成代码:

//代码清单4
if (isdigit(line[line_cnt]))
{while (isdigit(line[line_cnt]))coef[cnt++] = line[line_cnt++];coef[cnt] = '\0';new->coef = atoi(coef);strcpy(coef, "\0");cnt = 0;if (line[line_cnt] == '\0')//if (line[line_cnt] == '\n')是错的,因为字符串不是以'\n'结尾的。{new->exp = 0;new->alph = head->alph;new->next = NULL;break;}
}
elsenew->coef = 1;//结点系数

我们用这段代码替换代码清单2中的第18至25行代码

系数确定了,未知数字母自然不用多说,在 input() 中执行了代码清单4这部分代码后,line_cnt指向的自然就是单项式的字母了,所以代码清单2中的第26行代码保持不变,因为如果不执行这行代码的话只有一个可能,那就是这个单项式是常数,符合代码清单4中第10行的if判断结构中的语句块执行的条件从而跳出循环不再开辟一个新的结点

那么,跟在字母字符后面的是什么呢?可能是字符 '\0' ,例如单项式 -5x ,这说明该单项式的指数为1也可能是字符 '^' ,这说明这个单项式的指数是大于1的,例如单项式 5x^3 还可能是字符 '+' 和 '-' ,例如多项式 5x+3 ,当 line_cnt 指向单项式 5x 中的 x 的时候,可知line_cnt+1指向的便是 '+' ,所以在这种情况下 line_cnt 指向的字母所在的单项式的指数仍然同第一种情况一样为1,且该单项式后带一个大于或小于0的常数(由于按降幂顺序输入,在该单项式指数为1的情况下,其下一个单项式的指数为0,即一个常数)。我将这一段话翻译成代码便是:

//代码清单5
if (line[line_cnt + 1] == '^')
{line_cnt += 2;//省略'^'while (isdigit(line[line_cnt]))exp[cnt++] = line[line_cnt++];exp[cnt] = '\0';new->exp = atoi(exp);//结点指数cnt = 0;strcpy(exp, "\0");
}
else
{new->exp = 1;line_cnt++;
}

用来替换代码清单2中第27至35行的代码

最后,在结点指数被确定下来了,这个结点的基本属性就已经完善了,我们还剩下什么没有做呢?是的,结点的指针域还是空着的。该怎么办呢?是不加思考地直接置空?不,我们还剩下一点没有做。此时 line_cnt 已经指向了该单项式的下一个字符,我们还需要判断这个字符是什么?它可能是一个符号,例如多项式 5x^2-3x+1 ,在单项式 -3x 后,line_cnt 便指向其后的 '+' ,此时应当继续创建一个新的结点,用以存储后面的单项式 +1 ;如果不是符号,那就是字符 '\0' ,此时我们应当跳出循环,不再创建新的结点。所表述的问题与代码清单2中第36至46行代码符合,所以代码不做修改。

修改后的input()源代码:

//代码清单6
void input(poly* head)
{poly *new = head;poly *temp = NULL;char coef[4];char exp[4];int cnt = 0;char line[40];scanf("%s", line);strcpy(line, line + 5);int line_cnt = 0;while (1){if (isdigit(line[line_cnt]) || isalpha(line[line_cnt]))new->sign = '+';elsenew->sign = line[line_cnt++];//结点符号if (isdigit(line[line_cnt])){while (isdigit(line[line_cnt]))coef[cnt++] = line[line_cnt++];coef[cnt] = '\0';new->coef = atoi(coef);strcpy(coef, "\0");cnt = 0;if (line[line_cnt] == '\0')//if (line[line_cnt] == '\n')是错的,因为字符串不是以'\n'结尾的。{new->exp = 0;new->alph = head->alph;new->next = NULL;break;}}elsenew->coef = 1;//结点系数new->alph = line[line_cnt];//结点字母if (line[line_cnt + 1] == '^'){line_cnt += 2;//省略'^'while (isdigit(line[line_cnt]))exp[cnt++] = line[line_cnt++];exp[cnt] = '\0';new->exp = atoi(exp);//结点指数cnt = 0;strcpy(exp, "\0");}else{new->exp = 1;line_cnt++;}if (line[line_cnt] == '\0'){new->next = NULL;break;}else{temp = new;new = (poly*)malloc(sizeof(poly));temp->next = new;}}
}

输出:

解决了输入问题,相应地我们还需要确定如何输出链表,这是不难的。我们可以很简单地写出 output() 函数:

//代码清单7
void output(poly* head)
{poly *temp_ptr = head;printf("c(x)=");while (temp_ptr != NULL){printf("%c", temp_ptr->sign);printf("%d", temp_ptr->coef);printf("%c^", temp_ptr->alph);printf("%d", temp_ptr->exp);temp_ptr = temp_ptr->next;}putchar('\n');
}

这么一看,output() 函数的雏形就出来了。既然有了 input() 和 output() 函数,不妨在main() 函数中运行一下:

//代码清单8
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
int main()
{poly *head1 = (poly*)malloc(sizeof(poly));poly *head2 = (poly*)malloc(sizeof(poly));//输入input(head1);input(head2);//输出output(head1);output(head2);return 0;
}

结果为:

控制台中第1、2行是我们输入的多项式 a(x) 和 b(x) 的表达式,第3、4行分别输出了 a(x) 和 b(x) 的完整形式。但是,这两个多项式的输出形式不够美观,我们可以将它们变得和我们输入时的形式一样的美观,该怎么做呢?

首先,一个单项式的符号该不该被输出?对于第一个单项式,只有当它的符号是 '-' ,才该被输出,至于之后的每个单项式,它们的符号都应当被输出。 所以有:

//代码清单9
if (temp_ptr->sign == '-'||temp_ptr != head)//
{printf("%c", temp_ptr->sign);
}

用以替换代码清单7中的第8行代码

那么什么时候可以输出一个单项式的系数呢?一个是该单项式不为常数且其系数不为1,另一个是该单项式是常数,那么条件就可以设置为 temp_ptr->coef != 1&&temp_ptr->exp!=0 || temp_ptr->exp == 0 ,相当于一个命题 Q∧¬P∨P ⇔ Q∧P∨¬P∧P ⇔  Q∧P∨T ⇔ Q∧P 。那么,输出一个单项式的系数的条件可以变成 temp_ptr->coef != 1 || temp_ptr->exp == 0 。即:

//代码清单10
if (temp_ptr->coef != 1&&temp_ptr->exp!=0 || temp_ptr->exp == 0)printf("%d", temp_ptr->coef);

用以替换代码清单7中的第9行代码

至于是否输出字母和系数,它们应当并到一起来考虑,这样可以使得思路更加简单。也就是说,需不需要输出字母,如果要输出字母,那么又需要输出指数吗?现在有三种情况需要考虑:1、输出字母也输出指数;2、输出字母但不输出指数;3、字母和指数都不输出。对于第一种情况,复符合的条件是指数不为0同时不为1,若指数为0,不输出字母,自然不输出指数,指数为1,则不该输出指数;而第二种情况成立的条件是指数为1;第三种情况则是在指数为0是成立,我们对此种情况不必做任何输出处理。得到代码:

//代码清单11
if (temp_ptr->exp != 0 && temp_ptr->exp != 1)
{printf("%c^", temp_ptr->alph);printf("%d", temp_ptr->exp);
}
else if(temp_ptr->exp==1)printf("%c", temp_ptr->alph);

用以替换代码清单7中的第10、11行代码。需要注意代码清单11第7行 else if(temp_ptr->exp==1) 不可以直接更改为 else ,因为这里判断的是第二种情况,不能够将第二、三种情况混为一谈。

修改后的output()源代码:

//代码清单12
void output(poly* head)
{head = detach(head);poly *temp_ptr = head;printf("c(x)=");while (temp_ptr != NULL){if (temp_ptr->sign == '-'||temp_ptr != head){printf("%c", temp_ptr->sign);}if (temp_ptr->coef != 1&&temp_ptr->exp!=0 || temp_ptr->exp == 0)printf("%d", temp_ptr->coef);if (temp_ptr->exp != 0 && temp_ptr->exp != 1){printf("%c^", temp_ptr->alph);printf("%d", temp_ptr->exp);}else if(temp_ptr->exp==1)printf("%c", temp_ptr->alph);temp_ptr = temp_ptr->next;}putchar('\n');
}

重新运行一下 main() 函数:

相加:

终于到了最为关键的步骤,为此我建立了一个新的链表,将它命名为list3,由前两个多项式构建起的链表不妨分别命名为list1、list2。

假设list3中存储的多项式按照降幂顺序排列,那么具体的操作是怎么样的呢?首先,我们利用list1和list2的头指针head1和head2分别获取两个多项式的单项式,通过比较这两个单项式的指数来确定是否添加到list3当中来。若head1指向的结点代表的单项式的指数大于head2指向的结点代表的单项式的指数,那就将head1指向的结点的数据添加到list3中新开辟的结点当中来;若head1指向的结点代表的单项式的指数小于head2指向的结点代表的单项式的指数,那就将head2指向的结点的数据添加到list3中新创建的结点当中来;否则,即若head1指向的结点代表的单项式的指数等于head2指向的结点代表的单项式的指数,那就将两个所代表的单项式相加后添加到list3中新创建的结点当中来。

        这一过程不会停止,直到head1和head2均指向了NULL,为了更好地描述这个过程,我们用图片中的例子来表示这个过程:

到这一步相信各位对于我的想法已经了解得差不多了,而 plus() 函数的原理也很清晰明了了。

首先,我们用一个while循环来遍历list1和list2的每个结点,那么结束这个while循环的条件是什么呢?为了要遍历这两条链表,我们需要设置为head1!=NULL||head2!=NULL。为什么是||(或)而不是&&(且)呢?因为如果一条链表先遍历到头时,但另一条链表的仍然有剩余的未遍历的结点,加上||就相当于将这条链表的剩余的未遍历的结点直接连接到list3末尾了,使得while循环仍能正常进行下去。

而向list3添加结点的情况有3种,分别是1、将head1代表的单项式添加到list3;2、将head2代表的单项式添加到list3;3、将head1、head2代表的单项式相加后添加到list3。对于执行这三种情况的对应条件分别是:

1、head2 == NULL || head1 != NULL &&head1->exp > head2->exp

2、head1 == NULL || head2 != NULL &&head1->exp < head2->exp

3、¬1&&¬2

采用if()……else if()……else结构实现是个不错的选择,因为第3种情况执行的条件正是前两种情况能够执行的条件的否定加和。我们来解释一下这样设置第1个条件的原因,可以看出有两个原因符合将head1指向的结点代表的单项式添加到list3中去的情况:一个是list2已经遍历完了,另外一个是head1不指向NULL同时head1代表的单项式的指数大于head2代表的单项式的指数。对于第一个原因,很好理解,因为既然list2已经遍历完了,那么head2自然是指向NULL的,也就是head2==NULL是成立的;而对于第二个原因,则是head1、head2指向的结点非空同时head1代表的单项式的指数大于head2代表的单项式的指数,由于第一个原因已经表明head2==NULL,与之相对地,我们可以在第二个原因中略去head2!=NULL这个条件,所以总地表述为head1 != NULL &&head1->exp > head2->exp。需要强调的是,head2 == NULL 和 head1 != NULL &&head1->exp > head2->exp这两个表达式的位置千万不要互换。因为||表达式只要第一个条件为真,那么第二个及以后的条件就不会再去进行判断了,那么如果head1或head2指向NULL,编译器就会抛出异常:head1(head2)是nullptr。如果实在要互换的话,建议将上面的条件改为:

1、head1 != NULL && head2 != NULL && head1->exp > head2->exp || head2 == NULL

2、head1 != NULL && head2 != NULL && head1->exp < head2->exp || head1 == NULL

3、¬1&&¬2

现在,我们已经确定了在什么情况下将head1或head2代表的单项式或是它们的加和添加到list3,所以我们接下来讨论的是——如何将head1或head2代表的单项式或是它们的加和添加到list3?

第1、2种情况是等效的,因为我们只需直接将head1或head2代表的单项式直接添加到list3中即可。如何添加?因为此时list3中刚创建的结点为new,那么我们直接将单项式的数据填入new即可,让同类型的指针变量temp(和上面图片中的temp意义不同)也指向new所指向的结点。之后再创建一个结点,把它的地址赋给new,并将这个新创建的结点利用temp并入到list3当中来,以便进行下一次的添加。如果head1和head2均指向NULL,那就将这个new指向的结点的空间释放掉,同时使temp的指针域指向NULL,因为temp此时指向着new指向的前一个结点,new指向的结点没了,自然temp指向的下一个就是NULL了。但是对于第3种情况就没有如此简单了,还需要再次进行分类:1)、系数相加后等于0,不开辟新的结点空间,沿用旧的结点;2)、系数相加后不等于0,开辟新的结点空间,用新的结点。不论是哪种,都确实需要让两个结点的系数相加后与0进行比较。但我们设置了整个结点的coef域存放的数字均是整数啊,该如何利用到每个结点的sign域中存放的符号呢?可以想到,sign域中存放的是'+',那么将coef域的数字乘以 +1 后再存储回coef域即可;是'-',那么将coef域的数字乘以 -1 后再存储回coef域即可。用代码可以表述为:

//代码清单13
char sign[3]="+1\0";
sign[0] = head1->sign;//list1
head1->coef = atoi(sign)*head1->coef;
sign[0] = head2->sign;//list2
head2->coef = atoi(sign)*head2->coef;

另一种更为紧凑的方式是:

//代码清单14
head1->coef = head1->sign == '+' ? head1->coef : -head1->coef;//list1
head2->coef = head2->sign == '+' ? head2->coef : -head2->coef;//list2

在这里采用哪一种方式都可以,至于如何添入这个新的单项式,只要该单项式的系数不为0,直接以与第1、2种情况相同的方式添加即可。

plus()源代码:

//代码清单15
void plus(poly *head1, poly *head2)
{//add list2 and list1 to list3poly *head3 = (poly*)malloc(sizeof(poly));poly *new = head3;poly *temp = head3;char sign[3]="+1\0";while (head1 != NULL || head2 != NULL){if (head2 == NULL || head1 != NULL &&head1->exp > head2->exp)//不可颠倒{new->alph = head1->alph;new->coef = head1->coef;new->exp = head1->exp;new->sign = head1->sign;head1 = head1->next;temp = new;new = (poly*)malloc(sizeof(poly));temp->next = new;}else if (head1==NULL||head2 != NULL &&head1->exp < head2->exp){new->alph = head2->alph;new->coef = head2->coef;new->exp = head2->exp;new->sign = head2->sign;head2 = head2->next;temp = new;new = (poly*)malloc(sizeof(poly));temp->next = new;}else{sign[0] = head1->sign;//list1head1->coef = atoi(sign)*head1->coef;sign[0] = head2->sign;//list2head2->coef = atoi(sign)*head2->coef;if (head1->coef + head2->coef != 0){new->sign = head1->coef + head2->coef > 0 ? '+' : '-';//结点符号new->alph = head1->alph;new->coef = abs(head1->coef + head2->coef);new->exp = head1->exp;temp = new;new = (poly*)malloc(sizeof(poly));temp->next = new;}else{new->exp = 0;new->coef = 0;}head1->coef = abs(head1->coef);//还原head2->coef = abs(head2->coef);head1 = head1->next;head2 = head2->next;}}temp->next = NULL;if (new != head3){free(new);new = NULL;}output(head3);
}

请特别注意第64至68行的代码,此时是为了防止 a(x) 和 b(x) 相加等于0的情况的出现导致错误地释放掉唯一的结点,在这种情况下,list3中一直没有开辟新的结点,一直在沿用new所指向的结点。例如,当 a(x)=-x^3+x^2-3 和 b(x)=x^3-x^2+3 相加,直到while循环结束,new指向的结点的系数与指数也一直为0,new指向的结点成为list3中的唯一的结点直至结束。同时,这些原因也是第52至56行的代码存在的原因,如果两个多项式的和不为0,我们大可去掉这段代码,但为了增强程序的健壮性,我们需要添上去。

相减:

在相加的基础上,我们只需要将被减去的多项式的各个单项式的符号由'-'变成'+',由'+'变成'-'就可以了,之后进行相加操作,得到的就是两个多项式的差了。

minus()源代码:

//代码清单16
void minus(poly *head1, poly *head2)
{poly *temp = head2;while (temp != NULL){if (temp->sign == '+')temp->sign = '-';elsetemp->sign = '+';temp = temp->next;}plus(head1, head2);temp = head2;//还原while (temp != NULL){if (temp->sign == '+')temp->sign = '-';elsetemp->sign = '+';temp = temp->next;}
}

到这一步后,我们就可以实验一下plus()和minus()函数的功能了。设置main()函数为:

//代码清单17
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#include<math.h>
int main()
{poly *head1 = (poly*)malloc(sizeof(poly));poly *head2 = (poly*)malloc(sizeof(poly));input1(head1);input1(head2);plus(head1, head2);minus(head1, head2);return 0;
}

向控制台输入:

便得到:

可知,第3行的c(x)是两式相加的结果,第4行的c(x)是两式相减的结果。

利用文件进行输入输出:

既然在控制台上能够成功的得到我们从键盘键入的多项式的和与差,那么我们该考虑下一个阶段的问题了——如何从文件中读取多项式,以及如何将结果写入多项式?为了进行这个操作,我们需要用到基本的文件输入输出函数即fscanf()和fprintf()函数,以及文件开关函数fopen()以及fclose()函数:

1、fscanf():原型为int fscanf(FILE * stream, const char * format, [argument...]);,功能与scanf()函数相差不大,唯一的区别是fscanf()从stream指向的文件中输入数据,而scanf()直接从缓冲区中得到数据。但是,scanf()也可以用fscanf()替换,例如定义一个整型变量iInt,利用scanf()函数为其赋值是这样的:scanf("%d", &iInt);,但是也可以是:fscanf(stdin, "%d", &iInt);,这两种方式是等效的。

2、fprintf():原型为int fprintf( FILE *stream, const char *format, [ argument ]...);,功能是将数据打印到stream所指向的文件中,作用和printf()函数没有很大差别。

3、fopen():原型为FILE *fopen(const char *filename, const char *mode);,filename表示该位置的参数为一个文件名,mode表示对这个文件进行怎样的操作,是读还是写,抑或两者都进行,该函数的返回值就是一个指向文件的指针,故在使用fscanf()或fprintf()前均要先使用fopen()函数。

4、fclose():原型为int fclose( FILE *fp );,fp即指向一个文件的指针,该函数的作用是关闭文件。为什么是关闭文件呢?要关闭文件必然是先打开了文件,所以fopen()和fclose()函数是配套使用的,这也就是说一个文件不能连续打开两次或更多而中间不调用fclose()函数进行关闭。

借这四个函数,我们可以对input()函数和output()函数进行修改来达到利用文件输入输出的目的。

我们对input()函数修改一下:

//代码清单18
int flag = 0;
char line1[40];
poly *input(poly* head)
{poly *new = head;poly *temp = NULL;char coef[4];char exp[4];int cnt = 0;char line[40];FILE *fp = fopen("D:\\main1.txt", "r");if (flag == 1)strcpy(line, line1);else{fscanf(fp, "%s", line);fscanf(fp, "%s", line1);flag++;}strcpy(line, line + 5);int line_cnt = 0;while (1){if (isdigit(line[line_cnt]) || isalpha(line[line_cnt]))new->sign = '+';elsenew->sign = line[line_cnt++];//结点符号if (isdigit(line[line_cnt])){while (isdigit(line[line_cnt]))coef[cnt++] = line[line_cnt++];coef[cnt] = '\0';new->coef = atoi(coef);cnt = 0;if (line[line_cnt] == '\0'){new->exp = 0;new->alph = head->alph;new->next = NULL;break;}}//结点系数elsenew->coef = 1;//系数为1new->alph = line[line_cnt];//结点字母if (line[line_cnt + 1] == '^'){line_cnt += 2;//省略'^'while (isdigit(line[line_cnt]))exp[cnt++] = line[line_cnt++];exp[cnt] = '\0';new->exp = atoi(exp);//结点指数cnt = 0;strcpy(exp, "\0");}else{new->exp = 1;line_cnt++;}if (line[line_cnt] == '\0'){new->next = NULL;break;}else{temp = new;new = (poly*)malloc(sizeof(poly));temp->next = new;}}fclose(fp);return head;
}

我们定义了一个全局变量flag和line1[ ],flag用于确定是否将line1[ ]中的字符串复制到line[ ]中,line1[ ]就是用来存储第二个多项式的内容的。这两个变量在第13至20行代码起了重大作用。

再修改一下output()函数:

//代码清单19
void output(poly* head)
{poly *temp_ptr = head;FILE *fp = fopen("D:\\main2.txt", "a");fprintf(fp, "c(x)=");while (temp_ptr != NULL){if (temp_ptr->sign == '-' || temp_ptr != head)fprintf(fp, "%c", temp_ptr->sign);if (temp_ptr->coef != 1 || temp_ptr->exp == 0)fprintf(fp, "%d", temp_ptr->coef);if (temp_ptr->exp != 0 && temp_ptr->exp != 1){fprintf(fp, "%c^", temp_ptr->alph);fprintf(fp, "%d", temp_ptr->exp);}else if (temp_ptr->exp == 1)fprintf(fp, "%c", temp_ptr->alph);temp_ptr = temp_ptr->next;}fprintf(fp, "\n");fclose(fp);
}

看起来和原先的output()函数每什么很大差别,不过是输出的终端变成了文件。

在这里,还要说明一点,文件需要在先前就存在,要在硬盘中先创建好。我在PC的D盘当中是已经创建好了这两个文件的,各位在运行程序时自己要先弄好:

好了,本篇博客到此就完结了。完整的程序上面已经给出,大家也可以从我上传的资源中下载:1、控制台;2、文件,各位注意,是免费下载的啊,不需要C币。希望各位同志有所收获,也衷心祝愿各位能够平衡好学习和自己的课余生活,以昂扬的姿态一直向前。


欢迎指正我的上一篇博客:地址与值的更改

我的下一篇博客:一元多项式的相乘操作(链表)

一元多项式的相加和相减操作(链表)相关推荐

  1. 数据结构—— 一元多项式的运算(相加,相减,相乘)【C语言实现】

    用 C语言实现一元多项式的运算(相加,相减,相乘) 1.创建多项式时,无论指数项按什么顺序输入,输出均能实现以升幂顺序输出,且输入时有相同指数项时能够实现合并. 2.能够代入确切的X计算出最终多项式的 ...

  2. 一元多项式计算实验报告(相加,相减,相乘)

    一元多项式计算 任务:能够按照指数降序排列建立并输出多项式:能够完成两个多项式的相加.相减和相乘,并将结果输出: 在上交资料中请写明:存储结构.多项式相加的基本过程的算法(可以使用程序流程图).源程序 ...

  3. C指针与指针之间的相减操作

    指针与指针的相减操作,表示两个指针指向的内存位置之间相隔多少个元素(注意是元素,并不是字节数). 例如对于int类型的指针p和p1,p1-p的意义表示他们之间相隔多少个int类型的元素. 同样对于其他 ...

  4. keras中的Merge层(实现层的相加、相减、相乘)

    [时间]2019.03.25 [题目]keras中的Merge层(实现层的相加.相减.相乘) 详情请参考: Merge层 一.层相加 keras.layers.Add() 添加输入列表的图层. 该层接 ...

  5. Opencv中的图像相加,相减,相除,相乘(python实现)

    文章目录 1.相加 2.减法 3.乘法 4.除法 1.相加 代码实战 import os import cv2 import numpy as np#读取图片和缩放图片 lenna=cv2.imrea ...

  6. 两个函数相加、相减、相乘等之后的单调性

    1.同一区间上的两个单调函数乘积的单调性 2.两个在同一区间上的单调函数之和的单调性 增增相加得增, 减减相加得减,增减相加不定 3.两个在同一区间上的单调函数之差的单调性 增减相减得增 , 减增相减 ...

  7. java double相减_完美解决java double数相加和相减的方案

    我就废话不多说了,大家还是直接看代码吧~ /** * double的计算不精确,会有类似0.0000000000000002的误差,正确的方法是使用BigDecimal或者用整型 * 整型地方法适合于 ...

  8. 【OpenCV图像处理】一、图像相加、相减、相乘与相除的实现【转载】

    看完了数字图像处理后,从头开始使用opencv进行相关内容的实现,使用的环境是VS2013+OpenCV2.4.9. 1.图像相加 加运算就是两幅图像对应像素的灰度值或彩色分量进行相加.主要有两种用途 ...

  9. 两个阶跃函数相加或相减的傅里叶变换

    我自己思考的,不知道对否

  10. 一元多项式的相乘操作(链表)

    一元多项式的乘法运算如何实现,要求多项式采用链表存储结构. 目录 基本思路: 添加条件的分类: multiple()源代码: detach()源代码: 处理结果: 基本思路: 本篇博客不知觉间已经拖了 ...

最新文章

  1. SAP MM初阶之包含有Service item的服务采购订单
  2. 优化SQL步骤—— explain分析执行计划 (explain 之 id)
  3. 一个将当前目录下HEX文件的第一行数据删除的程序...
  4. RESTful架构详解【转】
  5. 【转】傅里叶分析之掐死教程(完整版)更新于2014.06.06
  6. 表格中建一个按钮_没想到手机还有扫描功能,纸质表格一键电子化,看完快去试试...
  7. 如何用iMazing来管理ios设备音频文件
  8. 用于制作app store的截图的工具:Brief Wrapper —— 最便捷的应用商店屏幕快照
  9. 如何使用XGBoost开发随机森林集成
  10. mysql导入数据库报错:ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_
  11. word修改或删除脚注或尾注分隔符
  12. 转载--12306刷票记
  13. Enhance Security with Port Knocking
  14. access()函数
  15. 霍兰德人格分析雷达图——python实现
  16. 小米、华为、智汀家庭云:让你实现不同设备之间的互联互通?
  17. 转载的一篇关于矢量字体的文章
  18. java基于ssm的道路求援车队管理系统
  19. eval()函数是什么?有什么作用?
  20. Checkra1n越狱更新0.12.4版本,支持iOS14.7.1越狱

热门文章

  1. Java中模拟评分系统_评分系统.java
  2. 领导合影站位图_领导主席台座次安排图解:政务礼仪
  3. 史上最搞笑的程序员段子,有图有真相!
  4. 宫廷秘方,给大家分享一下,祝大家身体健康
  5. Cobaltstrike内网工具的使用笔记
  6. 自然之美--记冬夏黄山
  7. AWS Account
  8. python描述对象静态特性的数据为_下列各项中,能同时影响资产和负债发生变化的是( )。...
  9. 单片机C51继电器控制C语言,51单片机对继电器的控制
  10. 删除文件unlink