储备知识:
1,储备知识:比如 L 是一个链表的首地址 如果通过 L = L -> next ; 来访问元素,那么,下一次你还能找到它的L原来的地址吗?就不能了是吧 所以,我们新建一个指针,p = L->next, 通过,p = p->next 来访问L,L的地址也不改变,但却可以通过 p 来访问,虽然简单,但是非常重要!!!!!
2,C++ 的用法, cin 等于 scanf() ; cout 等于 printf()
例如: cin >> a; 等于 scanf("%d",&a);
cout << a; 等于 printf("%d",a);
大家不要纠结这些,挺简单的
废话不多说,先看代码
一步一步来
第一步:链表的定义:
typedef struct{ double a; int e; }term; //这个结构体是用来储存结构体的指数和系数 typedef struct LNode { //a代表系数,n代表指数 term data; struct LNode *next; }*Link,*LinkList;
第二步,定义链表的头节点:
bool InitList(LinkList &l){ //初始化一个链表的头节点, l = new LNode; l->next = NULL; return true; }
定义成功后,返回true ,代表定义成功
第三步,链表的有序创建:
怎样实现有序呢?
这样就可了吧,找到第一个次数大于3 的,把x^3插入其前面
具体实现:
void CreatLinklist (LinkList &l, int len,int number){ //创建链表 InitList(l); //初始化一个链表的头节点 for(int i = 1 ;i <= len; i++){ //依次输入n个非0项 LinkList s = new LNode; //生成新节点s cout<<"---------------------"<<"\n"; cout<<"请输入第"<<number<<"个链表的第"<<i<<"项的系数和指数"<<"\n"; cout<<"---------------------"<<"\n"; cout<<"请输入:"<<"\n"; cin >> s->data.a >> s->data.e; //输入系数和指数 LinkList t = l; //生成两个节点,t代表s节点的前驱 LinkList t1 = l->next; //t1代表 s节点的后继, // 创建这两个的目的就是把s节点插入t和t1中间 while(t1 && t1->data.e < s->data.e){ //通过比较指数,找到第一个大于输入项指数的项 t = t1; t1 = t1->next; } s->next = t1; //把输入的s节点插入到第一个大于等于s节点指数的前面 t->next = s; } cout<<"---------------------"<<"\n"; cout<<"第"<<number<<"个链表创建成功"<<"\n"; }
这个实现了多项式的有序创建,这个不难,书上有原型,我稍微动了下(47页)
预警!!!!难点(大家啃下来第四步,这个程序就没问题了)
第四步:找元素,增删改查 的查:
试想,如果我们想合并一个多项式的相同元素,怎么办?
是不是要把相同的元素找出来合并,这个函数就是这个功能
int cmp(term a,term b) // 比较两个元素的指数 { if (a.e==b.e) return 0; else return (a.e-b.e)/abs(a.e-b.e); } /* 这个函数是看老师的,帮了我大忙,大家好好看看 L是我们的链表,e是我们要查找的元素,s, q ,是两个工具人, 是用来记录结果的 */ bool LocateElem(LinkList L,term e,Link &s,Link &q) { Link p; //定义一个链表类型的指针 s = L; //把第一个工具人s用L这个地址赋值 p = s->next; //把第P用s->next这个地址赋值 //发现了吗?目前,s=L ,p =l->next , 接着看; while (p && cmp(p->data,e) != 0) //当p指针指向的节点的指数,和我们要找的指数不相等时,循环继续,结束时,要么找到了,要么p指向了链表的尾节点// { s=p; p=p->next; } if(!p) { //如果指向了我们的尾节点,证明没有找到 s = q = null; return 0; } else { //反面就是找到啦,这时候用到我们的第二个工具人q, 因为p是我们找到的节点,把p 的值赋值给q 才能传回main()里面哦 q = p; return 1; } }
第五步:删除元素:
删除 L 中的 s 后面的那个节点:
void Delnext(LinkList &L,Link s) //删除某一节点 { Link q = s->next; s->next = q->next; delete q; }
第六步:多项式相加功能:
书上有我的代码的原型,在48页。这个不是大头,大头是相乘。
//多项式的相加 void Addlinklist(LinkList la , LinkList lb , LinkList &lc){ //用p1指向la 的首元节点,p2指向lb的首元节点 ,p3指向lc的头节点,相加后的结果放在lc里面 LinkList p1 = la->next , p2 = lb->next , p3 = lc; while(p1 && p2){ //当两个表不为空的时候 if(p1->data.e == p2->data.e){ //比较 la 和 lb 里的当前元素 的指数,如果相等,把它们加起来 int sum = p1->data.a + p2->data.a; // sum就是它们的和 if(sum != 0) { LinkList s = new LNode; // 定义一个临时节点 s s->data.a = sum; /* 下面的操作是把s的系数变成sum,然后把s这个临时节点,插入到lc,也就是我们的结果里 */ s->data.e = p1->data.e; s->next = p3->next; //插入操作 p3->next = s; p1 = p1->next; //继续向后加,两个指针向后移 p2 = p2->next; } } /* 如果la 和lb 当前位置元素的指数 la < lb ,那么就让la的指针向后移,顺便把当前元素插入到lc里面 */ else if(p1->data.e < p2->data.e){ LinkList s = new LNode; s->data.a = p1->data.a; s->data.e = p1->data.e; s->next = p3->next; p3->next = s; p1 = p1->next; } /* 如果lb < la 就让 lb 的指针,就让lb的指针向后移 */ else { LinkList s = new LNode; s->data.a = p2->data.a; s->data.e = p2->data.e; s->next = p3->next; p3->next = s; p2 = p2->next; } /* 迷了吗? p3 就是 lc 哦 */ p3 = p3->next; // 因为插入了元素,所以,lc 也向后移一位 } p3->next=p1?p1:p2; /* 当p1 为空的时候,说明循环退出,是因为p1 ,所以p2 还没到末尾哦 三目运算符,再给你们复习下相当于 if(p1){ p3->next = p1; } else p3->next = p2; */ }
第七步:一元多项式的相乘:
怎么实现呢:
假如有两个链表
是不是应该 把表1的 1号元素和表二的所有元素相乘,再加上表1的 2号元素和表二的所有元素相乘,再向后一直加....
,怎么用代码实现呢?? 双重for循环就ok.
因为两个表创建的时候就是有序的,相乘之后,只要按顺序记录,也就是有序的啦
//多项式的相乘 void MultiPList(LinkList la,LinkList lb, LinkList &lc){ LinkList p1 = la->next, p2 = lb->next, p3 = lc; int numbera = 0, numbere = 0; //代表两个多项式相乘后的系数和指数 for(int i = 1; i <= n; i++){ //n为第一个链表的元素数目,第一行定义过 int t = p1->data.a,t1 = p1->data.e; //t 代表p1(la)当前元素的系数,t1 代表指数 p1 = p1->next; //咱们已经知道了,p1 的数据,把它们向后移 for(int j= 1 ; j <= m; j++){ //m 在程序第一行定义过了 numbera = t * (p2->data.a); //相乘之后的系数 numbere = t1 + p2->data.e; //相乘之后的指数 LinkList s = new LNode; //用一个新节点s,把这次相乘的结果记下来 s->data.a = numbera; s->data.e = numbere; s->next = p3->next; p3->next = s; // 把s插入到p3后面 p2 = p2->next; p3 = p3->next; } p2 = lb->next; // 表1 的第一个元素,已经和表二的第一个元素相乘了,接下来是不是 表1的第1个元素该和表二的第二个元素想乘了,所以p2 = lb->next; } }
第八步,多项式的合并:
试想,如果我们输入两项是 2x 3x 那按照我们的程序,输出仍然是 常数C+ 2x + 3x + 更高次幂
明显不是我的的期望,所以要合并同类项
怎样合并呢
比如一个我们的结果是 1 + x + x + x^2 +x^2
那么就应该找 1 后面还有没有常数,找完一遍后,再去找 x 后面还有没有一次项 ,找到了,就合并
变成了 1 + 2x + x^2 + x^2 ,再去找 x^2 后面有没有2次项,找到了,就合并,如下图
看实现过程:
//多项式的合并 void MulMerge(LinkList &la){ LinkList p1 = la -> next ; // p1 指向 la 的首元节点 while(p1){ //当p1 不为空 term t = p1 -> data; // t 就等于当前元素的数据域 LinkList s,q; // 还记得怎么找元素吗 int N = n*m; while(N--){ //为什么要加这层循环大家想想 if(LocateElem(p1 , t , s, q)){ //如果找到了t,q返回的就是这个元素的地址,q = s->next; p1->data.a += q->data.a; // 合并过程啊,删除一个相同次幂项,把系数加到另一个相同项上 Delnext(p1, s); } } p1 = p1->next; //一次次找,把指针一次次向后移 } }
第九步,打印:(你们可以自己写哦):
代码
void PrintLinkList(LinkList l){ int cnt=0; while(l){ if(cnt == 1 && l->data.a){ if(l->data.e == 1){ if(l->data.a == 1){ cout<<"x"; } else cout<<l->data.a<<"x"; } else if(l->data.e == 0){ if(l->data.a == 1) cout<<"1"; else cout<<l->data.a; } else{ if(l->data.a == 1){ cout<<"x^"<<l->data.e; } else cout<<l->data.a<<"x^"<<l->data.e; } } else if(cnt > 1 && l->data.a){ if(l->data.e == 1){ if(l->data.a == 1){ cout<<"+"<<"x"; } else cout<<"+"<<l->data.a<<"x"; } else if(l->data.e == 0){ if(l->data.a == 1) cout<<"+"<<"1"; else cout<<"+"<<l->data.a; } else{ if(l->data.a == 1) cout<<"+"<<"x^"<<l->data.e; else cout<<"+"<<l->data.a<<"x^"<<l->data.e; } } l = l->next; cnt++; } cout<<"\n"; } //多项式的合并
看到这,说明你已经可以了,加油
最后一步main():
int main() { int number=1; LinkList la,lb,lc; cout<<"---------------------"<<"\n"; cout<<"请输入两个多项式的长度"<<"\n"; cout<<"---------------------"<<"\n"; cin>>n>>m; CreatLinklist(la, n,number++);//最后一个参数,是多项式的序号 MulMerge(la); //多项式的合并 cout<<"创建的多项式为:"; PrintLinkList(la); //打印 CreatLinklist(lb, m,number); MulMerge(la); cout<<"创建的多项式为:"; PrintLinkList(lb); cout<<"---------------------"<<"\n"; InitList(lc); // 这个必须写哦 // 加法 // Addlinklist(la, lb, lc); // MulMerge(lc); // cout << "最终的多项式为:"; // PrintLinkList(lc); MultiPList(la, lb, lc); //乘法 MulMerge(lc); //合并 cout<<"最终的多项式为:"; PrintLinkList(lc); cout<<"---------------------"<<"\n"; }
完整代码:
// // main.cpp // 数据结构2 // // Created by 宋玉良 on 2020/10/19. // /* 储备知识:比如 L 是一个链表的首地址 如果通过 L = L -> next ; 来访问元素,那么,下一次你还能找到它的L原来的地址吗 所以,我们新建一个指针,p = L->next, 通过,p = p->next 来访问L,L的地址也不改变,但却可以通过 p 来访问,虽然简单,但是非常重要!!!!! */ #include<bits/stdc++.h> #define null 0 using namespace std; int n,m; //代表输入的两个链表的长度,定义在外边,这两个变量全局有效(所有函数都可以直接用) typedef struct{ float a; int e; }term; //这个结构体是用来储存结构体的指数和系数 typedef struct LNode { //a代表系数,n代表指数 term data; struct LNode *next; }*Link,*LinkList; bool InitList(LinkList &l){ //初始化一个链表的头节点 l = new LNode; l->next = NULL; return true; } int cmp(term a,term b) // 比较两个元素的指数 { if (a.e==b.e) return 0; else return (a.e-b.e)/abs(a.e-b.e); } /* 这个函数是看老师的,帮了我大忙,大家好好看看 L是我们的链表,e是我们要查找的元素,s, q ,是两个工具人, 是用来记录结果的 */ bool LocateElem(LinkList L,term e,Link &s,Link &q) { Link p; //定义一个链表类型的指针 s = L; //把第一个工具人s用L这个地址赋值 p = s->next; //把第P用s->next这个地址赋值 //发现了吗?目前,s=L ,p =l->next , 接着看; while (p && cmp(p->data,e) != 0) //当p指针指向的节点的指数,和我们要找的指数不相等时,循环,结束时,要么找到了,要么p指向了链表的尾节点// { s=p; p=p->next; } if(!p) { //如果指向了我们的尾节点,证明没有找到 s = q = null; return 0; } else { //反面就是找到啦,这时候用到我们的第二个工具人q, 因为p是我们找到的节点,把p 的值赋值给q 才能传回main()里面哦 q = p; return 1; } } void Delnext(LinkList &L,Link s) //删除某一节点 { Link q = s->next; s->next = q->next; delete q; } void CreatLinklist (LinkList &l, int len,int number){ //创建链表 InitList(l); //初始化一个链表 for(int i = 1 ;i <= len; i++){ //依次输入n个非0项 LinkList s = new LNode; //生成新节点s cout<<"---------------------"<<"\n"; cout<<"请输入第"<<number<<"个链表的第"<<i<<"项的系数和指数"<<"\n"; cout<<"---------------------"<<"\n"; cout<<"请输入:"<<"\n"; cin >> s->data.a >> s->data.e; //输入系数和指数 LinkList t = l; //生成两个节点,t代表s节点的前驱 LinkList t1 = l->next; //t1代表 s节点的后继, // 创建这两个的目的就是把s节点插入t和t1中间 while(t1 && t1->data.e < s->data.e){ //通过比较指数,找到第一个大于输入项指数的项 t = t1; t1 = t1->next; } s->next = t1; //插入s节点 t->next = s; } cout<<"---------------------"<<"\n"; cout<<"第"<<number<<"个链表创建成功"<<"\n"; } //多项式的相加 void Addlinklist(LinkList la , LinkList lb , LinkList &lc){ //用p1指向la 的首元节点,p2 只想lb的首元节点 ,p3指向lc的头节点,相加后的结果放在lc里面 LinkList p1 = la->next , p2 = lb->next , p3 = lc; while(p1 && p2){ //当两个表不为空的时候 if(p1->data.e == p2->data.e){ //比较 la 和 lb 里的当前元素 的指数,如果相等,把它们加起来 float sum = p1->data.a + p2->data.a; // sum就是它们的和 if(sum != 0) { LinkList s = new LNode; // 定义一个临时节点 s s->data.a = sum; /* 下面的操作是把s的系数变成sum,然后把s这个临时节点,插入到lc,也就是我们的结果里 */ s->data.e = p1->data.e; s->next = p3->next; //插入操作 p3->next = s; p1 = p1->next; //继续向后加,两个指针向后移 p2 = p2->next; } } /* 如果la 和lb 当前位置元素的指数 la < lb ,那么就让la的指针向后移,顺便把当前元素插入到lc里面 */ else if(p1->data.e < p2->data.e){ LinkList s = new LNode; s->data.a = p1->data.a; s->data.e = p1->data.e; s->next = p3->next; p3->next = s; p1 = p1->next; } /* 如果lb < la 就让 lb 的指针,就让lb的指针向后移 */ else { LinkList s = new LNode; s->data.a = p2->data.a; s->data.e = p2->data.e; s->next = p3->next; p3->next = s; p2 = p2->next; } /* 迷了吗? p3 就是 lc 哦 */ p3 = p3->next; // 因为插入了元素,所以,lc 也向后移一位 } p3->next=p1?p1:p2; /* 当p1 为空的时候,说明循环退出,是因为p1 ,所以p2 还没到末尾哦 三目运算符,再给你们复习下相当于 if(p1){ p3->next = p1; } else p3->next = p2; */ } //多项式的相乘 void MultiPList(LinkList la,LinkList lb, LinkList &lc){ LinkList p1 = la->next, p2 = lb->next, p3 = lc; float numbera = 0 ;int numbere = 0; //代表两个多项式相乘后的系数和指数 for(int i = 1; i <= n; i++){ //n为第一个链表的元素数目,第一行定义过 float t = p1->data.a,t1 = p1->data.e; //t 代表p1(la)当前元素的系数,t1 代表指数 p1 = p1->next; //咱们已经知道了,p1 的数据,把它们向后移 for(int j= 1 ; j <= m; j++){ //m 在程序第一行定义过了 numbera = t * (p2->data.a); numbere = t1 + p2->data.e; LinkList s = new LNode; s->data.a = numbera; s->data.e = numbere; s->next = p3->next; p3->next = s; p2 = p2->next; p3 = p3->next; } p2 = lb->next; } } //多项式的输出 void PrintLinkList(LinkList l){ int cnt=0,flag =1; while(l){ if(cnt == 1){ if(l->data.a == 0) flag = 0; else{ if(l->data.e == 1){ if(l->data.a == 1){ cout<<"x"; } else cout<<l->data.a<<"x"; } else if(l->data.e == 0){ if(l->data.a == 1) cout<<"1"; else cout<<l->data.a; } else{ if(l->data.a == 1){ cout<<"x^"<<l->data.e; } else cout<<l->data.a<<"x^"<<l->data.e; } } } else if(cnt > 1 && l->data.a){ if(l->data.e == 1){ if(l->data.a == 1){ if(flag == 1)cout<<"+"<<"x"; else { cout << "x"; flag = 1; } } else { if(flag == 1) cout<<"+"<<l->data.a<<"x"; else { cout<<l->data.a<<"x"; flag = 1; } } } else if(l->data.e == 0){ if(l->data.a == 1){ if(flag == 1) cout<<"+"<<"1"; else{ cout << "1";flag = 1; } } else{ if(flag ==1 )cout<<"+"<<l->data.a; else { cout<<l->data.a; flag = 1; } } } else{ if(l->data.a == 1){ if(flag == 1) cout<<"+"<<"x^"<<l->data.e; else { cout<<"x^"<<l->data.e; flag = 1; } } else{ if(flag == 1) cout<<"+"<<l->data.a<<"x^"<<l->data.e; else { cout<<l->data.a<<"x^"<<l->data.e; flag = 1; } } } } l = l->next; cnt++; } cout<<"\n"; } //多项式的合并 void MulMerge(LinkList &la,int c){ LinkList p1 = la -> next ; while(p1){ term t = p1 -> data; LinkList s,q; int N = n * m; while(N--){ if(LocateElem(p1 , t , s, q)){ p1->data.a += q->data.a; Delnext(p1, s); if(c == 1) n--; else if(c == 2) m--; } } p1 = p1->next; } } int main() { int number=1; LinkList la,lb,lc; cout<<"---------------------"<<"\n"; cout<<"请输入两个多项式的长度(两个数之间用空格分开)"<<"\n"; cout<<"---------------------"<<"\n"; cin>>n>>m; CreatLinklist(la, n,number++); MulMerge(la,1); cout<<"创建的多项式为:"; PrintLinkList(la); CreatLinklist(lb, m,number); MulMerge(lb,2); cout<<"创建的多项式为:"; PrintLinkList(lb); cout<<"---------------------"<<"\n"; InitList(lc); // Addlinklist(la, lb, lc); // MulMerge(lc); // cout << "最终的多项式为:"; // PrintLinkList(lc); MultiPList(la, lb, lc); MulMerge(lc,3); cout<<"最终的多项式为:"; PrintLinkList(lc); cout<<"---------------------"<<"\n"; }
为什么要修改??
大家看这个输入样例:
(1 + 4x + 3x^2)* (2x + 3x^2 + 7x^3)
= 2x + 3x^2 +7x^3 + 8x^2 + 12 x^3 + 28x^4 + 6x^3 + 9x^4 +21 x^5
本来合并结果应该是
2x + 11x^2 + 25x^3 + 37x^4 + 21x^5
但是源代码合并结果是:
2x + 11x^2 + 19x^3 + 37x^4 + 6x^3 + 21x^5
为什么呢,因为结果里有三个三次方的项,而我们的源代码,找到一个,就不找了,所以就加个循环
。。。。。。。。。。。。。。。。。。。。