實(shí)驗(yàn)內(nèi)容
需要設(shè)計(jì)一個(gè)簡(jiǎn)單的一元稀疏多項(xiàng)式加減法計(jì)算器
實(shí)驗(yàn)要求
- 輸入并建立多項(xiàng)式
例如:
![][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}
- 輸出多項(xiàng)式
- 多項(xiàng)式A和B相加,建立多項(xiàng)式 C = A + B,并輸出相加的結(jié)果多項(xiàng)式 C
- 多項(xiàng)式A和B相減,建立多項(xiàng)式 C = A - B,并輸出相減的結(jié)果多項(xiàng)式C (選作)
如何做?
首先根據(jù)實(shí)驗(yàn)要求,多項(xiàng)式為稀疏一元多項(xiàng)式,因此采用鏈?zhǔn)骄€性存儲(chǔ)結(jié)構(gòu)表示,鏈?zhǔn)骄€性存儲(chǔ)結(jié)構(gòu)在理論課中介紹了:
- 單鏈表
- 雙向鏈表
- 循環(huán)鏈表
在此,為簡(jiǎn)便起見(jiàn),我們采用單鏈表結(jié)構(gòu)。如果使用單鏈表存儲(chǔ)一元多項(xiàng)式,則單鏈表中的每一個(gè)節(jié)點(diǎn)用于存儲(chǔ)該一元多項(xiàng)式中的某一項(xiàng)。
第一步 定義數(shù)據(jù)結(jié)構(gòu)類(lèi)型
由實(shí)驗(yàn)要求可知,一元多項(xiàng)式中每一項(xiàng)由系數(shù)和指數(shù)構(gòu)成,因此我們定義單鏈表中節(jié)點(diǎn)結(jié)構(gòu)為:
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;
單鏈表可以采用帶頭結(jié)點(diǎn)的單鏈表或頭指針單鏈表,為簡(jiǎn)便起見(jiàn),我們采用帶頭結(jié)點(diǎn)的單鏈表。
如上代碼所示,我們通過(guò) typedef struct tagNode *Poly 定義了多項(xiàng)式類(lèi)型。
第二步 定義操作接口
實(shí)驗(yàn)要求必須實(shí)現(xiàn)如下功能:
- 輸入并建立多項(xiàng)式
- 輸出多項(xiàng)式
- 多項(xiàng)式A和B相加,建立多項(xiàng)式 C = A + B,并輸出相加的結(jié)果多項(xiàng)式 C
- 多項(xiàng)式A和B相減,建立多項(xiàng)式 C = A - B,并輸出相減的結(jié)果多項(xiàng)式C
上述4個(gè)功能,可以映射為 對(duì) Poly 類(lèi)型的四個(gè)不同操作接口。
此外, 對(duì)于單鏈表而言,由于是動(dòng)態(tài)分配節(jié)點(diǎn)存儲(chǔ)空間,因此還需要考慮單鏈表的建立、銷(xiāo)毀操作;
對(duì)于多項(xiàng)式如何建立,可以再創(chuàng)建時(shí)既確定多項(xiàng)式中每一項(xiàng)的系數(shù)及指數(shù);也可以在多項(xiàng)式數(shù)據(jù)結(jié)構(gòu)實(shí)例創(chuàng)建完成后通過(guò)其他操作接口添加新的項(xiàng)。
根據(jù)上述要求,我們可以給出多項(xiàng)式操作接口的聲明:
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(); // 創(chuàng)建多項(xiàng)式數(shù)據(jù)結(jié)構(gòu)實(shí)例
bool DestroyPoly(Poly poly); // 銷(xiāo)毀多項(xiàng)式實(shí)例
void PrintPoly(Poly poly); // 輸出多項(xiàng)式
Poly Add(Poly polya, Poly polyb); // 多項(xiàng)式相加
Poly Substract(Poly ploya, Poly polyb); // 多項(xiàng)式相減
bool AddNode(Poly poly, float coef, int exp); // 為poly多項(xiàng)式添加新項(xiàng)(coef, exp)
第三步 建立項(xiàng)目文件
首先,建立一個(gè)C或C++項(xiàng)目工程。關(guān)于C或C++項(xiàng)目工程,由于使用開(kāi)發(fā)環(huán)境的不同,操作步驟不盡相同。這里,我們不使用IDE,直接使用gcc編譯器對(duì)源文件進(jìn)行編譯。
我們需要建立3個(gè)文件,他們分別是:
- Poly.h : 用于定義多項(xiàng)式數(shù)據(jù)結(jié)構(gòu)及操作接口聲明
- Poly.cpp : 用于多項(xiàng)式操作接口實(shí)現(xiàn)
- main.cpp : 用于整個(gè)實(shí)驗(yàn)項(xiàng)目的測(cè)試
Poly.h文件
通常而言,我們將數(shù)據(jù)結(jié)構(gòu)的 定義、操作接口 的聲明與 實(shí)現(xiàn) 分開(kāi),以便于項(xiàng)目的組織及代碼復(fù)用。
以下給出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(); // 創(chuàng)建多項(xiàng)式數(shù)據(jù)結(jié)構(gòu)實(shí)例
bool DestroyPoly(Poly poly); // 銷(xiāo)毀多項(xiàng)式實(shí)例
void PrintPoly(Poly poly); // 輸出多項(xiàng)式
Poly Add(Poly polya, Poly polyb); // 多項(xiàng)式相加
Poly Substract(Poly ploya, Poly polyb); // 多項(xiàng)式相減
bool AddNode(Poly poly, float coef, int exp); // 為poly多項(xiàng)式添加新項(xiàng)(coef, exp)
#endif _POLY_H_
Poly.h 文件中,文件頭2行和最后1行使用了 #ifndef 和#define 宏,主要作用是為防止 Poly.h 頭文件被重復(fù)引用的問(wèn)題。
Poly.cpp文件
在定義好 Poly.h 文件后,我們開(kāi)始實(shí)現(xiàn)多項(xiàng)式相關(guān)的操作接口。與多項(xiàng)式相關(guān)的操作接口都在 Poly.cpp 文件中實(shí)現(xiàn)。首先來(lái)看看 Poly.cpp 文件:
#include "Poly.h"
#include <stdio.h>
Poly CreatePoly()
{
...
}
bool DestroyPoly(Poly poly)
{
...
}
...
在 Poly.cpp 文件中,首先通過(guò)頭文件包含命令將 Poly.h 引用進(jìn)來(lái)。如此一來(lái),在 Poly.cpp 文件中便可使用 Poly.h 文件所定義的數(shù)據(jù)類(lèi)型及相關(guān)接口聲明。
CreatePoly接口
我們首先來(lái)實(shí)現(xiàn)創(chuàng)建多項(xiàng)式數(shù)據(jù)類(lèi)型實(shí)例的方法。由于采用頭結(jié)點(diǎn)單鏈表形式,因此我們需要在 CreatePoly 接口中完成如下幾步操作:
- 通過(guò)
malloc(sizeof(Node))分配頭結(jié)點(diǎn); - 完成 Poly 數(shù)據(jù)類(lèi)型實(shí)例初始化工作;
- 成功則返回 Poly 實(shí)例,否則返回錯(cuò)誤提示;
CreatePoly 接口實(shí)現(xiàn)代碼如下所示:
...
Poly CreatePoly()
{
Poly poly = NULL;
PNode head = NULL;
head = (PNode) malloc(sizeof(Node));
if (head != NULL)
{
// 不需要對(duì)頭結(jié)點(diǎn)的coef和exp數(shù)據(jù)域進(jìn)行設(shè)置
head->next = NULL; // 設(shè)置頭結(jié)點(diǎn)的next指針域?yàn)镹ULL
poly = head;
}
return poly; // 如果分配頭結(jié)點(diǎn)空間成功,則返回poly非空表示創(chuàng)建多項(xiàng)式實(shí)例操作成功,
// 否則返回值為NULL,表示創(chuàng)建多項(xiàng)式實(shí)例操作失敗
}
...
DestroyPoly接口
在完成 CreatePoly 接口后,我們來(lái)實(shí)現(xiàn) DestroyPoly 接口。 該接口需要完成的操作是將參數(shù) poly表示的多項(xiàng)式實(shí)例進(jìn)行銷(xiāo)毀。因?yàn)?C/C++ 中內(nèi)存的分配和釋放操作需由程序員自行完成。
實(shí)現(xiàn) CreatePoly 接口,需要完成如下幾步操作:
- 從頭節(jié)點(diǎn)開(kāi)始逐一釋放 poly 實(shí)例中每個(gè)節(jié)點(diǎn)的內(nèi)存空間;
- 返回操作成功與否的結(jié)果;
DestroyPoly 接口實(shí)現(xiàn)代碼如下所示:
...
bool DestroyPoly(Poly poly)
{
PNode p = NULL;
p = poly; // p指向頭結(jié)點(diǎn)
while (p != NULL)
{
poly = poly->next;
free (p);
p = poly;
}
return true;
}
...
PrintPoly接口
完成 CreatePoly 、 DestroyPoly 兩個(gè)接口后,我們來(lái)實(shí)現(xiàn) PrintPoly 接口。
PrintPoly 接口主要用于輸出多項(xiàng)式。此外,我們可以利用該接口測(cè)試其他接口實(shí)現(xiàn)是否正確。
例如,要測(cè)試多項(xiàng)式相加接口 Add 實(shí)現(xiàn)是否正確,可在調(diào)用 Add 接口之前及之后調(diào)用Print接口,通過(guò)Print接口的輸出結(jié)果可以較方便、快捷的判斷 Add 接口實(shí)現(xiàn)是否存在邏輯問(wèn)題 。
當(dāng)遇到程序邏輯問(wèn)題時(shí),通過(guò)調(diào)試方式檢測(cè)邏輯問(wèn)題是一件耗費(fèi)時(shí)間和精力的事情,在可以通過(guò)輸出中間結(jié)果進(jìn)行判斷檢測(cè)時(shí),盡量避免過(guò)早陷入調(diào)試程序的陷阱中。
...
Poly polyA, polyB, polyC;
...
PrintPoly(polyA);
polyC Add(polyA, polyB);
PrintPoly(polyC);
...
以下給出 PrintPoly 接口的實(shí)現(xiàn):
void PrintPoly(Poly poly)
{
PNode p = NULL;
if (poly == NULL)
return ;
p = poly->next; // 忽略頭結(jié)點(diǎn)
while (p != NULL)
{
if (p->exp != 0)
{
printf("%+.1fX^%d ", p->coef, p->exp);
}
else
{
printf("%+.1f ", p->coef);
}
}
printf("\n");
}
第四步 讓我們的代碼跑起來(lái)
完成CreatePoly、DestroyPoly、PrintPoly 三個(gè)接口后。對(duì)于剩余的Add、Subtract、AddNode接口,我們暫且放一邊。
接下來(lái)我們的目標(biāo)是將項(xiàng)目盡快跑起來(lái),以便對(duì)已實(shí)現(xiàn)的接口進(jìn)行測(cè)試(包括語(yǔ)法、邏輯錯(cuò)誤)。
實(shí)際項(xiàng)目開(kāi)發(fā)過(guò)程中,我們總是通過(guò)不斷的迭代開(kāi)發(fā)來(lái)完善我們的項(xiàng)目。因此,在開(kāi)發(fā)過(guò)程中盡快讓項(xiàng)目運(yùn)行起來(lái),能夠有一個(gè)可運(yùn)行版本(最小功能版本、最小系統(tǒng)版本)至關(guān)重要。
main.cpp
讓我們來(lái)編寫(xiě)測(cè)試代碼。我們把測(cè)試代碼放在main.cpp文件中。測(cè)試代碼主要對(duì)已編寫(xiě)的接口進(jìn)行測(cè)試,按照本文PrintPoly接口中所寫(xiě)的類(lèi)似,可以先調(diào)用CreatePoly、再調(diào)用PrintPoly接口對(duì)其他接口進(jìn)行測(cè)試。
#include "Poly.h"
#include <stdio.h>
int main ()
{
Poly ploya = NULL;
polya = CreatePoly();
PrintPoly(polya);
DestroyPoly(polya);
return 0;
}
完成 main.cpp 中測(cè)試代碼的編寫(xiě)后,我們來(lái)對(duì)整個(gè)項(xiàng)目進(jìn)行編譯。在此,我們使用gcc編譯套件進(jìn)行編譯工作。
Step 1
進(jìn)入 lab01 目錄,使用 ls 命令查看當(dāng)前目錄信息:
[localhost:lab01 xgqin]$ ls
Poly.cpp Poly.h main.cpp
Step 2
運(yùn)行 g++ 命令,對(duì) main.cpp 、 Poly.cpp 進(jìn)行編譯。編譯后,無(wú)語(yǔ)法錯(cuò)誤,默認(rèn)生成 a.out 執(zhí)行文件。
[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
通過(guò) ./a.out 運(yùn)行編譯后的執(zhí)行文件。從運(yùn)行結(jié)果看, a.out 文件執(zhí)行后,無(wú)任何輸出。因?yàn)?main.cpp 文件中調(diào)用了 CreatePoly 和 PrintPoly 兩個(gè)方法,但由于 CreatePoly 方法僅僅創(chuàng)建了一個(gè)帶頭結(jié)點(diǎn)的空鏈表,因此 PrintPoly 方法無(wú)任何輸出。
[localhost:lab01 xgqin]$ ls
Poly.cpp Poly.h a.out main.cpp
[localhost:lab01 xgqin]$ ./a.out
[localhost:lab01 xgqin]$
通過(guò)上述運(yùn)行結(jié)果,我們并不知道在 Poly.cpp 中所寫(xiě)的 CreatePoly 、 PrintPoly 等接口是否無(wú)問(wèn)題。那接下來(lái)我們?cè)撊绾巫觯靠梢钥紤]以下幾個(gè)方案:
- 實(shí)現(xiàn) AddNode 接口,從而可以構(gòu)造出非空的多項(xiàng)式,然后通過(guò) PrintPoly 接口進(jìn)行測(cè)試;
- 在 CreatePoly 接口中新增添加節(jié)點(diǎn)的測(cè)試代碼,再通過(guò)PrintPoly接口進(jìn)行測(cè)試;
在此,我們選擇方案2,因?yàn)榭杀苊庠谖赐瓿傻谝淮蔚_(kāi)發(fā)前再加入新的接口,盡可能避免讓項(xiàng)目代碼在開(kāi)發(fā)早期過(guò)于復(fù)雜。
為CreatePoly接口增加測(cè)試代碼
新加的測(cè)試代碼目的是便于我們進(jìn)行測(cè)試,如下代碼所示:
Poly CreatePoly()
{
Poly poly = NULL;
PNode head = NULL;
PNode cur = NULL; // 始終指向存儲(chǔ)多項(xiàng)式的單鏈表最后一個(gè)節(jié)點(diǎn)的指針
int num; // 存儲(chǔ)所需輸入多項(xiàng)式項(xiàng)數(shù)
float coef;
int exp;
head = (PNode) malloc(sizeof(Node));
if (head != NULL)
{
// 不需要對(duì)頭結(jié)點(diǎn)的coef和exp數(shù)據(jù)域進(jìn)行設(shè)置
head->next = NULL; // 設(shè)置頭結(jié)點(diǎn)的next指針域?yàn)镹ULL
poly = head;
}
cur = poly; // 初始化cur指針,cur當(dāng)前指向頭結(jié)點(diǎn)
printf("請(qǐng)輸入節(jié)點(diǎn)個(gè)數(shù):");
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) // 分配節(jié)點(diǎn)成功
{
cur = cur->next; // 確保cur指向最后一個(gè)新節(jié)點(diǎn)
cur->coef = coef;
cur->exp = exp;
cur->next = NULL;
}
}
return poly; // 如果分配頭結(jié)點(diǎn)空間成功,則返回poly非空表示創(chuàng)建多項(xiàng)式實(shí)例操作成功,
// 否則返回值為NULL,表示創(chuàng)建多項(xiàng)式實(shí)例操作失敗
}
完成 CreatePoly 接口修改后,我們?cè)俅伟凑?strong>Step 1 至 Step 2的步驟對(duì)當(dāng)前項(xiàng)目進(jìn)行編譯:
[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
請(qǐng)輸入節(jié)點(diǎn)個(gè)數(shù):4
4 -1
7 0
3 1
9 8
+4.0X^-1 +7.0X +3.0X^1 +9.0X^8
[localhost:lab01 xgqin]$
再次使用 g++ 命令可以對(duì) main.cpp、Poly.cpp 進(jìn)行編譯,如無(wú)語(yǔ)法錯(cuò)誤,編譯器會(huì)生成新的 a.out 文件覆蓋舊版本 a.out 文件。
從程序再一次的運(yùn)行結(jié)果看,我們創(chuàng)建了一個(gè)包含4個(gè)項(xiàng)的一元多項(xiàng)式,且輸入每個(gè)項(xiàng)的系數(shù)與指數(shù)時(shí),按照指數(shù)升序的形式進(jìn)行輸入、存儲(chǔ)。
![][3]
[3]: http://latex.codecogs.com/gif.latex?A=4x{-1}+7+3x+9x{8}
輸入所表示的多項(xiàng)式如上圖所示,輸出結(jié)果如下所示。
+4.0X^-1 +7.0X +3.0X^1 +9.0X^8
從程序運(yùn)行輸出結(jié)果看,目前 CreatPoly 、 PrintPoly 兩個(gè)接口未發(fā)現(xiàn)邏輯錯(cuò)誤(未發(fā)現(xiàn)邏輯錯(cuò)誤 不等價(jià)于 沒(méi)有邏輯錯(cuò)誤)。
你可以多次運(yùn)行 a.out 文件輸入不同數(shù)據(jù)對(duì)程序進(jìn)行測(cè)試或修改main.cpp中的測(cè)試代碼(例如:聲明新的Poly指針,新增PrintPoly接口調(diào)用代碼等)。