一道 google曾出过的笔试题:编程实现对数学一元多项式的相加和相乘操作(1)
2014-11-16 01:54
393 查看
数学中一元n次多项式可表示成如下的形式:
Pn(x)=p0+p1x+p2x^2+…+pnx^n (最多有 n+1 项,n +1 个系数唯一确定她)
(1)请设计一套接口用以表示和操作一元多项式
(2)根据上述设计实现一元n次多项式的加法运算
(3)根据上述设计实现一元n次多项式的乘法运算
分析:
题目大概意思:
数学里常见的一元 n 次表达式,设计出加减乘除的函数(方法),同时提供对外接口,把内部实现封装起来,而 n 的大小没有指定。
问题的本质:
就是一系列的单个未知数 x,指数和系数数字p(i),0 =< i <= n组成的长串的表达式,然后把长长的表达式进行数学运算,假发和乘法,那么立马想到,这里需要线性表这个数据结构,符号多项式的表示和操作是线性表处理的典型用例。 且有变量 N,那么自然会联系到范围问题。
解决思路:
对问题进行进一步分解,常用的线性表,无非就是链表和顺序表,又分静态和动态内存分配的。数学的一元多项式,用一个线性表 P 来表示:P = ( p0, p1, p2, …, pn )
每一项,x的指数 隐含在其系数 p(i) 的下标i里。这样的话,只是存储系数就 ok了,未知数 x 的指数让系数下标标识。比较简单是用数组(静态顺序表存储),那么问题来了……
存储空间不够了怎么办?
显然,可以使用动态线性表,这样,伸缩自如!再也不担心不够存储了!继续分析,这样解决了空间大小的问题(有限范围内的内存),剩下的就是设计计算的算法,联想中学时代的算数,多项式并不是每一个项都必须写出来的,那么问题来了……
如果 p 里含有大量的0怎么办?
显然这样的存储结构也不太好,会大量浪费内存。比如 p=1 + 7x^12000,要用一个长度为 12001 的线性表来表示,表中仅有 两 个非零系数,会浪费大量存储空间。 不划算。需要改变策略,首先一个原则就是:系数=0不存储!那么自然是只存储非0系数,而指数就必须同时也要存储!否则无法标识指数。其实,日常生活里,也是这样的,一个数学或者物理化学的公式,表达式,系数为0的项,没人会把它写出来吧?!而且这样的多项式数序上叫稀疏多项式。
最终,确定使用链表来存储,因为,系数为0的项,经过计算,可能变为非0,相反非0的项经过计算,也可能变味0,必然要用到删除和插入算法,那么显然链表还是最佳的表示方法,效率比较高,不用大量移动元素,且可以动态增长。节省存储空间。
定义 ADT三要素:数据对象,数据关系,数据操作
PS:其实还是面向对象表达 ADT 比较好,当然 c 也完全 ok,个人 c++不是很融汇贯通,高级的语法和特性不敢乱用,免得班门弄斧,贻笑大方……不使用高级特性和复杂语法的话,cpp就索然无味,索性一直用 纯真的 c 来练习一些东西,因为这样更好的把重点放到算法和工程问题本身,而不是复杂庞大的 c++语言的枝端末节上,c++还是比 c 多了很多复杂繁琐的东西(这里又想到了一道奇葩的问题——如何用 c 实现面向对象?)
google 的这个问题充分暴露了本人c++功底不过关, c++求解实现的过程因为使用的是类模版,函数模版,继承,和输入输出等的运算符重载,导致程序代码较多,且出现了很多错误,考虑编码练习和算法设计,重点学习的是思想和方法,还是用c写,c++的巩固和加强完全可以放到其他闲散时间完成.
这个题的难点其实是最后一问!
因为既然是google的题,肯丢最后要考虑时间复杂度和优化问题。只有结果肯丢过不了关,这里先提供一个最直接的思路。也是比较费时间的。o(n*m)
//直接思考,多项式相乘,每一项一一顺次(考虑两个嵌套循环)的去做乘法,指数相加,系数相乘,保存到临时变量,又考虑到有多项,自然是数组保存了……
//这是正常的很直观的思路,可以依靠数组的下标去映射指数,把系数存到对应指数(下标)处,这里是累加的和。
然后再依靠一个循环,顺次去判断数组的内容,取出想要的系数和对应下标(就是指数),组成一个新项,那就ok了。
//最后的阶数必然是两式子最高阶之和,自然这个数组的长度=这个和+1
注意:
1、
//数组维数在c99之前必须是常量,c99之后可以是变长数组,但是很多编译器还不支持。
//double receive[maxNum + 1] = {0};目前来说error!
2、
最后销毁的时候销毁B就行了,因为把A插到B,B就是C,C就是B,A只剩下头结点,在相加函数里,早已经被删除!如果还销毁B,铁定报错!重复析构。
3、
多项式相乘(其实就是两个链表的合并问题),这里有两个方法比较常见:
最简单也是最费时间(时间复杂度 o(n*m)最高的实现方法)的直接相乘法:
其实很简单,把表 A 的每一项系数分别和表 B 的每一项系数做乘法,同时,把他们的指数相加,存储到临时数组里,这样得到 N(A)x N(B)个新的项,按照指数相同的,把他们的系数相加组合为一新的项,附带这个指数,输出,得结果。
比较经典的是分治法。
还有一种改进的快速的傅里叶变换算法实现(未完待续)
Pn(x)=p0+p1x+p2x^2+…+pnx^n (最多有 n+1 项,n +1 个系数唯一确定她)
(1)请设计一套接口用以表示和操作一元多项式
(2)根据上述设计实现一元n次多项式的加法运算
(3)根据上述设计实现一元n次多项式的乘法运算
分析:
题目大概意思:
数学里常见的一元 n 次表达式,设计出加减乘除的函数(方法),同时提供对外接口,把内部实现封装起来,而 n 的大小没有指定。
问题的本质:
就是一系列的单个未知数 x,指数和系数数字p(i),0 =< i <= n组成的长串的表达式,然后把长长的表达式进行数学运算,假发和乘法,那么立马想到,这里需要线性表这个数据结构,符号多项式的表示和操作是线性表处理的典型用例。 且有变量 N,那么自然会联系到范围问题。
解决思路:
对问题进行进一步分解,常用的线性表,无非就是链表和顺序表,又分静态和动态内存分配的。数学的一元多项式,用一个线性表 P 来表示:P = ( p0, p1, p2, …, pn )
每一项,x的指数 隐含在其系数 p(i) 的下标i里。这样的话,只是存储系数就 ok了,未知数 x 的指数让系数下标标识。比较简单是用数组(静态顺序表存储),那么问题来了……
存储空间不够了怎么办?
显然,可以使用动态线性表,这样,伸缩自如!再也不担心不够存储了!继续分析,这样解决了空间大小的问题(有限范围内的内存),剩下的就是设计计算的算法,联想中学时代的算数,多项式并不是每一个项都必须写出来的,那么问题来了……
如果 p 里含有大量的0怎么办?
显然这样的存储结构也不太好,会大量浪费内存。比如 p=1 + 7x^12000,要用一个长度为 12001 的线性表来表示,表中仅有 两 个非零系数,会浪费大量存储空间。 不划算。需要改变策略,首先一个原则就是:系数=0不存储!那么自然是只存储非0系数,而指数就必须同时也要存储!否则无法标识指数。其实,日常生活里,也是这样的,一个数学或者物理化学的公式,表达式,系数为0的项,没人会把它写出来吧?!而且这样的多项式数序上叫稀疏多项式。
最终,确定使用链表来存储,因为,系数为0的项,经过计算,可能变为非0,相反非0的项经过计算,也可能变味0,必然要用到删除和插入算法,那么显然链表还是最佳的表示方法,效率比较高,不用大量移动元素,且可以动态增长。节省存储空间。
定义 ADT三要素:数据对象,数据关系,数据操作
PS:其实还是面向对象表达 ADT 比较好,当然 c 也完全 ok,个人 c++不是很融汇贯通,高级的语法和特性不敢乱用,免得班门弄斧,贻笑大方……不使用高级特性和复杂语法的话,cpp就索然无味,索性一直用 纯真的 c 来练习一些东西,因为这样更好的把重点放到算法和工程问题本身,而不是复杂庞大的 c++语言的枝端末节上,c++还是比 c 多了很多复杂繁琐的东西(这里又想到了一道奇葩的问题——如何用 c 实现面向对象?)
google 的这个问题充分暴露了本人c++功底不过关, c++求解实现的过程因为使用的是类模版,函数模版,继承,和输入输出等的运算符重载,导致程序代码较多,且出现了很多错误,考虑编码练习和算法设计,重点学习的是思想和方法,还是用c写,c++的巩固和加强完全可以放到其他闲散时间完成.
这个题的难点其实是最后一问!
因为既然是google的题,肯丢最后要考虑时间复杂度和优化问题。只有结果肯丢过不了关,这里先提供一个最直接的思路。也是比较费时间的。o(n*m)
//直接思考,多项式相乘,每一项一一顺次(考虑两个嵌套循环)的去做乘法,指数相加,系数相乘,保存到临时变量,又考虑到有多项,自然是数组保存了……
//这是正常的很直观的思路,可以依靠数组的下标去映射指数,把系数存到对应指数(下标)处,这里是累加的和。
然后再依靠一个循环,顺次去判断数组的内容,取出想要的系数和对应下标(就是指数),组成一个新项,那就ok了。
//最后的阶数必然是两式子最高阶之和,自然这个数组的长度=这个和+1
/************************************************************************/ // 头文件Polynomial.h // 定义链表结构 /************************************************************************/ #ifndef POLYNOMIAL_H #define POLYNOMIAL_H #include <stdlib.h> #include <stdio.h> #include <float.h> //链表结构 typedef struct Node{ struct Node *next; double coefficient; int exponent; } Node, *Polynomial; //链表初始化 void initList(Polynomial *L) { //头结点 if (NULL == *L) { *L = (Polynomial)malloc(sizeof(Node)); (*L)->coefficient = 0.0; (*L)->exponent = -1; (*L)->next = NULL; } else { puts("表已经存在!"); } } //判断指数同否 int compareExponent(Polynomial nodeA, Polynomial nodeB) { int a = nodeA->exponent; int b = nodeB->exponent; if (a == b) { return 0; } else { return a > b ? 1 : -1; } } //系数判断 bool isZeroByCoefficient(Polynomial node) { if (node->coefficient >= -LDBL_EPSILON && node->coefficient <= LDBL_EPSILON) { return true; } else { return false; } } //判断2 //系数判断 bool isZeroByDouble(double a) { if (a >= -LDBL_EPSILON && a <= LDBL_EPSILON) { return true; } else { return false; } } //尾插法建表 void creatListByTail(Polynomial *L, int n) { //头结点 if (NULL == *L) { *L = (Polynomial)malloc(sizeof(Node)); (*L)->coefficient = 0.0; (*L)->exponent = -1; (*L)->next = NULL; Polynomial tail = NULL; Polynomial ptr = *L; //初始化? if (NULL == (*L)->next) { puts("请按照指数升幂,连续的输入项的系数(double)和指数(int):(中间空格隔开)"); //循环建表 for (int i = 0; i < n; i++) { tail = (Polynomial)malloc(sizeof(Node)); tail->next = NULL; scanf("%lf %d", &tail->coefficient, &tail->exponent); while (getchar() != '\n') { continue; } //链接 ptr->next = tail; //移动指针 ptr = ptr->next; //尾结点 } } else { puts("表已经建立!"); } } else { puts("表头已经存在!"); } } //遍历 void traverseList(Polynomial L) { Polynomial ptr = L->next; int i = 1; while (ptr != NULL) { printf("一元多项式的第%d项:%g X ^ %d\n", i, ptr->coefficient, ptr->exponent); i++; ptr = ptr->next; } } //求最高阶数 int getMaxExp(Polynomial L) { Polynomial ptr = L; while (ptr->next != NULL) { ptr = ptr->next; } return ptr->exponent; } //删除结点,删除L中ptr指向的结点 void deleteNode(Polynomial L, Polynomial ptr) { Polynomial p = L; while (p->next != ptr) { p = p->next; } ptr = p->next; p->next->next = ptr->next; free(ptr); ptr = NULL; } //多项式相加,本质是链表的归并算法 //可以另外开辟空间,也可以使用已存在的空间存储,这里使用后者的算法 void addPolynomial(Polynomial LA, Polynomial LB) { //不再开辟内存 Polynomial a = LA->next; Polynomial b = LB->next; Polynomial LC = LB; Polynomial tail = LC; while (a != NULL && b != NULL) { //判断指数的关系 a > b ? 1 : -1 else 0 switch (compareExponent(a, b)) { case 1: tail->next = b; tail = tail->next; b = b->next; break; case -1: tail->next = a; tail = tail->next; a = a->next; break; default: double temp = a->coefficient + b->coefficient; // 0? if (isZeroByDouble(temp)) { a = a->next; b = b->next; //删除 deleteNode(LC, tail->next); } else { tail->next = b; tail = tail->next; b->coefficient = temp; a = a->next; b = b->next; }// end of if }// end of switch }//end of while //一表比完 if (NULL == a) { tail->next = b; } else { tail->next = a; }// end of if free(LA); LA = NULL; } //多项式相乘 void mulPolynomial(Polynomial LA, Polynomial LB, Polynomial LC) { Polynomial a = LA->next; Polynomial b = LB->next; Polynomial c = LC; Polynomial ptr = NULL; //两多项式的阶数 int numA = getMaxExp(LA); int numB = getMaxExp(LB); //结果多项式的阶数 int maxNum = numA + numB; //动态开辟数组空间 double *receive = (double *)malloc((maxNum + 1) * sizeof(double)); //为数组赋值 for (int i = 0; i < maxNum + 1; i++) { //i相当于指数,数组值就是相应指数的系数 receive[i] = 0.0; } //指数及数组下标 int expByIndex = 0; //顺次扫描A while (a != NULL) { //A不空,顺次扫描B while (b != NULL) { //两项做乘法之后的指数和 expByIndex = a->exponent + b->exponent; //系数之间做乘,结果保存到对应的指数下(下标), receive[expByIndex] += (a->coefficient) * (b->coefficient); b = b->next; } b = LB->next; a = a->next; }// end of while //数组保存的是全部项,两两分别乘法之后的结果,保存在对应的下标(数组位置) for (int i = 0; i < maxNum + 1; i++) { // 0? if (isZeroByDouble(receive[i])) { //not do sth } else { //生成结点 ptr = (Polynomial)malloc(sizeof(Node)); //接到 LC 表 c->next = ptr; c = c->next; //赋值 c->coefficient =receive[i]; c->exponent = i; }// end of if }// end of for c->next = NULL; } //链表销毁 void destroyList(Polynomial *L) { Polynomial ptr = NULL; while (*L != NULL) { ptr = (*L)->next; free(*L); *L = ptr; } // *L = NULL; puts("销毁完毕"); } #endif
注意:
1、
//数组维数在c99之前必须是常量,c99之后可以是变长数组,但是很多编译器还不支持。
//double receive[maxNum + 1] = {0};目前来说error!
2、
最后销毁的时候销毁B就行了,因为把A插到B,B就是C,C就是B,A只剩下头结点,在相加函数里,早已经被删除!如果还销毁B,铁定报错!重复析构。
3、
多项式相乘(其实就是两个链表的合并问题),这里有两个方法比较常见:
最简单也是最费时间(时间复杂度 o(n*m)最高的实现方法)的直接相乘法:
其实很简单,把表 A 的每一项系数分别和表 B 的每一项系数做乘法,同时,把他们的指数相加,存储到临时数组里,这样得到 N(A)x N(B)个新的项,按照指数相同的,把他们的系数相加组合为一新的项,附带这个指数,输出,得结果。
比较经典的是分治法。
#include "Polynomial.h" int main(void) { puts("第一波计算加法:初始化表A,B"); Polynomial LA = NULL; Polynomial LB = NULL; puts("建表A"); creatListByTail(&LA, 4); puts("打印A"); traverseList(LA); puts("建立表B"); creatListByTail(&LB, 3); puts("打印表B"); traverseList(LB); //相加 puts("表A,B相加"); addPolynomial(LA, LB); puts("打印和"); traverseList(LB); //销毁B即可 puts("销毁表A,B"); destroyList(&LB);
puts("第二波计算乘法:初始化表A,B,C"); Polynomial LAX = NULL; Polynomial LBX = NULL; Polynomial LCX = NULL; initList(&LCX); puts("建表A,B"); creatListByTail(&LAX, 4); puts("打印表A"); traverseList(LAX); puts("建立表B"); creatListByTail(&LBX, 3); puts("打印表B"); traverseList(LBX); //相乘 puts("表A,B做乘法"); mulPolynomial(LAX, LBX, LCX); puts("打印结果"); traverseList(LCX); //销毁 puts("销毁表ABC"); destroyList(&LAX); destroyList(&LBX); destroyList(&LCX); system("pause"); return 0; }
还有一种改进的快速的傅里叶变换算法实现(未完待续)
相关文章推荐
- 一道 google曾出过的笔试题:编程实现对数学一元多项式的相加和相乘操作
- 两个一元多项式相乘,数组与链表实现
- 用单链表实现一元多项式相加 C++代码
- 链表实现多项式相加 相乘
- 两个一元多项式相加(链表 || 顺序表实现)
- 南邮数据结构实验1.3 一元多项式的相加和相乘
- 一元多项式相加的算法和C++实现
- 【数据结构 链表的应用】一元多项式相加及相乘 和对问题的分析
- 编程珠玑的一道令我surprise的题目,竟然和Google的笔试题一样。
- Polynomial 一元多项式的表示及相加 (线性链表实现 严蔚敏版)
- 数据结构学习笔记2(链表 上 单链表基础操作&实现多项式相乘)
- 一元多项式的表示及相加(抽象数据类型Polynomial的实现)
- 编程珠玑的一道令我surprise的题目,竟然和Google的笔试题一样。
- 数据结构课程设计一元多项式的相加和相乘
- 链表实现的多项式相加相乘
- (链表实现)写出两个一元多项式相加的算法
- 重学数据结构001——链表基本操作与一元多项式相加
- 重学数据结构(一):链表基本操作与一元多项式相加
- 笔试面试常考数据结构-单链表常用操作编程实现
- 线性表之单链表实现一元多项式相加,求值,求导