數據結構—— 一元多項式的加法運算

一、 需求分析
0.問題描述

在數學上,一個一元n次多項式 可按降序寫成:
在這裏插入圖片描述
它由n+1個係數唯一確定,因此,在計算機裏他可以用一個線性表表示:
( )。

設Pn(x)和Qn(x)分別爲兩個一元多項式,請求出兩個一元多項式的加法運算的結果,要求元素按照多項式的次數遞減的次序排列。

1.問題分析

需求:實現兩個一元多項式的加法運算。
實現功能:①通過鍵盤接收輸入的整數(浮點數)
②以係數和指數對應的形式,儲存到計算機內存中
③實現一元多項式的加法運算
④通過屏幕輸出一元多項式加法運算後的結果

2.輸入數據

分別輸入兩組二元組數據,每一組二元組數據分別代表一個多項式,每個二元組包含兩個整數(一個浮點數和一個整數),第一個整數(浮點數)代表多項式的項的係數,第二個整數代表該項的次數(指數),且在輸入過程中,要求同一個多項式中不能出現兩個項的指數相同。(使用模板設計,以下設計以係數爲整數爲例)

3.輸出數據

輸出兩個一元二次多項式相加運算之後的結果,形式如 在這裏插入圖片描述

4.測試樣例設計
(1)樣例輸入1:
4
3 2
4 5
1 1
2 3
3
-3 2
-4 5
2 4
(1)樣例輸出1:
第一個多項式爲:
4x^5+2x^3+3x^2+1x^1
第二個多項式爲:
-4x^5+2x^4-3x^2
兩個多項式的和爲:
2x^4+2x^3+1x^1

目的:本例爲了測試在兩組多項式相加時,兩個多項式中恰有相同指數項的係數互爲相反數,即相加之後爲0,能否實現正確計算。

(2)樣例輸入2:
2
0 1
2 3
3
2 6
3 9
4 3
(2)樣例輸出2:
第一個多項式爲:
2x^3
第二個多項式爲:
3x^9+2x^6+4x^3
兩個多項式的和爲:
3x^9+2x^6+6x^3

目的:本例爲了檢測若輸入的項的係數是0,那麼輸出是否會出現錯誤,會不會把項 爲0的也輸出出來,能否實現正確運算。

(3)樣例輸入3:
1
2 3
1
3 3
(3)樣例輸出3:
第一個多項式爲:
2x^3
第二個多項式爲:
3x^3
兩個多項式的和爲:
5x^3

目的:本例爲了測試在兩組多項式只有一項的情況下,並且這項的冪相等,觀察輸出是否是一項。

(4)樣例輸入4:
2
0 5
0 4
5
6 5
3 8
-2 2
3 1
2 4
(4)樣例輸出4:
第一個多項式爲:
(此處輸出爲空)
第二個多項式爲:
3x^8+6x^5+2x^4-2x^2+3x^1
兩個多項式的和爲:
3x^8+6x^5+2x^4-2x^2+3x^1

目的:當某一個多項式所有項的係數均爲0時,觀察該多項式樣式輸出 及運算結果。

(5)樣例輸入5:
3
4 2
-2 2
-1 2
2
0 2
6 2
(5)樣例輸出5:
第一個多項式爲:
4x^2-2x^2-1x^2
第二個多項式爲:
6x^2
兩個多項式的和爲:
10x^2-2x^2-1x^2

目的:本例是反例,若不符合要求同一個多項式不能有相同的冪的項存在,觀察該多項式輸出及運算結果。

二、概要設計
1.抽象數據類型

問題的輸入爲多項式的,多項式由一個或者多個項構成,而每個項又是由一個指數和一個係數唯一確定,那麼這個指數和這個係數就是一個具有關係的數對,我們可以用結構體進行存儲,可以形象的定義爲一個二元組(ai,bi),那麼這些二元組按照多項式的次數遞減的次序排列,則具有線性關係,則兩個多項式可以分別用兩個線性表表示,線性表中的數據元素即爲多項式的項的二元組。

數據對象:

D={ 多項式的項,  項的係數, 項的指數,i=1,2,3....n,n≥1}

數據關係:

因爲多項式按照多項式的次數遞減的次序排列,相應的指數對應的係數也就排列好前後位置,因此具有線性結構,這些二元組之間都存在着有序的關係。即
R={ D,i=1,2,3....n,n>0}

