多项式加减法 (一)

实验内容

需要设计一个简单的一元稀疏多项式加减法计算器

实验要求

  • 输入并建立多项式

例如:
![][1]
[1]: http://latex.codecogs.com/gif.latex?A(x)=7+3x+9x{8}+5x{17}
或:
![][2]
[2]: http://latex.codecogs.com/gif.latex?B(x)=8x+22x{7}-9x{8}

  • 输出多项式
  • 多项式A和B相加,建立多项式 C = A + B,并输出相加的结果多项式 C
  • 多项式A和B相减,建立多项式 C = A - B,并输出相减的结果多项式C (选作)

如何做?

首先根据实验要求,多项式为稀疏一元多项式,因此采用链式线性存储结构表示,链式线性存储结构在理论课中介绍了:

  • 单链表
  • 双向链表
  • 循环链表

在此,为简便起见,我们采用单链表结构。如果使用单链表存储一元多项式,则单链表中的每一个节点用于存储该一元多项式中的某一项。

第一步 定义数据结构类型

由实验要求可知,一元多项式中每一项由系数和指数构成,因此我们定义单链表中节点结构为:

struct tagNode
{
    float chef;
    int exp;
    struct tagNode *next;
};

typedef struct tagNode Node;
typedef struct tageNode * pNode;

typedef struct tagNode * LinkList;
typedef struct tagNode * Poly;

单链表可以采用带头结点的单链表或头指针单链表,为简便起见,我们采用带头结点的单链表。

如上代码所示,我们通过 typedef struct tagNode *Poly 定义了多项式类型。

第二步 定义操作接口

实验要求必须实现如下功能:

  • 输入并建立多项式
  • 输出多项式
  • 多项式A和B相加,建立多项式 C = A + B,并输出相加的结果多项式 C
  • 多项式A和B相减,建立多项式 C = A - B,并输出相减的结果多项式C

上述4个功能,可以映射为 对 Poly 类型的四个不同操作接口。

此外, 对于单链表而言,由于是动态分配节点存储空间,因此还需要考虑单链表的建立、销毁操作;

对于多项式如何建立,可以再创建时既确定多项式中每一项的系数及指数;也可以在多项式数据结构实例创建完成后通过其他操作接口添加新的项。

根据上述要求,我们可以给出多项式操作接口的声明:

struct tagNode
{
    float chef;
    int exp;
    struct tagNode *next;
};

typedef struct tagNode Node;
typedef struct tageNode * pNode;

typedef struct tagNode * LinkList;
typedef struct tagNode * Poly;

Poly CreatePoly(); // 创建多项式数据结构实例
bool DestroyPoly(Poly poly); // 销毁多项式实例
void PrintPoly(Poly poly); // 输出多项式
Poly Add(Poly polya, Poly polyb); // 多项式相加
Poly Substract(Poly ploya, Poly polyb); // 多项式相减

bool AddNode(Poly poly, float coef, int exp); // 为poly多项式添加新项(coef, exp)

第三步 建立项目文件

首先,建立一个C或C++项目工程。关于C或C++项目工程,由于使用开发环境的不同,操作步骤不尽相同。这里,我们不使用IDE,直接使用gcc编译器对源文件进行编译。

我们需要建立3个文件,他们分别是:

  • Poly.h : 用于定义多项式数据结构及操作接口声明
  • Poly.cpp : 用于多项式操作接口实现
  • main.cpp : 用于整个实验项目的测试

Poly.h文件

通常而言,我们将数据结构的 定义、操作接口 的声明与 实现 分开,以便于项目的组织及代码复用。
以下给出Poly.h文件的代码:

#ifndef _POLY_H_
#define _POLY_H_

#include <stdio.h>

struct tagNode
{
    float chef;
    int exp;
    struct tagNode *next;
};

typedef struct tagNode Node;
typedef struct tageNode * pNode;

typedef struct tagNode * LinkList;
typedef struct tagNode * Poly;

Poly CreatePoly(); // 创建多项式数据结构实例
bool DestroyPoly(Poly poly); // 销毁多项式实例
void PrintPoly(Poly poly); // 输出多项式
Poly Add(Poly polya, Poly polyb); // 多项式相加
Poly Substract(Poly ploya, Poly polyb); // 多项式相减

bool AddNode(Poly poly, float coef, int exp); // 为poly多项式添加新项(coef, exp)

#endif _POLY_H_

Poly.h 文件中,文件头2行和最后1行使用了 #ifndef#define 宏,主要作用是为防止 Poly.h 头文件被重复引用的问题。

Poly.cpp文件

在定义好 Poly.h 文件后,我们开始实现多项式相关的操作接口。与多项式相关的操作接口都在 Poly.cpp 文件中实现。首先来看看 Poly.cpp 文件:

#include "Poly.h"
#include <stdio.h>

