背景
在数学上,一个一元n次多项式Pn(x)可按降序写成:
它是由n+1个系数唯一确定。因此,在计算机里它可以用一个线性表P来表示:
P=(Pn, Pn-1, …, P1, Po)一元多项式的运算包括加法、减法和乘法,而多项式的减法和乘法都 可以用加法来实现。
实验目的
线性表ADT的应用,基于线性表设计和实现两个一元多项式的加法运算。
提示:用有序线性表表示一元多项式。表内元素按照多项式的次数递减的次序排列。
基本要求
1.参考课程“线性表ADT的设计和表示”,定义、设计并实现一个有序线性表ADT。
2.需要利用链表来实现有序线性表。
3.需要基于有序线性表的基本操作来实现一元多项式的加法运算。
实验报告
一、 需求分析
0.问题描述
在数学上,一个一元n次多项式Pn(x)可按降序写成:
它是由n+1个系数唯一确定。两个多项式相加就是把它们的同次项的系数相加,给定两个多项式:
并且设m<=n,则多项式f(x)和g(x)的和指的是:
这里当m<n时,
现在给出两个多项式,请你求出这两个多项式的和。
1.问题分析
实现的功能:
1.分别通过键盘输入两个多项式的项数和每一项的指数和系数。
2.将两个多项式进行求和。
3.通过屏幕显示忽略0系数项的求和后的多项式。
2.输入数据
-
共输入两组数据,其中每组数据的第一行为项数m,接下来的m行中,每行第一个数为系数,第二个数为指数,数据之间用空格隔开。
-
系数采用整型int,指数采用整形int。
-
输入的每个多项式的指数保证各不相同,且以由大到小的顺序输入。
-
数据规模和用例:对于所有评测用例,保证每个多项式的项数0<m<20,且输入的系数满足double型,指数满足int型且不小于0。
3.输出数据
通过屏幕输出求和后的多项式,形式如,当某项系数为1时,输出忽略数字1;忽略系数为0的项。
4.测试样例设计
样例一:检验某项系数和为0的情况
3
2 4
1 2
3 1
4
1 4
-1 2
-2 1
3 0
输出:
样例二:检验所有系数和为0的情况
2
3 4
-1 1
2
-3 4
1 1
输出:0
样例三:检验每项指数都相同的情况
2
2 3
4 1
2
-1 3
6 1
输出:
样例四:检验每项指数都不同的情况
3
2 5
1 2
-4 1
3
5 4
2 3
3 0
输出:
样例五:检验含有常数项系数的情况
2
-3 4
1 0
3
2 5
1 3
3 0
输出:
二、 概要设计
0.数据类型
由于多项式的每一项都有指数和系数两个属性,因此这里可以定义一个二元组poly,其中系数(num)和指数(exp)是其数据成员。
1.抽象数据类型
数据对象:一元n次多项式的所有项
数据关系:每一项前后相继,具有一定的线性特征,为线性关系
基本操作:
1.准备能储存这组数据的储存空间
2.添加元素
3.访问元素
线性表ADT的设计:
ADT node{
数据对象:D={pi|pi∈ploy,i=1,2,... ,n,n∈整数}
数据关系:R={<pi-1,pi>|pi-1,pi∈D}
基本操作:
void init();
//操作功能:初始化线性表,构造出一个空表
void Append(ploy &Elem);
//操作功能:将元素Elem添加到线性表的表尾
void next();
//操作功能:将当前位置移动到下一位置
poly& getvalue();
//操作功能:获取当前位置的元素值
void moveToStart();
//操作功能:将当前位置移动定位到表头
bool empty();
//操作功能:判断线性表是否为空,若为空返回真,否则返回假
}
2.算法的基本思想
- 两个多项式用线性表A和B表示,每个多项式的项作为一个元素储存在线性表的一个位置中,每个元素都有系数和指数两个属性;
- 同时从A和B线性表表头的第一个元素开始比较其元素的指数;
- 如果A的指数大于B,则将A中的该项存到线性表C中,然后再比较B的当前项和A的后一项的指数;如果A的指数小于B,则将B的该项放入C中的,然后再比较A的当前项和B的后一项的指数;如果相等则将系数相加,系数不为0时,则将其系数与指数存入C中,然后同时比较A和B的后一项;
- 最后将A或B的剩余项存入C中;
- 遍历线性表C,将相加后多项式的各项输出。
3.程序的流程
共分为三个模块:输入模块、比较模块、输出模块
输入模块:初始化线性表A和B,调用添加函数,将通过键盘输入的系数和指数存储到A、B中。
比较模块:遍历A、B,合成新的系数,赋给线性表C的元素。
输出模块:遍历C,获取每个项的系数和指数,并通过屏幕输出。
三、详细设计
1.物理数据类型
物理数据类型:每个数据对象可以选用结构体这种数据类型,系数和指数为其数据成员。
物理数据结构:由于当相加后的多项式中存在大量的零系数时,采用顺序储存结构会浪费大量的储存空间,所以可采用链式储存结构来表示多项式,这里我们选用单向链表,多项式的每个系数非0的项作为一个数据对象储存在链表每个结点的数据域之中。
ADT的实现:
void init() // 链表初始化
{
current = tail = head = new link();
length= 0;
}
void next()//移动指针到下一位置
{
if (current != tail) current = current->next;
else return;
}
ploy& getvalue() // 返回当前元素
{
assert(current != NULL);//内容为空
return current->elem;
}
void append(poly p) // 在列表的尾部追加结点并赋值
{
tail = tail->next = new Link(p, NULL);
length++;
}
int getlength() //返回线性表长度
{
return length;
}
bool empty();//判断链表是否为空
{
if(length==0) return ture;
return false;
}
//移动至链表开始位置
void moveToStart()
{
current = head;
}
2.输入和输出的格式
输入格式:
第一行:输入第一组数据的项数m;
接下来的m行:输入每一项的系数和指数,系数和指数之间用空格隔开;
第m+1行:输入第二组数据的项数n;
接下来的n行:输入每一项的系数和指数,系数和指数之间用空格隔开;
输出格式:将合并后的多项式在屏幕上显示。形式如
当某项系数为1时,输出忽略数字1;忽略系数为0的项。
3.算法的具体步骤
输入模块:
//申请两个链表list1和list2 list list1,list2; poly p;
//输入多项式a的项数 cout<<"请输入多项式A的项数n:";
int n;cin>>n;
cout<<"请输入多项式A的系数和指数: ";
//输入多项式的系数和指数,, for(int i=0;i<n;i++){
先将其储存到结构体对象p中 cin>>num>>index;
,然后将p添加到链表1中 list1.Append(num,index);
}
//输入多项式b的项数 cout<<"请输入多项式B的项数m:";
int m;cin>>m;
cout<<"输入多项式B的系数和指数:”
//输入多项式的系数和指数 for(int i=0;i<m;i++){
,先将其储存到结构体p中, cin>>num>>index;
再将p添加到链表2的表尾 List2.Append(num,index);
}
比较模块:
//L1和L2储存已知多项式, L3List add(L1,L2, L3)
储存相加后的多项式 {
poly pp;
int x1=L1.getlength(),x2=L2.getlength();
//从头开始遍历L1和L2, while(x1>0&&x2>0)
当有其中的一个链表遍历结束 {
时,循环结束 if (L1.getvalue().exp == L2.getvalue().exp)
//当两个结点中元素指数相同时 {
if(L1.getvalue().num+L2.getvalue().num != 0)
//对两项进行合并,相加后的系数 {
不为0时,将该项储存到L3中 pp.exp=L1.getvalue().exp;
pp.num=L1.getvalue().num+L2.getvalue().num;
L3.append(pp);
}
L1.next();L2.next();x1--;x2--;}
//由于给定的两个多项式都是降幂排列, else if (L1.getvalue().exp > L2.getvalue().exp)
所以两个链表的结点的指数大的项不需 {
要合并 pp.exp=L1.getvalue().exp;
pp.num=L1.getvalue().num;
L3.append(pp);
L1.next();
x1--;
//将指数较大的项储存到链表L2的结点 }
else if(L1.getvalue().exp < L2.getvalue().exp)
{
pp.exp=L2.getvalue().exp;
pp.num=L2.getvalue().num;
L3.append(pp);
L2.next();
x2--;}}
//链表当前位置每改变一次,
长度x1,x2减少1 if (x1==0&&x2==0) return L3;
//x1=x2=0时两个链表同时遍历完 else if(x1>0)
返回L3 {while(x1>0){
pp.exp=L1.getvalue().exp;
//若x1>0,则L1没有遍历结束 pp.num=L1.getvalue().num;
将L1中剩下的结点的元素赋给 L3.append(pp);
L3 L1.next();
x1--;
}
return L3;}
//若x1>0,则L1没有遍历结束 else if(x2>0){
将L1中剩下的结点的元素赋给 while(x2>0){
L3 pp.exp=L2.getvalue().exp;
pp.num=L2.getvalue().num;
L3.append(pp);
L2.next();
x2--;}
return L3;}
} }
输出模块:
void print(list &L){
//如果L为空,则两多项式 if(L.empty()) cout<<0;
相加结果为0 else{
x=L.getlength();
while(x>0)
//否则,依次访问L中结点存储的元素 {
cout<<L.getvalue().num<<"x^"<<L.getvalue().exp;
x--; }
}
}
4.算法的时空分析
输入模块:要一直从链表的尾部插入节点,所以时间复杂度是O(n)。
比较模块:要同时遍历两个链表并比较其元素指数,所以时间复杂度是O(n^2)。
输出模块:获取节点的值的时间代价为常数,但要遍历整个链表C,因此时间复杂度是O(n)。
四、测试结果
测试数据一:
分析:两个多项式同有指数为4和2的项,可进行合并
测试数据二:
分析:合并后系数都为0,因此最后输出为0
测试数据三:
测试数据四:
测试数据五:
五、实验日志(选做)
2018-10-25:
看了超星中老师的实验报告解读录像,编撰了预习报告
2018-10-28:
根据老师对预习报告的批语,对问题需求分析进行改动,完善了实验题目
2018-11-1:
继续改动实验报告,对ADT的设计进行修改,运用了二元组
2018-11-2:
根据实验报告编写了代码,发现遇到的错误全部来源于指针的指向
2018-11-3:
修改代码,进行调试分析,完善最终的实验报告
附:代码
**Link.h**
#include <iostream>
#ifndef _LINK_H_
#define _LINK_H_
typedef struct poly
{
int num;
int exp;
}poly;
class Link
{
public:
poly p; //结点值
Link *next; //结点指针:在链表中指向下一结点
//构造函数
Link(poly& elemval, Link* nextval =NULL)
{ p = elemval; next = nextval; }
Link(Link*nextval = NULL) { next = nextval; }
};
#endif
**list.h**
#include <iostream>
#ifndef _LINK_H_
#define _LINK_H_
typedef struct poly
{
int num;
int exp;
}poly;
class Link
{
public:
poly p; //结点值
Link *next; //结点指针:在链表中指向下一结点
//构造函数
Link(poly& elemval, Link* nextval =NULL)
{ p = elemval; next = nextval; }
Link(Link*nextval = NULL) { next = nextval; }
};
#endif
**llist.h**
#include "Link.h"
#include "list.h"
#include<assert.h>
void list::init() // 链表初始化
{
curr = tail = head = new Link( );
length = 0;
}
void list::next()//移动指针到下一位置
{
if (curr != tail) curr = curr->next;
else return;
}
poly& list::getvalue( ) // 获取当前位置的元素值,赋值给Elem
{
assert(curr != NULL);//内容为空
return curr->p;
}
void list::insert(poly &P) // 有序的插入结点
{
curr = head;
int i=0;
for (i=0; i<length; i++)
{
if (P.exp > curr->next->p.exp)
{
curr->next = new Link(P,curr->next);
length++;
break;
}
curr = curr->next;
}
if (i == length)
{
tail = tail->next = new Link(P, NULL);
length++;
}
}
void list::append(poly &P)
{
tail = tail->next = new Link(P, NULL);
length++;
}
//移动至链表开始位置
void list::moveToStart(){ curr = head->next; }
int list::getlength() //返回线性表的长度
{
return length;
}
bool list::empty()
{
if (length == 0) return true;
return false;
}
**main**
#include <iostream>
#include "Link.h"
#include "list.h"
#include "llist.h"
using namespace std;
list add(list L1,list L2,list &L3);
void print(list L);
int main()
{
list list1,list2,list3; poly p1;
cout<<"请输入多项式a的项数n:";
int n; cin>>n;
list1.init();
cout<<"请对多项式a的系数和指数赋值,如: 2 4 3 1 "<<endl;
for(int i=0;i<n;i++)
{
cin>>p1.num>>p1.exp;
list1.insert(p1);
}
cout<<"请输入多项式b的项数m:";
int m; cin>>m;
list2.init();
cout<<"请对多项式b的系数和指数赋值,如: 2 4 3 1 "<<endl;
for(int i=0;i<m;i++)
{
cin>>p1.num>>p1.exp;
list2.insert(p1);
}
list3.init();
cout<<"请输出相加后的多项式,形如:2x^4+x^3-7x^2+1:"<<endl;
add(list1,list2,list3);
print(list3);
return 0;
}
list add(list L1,list L2,list &L3)
{
L1.moveToStart();
L2.moveToStart();
poly pp;
int x1=L1.getlength(),x2=L2.getlength();
while(x1>0&&x2>0)
{
if (L1.getvalue().exp == L2.getvalue().exp)
{
if (L1.getvalue().num+L2.getvalue().num != 0)
{
pp.exp=L1.getvalue().exp;
pp.num=L1.getvalue().num+L2.getvalue().num;
L3.append(pp);
}
L1.next();L2.next();
x1--;x2--;
}
else if (L1.getvalue().exp > L2.getvalue().exp)
{
pp.exp=L1.getvalue().exp;
pp.num=L1.getvalue().num;
L3.append(pp);
L1.next();
x1--;
}
else if (L1.getvalue().exp < L2.getvalue().exp)
{
pp.exp=L2.getvalue().exp;
pp.num=L2.getvalue().num;
L3.append(pp);
L2.next();
x2--;
}
}
if (x1==0&&x2==0) return L3;
else if(x1>0)
{
while(x1>0)
{
pp.exp=L1.getvalue().exp;
pp.num=L1.getvalue().num;
L3.append(pp);
L1.next();
x1--;
}
return L3;
}
else if(x2>0)
{
while(x2>0)
{
pp.exp=L2.getvalue().exp;
pp.num=L2.getvalue().num;
L3.append(pp);
L2.next();
x2--;
}
return L3;
}
}
void print(list L)
{
if(L.empty()) cout<<0;
else{
L.moveToStart();
if (L.getvalue().num == 1)
{
if (L.getvalue().exp == 1) cout<<"x";
else if (L.getvalue().exp > 1) cout<<"x^"<<L.getvalue().exp;
else if(L.getvalue().exp == 0) cout<<1;
}
else
{
cout<<L.getvalue().num;
if (L.getvalue().exp == 1) cout<<"x";
else if (L.getvalue().exp > 1) cout<<"x^"<<L.getvalue().exp;
}
for (int i=1; i<L.getlength(); i++)
{
L.next();
if (L.getvalue().num>0)
{
cout<<"+";
if (L.getvalue().num == 1)
{
if (L.getvalue().exp == 1) cout<<"x";
else if (L.getvalue().exp > 1) cout<<"x^"<<L.getvalue().exp;
else if(L.getvalue().exp == 0) cout<<1;
}
else
{
cout<<L.getvalue().num;
if (L.getvalue().exp == 1) cout<<"x";
else if (L.getvalue().exp > 1) cout<<"x^"<<L.getvalue().exp;
}
}
else if (L.getvalue().num<0)
{
if (L.getvalue().num == -1)
{
if (L.getvalue().exp == 1) cout<<"-x";
else if (L.getvalue().exp > 1) cout<<"-x^"<<L.getvalue().exp;
else cout<<-1;
}
else
{
if (L.getvalue().exp == 1) cout<<L.getvalue().num<<"x";
else if (L.getvalue().exp > 1)cout<<L.getvalue().num<<"x^"<<L.getvalue().exp;
else cout<<L.getvalue().num;
}
}
}
}
}