基本操作:
①	link();                       // 構造線性表 
②	~link();                      // 摧毀線性表 
③	void init();                  // 初始化線性表 
④	void next();                  // 當前位置後移 
⑤	void movetostart();           // 移動當前位置到線性表表頭 
⑥	arrary<E> getvalue();         // 獲取當前位置存儲單元內的數據元素
⑦	void insert(arrary<E> elem);   // 插入數據元素(實現線性表的有序性) 
⑧	int getsize();                // 返回線性表長度
⑨	void change(arrary<E> elem);  // 改變儲存數據元素的值
2.算法的基本思想

存儲:解決本問題首先要想到的就是怎麼把一個亂序輸入的二元組按照第二個數的從大到小的順序排列下來,可以這麼想,每次要添加一個二元組的時候都要把已經存在的二元組進行一次從頭的搜索,查找到一個適當位置,使得二元組插入後,線性表數據元素滿足以二元組第二個數依次遞減的次序排列。把二元組的前後邏輯線性關係存儲在鏈表中,通過修改插入函數實現上面存儲中所提到的查找位置問題,然後把輸入的二元組插入即可完成有序鏈表的構建。
相加:把第二個多項式的線性表中的每一個數據元素二元組中第二個數依次與第一個多項式的線性表中的每一個數據元素二元組中第二個數比較,如果相同,通過修改第一個多項式的線性表中的每一個數據元素二元組中第一個數,實現相同指數項的相加,否則通過插入操作把該二元組插入到第一個多項式的線性表中。
輸出:完成相加後,計算結果儲存在第一個多項式的線性表中,且由於插入時已完成了有序性的排列,按照線性表的線性順序及多項式的寫法規則輸出完成相加後第一個多項式線性表中的數據元素。

3.程序的流程

① 第一個模塊:輸入模塊。提示輸入多項式的項數,之後提示輸入多項式的每一項的係數和指數,調用insert(有序插入)等基本操作實現有序存儲數據,將多項式分別儲存到線性表中,然後根據多項式的寫法規則調用基本操作進行打印多項式。
② 第二個模塊:計算模塊。根據第一個模塊接受的數據,利用多重循環,對第二個多項式與第一個多項式進行遍歷比較,並調用change、insert等基本操作完成兩個多項式的相加,計算結果存儲到第一個多項式的線性表中。
③ 第三個模塊:輸出模塊。通過提示,按照多項式的寫法規則打印出相加後的多項式的形式。

三、詳細設計
1.物理數據類型

多項式的係數爲整數(浮點數),指數爲整數,所以物理數據類型可以爲int等(採用模板)。
多項式的項按照指數從大到小排列,相加運算時會改變項的個數。選用基於鏈表實現的線性表。
① 結點SNode的構造:結構體SNode在這個題中要存儲的是一個多項式的項,其包含兩個元素,多項式的係數和指數,所以我添加了一個結構體arrary作爲一個二元組,包含係數(value),指數(element)將這個結構體類型的元素作爲結點的數據元素
僞代碼:

struct arrary
 {
 	      E value;
 	      E element;
};
 struct SNode 
           {
                  arrary<E> elem;
                  SNode *next; 
};

② 基本操作:基於有序鏈表實現線性表ADT,要進行的基本操作有:
Ⅰ構造線性表
僞代碼:

link()
{ init();}

Ⅱ摧毀線性表
僞代碼:

~link()
{removeall();}

流程:通過link類的一個私有函數removeall完成操作。

Ⅲ初始化線性表

僞代碼:

void init()
    {
    	               curr=tail=head=new SNode<E>;
    	               linksize=0;
    }

Ⅳ把當前位置從前到後移動一個存儲單元。
僞代碼:

void next()
{
			if(curr!=tail)
		    curr=curr->next;
}

流程:當前位置移動到下一個節點,只要把當前位置的指針curr的指向修改爲他所指向的節點中的指針next的指向就可以做到,但是要注意表尾操作。
Ⅴ移動當前位置到線性表表頭。
僞代碼:

void moveToStart()
{curr=head;}

流程:把頭節點的存儲地址的值賦值給當前位置curr就可以實現當前位置頭置。
Ⅵ獲取當前位置存儲單元內的數據元素。
僞代碼:

arrary<E> getvalue()
{return curr->elem;}

流程:函數返回當前位置指向的節點中包含的數據元素。

Ⅶ向線性表插入一個數據元素(有序線性表的實現函數)。

僞代碼:

