纪念一下,目前写过的最长的程序,比上次作业题airport还长(๑╹◡╹)ノ"""
现在写完了,回想一下也不难,比起难度还是繁琐多一点。
不过做程序员就是要细心啊,再繁琐也不能出错。
所以做完这个感觉自己离成为程序员又近了一步,撒花✿✿ヽ(°▽°)ノ✿
下面进入正题。
题目
写一个程序,功能如下:
输入下列一个指令,实现所描述的功能:
[ ? ] 读入一个多项式,这个多项式包含一个含x的单项式和一个常数项,常数项可以为0。
[ + ] 对栈中最上面的两个多项式做加法,并把结果再压入栈中。
[ - ] 对栈中最上面两个元素做减法,并把结果再压入栈中。
[ * ] 取出栈顶多项式,再输入一个多项式,做乘法,结果压入栈中。
[ = ] 展示栈顶元素。
[ q ] 退出程序。
运行示例:
①功能索引,我选择 [ ? ] —— 读入一个多项式。
②我在这里先输入了 x^2+3 。
③ 再选择[ ? ] ,读入了 2x^2 。
④ 选择[ + ], 让刚读入的两个多项式做加法,把结果压入栈。
⑤ 选择[ * ],取出栈顶多项式,与我现在输入的 7x^3 做乘法,把结果压入栈。
代码&思路
一.从大方向上看,多项式的存储用链表。栈中元素类型是链表头指针。
多项式是由一个个单项式组成的,对于同一个未知数而言,单项式特征值只有倍数和指数。
所以链表结点定义如下:
struct xiang{
double beishu;
int degree;
struct xiang * next;
};
二. 实现这个程序所示的功能,我用到了如下函数:
①打印索引,无需赘述。
void print_introductions();
void print_introduction_1();
②创建一个多项式。
struct xiang* creat_a_term();
③打印多项式。
void show_the_term(struct xiang *head);
④做运算的函数,返回值是储存计算结果多项式的链表的头指针。
struct xiang* add_two_terms(struct xiang *head1, struct xiang *head2);
struct xiang* minus_two_terms(struct xiang *head1, struct xiang *head2);
struct xiang* multiply_two_terms(struct xiang *head1, struct xiang *head2);
⑤用于化简多项式的函数,把指数相同的项合并。
void simplify(struct xiang *head);
三.函数具体讲解,以及我遇到的问题。
①创建一个多项式。
(因为题目中输入的多项式最多只有两项,所以这是个只有两个结点的链表)
注意不要忘记把后一个结点的next指针设置成NULL!
我一开始忘记了,结果后面调用show_the_term函数的时候程序会异常退出。找了好久才找到orz…
struct xiang * creat_a_term(){
struct xiang *head=(struct xiang*)malloc(sizeof(struct xiang));
printf("coefficient?");
scanf("%lf",&head->beishu);
printf("exponent?");
scanf("%d",&head->degree);
head->next=(struct xiang*)malloc(sizeof(struct xiang));
printf("coefficient?");
scanf("%lf",&head->next->beishu);
if(head->next->beishu==0)
{
head->next->degree=0;
head->next->next=NULL; /**这个地方易出错**/
return head;
}
else{
printf("exponent?");
scanf("%d",&head->next->degree);
if((head->next->degree)!=0)
{
printf("Bad exponent: Polynomial terminates without its last term.\n");
head->next->beishu=0;
}
}
head->next->next=NULL; /**还有这**/
return head;
};
②打印多项式。
emm怎么说呢,也挺麻烦,要考虑什么时候显示x,什么时候显示x的系数和指数,什么时候显示加号,什么时候显示减号……
不过抛开那些我这里新学了一招:
用%g打印double类型浮点数,会自动去除多余的零,非常简洁。
void show_the_term(struct xiang *head){
struct xiang *p=head;
double coefficient;
int exponent;
printf(" ");
while(p!=NULL)
{
coefficient=p->beishu;
exponent=p->degree;
if(coefficient!=0)
{
if((coefficient!=1&&coefficient!=-1)||exponent==0)
{
if(coefficient<0)
printf("%g",-coefficient);
else
printf("%g",coefficient);
}
if(exponent!=0)
printf("x");
if(exponent!=0&&exponent!=1)
printf("^%d",exponent);
}
if(p->next!=NULL&&p->next->beishu > 0)
printf(" + ");
if(p->next!=NULL&&p->next->beishu < 0)
printf(" - ");
p=p->next;
}
printf("\n");
}
③实现多项式运算。
加法:
思想很朴素,就是把q链表连到p链表末。因为我们有化简函数,所以即便有可以合并的同类项也不用在这里写。化简函数NB!!(破音
struct xiang* add_two_terms(struct xiang *head1, struct xiang *head2){
struct xiang *p;
for(p=head1; p->next!=NULL; p=p->next);
p->next=head2;
return head1;
}
减法:
思路与加法相同,把q的每一项的倍数乘以-1,再连接到p链表末。
struct xiang* minus_two_terms(struct xiang *head1, struct xiang *head2){
struct xiang *p, *q;
for(q=head2; q!=NULL; q=q->next)
q->beishu = -1 * (q->beishu);
for(p=head1; p->next!=NULL; p=p->next);
p->next=head2;
return head1;
}
乘法:
思路也非常朴素,两个循环遍历两个链表,使多项式q中的每一项与多项式p的每一项相乘(倍数相乘,指数相加),相乘结果用一个新链表储存。
需要小小注意一下,这里为了循环更好写,新链表的头结点是没有储存我们的计算结果的,所以返回值是新链表的第二个结点。
struct xiang* multiply_two_terms(struct xiang *head1, struct xiang *head2){
struct xiang *p, *q,*result,*r;
result=(struct xiang*)malloc(sizeof(struct xiang));
r=result;
for(q=head1;q!=NULL;q=q->next)
for(p=head2;p!=NULL;p=p->next)
{
r->next=(struct xiang*)malloc(sizeof(struct xiang));
r=r->next;
r->beishu=q->beishu * p->beishu;
r->degree=q->degree + p->degree;
}
r->next=NULL; //不要忘记哦
return result->next;
}
④肥肠好用的化简函数。
思路就是两个循环遍历这一个链表,指数相等则倍数相加,相加结果储存在其中一项中,另一项置零。
其实倍数置零就OK,但我保险起见还是都置零好了。
void simplify(struct xiang *head){
struct xiang *p,*q;
for(p=head;p!=NULL;p=p->next)
for(q=head;q!=NULL;q=q->next)
{
if(p!=q && q->degree == p->degree)
{
p->beishu += q->beishu;
q->beishu=0;
q->degree=0;//没有也行
}
}
}
四.简单的主函数。
int main(){
char command;
stack< struct xiang* > terms;
struct xiang *p,*q,*r;
printf("This is a polynomials program.\n");
print_introductions();
scanf("%s",&command);
while(command!='q')
{
switch(command)
{
case '?':
print_introduction_1();
p=creat_a_term();
simplify(p);
terms.push(p);
printf("The following Polynomial has been pushed into the stack:\n");
show_the_term(p);
break;
case '=':
if(!terms.empty())
{
p=terms.top();
show_the_term(p);
}
else
printf("empty stack!\n");
break;
case '+':
if(!terms.empty())
{
p=terms.top();
terms.pop();
if(!terms.empty())
{
q=terms.top();
terms.pop();
add_two_terms(p,q);
simplify(p);
}
show_the_term(p);
terms.push(p);
}
else printf("empty stack!\n");
break;
case '-':
if(!terms.empty())
{
p=terms.top();
terms.pop();
if(!terms.empty())
{
q=terms.top();
terms.pop();
minus_two_terms(p,q);
simplify(p);
}
terms.push(p);
show_the_term(p);
}
else printf("empty stack!\n");
break;
case '*':
if(!terms.empty())
{
p=terms.top();
terms.pop();
}
else printf("empty stack!\n");
q=creat_a_term();
r=multiply_two_terms(p,q);
simplify(r);
terms.push(r);
show_the_term(r);
break;
default :
print_introductions();
}
printf("\nSelect command and press <Enter>:\n");
scanf("%s",&command);
}
}
我的运行结果
‘’
以上就是我的解答。
今上午以为自己写的足够好了,结果下午在写这篇博客的时候仍是发现了很多不周之处。
也许这就是写博客的好处吧,小黄鸭debug法 (๑╹◡╹)ノ"""
‘’
——2019.3.31 于图书馆