Poly CreatePoly()
{
    ...
}
bool DestroyPoly(Poly poly)
{
    ...
}

...

Poly.cpp 文件中,首先通过头文件包含命令将 Poly.h 引用进来。如此一来,在 Poly.cpp 文件中便可使用 Poly.h 文件所定义的数据类型及相关接口声明。

CreatePoly接口

我们首先来实现创建多项式数据类型实例的方法。由于采用头结点单链表形式,因此我们需要在 CreatePoly 接口中完成如下几步操作:

  1. 通过 malloc(sizeof(Node)) 分配头结点;
  2. 完成 Poly 数据类型实例初始化工作;
  3. 成功则返回 Poly 实例,否则返回错误提示;

CreatePoly 接口实现代码如下所示:

...

Poly CreatePoly()
{
    Poly poly = NULL;
    PNode head = NULL; 
    
    head = (PNode) malloc(sizeof(Node));
    if (head != NULL)
    {
        // 不需要对头结点的coef和exp数据域进行设置
        head->next = NULL; // 设置头结点的next指针域为NULL
        poly = head;
    }

    return poly;  // 如果分配头结点空间成功,则返回poly非空表示创建多项式实例操作成功,
                  // 否则返回值为NULL,表示创建多项式实例操作失败
}

...

DestroyPoly接口

在完成 CreatePoly 接口后,我们来实现 DestroyPoly 接口。 该接口需要完成的操作是将参数 poly表示的多项式实例进行销毁。因为 C/C++ 中内存的分配和释放操作需由程序员自行完成。

实现 CreatePoly 接口,需要完成如下几步操作:

  1. 从头节点开始逐一释放 poly 实例中每个节点的内存空间;
  2. 返回操作成功与否的结果;

DestroyPoly 接口实现代码如下所示:

...

bool DestroyPoly(Poly poly)
{
    PNode p = NULL;
    
    p = poly; // p指向头结点

    while (p != NULL)
    {
        poly = poly->next;
        free (p);
        p = poly;
    }
    
    return true;
}

...

PrintPoly接口

完成 CreatePolyDestroyPoly 两个接口后,我们来实现 PrintPoly 接口。

PrintPoly 接口主要用于输出多项式。此外,我们可以利用该接口测试其他接口实现是否正确。

例如,要测试多项式相加接口 Add 实现是否正确,可在调用 Add 接口之前及之后调用Print接口,通过Print接口的输出结果可以较方便、快捷的判断 Add 接口实现是否存在逻辑问题 。

当遇到程序逻辑问题时,通过调试方式检测逻辑问题是一件耗费时间和精力的事情,在可以通过输出中间结果进行判断检测时,尽量避免过早陷入调试程序的陷阱中

...
Poly polyA, polyB, polyC;
...

PrintPoly(polyA);
polyC Add(polyA, polyB);
PrintPoly(polyC);

...

以下给出 PrintPoly 接口的实现:

void PrintPoly(Poly poly)
{
    PNode p = NULL;

    if (poly == NULL)
        return ;
    
    p = poly->next; // 忽略头结点
    while (p != NULL)
    {
        if (p->exp != 0)
        {
            printf("%+.1fX^%d ", p->coef, p->exp);
        }
        else
        {
            printf("%+.1f ", p->coef);
        }
    }

    printf("\n");
}

第四步 让我们的代码跑起来

完成CreatePolyDestroyPolyPrintPoly 三个接口后。对于剩余的AddSubtractAddNode接口,我们暂且放一边。

接下来我们的目标是将项目尽快跑起来,以便对已实现的接口进行测试(包括语法、逻辑错误)。

实际项目开发过程中,我们总是通过不断的迭代开发来完善我们的项目。因此,在开发过程中尽快让项目运行起来,能够有一个可运行版本(最小功能版本最小系统版本)至关重要。

main.cpp

让我们来编写测试代码。我们把测试代码放在main.cpp文件中。测试代码主要对已编写的接口进行测试,按照本文PrintPoly接口中所写的类似,可以先调用CreatePoly、再调用PrintPoly接口对其他接口进行测试。

#include "Poly.h"
#include <stdio.h>

int main ()
{
    Poly ploya = NULL;
    
    polya = CreatePoly();
    PrintPoly(polya);
    DestroyPoly(polya);

    return 0;
}

完成 main.cpp 中测试代码的编写后,我们来对整个项目进行编译。在此,我们使用gcc编译套件进行编译工作。

Step 1

进入 lab01 目录,使用 ls 命令查看当前目录信息:

[localhost:lab01 xgqin]$ ls
Poly.cpp Poly.h   main.cpp

Step 2

运行 g++ 命令,对 main.cppPoly.cpp 进行编译。编译后,无语法错误,默认生成 a.out 执行文件。