void insert(arrary<E> elem)
    {
    	       for(curr=head;curr!=tail;curr=curr->next)
    	       {
    		        if(temp.element>curr->next->elem.element)break;
                }
    	       SNode<E>* tempnew =new SNode<E>;
    	       tempnew->elem.value=temp.value;
    	       tempnew->elem.element=temp.element;
    	       tempnew->next=curr->next;
                curr->next=tempnew;
    	       if(tail==curr)tail=curr->next;
    	       linksize++;
    }

流程:首先把當前位置設定在頭指針的位置,之後開始用函數參數elem的element和當前位置的數據元素(也就是柵欄後面的節點的二元組中的第二個數)比較大小,如果找到了一個位置使得後面的節點中的指數小於要輸入的節點的指數那麼終止循環,如果沒找到這樣的位置,說明輸入的項指數比已經有的所有項都要小,那麼就會把這個位置定在表尾,之後讓當前位置所指節點的next指針指向新的節點,也就是存儲新項的地方,新節點的前兩個參數就是函數的兩個參數,指針肯定指向下一節點,若新節點是最後一個節點,也就是他存儲的項的指數最小,那麼要讓表尾指針指向新節點 ,同時鏈表長度加一。

Ⅷ 獲取線性表的長度。

僞代碼:

int getsize()
    {return  linksize;}

流程:在每一次進行插入等操作時,都會有一個變量linksize發生變化,它記錄了鏈表的節點數目,函數返回他就可以得到線性表長度。

Ⅸ修改當前存儲單元的值。

僞代碼:

void  change(arrary<E> temp)
    {
    	         curr->elem.value=temp.value;
    	         curr->elem.element=temp.element; 
    }

流程:爲了實現兩個多項式相加的操作同時又不影響鏈表的通用性,那麼將需要修改的數據元素的值作爲函數的參數,把原來的節點中的value係數和element指數全部修改爲函數的參數。

2.輸入和輸出的格式

輸入
① 開始提示“請輸入第一個一元多項式的項數:”,提示輸入一個整數,之後提示"接下來 n行,分別輸入每一項的係數和指數,如:3 2(其中3爲係數,2爲指數,中間以空格分隔)"。完成輸入第一個多項式。
② 然後提示“請輸入第二個一元多項式的項數:”,提示輸入一個整數,之後提示"接下來 n行,分別輸入每一項的係數和指數,如:3 2(其中3爲係數,2爲指數,中間以空格分隔)"。輸入第二個多項式的輸入。
輸出
① 提示"第一個多項式爲:",之後打印第一個多項式多項式的排布,打印之後輸出一行分隔符。
② 提示"第二個多項式爲:",之後打印第二個多項式多項式的排布,打印之後輸出一行分隔符。
③ 提示"兩個多項式的和爲:",之後打印兩個多項式相加並存入第一個多項式線性表後的結果。

3.算法的具體步驟
(1)模塊分析:

①第一個模塊
【1】第一個多項式的輸入和輸出:
Ⅰ通過一個n代表第一個多項式的項數的輸入進入第一個多項式的每一項係數和指數的輸入,調用修改的插入函數實現數據的存入。

cin>>n;
cin>>temp.value>>temp.element;
a.insert(temp);

Ⅱ通過調用movetostart先把當前位置移動到表頭,之後從頭往後依次處理每個存儲位置,特殊處理第一個輸出還有係數爲0的輸出,調用getvalue和getelement實現輸出。

a.movetostart();
a.getvalue();

【2】第二個多項式的輸入和輸出
Ⅰ通過一個m代表第二個多項式的項數的輸入進入第二個多項式的每一項係數和指數的輸入,調用修改的插入函數實現數據的存入。

cin>>n;
cin>>temp.value>>temp.element;
b.insert(temp);

Ⅱ通過調用movetostart先把當前位置移動到表頭,之後從頭往後依次處理每個存儲位置,特殊處理第一個輸出還有係數爲0的輸出,調用getvalue和getelement實現輸出。

b.movetostart();
b.getvalue();

②第二個模塊(實現多項式的加法運算,並將結果保存到第一個多項式的線性表中):首先對存儲第二個多項式的線性表進行從頭到尾的元素遍歷,每一個第二個多項式中的元素都要再分別遍歷第一個多項式中的元素,若查找到指數相同的項,那麼調用change把第二個多項式中的係數加到第一個多項式中,如果沒有找到,那就直接調用insert函數實現存入。
b.movetoStart();
a.movetoStart();
如果查找到:

