(数据结构)一元稀疏多项式计算器

一.实习题目与要求
【问题描述】
设计一个一元稀疏多项式简单计算器。
【基本要求】
(1)输入并建立两个多项式;
(2)多项式a与b相加,建立和多项式c;
(3)多项式a与b相减,建立差多项式d;
(3)输出多项式a,b,c,d。输出格式:比如多项式a为:A(x)=c1xe1+ c2xe2+… + cmxem,其中,ci和ei分别为第i项的系数和指数,且各项按指数的升幂 排列,即0≤e1<e2<…<em。
【测试数据】
(1)(1+x+x2+x3+x4+x5)+(-x3-x4)=(1+x+x2+x5)
(2)(x+x100)+(x100+x200)=(x+2x100+x200)
(3)(2x+5x8-3x11)+(7-5x8+11x9)=(7+2x+11x9-3x11)
(4)(6x-3-x+4.4x2-1.2x9)-(-6x-3+4.4x2+7.8x15)
=(12x-3-x-1.2x9-7.8x15)
【实现提示】
(1)用带头结点的单链表存储多项式。
(2)三个多项式链表中都只存储非零系数项。若多项式a与b中指数相等 的两项相加后,系数为零,则在和多项式c中不存储该指数项。
【拓展内容】
计算器的仿真界面。
二.需求分析
问题描述:输入任意两个多项式,输出这两个多项式的和及差
系统环境:visual studio 2017
运行要求:手动创建一个名为“多项式测试数据”的文本文件
三.概要设计
数据结构:链表
存储结构:头结点放在类里,实例化后存储在栈里,多项式的每个项放在堆里
算法设计:顺序取源多项式的每一项,然后遍历目标多项式。
如果找到指数一样的项,便直接对目标多项式的系数进行加减;
如果找到指数更大的项,便插入到该项的前面。
模块设计:term类:对项的描述,项的属性与方法
Polynome类:对多项式的描述,多项式的头结点和操作
Main函数:从文件里取数据,输出多项式相加相减结果
四.详细设计
类设计:
Term类:
数据成员:ceof:项的系数;exp:项的指数;next:下一项的地址
函数成员:构造函数term():对类成员列表初始化
析构函数:默认

Polynome类:
数据成员:first:链表头结点,串起多项式的每一项
函数成员:
构造函数:对从文件里读来的多项式string形式进行分析,初始化整个链表
复制构造函数:复制一个新对象,且新对象的first指针 也指向一个相同的链表
析构函数:从头结点出发,delete后面的项结点
静态 convert函数:在构造函数里读到的字符数组转换为数字

其他模块:
Term类的友元函数:
<<运算符重载:输出项
<<运算符重载:输出多项式
+运算符重载:多项式相加
-运算符重载:多项式相减
Polynome类的友元函数:
<<运算符重载:输出多项式
+运算符重载:多项式相加
-运算符重载:多项式相减
五.调试分析
调试过程中遇到的问题:
1类型不匹配:把字符写成字符串数组,判断相等,把””改成‘’
2在类外部使用类的私有成员:把类外部的函数或者类设为该类的 友元函数或者友元类
3const对象赋给一个非const对象:在类的接口函数形参列表后加 上const限定符
4访问未知空间导致程序异常终止:返回的类对象在调用<<重载符 之前已被析构,用一个对象接收返回的对象,不要使用一个临时对象
5vector访问越界:vector遍历时将遍历参数的类型声明为int, 而vector的下标类型是size_t,是unsigned,这样会导致越界。改unsigned。
对设计和编码的回顾:从string对象读取有用的信息存下来是很麻烦的一 件事,在这个过程中,是很多次不断的调式与检查,也是最耗费时间的模块
算法的时间复杂度和空间复杂度:
构造函数里读字符串的算法:
时间复杂度:O(n),n表示字符串的长短,
空间复杂度:O(n),n表示字符串的长短
多项式加减算法:
时间复杂度:O(mn),m和n分别表示目标多项式和源多项式的项数
空间复杂度:O(m+n),同上
算法的进一步改进:
读字符串的算法。
1可以减少时间复杂度的常数级。可以尽量少些一些if分支,尽量让一个分支能顾及到多种情况,如只有常数项和一次项,这两者都没有指数项。这样也能使程序结构更加清晰。
2可以减少空间复杂度的常数级。通过父循环和子循环的合理调配和循环条件的改变,可以少一些flag标志。
多项式的算法:
3可以减少时间复杂度的一次项。由于输入的多项式每一项都是升幂排列,每次插入目标多项式时的后一项可以把上一次插入的位置标志下来,这样不必每次都要丛头开始遍历。
4可以减少空间复杂度到O(m).可以不用再初始化一个对象来赋值目标多项式,直接在引用上进行操作。但这样会破坏目标多项式。所以这样做也有不足之处。
六.使用说明
1要创建一个名为“多项式测试数据的文件”
2在向文件输入时按照普通的多项式格式输入就行,指数用^符号分隔
3输入时一个多项式之间要紧密输入,不能有空格。
4目标多项式放在第一行,源多项式放在第二行。然后另起一行,用#隔开,输入下一组数据。最后一行无需用#
5程序会自动输出每一组的和多项式和差多项式,组与组之间是用空格隔开的
七.测试结果
在这里插入图片描述
下面附上测试数据

