Exp01 多项式乘法问题
Author: Maskros
实验目的
设计一个一元稀疏多项式简单计算器
实验内容与要求
一元稀疏多项式简单计算器的基本功能是:
(1)输入并建立多项式。
(2)输出多项式,输出形式为整数序列: n , c 1 , e 1 , c 2 , e 2 , . . . , c n , e n n,c_1,e_1,c_2,e_2,...,c_n,e_n n,c1,e1,c2,e2,...,cn,en, 其中 n n n 是多项式的项数, c i c_i ci 和 e i e_i ei 分别是第 i i i 项的系数和指数,序列按指数 降序排列。
(3)多项式 a a a 与多项式 b b b 相乘,建立多项式。
实验内容和实验步骤
在这里我们分别通过顺序表和链表两种方法来实现实验要求
- 大体思路:
- 链表
O
(
N
2
)
O(N^2)
O(N2):
- 结构体
Pol
存储多项式每项的系数和指数 CreatePol(Pol *&head)
函数用于接收输入的多项式元素,创建一个多项式PrintPol(Pol *&head)
函数用于打印多项式MultiPol(Pol *&a, Pol *&b, Pol *&c)
函数用于实现多项式的乘法,每次插入一项顺序遍历新链表 ,按指数升序插入或合并,最后得到以*c
为头结点的链表,即所求结果Getlength(Pol &pol)
函数遍历链表返回多项式的项数,用于打印时使用- ps:计算出来是空项的判断依据为
pol->c=0
, 故在输出和计算长度时将其直接忽略
- 结构体
- 顺序表
O
(
(
l
o
g
N
)
2
)
O((logN)^2)
O((logN)2):
- 使用定义的map类型
typedef map<int, int> Pol
存储多项式每项的系数和指数,key
为指数,value
为系数,map.size()
为多项式项数 create()
函数用于接收输入的多项式元素,创建一个多项式print(Pol &pol)
函数用于打印多项式multi(Pol &p1, Pol &p2)
函数用于实现多项式的乘法,完成合并后,返回一个Pol类型的多项式- 使用map的好处:由于map遍历的特性,不用再次从小到大排序,并且易于查找和合并同类项
- 使用定义的map类型
- 链表
O
(
N
2
)
O(N^2)
O(N2):
- 输入形式:两个多项式,两个整数序列 n , c 1 , e 1 , c 2 , e 2 , . . . , c n , e n n,c_1,e_1,c_2,e_2,...,c_n,e_n n,c1,e1,c2,e2,...,cn,en, 其中 n n n 是多项式的项数, c i c_i ci 和 e i e_i ei 分别是第 i i i 项的系数和指数,序列按指数 降序排列
- 输出形式:一个整数序列 n , c 1 , e 1 , c 2 , e 2 , . . . , c n , e n n,c_1,e_1,c_2,e_2,...,c_n,e_n n,c1,e1,c2,e2,...,cn,en,标号规则同上
链表实现
//presented by Maskros - 2021/03/31
#include<bits/stdc++.h>
using namespace std;
struct Pol{
int c; //coefficient
int e; //exponent
Pol *next;
};
int Getlength(Pol *&head){ //Calculate the number of terms in a polynomial
if(head==NULL) {cout<<"NO_ELEM_ERROR"<<endl; return 0;}
Pol *p=head;
int cnt=0;
while(p->next!=NULL){
p=p->next;
if(p->c==0) cnt--; //Empty items are not included in the count
cnt++;
}
return cnt;
}
void CreatePol(Pol *&head){ //Create a polynomial
//initialization
head=(Pol *) new Pol;
head->c=0;
head->e=0;
head->next=NULL;
Pol *pol=head;
int n; cin>>n;
for(int i=1;i<=n;i++){
pol->next=(Pol *) new Pol;
pol=pol->next;
cin>>pol->c>>pol->e;
pol->next=NULL;
}
return;
}
void PrintPol(Pol *&head){ //Print polynomial
Pol *p=head;
int n=Getlength(p);
cout<<n<<" ";
while(p->next!=NULL){
p=p->next;
if(p->c!=0) cout<<p->c<<" "<<p->e<<" "; //Do not print empty items
}
return;
}
void MultiPol(Pol *&a, Pol *&b, Pol *&c){ //Polynomial multiplication
Pol *p1=a, *p2=b;
int tmpc,tmpe;
bool t; //Check if the item has been inserted
c=(Pol *) new Pol;
c->c=0; c->e=0; c->next=NULL; //initialization
Pol *p3,*pre; //$pre$ is the node before the current position
while(p1->next!=NULL){
p1=p1->next;
p2=b;
while(p2->next!=NULL){
p2=p2->next;
t=false;
tmpc=p1->c*p2->c;
tmpe=p1->e+p2->e;
p3=c;
pre=c;
while(p3->next!=NULL){
p3=p3->next;
if(p3->e>tmpe){ //The insertion position is before the current position
pre->next=(Pol *)new Pol;
pre=pre->next;
pre->e=tmpe;
pre->c=tmpc;
pre->next=p3;
t=true;
}
else if(p3->e==tmpe){ //Combine similar items at current location
p3->c+=tmpc;
t=true;
}
else if(p3->next!=NULL && p3->e<tmpe && p3->next->e>tmpe){ //The insertion position is after the current position
pre=p3->next;
p3->next=(Pol *)new Pol;
p3=p3->next;
p3->e=tmpe;
p3->c=tmpc;
p3->next=pre;
t=true;
}
if(t==true) break;
pre=p3;
}
if(t==false){ //Insertion position is at the end of the linked list
p3->next=(Pol *)new Pol;
p3=p3->next;
p3->e=tmpe;
p3->c=tmpc;
p3->next=NULL;
}
}
}
return ;
}
int main(){
Pol *p1,*p2,*p3;
p1=NULL,p2=NULL,p3=NULL;
CreatePol(p1);
CreatePol(p2);
MultiPol(p1,p2,p3);
PrintPol(p3);
return 0;
}
顺序表实现
//presented by Maskros - 2021/03/31
#include<bits/stdc++.h>
using namespace std;
typedef map<int, int> Pol;
Pol p1,p2,p3;
void create(Pol &pol){ //Create a polynomial
int co,ex,n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>co>>ex;
pol[ex]+=co;
}
}
void print(Pol &pol){ //Print polynomial
map<int,int>::iterator it;
cout<<pol.size()<<" ";
for(it=pol.begin();it!=pol.end();it++){
cout<<it->second<<" "<<it->first<<" ";
}
}
Pol multi(Pol &p1, Pol &p2){ //Polynomial multiplication
Pol p3;
map<int,int>::iterator it1,it2;
for(it1=p1.begin();it1!=p1.end();it1++){
for(it2=p2.begin();it2!=p2.end();it2++){
p3[it1->first+it2->first]+=it1->second*it2->second; //Insert p3 after multiplying directly
if(p3[it1->first+it2->first]==0) p3.erase(it1->first+it2->first); //Delete extra items
}
}
return p3;
}
int main(){
create(p1);
create(p2);
p3=multi(p1,p2);
print(p3);
return 0;
}
实验用测试数据和相关结果分析
INPUT1:
2 1 2 3 4
3 1 3 1 5 2 6
OUTPUT1:
5 1 5 4 7 2 8 3 9 6 10
INPUT2:
3 2 0 3 2 4 3
3 -1 0 1 1 4 3
OUTPUT2:
7 -2 0 2 1 -3 2 7 3 4 4 12 5 16 6
IUPUT3:
3 -1 -1 1 0 1 1
2 2 -2 3 2
OUTPUT3:
6 -2 -3 2 -2 2 -1 -3 1 3 2 3 3
INPUT4:
2 1 0 1 1
2 -1 0 1 1
OUTPUT4:
2 -1 0 1 2
- 结果分析:由此可见,无论是错序合并,系数、项数取正、负、零,以及结果出现零项的处理均可实现,输出正确答案
实验总结
- 链表写起来比较基础,对于插入、遍历、删除等操作无法取巧,包括在计算多项式的长度时都需要遍历,只能扎扎实实来写,排序的工作在插入的时候直接就按照顺序插入来完成,如果数据量比较大的话感觉时间复杂度有点高,所以有点小麻烦,但是感觉写下来一趟,强化了我对于指针、地址、空间等方面的理解,写起来也没有之前那么生疏了
- 顺序表一开始打算用 struct 型的 vector 来存,然后直接插入,最后再
sort()
一下方便排序,结果在相同项合并的操作上发现还是需要像链表一样遍历去查找位置,所以感觉有点麻烦,本来写好了就全删了,换了一种思路,用 map 来实现
式的长度时都需要遍历,只能扎扎实实来写,排序的工作在插入的时候直接就按照顺序插入来完成,如果数据量比较大的话感觉时间复杂度有点高,所以有点小麻烦,但是感觉写下来一趟,强化了我对于指针、地址、空间等方面的理解,写起来也没有之前那么生疏了 - 顺序表一开始打算用 struct 型的 vector 来存,然后直接插入,最后再
sort()
一下方便排序,结果在相同项合并的操作上发现还是需要像链表一样遍历去查找位置,所以感觉有点麻烦,本来写好了就全删了,换了一种思路,用 map 来实现 - 用 map 太爽了,30来行就完事了,不用手动排序,查找合并也很方便,除了加了一个空项的
erase()
删除工作,基本上没什么坑,遍历也很舒服,stl永远滴神