if(b.getvalue().element==a.getvalue().element)
{       arrary<int> temp2;
	 	   temp2.value=a.getvalue().value+b.getvalue().value;
	 	   temp2.element=a.getvalue().element;
a.change(temp2); 
}

如果沒查找到那麼:

arrary<int>temp;
temp.value=b.getvalue().value;
temp.element=b.getvalue().element;
a.insert(temp);

③第三個模塊(輸出兩個多項式相加的結果):和前文輸出方法一致,通過調用movetoStart先把當前位置移動到表頭,之後從頭往後依次處理每個存儲位置,特殊處理第一個輸出還有係數爲0的輸出,調用getvalue和getelement實現輸出。

a.movetoStart();
a.getvalue();

流程圖:
第一個模塊:
在這裏插入圖片描述
第二個模塊:
在這裏插入圖片描述
第三個模塊:
在這裏插入圖片描述

4.算法的時空分析

第一個模塊:對於第一個多項式的輸入,首先要輸入n個數對,存儲時每個數對都要對已有的鏈表元素遍歷一次,輸入操作的時間代價根據化簡規則略去低階項和常數項,得到輸入時間代價是Θ(n2);輸出是從頭到尾依次輸出,所以時間代價是Θ(n),所以,輸入並打印第一個多項式的時間代價是Θ(n2);與第一個多項式相同輸入第二個多項式,所以第二個多項式輸入並打印的時間代價是Θ(m2);綜上,第一個模塊的時間代價是Θ(max{ n2,m2 })。
第二個模塊:在對應多項式A,多項式B的每一個值都去遍歷一次,這個雙重嵌套for循環的時間代價爲Θ(nm),之後進入一個也在第一重for循環中的if解決是否要用到插入操作,那麼這個語句的時間代價是Θ(n2)。
所以總共的時間代價是Θ(max{nm,n2 })。
第三個模塊:假設兩個多項式相加之後的第一個多項式的長度爲x,那麼輸出A的時間代價就是Θ(x),第四個模塊的時間代價就是Θ(x)。
綜上所述:整個算法的時間代價就是Θ(max{ nm,n2 })

四、調試分析
1.調試方案設計

調試目的:用手工或編譯程序等方法進行測試,修正語法錯誤和邏輯錯誤的過程。這是保證計算機信息系統正確性的必不可少的步驟。編完計算機程序,必須送入計算機中測試。根據測試時所發現的錯誤,進一步診斷,找出原因和具體的位置進行修正。對於本題目,根據調試過程及結果檢測兩個多項式加法運算相關函數過程及正確性。
樣例:(設計樣例中的第一個樣例)

(1)樣例輸入1:
4
3 2
4 5
1 1
2 3
3
-3 2
-4 5
2 4
(2)樣例輸出1:
第一個多項式爲:
4x^5+2x^3+3x^2+1x^1
第二個多項式爲:
-4x^5+2x^4-3x^2
兩個多項式的和爲:
2x^4+2x^3+1x^1

調試計劃:該樣例的設計中兩個多項式存在係數互爲相反數且指數相同的項,按照設計,當兩個多項式進行相加運算時,第二個多項式的項會與第一個多項式中指數相同的項通過函數修改第一個項中對應的元素值,完成相加運算,同時第一個多項式的項數不會改變,及線性表長度不變,觀察第一個多項式線性表的長度,驗證設計過程的正確性。
設置:在加法運算循環處設置斷點,觀察調試過程。

2.調試過程和結果,及分析

設置斷點
在這裏插入圖片描述
開始調試
(1)當完成第一個模塊輸入之後,此時兩個多項式線性表的長度均爲項的個數,運行正常,結果如下圖。
在這裏插入圖片描述
(2)從設置斷點進入for循環語句,開始進行兩個多項式的相加操作,由於兩個多項式的第一個項的指數相同,程序進入if語句,如下圖。
在這裏插入圖片描述
(3)和預測的結果相同,兩個項的指數相同完成當前第一個項的相加操作之後,第一個多項式的線性表長度沒有發生變化,如下圖。
在這裏插入圖片描述
(4)接下來對第二個多項式中的第二項即指數爲4的項在第一個多項式中查找,與預測相同,當循環到最後一個時,此時j==3,沒有發現相同的項,進入第二個if語句完成插入操作,第一個線性表的長度發生改變,變爲5,如下圖。
在這裏插入圖片描述
(5)繼續進行循環,第二個多項式中的第三項,根據預測與第一個多項式中的第四項相同,會同第一項完成相加時的操作相同,第一個多項式線性表長度不會改變,此時i=2,j=3,調試過程正確,如下圖。
在這裏插入圖片描述
(6)完成相加操作後,最後輸出第一個多項式,得到兩個多項式相加的結果,與預測結果相同,調試結果爲正確,如下圖。
在這裏插入圖片描述
總結:調試結束,結果與預料一致,說明程序對這組數據的處理沒有問題。