1+x+x^2+x^3+x^4+x^5
-x^3-x^4
#
x+x^100
x^100+x^200
#
2x+5x^8-3x^11
7-5x^8+11x^9
#
6x^-3-x+4.4x^2-1.2x^9
-6x^-3+4.4x^2+7.8x^15
#
5.8x^4-7.855x^-2+2x^2+3x^-1
2x^-1-4.8x^4+x

八.源程序清单

friend.h
   #pragma once
#include "term.h"
#include "polynome.h"
using namespace std;
ostream& operator <<(ostream&, const term&);
ostream& operator <<(ostream&, const polynome&);
polynome operator +(polynome, const polynome);
polynome operator -(polynome, const polynome);

friend.cpp
#include "friend.h"

ostream& operator <<(ostream& out, const term& x) {
	if ((x.ceof == 1 || x.ceof == -1) && x.exp != 0) {
		if (x.ceof == -1) {
			out << "-";
		}
		if (x.exp == 1)   out << "x";
		else      out << "x^" << x.exp;
		return out;
	}
	if (x.ceof == 0)   return out;
	out << x.ceof;//注意:这里不加setflags(ios::fixed)
	if (x.exp == 0)   return out;
	if (x.exp == 1)   out << "x";
	else      out << "x^" << x.exp;
	return out;
}

ostream& operator <<(ostream&out, const polynome&p) {
	bool h = 0;
	for (term* cur = p.first->next; cur != NULL; cur = cur->next) {
		if (h == 1) {
			if (cur->ceof > 0)  out << "+";
		}
		h = 1;
		out << *cur;
	}
	return out;
}
polynome operator +(polynome p1, const polynome p2) {
	polynome temp;
	term *x1 = p1.first->next, *x2 = p2.first->next, *last = temp.first;
	while (1) {//对于顶层const,是否类型一致都可以赋值//但是对于底层const,const = 非const,
		if (x1 == NULL) {
			last->next = x2;
			break;
		}
		if (x2 == NULL) {
			last->next = x1;
			break;
		}
		if (x1->exp < x2->exp) {
			last->next = x1;
			last = x1;
			x1 = x1->next;
			continue;
		}
		if (x1->exp > x2->exp) {
			last->next = x2;
			last = x2;
			x2 = x2->next;
			continue;
		}
		if (x1->exp == x2->exp) {
			x1->ceof += x2->ceof;
			last->next = x1;
			last = x1;
			term *del = x2;
			x2 = x2->next;
			delete del;
			x1 = x1->next;

		}
	}
	(p1.first)->next = NULL;
	(p2.first)->next = NULL;//析构函数不会重复析构同一个堆空间,注意析构顺序
	return temp;//明确析构顺序
}

polynome operator -(polynome p1, const polynome p2) {
	for (term* i = p2.first->next; i != NULL; i = i->next) {
		i->ceof = -i->ceof;
	}
	return (p1 + p2);
}

polynome.h

#pragma once
#include <iostream>
#include "term.h"
#include <cmath>
#include <cctype>
#include <vector>
#include <string>
using namespace std;
class polynome {
public:
	polynome(const string&);
	polynome() = default;
	polynome(const polynome&);
	~polynome();
	static double convert(const vector<int>&, bool);
	void sort();
	friend ostream& operator <<(ostream&, const polynome&);
	friend polynome operator +(polynome, const polynome);
	friend polynome operator -(polynome, const polynome);
private:
	term* first = new term(0, -1,NULL);
};

polynome.cpp

#include "polynome.h"
double polynome::convert(const vector<int>& v, bool sign) {
	if (v.empty())  return 0;
	double result = 0;
	unsigned x = v.size();
	for (unsigned i = 0; i < v.size(); ++i) {//写了个等号,导致vector访问越界,当然,vector访问越界还有几种情况
		result += v[i] * pow(10, x - i - 1);
	}
	if (sign == 0)   return -result;
	return result;
}

