1. 选题背景
本文回顾了我在大学期间进行的一个数据结构课程设计,要求设计一个一元稀疏多项式的计算器。这对测试我们的数据结构知识有帮助。
完整文档链接: 基于C/C++/QT的一元稀疏多项式简单计算器系统说明书
Github链接: 基于C/C++/QT的一元稀疏多项式简单计算器系统
2. 系统设计
2.1 系统需求
- 输入并建立多项式
- 输出多项式,按指数降序排列
- 实现多项式的加减运算
- 规范格式化地输出结果
2.2 模块设计
- 界面模块:实现计算器界面,接收输入调用运算模块
- 运算模块:实现多项式创建、运算、输出等核心功能
- 数据模块:使用链表存储多项式数据
3. 核心算法
3.1 多项式创建
- 将输入存储在二维数组
- 对数组排序
- 存入链表,合并指数相同项
3.2 多项式加减
- 遍历两个多项式链表
- 指数相同则运算,不同则依次存入结果链表
- 按指数降序输出结果链表
4. 核心代码
4.1 程序中编写的的头文件
4.1.1 func.h
#ifndef FUNC_H
#define FUNC_H
# include <stdio.h>
# include <malloc.h>
# include <stdlib.h>
# include <string.h>
class func
{
public:
func();
};
#endif // FUNC_H
4.1.2 widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QStack>
#include <string.h>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton0_clicked();
void on_pushButton1_clicked();
void on_pushButton2_clicked();
void on_pushButton3_clicked();
void on_pushButton4_clicked();
void on_pushButton5_clicked();
void on_pushButton6_clicked();
void on_pushButton7_clicked();
void on_pushButton8_clicked();
void on_pushButton9_clicked();
void on_pushButton_add_clicked();
void on_pushButton_sub_clicked();
void on_pushButton_x_clicked();
void on_pushButton_more_clicked();
void on_pushButton_left_clicked();
void on_pushButton_right_clicked();
void on_pushButton_clear_clicked();
void on_pushButton_del_clicked();
void on_pushButton_equal_clicked();
private:
Ui::Widget *ui;
QString expression;
int Priority(char ch);
};
#endif // WIDGET_H
4.2 程序中调用的头文件、全局变量、程序中通用的符号常量
#include "func.h"
#include "widget.h"
#include <QApplication>
#include "ui_widget.h"
#include <QDebug>
typedef struct LNode {
int coef;//系数
int exp;//指数
struct LNode *next;
} node;
char s[1280];
int e1[1280][2];
int e2[1280][2];
4.3 所有代码及函数的原型,功能、参数的意义
4.3.1 func.cpp
4.3.1.1 该程序头文件调用情况;
#include "func.h"
4.3.1.2 node *CCreate(int n, int e[][2])
node *CCreate(int n, int e[][2]) {
node *head, *p, *q;
head = (node *)malloc(sizeof(node));
p = q = head;
head->next = NULL;
int i, j, k, t1, t2;
for (j = 0; j < n - 1; j++) { //冒泡排序
for (k = 0; k < n - j - 1; k++) {
if (e[k][1] == e[k + 1][1]) {//当指数相等,系数相加(减)
e[k][0] += e[k + 1][0];
e[k + 1][1] = 0;
e[k + 1][0] = 0;
} else if (e[k + 1][1] > e[k][1]) {
t1 = e[k + 1][1];
t2 = e[k + 1][0];
e[k + 1][1] = e[k][1];
e[k + 1][0] = e[k][0];
e[k][1] = t1;
e[k][0] = t2;
}
}
}
for (i = 0; i < n; i++) {
if (e[i][0] != 0) {
q = (node *)malloc(sizeof(node));
if (!q)
exit(-1);
q->coef = e[i][0];
q->exp = e[i][1];
p->next = q;
q->next = NULL;
p = q;
}
}return head;
}
4.3.1.3 void Listadd(node *listya, node *listyb)
void Listadd(node *listya, node *listyb) { //ya=a1*x^t1+a2*x^t2;yb=b1*x^n1+b2*x^n2
node *p, *q, *pre, *temp;
int sum = 0;;
p = listya->next;
q = listyb->next;
pre = listya;
while (p && q) {
if (p->exp > q->exp) {
pre->next = p;//确定下一项
pre = p;//新表尾
p = p->next;//p继续遍历
} else if (p->exp < q->exp) {
pre->next = q;
pre = q;
q = q->next;
} else { //两项系数相等
sum = p->coef + q->coef;
if (sum) {//!=0
p->coef = sum;//存入p(lya)
pre->next = p;//确定下一项
pre = p;//新表尾
p = p->next;//p继续遍历
temp = q->next;//去尾标记下一项
free(q); //将yb链中相应项释放掉,和放到ya链中
q = temp;//新链接
} else { //sum==0删除两个空项
temp = p->next;
free(p);
p = temp;
temp = q->next;
free(q);
q = temp;
}
}
}
pre->next = p ? p : q;
}
4.3.1.4 char *pPrint(node *h, char *s)
char *pPrint(node *h, char *s) {//结果格式化规范输出函数
node *p = h->next;
int L = 0;
if (!p) {
sprintf(s + L, "0");
L = strlen(s);
}
if (p && p->exp == 0) {
sprintf(s + L, "%d", p->coef);
L = strlen(s);
p = p->next;
} else if (p && p->exp != 0) {
sprintf(s + L, "%d*x^(%d)", p->coef, p->exp);
L = strlen(s);
p = p->next;
}
while (p) {
if (p->exp != 0) {
if (p->coef > 0)
sprintf(s + L, "+%d*x^(%d)", p->coef, p->exp);
else if (p->coef < 0)
sprintf(s + L, "%d*x^(%d)", p->coef, p->exp);
L = strlen(s);
p = p->next;
}
else if (p->exp == 0) {
if (p->coef > 0)
sprintf(s + L, "+%d", p->coef);
else if (p->coef < 0)
sprintf(s + L, "%d", p->coef);
L = strlen(s);
p = p->next;
}
}
return s;
}
4.3.2 widget.cpp
4.3.2.1 该程序头文件调用情况
#include "func.h"
#include "widget.h"
#include "ui_widget.h"
#include "func.cpp"
#include <QDebug>
4.3.2.2 计算器界面整体设计
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget) {
ui->setupUi(this);
this->setMaximumSize(500, 640);
this->setMinimumSize(500, 640);
this->setWindowTitle(tr("多项式计算器"));
QFont f("仿宋", 14);}
Widget::~Widget() {
delete ui;
}
4.3.2.3 计算器数字按键槽函数
void Widget::on_pushButton0_clicked() {
expression += "0";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton1_clicked() {
expression += "1";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton2_clicked() {
expression += "2";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton3_clicked() {
expression += "3";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton4_clicked() {
expression += "4";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton5_clicked() {
expression += "5";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton6_clicked() {
expression += "6";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton7_clicked() {
expression += "7";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton8_clicked() {
expression += "8";
ui->lineEdit->setText(expression);
}
void Widget::on_pushButton9_clicked() {
expression += "9";
ui->lineEdit->setText(expression);
}
4.3.2.4 计算器符号按键函数
void Widget::on_pushButton_add_clicked() {
QString operatorSignal = "+-*=^"; // 一些操作运算符
QString laststr = ui->lineEdit->text().right(1);
qDebug() << laststr << endl;
// /* 判断最后一个字符是不是不是下面这些符号 */
bool iscontains = operatorSignal.contains(laststr, Qt::CaseSensitive); // 大小写敏感
if (!iscontains) { // 包含
expression += "+";
ui->lineEdit->setAlignment(Qt::AlignLeft); // 左对齐
ui->lineEdit->setText(expression);
}
}
void Widget::on_pushButton_sub_clicked() {
QString operatorSignal = "+-="; // 一些操作运算符
QString laststr = ui->lineEdit->text().right(1);
qDebug() << laststr << endl;
// /* 判断最后一个字符是不是不是下面这些符号 */
bool iscontains = operatorSignal.contains(laststr, Qt::CaseSensitive); // 大小写敏感
if (!iscontains || laststr == '\0') { // 包含
expression += "-";
ui->lineEdit->setAlignment(Qt::AlignLeft); // 左对齐
ui->lineEdit->setText(expression);
}
}
void Widget::on_pushButton_x_clicked() {
QString operatorSignal = "=x"; // 一些操作运算符
QString laststr = ui->lineEdit->text().right(1);
qDebug() << laststr << endl;
// /* 判断最后一个字符是不是不是下面这些符号 */
bool iscontains = operatorSignal.contains(laststr, Qt::CaseSensitive); // 大小写敏感
if (!iscontains || laststr == '\0') { // 包含
expression += "x";
ui->lineEdit->setAlignment(Qt::AlignLeft); // 左对齐
ui->lineEdit->setText(expression);
}
}
void Widget::on_pushButton_more_clicked() {
QString operatorSignal = "+-*/.=^"; // 一些操作运算符
QString laststr = ui->lineEdit->text().right(1);
qDebug() << laststr << endl;
// /* 判断最后一个字符是不是不是下面这些符号 */
bool iscontains = operatorSignal.contains(laststr, Qt::CaseSensitive); // 大小写敏感
if (!iscontains) { // 包含
expression += "^";
ui->lineEdit->setAlignment(Qt::AlignLeft); // 左对齐
ui->lineEdit->setText(expression);
}
}
void Widget::on_pushButton_left_clicked() {
QString operatorSignal = "=^"; // 一些操作运算符
QString laststr = ui->lineEdit->text().right(1);
qDebug() << laststr << endl;
// /* 判断最后一个字符是不是不是下面这些符号 */
bool iscontains = operatorSignal.contains(laststr, Qt::CaseSensitive); // 大小写敏感
if (!iscontains || laststr == '\0') { // 包含
expression += "(";
ui->lineEdit->setAlignment(Qt::AlignLeft); // 左对齐
ui->lineEdit->setText(expression);
}
}
void Widget::on_pushButton_right_clicked() {
QString operatorSignal = "=x^"; // 一些操作运算符
QString laststr = ui->lineEdit->text().right(1);
qDebug() << laststr << endl;
// /* 判断最后一个字符是不是不是下面这些符号 */
bool iscontains = operatorSignal.contains(laststr, Qt::CaseSensitive); // 大小写敏感
if (!iscontains) { // 包含
expression += ")";
ui->lineEdit->setAlignment(Qt::AlignLeft); // 左对齐
ui->lineEdit->setText(expression);
}
}
4.3.2.5 计算器清空与退格函数
void Widget::on_pushButton_clear_clicked() {
expression.clear();
memset(e1, 0, sizeof(e1));
memset(e2, 0, sizeof(e2));
ui->lineEdit->clear();
}
void Widget::on_pushButton_del_clicked() {
expression.chop(1);
ui->lineEdit->setText(expression);
}
4.3.2.6 void Widget::on_pushButton_equal_clicked()
void Widget::on_pushButton_equal_clicked() {
QStack<int> s_num, s_opt, s_more;
QString c;
char equation[1280] = {0};
int i = 0, sum1 = 0, sum2 = 0;
int fir = 0, sec = 0;//数组内元素数量
int sec_arr = 1;
int fir_arr = 1;
int xflag = 0;//x前是否有系数标志:1有 0没有
int exp_minus = 1;
int fn = 1;
int tnmp = 0;
int numflag = 0;
int errorflag=0;
node *lista, *listb;
QByteArray eqt;
eqt = expression.toLatin1();//把Qstring转换成QByteArray
strcpy(equation, eqt.data()); //data可以把QByteArray转换成const char *
while ((equation[i] != '\0' || !s_opt.empty())&&errorflag!=1) {
//字符串未读取结束或
//符号栈不为空持续循环
xflag = 0;
if (equation[i] == '('&&equation[i-1]=='-') {//负负得正
if (equation[i + 1] == '-'){
fn *= -1;
tnmp++;
if(tnmp%2==0)
fn*=-1;
}
else if (equation[i + 1] == '+')
equation[i+1]='0';
}
if(equation[i]=='0')i++;
if ((equation[i] >= '0' && equation[i] <= '9') || equation[i] == 'x') { //值0-9或x前无系数
if (equation[i] != 'x') { //x前有不为1系数
xflag = 1;
while (equation[i] >= '0' && equation[i] <= '9') { //下一个字符是数字继续循环
sum1 = sum1 * 10 + equation[i] - '0'; //进位
i++;
}
if (sum1 != 0) {
s_num.push(fn * sum1); //入数字栈
sum1 = 0;
fn = 1;
numflag = 1;
}
}
if (equation[i] == 'x') {
//1: x前系数为1
//2: 系数已入栈
if (xflag == 0)
s_num.push(1);
i++;
if (equation[i] == '^') {
if (equation[i + 1] == '-') { //指数为负数
exp_minus = -1;
i++;
}
i++;
while (equation[i] >= '0' && equation[i] <= '9') {//下一个字符是数字继续循环
sum2 = sum2 * 10 + equation[i] - '0'; //进位
i++;
}
if (sum2 != 0) {
s_more.push(exp_minus * sum2); //入指数栈
sum2 = 0;
exp_minus = 1; //重置为正
}
if(equation[i]=='x'){//3x^-2x^2问题
errorflag=1;
}
}
else if(equation[i]=='+'||equation[i]=='-'||equation[i]=='\0'){//2x+3x
s_more.push(1);
}
else
errorflag=1;
}
else if (equation[i] != 'x' && numflag == 1) {//值为常数指数置零
s_more.push(0);
numflag = 0;
}
}
else {
if (s_opt.empty() || (s_opt.top() == '(' && equation[i] != ')') || Priority(equation[i]) > Priority(s_opt.top())) {
s_opt.push(equation[i]);
i++;
continue;
}
if (s_opt.top() == '(' && equation[i] == ')') {
s_opt.pop();
i++;
continue;
}
if ((equation[i] == ')' && s_opt.top() != '(') || Priority(equation[i]) <= Priority(s_opt.top()) || (equation[i] == '\0'&& !s_opt.empty())) {
//符号栈内有优先级高的运算符或字符串读取结束且栈不为空
//进入运算
char ch = s_opt.top();
s_opt.pop();
switch (ch) {
case '+':
if(s_num.size()!=0){
if (s_num.size() == 2) {
//1: a+b 入a,b
//2: a+b+c+d+……入a,b
e1[fir_arr][0] = s_num.top();
e1[fir_arr][1] = s_more.top();
fir++;
s_num.pop();
s_more.pop();
e1[fir_arr-1][0] = s_num.top();
e1[fir_arr-1][1] = s_more.top();
fir++;
s_num.pop();
s_more.pop();
fir_arr++;
fir_arr++;
}else {
//1: -a+b入b
//2: a+b+c+d+……入c,d……
e1[fir_arr-1][0] = s_num.top();
e1[fir_arr-1][1] = s_more.top();
fir++;
s_num.pop();
s_more.pop();
fir_arr++;
}
}break;
case '-':
if(s_num.size()!=0){
if (s_num.size() == 2) {
//1: a-b 入a,b
//2: a-b-c-d-…… 入 a,b
e2[sec_arr - 1][0] = -s_num.top();
e2[sec_arr - 1][1] = s_more.top();
sec++;
s_num.pop();
s_more.pop();
e1[fir_arr - 1][0] = s_num.top();
e1[fir_arr - 1][1] = s_more.top();
fir++;
s_num.pop();
s_more.pop();
sec_arr++;
fir_arr++;
}
else {
//1: -a+b 入 -a
//2: a-b-c-d-…… 入 c,d……
//3: -a-b-c-d-…… 入 a,b,c,d……
e2[sec_arr - 1][0] = -s_num.top();
e2[sec_arr - 1][1] = s_more.top();
sec++;
s_num.pop();
s_more.pop();
sec_arr++;
}
}break;
}
}
}
if(i>100){
memset(e1, 0, sizeof(e1));
memset(e2, 0, sizeof(e2));
errorflag=1;
break;
}
}
if(!s_num.empty()&&fir==0&&sec==0){
e1[fir][0]=s_num.top();
if(!s_more.empty())
e1[fir][1]=s_more.top();
fir++;
}
if(errorflag==0){
lista = CCreate(fir + 1, e1);
listb = CCreate(sec + 1, e2);
Listadd(lista, listb);
c = QString(pPrint(lista, s));
}
else c = "输入有误";
ui->lineEdit->setText(c);
memset(e1, 0, sizeof(e1));
memset(e2, 0, sizeof(e2));
expression.clear();
}
4.3.2.7 int Widget::Priority(char ch)
int Widget::Priority(char ch) {
switch (ch) {
case '+':
case '-':
return 1;
case '(':
return 2;
default:
return 0;
}
}
5. 收获与展望
- 收获:巩固数据结构知识,提高算法设计能力
- 展望:可以扩展更多运算,优化算法,增强健壮性
参考文献
[1] 严蔚敏,吴伟民. 数据结构: (C语言版)[M] .北京:清华大学出版社,,2007(2021.1重印)
[2] 啊哈磊. 啊哈C!思考快你一步:用编程轻松提升逻辑力[M]. 北京:电子工业出版社,2013.9
[3] (日)石田保辉,(日)宫崎修著:张贝译. 我的第一本算法书[M]. 北京:人民邮电出版社,2018.11
[4] (美)普拉达(Prata,S.)著;姜佑译. C Primer Plus (第6版)中文版[M]. 北京:人民邮电出版社,2016.4 (2016.6 重印)
[5] 程杰. 大话数据结构[M]. 北京:清华大学出版社,2011.6
[6] 左飞,李召恒. C语言参悟之旅[M]. 中国铁道出版社,2010
[7] 刘涛,叶明全. C语言程序设计学习指导与实践教程[M]. 上海:上海交通大学出版社2017(2021重印)
[8] 钟志水,周鸣争. C语言程序设计[M]. 成都:电子科技大学出版社,2015.5(2021.1重印)