实现一元多项式的加减法运算,要求多项式采用链表存储结构。多项式的输入输出格式不做要求,如可以用键盘按系数、指数格式输入,输入(0,0)代表输入结束,也可以用文件数据形式输入。只要把输入输出描述清楚即可。
目录
题前想法:
题目已经表述得很清楚了,而我们关键要做的就是如何实现两个一元多项式的加法。在这个基础上,减法操作相当于为减号后面的多项式各个项的系数乘以 -1 后与减号前面的多项式相加即可。我们给出几个实例(采用降幂输入):
(1)a(x)=3x^5+7x^3+1
b(x)=x^5-x^3+2x+8
加法运算结果:
c(x)=4x^5+6x^3+2x+1
(2)a(x)=3x^5+7x^3+1
b(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+1
b(x)=-3x^5-7x^3-1
加法运算结果:
c(x)=0
(4)a(x)=0
b(x)=1
加法运算结果:
c(x)=1
(5)a(x)=x^4+x^2+1
b(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 = '+'; else new->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; } } else new->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 = '+';
else
new->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;
}
}
else
new->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 list3
poly *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;//list1
head1->coef = atoi(sign)*head1->coef;
sign[0] = head2->sign;//list2
head2->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 = '-';
else
temp->sign = '+';
temp = temp->next;
}
plus(head1, head2);
temp = head2;//还原
while (temp != NULL)
{
if (temp->sign == '+')
temp->sign = '-';
else
temp->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 = '+';
else
new->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;
}
}//结点系数
else
new->coef = 1;//系数为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;
}
}
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币。希望各位同志有所收获,也衷心祝愿各位能够平衡好学习和自己的课余生活,以昂扬的姿态一直向前。
欢迎指正我的上一篇博客:地址与值的更改
我的下一篇博客:一元多项式的相乘操作(链表)