前言
作为日后必将成为大佬的 新手我来说,数据结构是一片崭新的领域,对它既有兴奋好奇,亦有担心疑虑。站在前人的肩膀上,他们告诉我,数据结构的意义=>link link
昨天一位学长(前端方向)面了字节,在面经中提到一点是面试官最后对学长的评价:
“觉得你可能太偏重于业务实现,或者把项目做出来就行了。或者你这个项目的深度根本就不够…需要去买书来系统性地学习下。比如说缓存,比如说跨域…是你必须了解的。”
这对我也是一种启示,光说不练假把式,反之亦然。理论联系实际,我期待着从项目中能不断深厚理论功底,这才能无往不胜啊!
链表
有机会链表专门写一篇,假装先留一个链接 [link] 溜了~
详情
方法
实现多项式加法乘法有三种方式:数组、链表、动态数组,其中动态数组是一种比较好的方法,但出于练习链表熟练度的目的,采用链表实现。自顶向下进行程序设计,在main函数中将有如下函数:
int main() {
//读入p1、p2两个多项式,pp是加法结果,pm是乘法结果
polynomial p1, p2, pp, pm;
/*读入多项式
以指数递减方式输入各项系数、指数
各数间以空格隔开*/
p1 = readpoly();
p2 = readpoly();
//多项式乘法
pm = mult(p1, p2);
//多项式加法
pp = add(p1, p2);
//以输入格式输出多项式
print(pm);
cout << endl;
print(pp);
cin.get();
return 0;
}
其中我们强制规定输入时以指数递减方式输入多项式各项
定义数据结构
typedef struct polynode* polynomial;
struct polynode
{
int coef;
int expon;
polynomial next;
};
建立链表结点结构体,并重新定义指向结点的指针为polynomial这种新的变量类型。在结点中保存coefficient(系数)、exponent(指数)两类int型数据和一个指向下一个结点的指针。
(使用typedef的好处:1.清晰表达,明白具体变量含义;2.使用方便;3.可移植性好。而这种新变量类型实质上是一种别名。)
函数实现
readpoly()及Attach()
//多项式的形成由它实现(串起来)
void Attach(int c,int e, polynomial* pRear)
{
//开辟一个空节点
polynomial p = new polynode;
p->coef = c;
p->expon = e;
p->next = NULL;
//*pRear是传入的rear指针,pRear是传入的rear指针的地址
(*pRear)->next = p;
*pRear = p;
}
polynomial readpoly()
{
polynomial rear, t;
polynomial p = new polynode;
int c, e, n;
rear = p;
//读入多项式项数n
cin >> n;
while (n--) {
//要求以指数递减方式输入
cin >> c >> e;
Attach(c, e, &rear);
}
t = p;
p = p->next;
delete(t);
return p;
}
读入多项式要借助尾指针(rear)不断在链表尾部插入新的结点,这即要求rear始终指向表尾,也意味着rear要不停一项项后移至表尾。
首先我们开辟一个空结点,让p指向它,并让rear也指向这个空结点(rear = p)。然后我们在键盘输入多项式的项数n,利用while循环输入n项,每次输入格式要求指数递减,并利用Attach()函数连上各个结点。
Attach函数所需参数是c, e和指向rear的指针(why?因为形参、实参是值传递,要改变形参的值得传入地址,而此处显然需要改变rear指针的值) 在Attach函数中开辟一个空结点p,将c, e赋给结点,使next = NULL,并使(*pRear)即rear指针的next指向p,再让rear指向p实现rear始终在表尾。
最后有关键性一步就是将表头空结点删除,因为插入是在Attach中又构造一个新结点,所以表头始终是空的,要删去,此时需借助临时变量t。
add()
polynomial add(polynomial p1, polynomial p2)
{
//开辟新结点用于存放要返回的加法结果
polynomial p = new polynode;
polynomial rear, t;
p->next = NULL;
rear = p;
//当p1、p2都不空时
while (p1 && p2)
{
if (p1->expon == p2->expon) {
//保证两系数相加不等于0再Attach
if (p1->coef + p2->coef) {
Attach(p1->coef + p2->coef, p1->expon, &rear);
}
p1 = p1->next;
p2 = p2->next;
}
else if (p1->expon > p2->expon) {
Attach(p1->coef, p1->expon, &rear);
p1 = p1->next;
}
else {
Attach(p2->coef, p2->expon, &rear);
p2 = p2->next;
}
}
//假如p2先加完了
while (p1)
{
Attach(p1->coef, p1->expon, &rear);
p1 = p1->next;
}
//假如p1先加完了
while (p2)
{
Attach(p2->coef, p2->expon, &rear);
p2 = p2->next;
}
t = p;
p = p->next;
delete(t);
return p;
}
加法的实现方法:
传入两个多项式(的头结点),开辟一个新的结点用于存放结果;三个while循环分别对应p1p2都不空,p1先加完剩p2,p2先加完剩p1;同时在p1p2都不空时,判断指数大小的三种情况,并在指数相等时,继续判断系数和是否为零,若为0则需要删除结点,若不为0则系数相加即可。
最后,同样的,要delete表头空结点。
mult()
/*乘法实现:
先使p1的第一项乘以p2的每一项,
再使p1第一项后的每一项乘以p2的每一项,再逐个插入链表*/
polynomial mult(polynomial p1, polynomial p2)
{
polynomial p = new polynode;
polynomial rear, t, t1, t2;
int c, e;
p->next = NULL;
rear = p;
//为什么需要这样?因为在第一次while中p2将指向最后一个节点,但之后仍需从头结点开始,故要让p2保留在指向头结点
t1 = p1; t2 = p2;
//判断有没有空多项式
if (!t1 || !t2) return NULL;
//先使p1的第一项乘以p2的每一项
while (t2)
{
Attach(t1->coef * t2->coef, t1->expon + t2->expon, &rear);
t2 = t2->next;
}
//指向p1的第二项
t1 = t1->next;
//再使p1第一项后的每一项乘以p2的每一项
while (t1)
{
//让rear重新指向链表头空节点
rear = p;
t2 = p2;
while (t2)
{
c = t1->coef * t2->coef;
e = t1->expon + t2->expon;
while (rear->next && rear->next->expon > e) rear = rear->next;
//若两者指数相等
if (rear->next && rear->next->expon == e) {
//若且系数和不为零,则只改变系数
if (rear->next->coef + c) {
rear->next->coef += c;
}
//若且系数和为零,删除节点
else {
t = rear->next;
rear->next = t->next;
delete(t);
}
}
//若插入项指数大于rear.next的指数,插入
else {
t = new polynode;
t->coef = c;
t->expon = e;
t->next = rear->next;
rear->next = t;
}
t2 = t2->next;
}
t1 = t1->next;
}
t = p;
p = p->next;
delete(t);
return p;
}
乘法的实现方法:
一言以蔽之——逐项插入
先使p1第一项乘以p2每一项得到一组多项式(记为P0),之后p1第二项开始直到最后一项再分别与p2每一项相乘,得到的每一个项插入到P0中对应位置(满足指数递减排列)。
得到P0是比较容易的,系数相乘,指数相加,Attach串起来。
值得注意的是, 我们在这里有这么一步
t1 = p1; t2 = p2;
原因是,我们在之后插入项到P0中时,每次都得从头结点一个个往后找,直到(下一个结点的指数)<=(要插入的项的指数),故得始终有一个指针指向头结点,在插入时让rear重新指回头结点,再一个个往后找。
//先使p1的第一项乘以p2的每一项
while (t2)
{
Attach(t1->coef * t2->coef, t1->expon + t2->expon, &rear);
t2 = t2->next;
}
//先使p1的第一项乘以p2的每一项
while (p2)
{
Attach(p1->coef * p2->coef, p1->expon + p2->expon, &rear);
p2 = p2->next;
}
比较以上,容易看到,若没有 t1=p1; t2=p2; 这两步,在第一次相乘时p2就已经指向到最后一个结点,之后rear就没法重新指回到p2的头结点了(p1亦然)。
再然后就是情况的讨论了,指数同系数和不为0改变系数,系数和为0删除结点;插入指数大则插入结点。
最后相同地,删去头结点。
print()
void print(polynomial p)
{
//flag实现隔一个空格输出一个数
int flag = 0;
//若无多项式输出0 0
if (!p) {
cout << "0 0";
return;
}
while (p) {
if (!flag)flag = 1;
else cout << " ";
cout << p->coef << " " << p->expon;
p = p->next;
}
}
值得关注的点 就是利用flag自动实现每两个数间输出一个空格。
最后
希望有志于在大学期间掌握良好编程技能,或希望日后入职大型互联网公司或金融行业的同道中人能和我互关,交交朋友,毕竟,1+1>2嘛,大家一起进步啊!
2020/7/15 更新
麻烦大家收藏的同时也动动手指点赞呀,点赞会是对我最大的鼓励!