五、測試結果
(1) 測試樣例1

在這裏插入圖片描述
結論分析:兩組多項式相加時,兩個多項式中恰有相同指數項的係數互爲相反數,即相加之後爲0,能夠實現正確計算。

(2) 測試樣例2

在這裏插入圖片描述
結論分析:輸入的項的係數是0,輸出未出現錯誤,不會把項爲0的也輸出出來,能夠實現正確運算。

(3) 測試樣例3

在這裏插入圖片描述
結論分析:在兩組多項式只有一項的情況下,並且這項的冪相等,輸出爲一項,程序計算結果正確。

(4) 測試樣例4

在這裏插入圖片描述
結論分析:當某個多項式所有項的係數均爲零時,該多項式不會輸出,滿足係數爲0的項不會輸出的設計,最後的計算結果正確,程序運行正常。

(5) 測試樣例5

在這裏插入圖片描述
結論分析:本例是反例,若不符合要求同一個多項式不能有相同的冪的項存在,當儲存一個多項式時會把指數相同的項當作多個項進行儲存,運算時,也會把這些所有項的係數對第一個多項式中的第一個該指數的元素進行操作,完成相加,這也是符合當前該設計的計算過程的,即程序運行正常,由於不符合同一個多項式不能有相同的冪的項存在的要求,輸出結果錯誤。

六、實驗日誌(選做)

實驗時間
2018.10.24-2018.10.30
實驗過程
10.24
19:30-21:40
① 完成需求分析模塊。
② 完成概要設計模塊。
10.25
12:20-15:50
完成詳細設計模塊。
21:00-22:44
基本完成線性表ADT的設計和實現。
10.26
13:00-17:24
① 完成有序插入基本操作的設計與實現。
② 完成修改儲存數據元素的值基本操作的設計與實現。
③ 完成源代碼。
10.27
13:00-15:25
完成調試分析模塊
10.30
14:30-16:00
針對預備報告進行了完善與修改。
19:30-21:18
① 修改源代碼。
② 調試修改後的源代碼。
③ 完成最終報告。
實驗中遇到的問題及解決分析
(1) 如何實現有序線性表?
分析及解決:對於線性表的有序性,可以通過修改插入基本操作,在插入儲存時實現有序儲存,即實現了線性表的有序性。
(2)使用模板 template 實現數據對象的多用性。出現報錯報錯(GCC): error: need ‘typename’ before ‘E::xxx’ because ‘E’ is a dependent scope
分析及解決:通過查詢,得知了這樣兩個概念——
從屬名稱(dependent names): 模板(template)內出現的名稱, 相依於某個模
(template)參數, 如 E t;
嵌套從屬名稱(nested dependent names):從屬名稱在 class 內呈嵌套裝, 如
E::const_iterator ci;
如果不特定指出 typename, 嵌套從屬名稱, 有可能產生解析(parse)歧義.
所以,任何時候在模板(template)中指涉一個嵌套從屬類型名稱, 需要在前一個
位置, 添加關鍵字 typename;進行如下操作解決了問題
(3)如何實現加法運算?
分析及解決:我們實現這樣一個算法過程——遍歷操作,每一個第二個多項式中的元素都要再分別遍歷第一個多項式中的元素,若查找到指數相同的項,那麼調用change把第二個多項式中的係數加到第一個多項式中,如果沒有找到,那就直接調用insert函數實現有序存入,完成加法運算。

實驗心得
實驗設計比較完善,解決了基於有序鏈表實現多項式的輸入輸出和相加的功能,但是還是那個反例的問題怎麼解決呢,我認爲把兩個多項式的項逐一存入另一個線性表中是一個好辦法,這樣的話,根據change函數,在每一次存入多項式的值的時候,新的線性表就構成了一個新的鏈表,那麼就算多項式中有相同指數的項,到了存儲進新的線性表中的時候,也會和前面已經存過的指數相同項進行一次係數的相加而不是增多一個節點,但是這個方法每次都要遍歷前面已經存儲過的節點時間代價有些大是它的缺點。

具體代碼實現:
數據結構——一元多項式的加法運算
僞代碼部分排版有點問題,請見諒!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章