void polynome::sort() {
	term *p1, *p2, *p;
	for (term* end = NULL; end != first; end = p) {
		for (p = p1 = first; p1->next->next != end; p1 = p1->next) {
			if (p1->next->exp > p1->next->next->exp) {
				p2 = p1->next->next;
				p1->next->next = p2->next;
				p2->next = p1->next;
				p1->next = p2;
				p = p1->next->next;
			}
		}
	}
}
polynome::polynome(const string& express) {
	term *cur = first;//注意类内初始化和构造函数调用的先后顺序
	//从文件里读出一个项来
	double ceof;
	int exp;
	bool ceofSign = 1;
	string::const_iterator p = express.begin();
	while (p != express.end()) {
		vector<int> s1, s2, addit;
		//判断符号
		if (*p == '-' || *p == '+') {
			if (*p == '-')      ceofSign = 0;
			if (*p == '+')      ceofSign = 1;
			p++;
		}
		//读一个项的系数的整数部分
		for (; isdigit(*p); ++p) {///"+"和"-"的区别,这里不能改成isdigit()
			s1.push_back(*p - 48);
			continue;
		}
		if (p != express.end() && *p == '.') {
			p++;
			//读一个项的系数的小数部分
			while (p != express.end() && isdigit(*p)) {//若是读到一个小数点
				addit.push_back(*p - 48);
				p++;
				continue;
			}
		}
		//如果只读到一个常数,直接继续读.为了避免只有一个多项式只有一个常数的情况,
		if (p != express.end() && (*p == '+' || *p == '-')) {//在大while循环里加上判断尾部判断
			double result = convert(s1, ceofSign) + convert(addit, ceofSign) / pow(10, addit.size());
			term* temp = new term(result, 0, NULL);
			cur->next = temp;
			cur = temp;
			continue;
		}
		//跳过字母
		p++;
		//读一个项的指数,注意符号
		bool expSign = 1;
		if (p != express.end() && *p == '^') {
			p++;
			if (p != express.end() && *p == '-') {
				expSign = 0;
				++p;
			}
			for (; p != express.end() && isdigit(*p); ++p) {                                             //操作类型不兼容,字符串和字符的区别
				s2.push_back(*p - 48);
			}
		}
		//对收集到的指数字符进行转换
		if (s2.empty())  exp = 1;
		else      exp = (int)convert(s2, expSign);
		//对收集到的系数字符进行转换
		if (s1.empty()) {
			if (ceofSign == 1)  ceof = 1;
			else  ceof = -1;
		}
		else {
			if (addit.empty())   ceof = convert(s1, ceofSign);
			else {
				double deci = convert(addit, ceofSign) / (int)pow(10, (int)addit.size());//unsigned可以自动转化为double,因为double是较高的类型
				ceof = convert(s1, ceofSign) + deci;
			}
		}
		//连接链表

		term* temp = new term(ceof, exp, NULL);
		cur->next = temp;
		cur = temp;
	}
	sort();
}

polynome::polynome(const polynome&T) {
	//first = T.first;就是这一句,害我找了两个星期
	first = new term(0, -1,NULL);/
	term* newNode = first;
	for (term* cur = T.first->next; cur != NULL; cur = cur->next) {
		term* temp = new term(*cur);
		newNode->next = temp;
		newNode = temp;
	}
	newNode->next = NULL;
}

polynome::~polynome() {
	term *del, *temp;
	temp = first;
	while (1) {
		del = temp;
		temp = temp->next;
		delete del;
		if (temp == NULL)     break;
	}
}

term.h

#pragma once
#include <iostream>
using namespace std;
class polynome;//这个前向声明感觉可以用头文件来代替,先暂且不用
class term {
	friend class polynome;
public:
	term(double, int, term* );
	~term();
	friend ostream& operator <<(ostream&, const term&);
	friend ostream& operator <<(ostream&, const polynome&);
	friend polynome operator +(polynome, const polynome);
	friend polynome operator -(polynome, const polynome);
private:
	double ceof;
	int exp;
	term* next;
};

term.cpp

#include "term.h"
term::term(double ceof, int exp, term* next = NULL) :ceof(ceof), exp(exp), next(next) {}
term::~term() = default;

main.cpp

#include <iostream>
#include "polynome.h"
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
int main() {
	string flag;
	ifstream infile("多项式测试数据.txt", ios::in);
	do {
		string str1, str2;
		getline(infile, str1);
		getline(infile, str2);
		polynome pol1(str1), pol2(str2);
		polynome add = pol1 + pol2;
		polynome sub = pol1 - pol2;//发现两个错误 
		cout << add << endl;
		cout << sub << endl;//这里注意,暂时值会被析构函数析构,导致重载函数访问一个未定义的空间
		cout << endl;
	} while (infile.good() && getline(infile, flag) && flag == "#");
	system("pause");
}
  • 16
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值