[localhost:lab01 xgqin]$ g++ main.cpp Poly.cpp 
[localhost:lab01 xgqin]$ ls
Poly.cpp Poly.h   a.out    main.cpp
[localhost:lab01 xgqin]$ 

Step 3

通过 ./a.out 运行编译后的执行文件。从运行结果看, a.out 文件执行后,无任何输出。因为 main.cpp 文件中调用了 CreatePolyPrintPoly 两个方法,但由于 CreatePoly 方法仅仅创建了一个带头结点的空链表,因此 PrintPoly 方法无任何输出。

[localhost:lab01 xgqin]$ ls
Poly.cpp Poly.h   a.out    main.cpp
[localhost:lab01 xgqin]$ ./a.out 
[localhost:lab01 xgqin]$ 

通过上述运行结果,我们并不知道在 Poly.cpp 中所写的 CreatePolyPrintPoly 等接口是否无问题。那接下来我们该如何做?可以考虑以下几个方案:

  1. 实现 AddNode 接口,从而可以构造出非空的多项式,然后通过 PrintPoly 接口进行测试;
  2. CreatePoly 接口中新增添加节点的测试代码,再通过PrintPoly接口进行测试;

在此,我们选择方案2,因为可避免在未完成第一次迭代开发前再加入新的接口,尽可能避免让项目代码在开发早期过于复杂。

CreatePoly接口增加测试代码

新加的测试代码目的是便于我们进行测试,如下代码所示:

Poly CreatePoly()
{
    Poly poly = NULL;
    PNode head = NULL; 
    PNode cur = NULL; // 始终指向存储多项式的单链表最后一个节点的指针
    int num; // 存储所需输入多项式项数
    float coef;
    int exp;

    head = (PNode) malloc(sizeof(Node));
    if (head != NULL)
    {
        // 不需要对头结点的coef和exp数据域进行设置
        head->next = NULL; // 设置头结点的next指针域为NULL
        poly = head;
    }
    
    cur = poly; // 初始化cur指针,cur当前指向头结点
    
    printf("请输入节点个数:");
    scanf("%d", &num);
    
    for (int i = 0; i < num; i++)
    {
        scanf("%f%d", &coef, &exp);
        cur->next = (PNode) malloc(sizeof(Node));
        if (cur->next != NULL)  // 分配节点成功
        {
            cur = cur->next;  // 确保cur指向最后一个新节点
            cur->coef = coef;
            cur->exp = exp;
            cur->next = NULL;
        }
    }

    return poly;  // 如果分配头结点空间成功,则返回poly非空表示创建多项式实例操作成功,
                  // 否则返回值为NULL,表示创建多项式实例操作失败
}

完成 CreatePoly 接口修改后,我们再次按照Step 1Step 2的步骤对当前项目进行编译:

[localhost:lab01 xgqin]$ ls
Poly.cpp Poly.h   a.out    main.cpp
[localhost:lab01 xgqin]$ g++ main.cpp Poly.cpp 
[localhost:lab01 xgqin]$ ls
Poly.cpp Poly.h   a.out    main.cpp
[localhost:lab01 xgqin]$ ./a.out
请输入节点个数:4
4 -1
7 0
3 1
9 8
+4.0X^-1 +7.0X +3.0X^1 +9.0X^8
[localhost:lab01 xgqin]$

再次使用 g++ 命令可以对 main.cppPoly.cpp 进行编译,如无语法错误,编译器会生成新的 a.out 文件覆盖旧版本 a.out 文件。

从程序再一次的运行结果看,我们创建了一个包含4个项的一元多项式,且输入每个项的系数与指数时,按照指数升序的形式进行输入、存储。

![][3]
[3]: http://latex.codecogs.com/gif.latex?A=4x{-1}+7+3x+9x{8}

输入所表示的多项式如上图所示,输出结果如下所示。

+4.0X^-1 +7.0X +3.0X^1 +9.0X^8

从程序运行输出结果看,目前 CreatPolyPrintPoly 两个接口未发现逻辑错误(未发现逻辑错误 不等价于 没有逻辑错误)。

你可以多次运行 a.out 文件输入不同数据对程序进行测试或修改main.cpp中的测试代码(例如:声明新的Poly指针,新增PrintPoly接口调用代码等)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 在多项式加法 (一)中,我们介绍了多项式对应的单链表的几个接口,现在我们来完成本次实验剩余的其他几个接口。 第五步...
    我叫卡卡算了阅读 1,254评论 4 3
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 第二次支教,却意外遇到了第一次支教过的一位小朋友 一见面就给了我一个紧紧的拥抱,临走时还悄悄塞给了我一封信。当时没...
    Infinity999阅读 285评论 0 0
  • 你像风,捉摸不住,对付风最好的办法就是化作空气。在你飘忽不定时,我又能无孔不入。 可是我这空气是稀薄的,不想让你窒...
    卜蝉明阅读 171评论 0 2