連結串列實現兩個多項式的加法
最近遇到這樣一個題目,使用連結串列來實現兩個多項式的加法,剛開始覺得應該比較簡單,也可能是自己基礎不紮實吧,這其中也是踩了很多的坑啊,最終還是成功了,特此寫部落格記錄一下。
一、題目要求
使用連結串列來實現兩個多項式的加法,輸出最終相加後的多項式。多項式預設是按指數(冪次)依次遞增的,輸入時會依次輸入每一項的係數和指數,兩個多項式之間的資料以連續兩個0來結束一個多項式的輸入,最終輸出兩個多項式相加的多項式結果。例如:
多項式A: X^1 + 2 * X^2 + 3 * X^3
多項式B: X^1 + 2 * X^2 + 3 * X^3
輸入:
1 1 2 2 3 3 0 0 1 1 2 2 3 3 0 0
輸出:
2 * X^1 + 4 * X^2 + 6 * X^3
上面便是題目的要求,下面我們開始正式分析。
二、題目分析
我們先要解決的問題是多項式的儲存問題,即它在計算機中的表現形式,很明顯我們只需要儲存了多項式的係數和指數即可以開始後面加法計算。這裡我們又兩種儲存方式,一種是線性儲存,另一種是鏈式儲存。
線性儲存,比如我們採用陣列來儲存這些資料,理論來說是可以的,但是由於我們的多項式的項數是不確定的,但陣列必須在剛開始就必須
先給定一個初始的大小,這個初始大小我們不好確定,分配少了,沒法儲存全部多項式,分配多了,會造成空間浪費,所以我們在這裡採用連結串列來儲存多項式,每次輸入一個多項式節點,我們就分配一個連結串列的節點,這樣便可以合理的節約空間。
接著我們分析的是具體的實現步驟,我在這裡將其分為三步:
第一步:
接收使用者控制檯的輸入,並且生成對應的兩個多項式的連結串列表示。
我們的每個連結串列節點有三個域,前兩個域是資料域,依次表示多項式的係數和指數,第三個域是指標域,指向下一個節點
這裡我們為了方便後續處理,給每個連邊增加一個頭結點,頭結點的資料域內都放-1,沒有實際意義。如下圖所示:
第二步:
依次對兩個連結串列中的資料進行加法處理
這一步是我們的關鍵步驟,下面將以圖文結合的方式來進行解釋:
如圖中所示,我們準備了A、B兩個連結串列
連結串列A表示的多項式: 7 * X^1 + 2 * X^2 + 8 * X^5
連結串列B表示的多項式: 2 * X^1 + (-2 * X^2) + 2 * X^3
連結串列C是我們最終的和連結串列
這裡我們按照這樣的步驟進行處理:
1 . 我們規定三個頭指標,分別指向三個連結串列的頭,然後再規定三個移動指標,分別指向當前三個連結串列中正在處理的那個節點
2 . 我們讓A、B、C的移動指標剛開始也處於頭指標的位置,然後,我們拿A第一個節點中的指數和B第一個節點中的指數進行比較,這個時候有三種情況:
a情況 . A中當前的節點指數 < B中當前的節點指數 —— 我們將A中的當前節點插入C中,然後向後移動A和C的指標(因為A中當前節點已經處理了)
b情況 . A中當前的節點指數 > B中當前的節點指數 —— 我們將B中的當前節點插入C中,然後向後移動B和C的指標(因為B中當前節點已經處理了) 即圖中⑧的情況。
c情況 . A中當前的節點指數 > B中當前的節點指數 —— 此時A和B當前節點指數相同,可以進行係數相加,這時候也會出現兩種情況:
情況1 . 係數之和不為0 —— 我們此時將係數之和放到A中的當前節點的係數域,然後將A中的該節點插入C中,然後向後移動C的指標(記住,我們這裡不是產生一個新的節點,而是直接更改A的係數域,然後將A的當前節點插入C中),即圖中的①和②產生③的過程。
情況2 . 係數之和為0 —— 此時我們不能將係數和為0的項放入連結串列C中,理論來說我們什麼都不用做,但是這裡有一個小問題,因為按照情況1來看,我們在係數和不為0時是將A節點直接插到C中,我們假設我們在係數和為0後什麼都不做,繼續處理A中後續節點,後面遇到一個係數和不為0的情況,我們將後面遇到的這個係數不為0的節點插入C中,那其實也將前面那個係數為0的項也一併插入C中了,以為前面那個係數為0的節點和其他後面的節點一直保持聯絡。所以我們此時必須在係數和為0時,將A中的當前節點刪除了。即圖中的④和⑤產生⑥的過程。
無論上面是情況1還是情況2,總之我們都同時處理了節點A和節點B,所以,我們還需要同時將節點A和B的移動指標向後移動。
這裡還有一個情況,我們的A、B連結串列可能長度不是一致的,那麼就有可能其中一個連結串列的移動指標已經移動到了末尾,那麼此時,我們就不需要繼續移動了,我們只需要將另一個連結串列中未處理的資料直接接在當前已經生產的C連結串列的後面即可。
第三步:
列印輸出最終計算所得的和連結串列表示式
三、程式碼實現
經過上面的分析,我們這裡分別採用C語言和Java來實現上述思路,程式碼中有詳細的註釋,下面只進行簡單解釋。
C語言實現:
#include<stdio.h>
#include<malloc.h>
typedef struct node{
float coef;
int expn;
node *next;
}Lnode, * Dxs;
Dxs create();
Dxs add_dxs(Dxs firsta, Dxs firstb);
void printDxs(Dxs h);
void deleteNode(Dxs h, Dxs p);
int main(){
Dxs ha, hb, hc;
printf("請依次輸入第一個多項式的係數和指數\n");
ha = create();
printf("請依次輸入第二個多項式的係數和指數\n");
hb = create();
printf("輸入的第一個多項式是: ");
printDxs(ha->next);
printf("輸入的第二個多項式是: ");
printDxs(hb->next);
hc = add_dxs(ha, hb);
printf("兩個多項式的和為: ");
printDxs(hc->next);
return 0;
}
//建立連結串列(讀入資料以 0 0 結束)
Dxs create(){
float coef;
int expn;
Dxs first, qa, s;
first = (Dxs)malloc(sizeof(Lnode));
first->coef = -1;
first->expn = -1;
first->next = NULL;
qa = first;
while(1){
scanf("%f", &coef);
scanf("%d", &expn);
if(coef == 0 && expn == 0){
break;
}
s = (Dxs)malloc(sizeof(Lnode));
s->coef = coef;
s->expn = expn;
s->next = NULL;
qa->next = s;
qa = s;
}
return first;
}
//連結串列相加
Dxs add_dxs(Dxs firsta, Dxs firstb){
Dxs firstc, ha, hb, pc, s;
int a, b;
float sum;
firstc = (Dxs)malloc(sizeof(Lnode));
firstc->coef = -1;
firstc->expn = -1;
firstc->next = NULL;
pc = firstc;
ha = firsta->next;
hb = firstb->next;
while(ha!= NULL && hb != NULL){
a = ha->expn;
b = hb->expn;
if(a < b){
//將a加入c中,移動a和c的指標
pc->next = ha;
pc = pc->next;
ha = ha->next;
}else if(a > b){
//將b加入c中,移動b和c的指標
pc->next = hb;
pc = pc->next;
hb = hb->next;
}else{
sum = ha->coef + hb->coef;
if(sum != 0.0){
//將和加入a中,再將a加入c中,移動c的指標
ha->coef = sum;
pc->next = ha;
pc = pc->next;
}else{
//查詢刪除A中係數之和為0的那個節點
s = firsta;
while(s != ha){
s = s->next;
}
s->next = ha->next;
}
//ab已經處理完成,同時後移一位
ha = ha->next;
hb = hb->next;
}
}
//將剩餘部分加入c後面
if(ha != NULL){
pc->next = ha;
}
if(hb != NULL){
pc->next = hb;
}
return firstc;
}
//遍歷顯示連結串列
void printDxs(Dxs h){
while(h != NULL){
printf("%0.2f*X^%d + ", h->coef, h->expn);
h=h->next;
}
printf("\n");
}
Java語言描述:
package cn.codekong;
import java.util.Scanner;
/**
* 連結串列類
* @author szh
*
*/
public class MyLink {
/**
* 連結串列節點類
* @author szh
*
*/
class Node{
//多項式的係數
private float coef;
//多項式的指數
private int expn;
//指向下級節點
public Node next = null;
public Node(float coef, int expn){
this.coef = coef;
this.expn = expn;
}
}
/**
* 建立連結串列類
* 從從控制檯不斷讀入資料,以(0 0)結束
* @return 建立好連結串列的第一個節點
*/
public Node createLink(){
//存取從控制檯讀到的係數和指數
float coef = 0.0f;
int expn = 0;
//頭尾節點(尾節點方便插入)
Node head, tail;
head= new Node(-1, -1);
head.next = null;
tail = head;
Scanner scanner = new Scanner(System.in);
while(true){
String res = scanner.nextLine();
//以空格分割一行中的字串
String[] resArray = res.split("\\s+");
coef = Float.parseFloat(resArray[0]);
expn = Integer.parseInt(resArray[1]);
if(coef == 0 && expn == 0.0f){
break;
}
Node node = new Node(coef, expn);
node.next = null;
tail.next = node;
tail = tail.next;
}
return head;
}
/**
* 列印連結串列
* @param head 連結串列首節點
*/
public void printLink(Node head){
while(head != null){
System.out.format("%.2f*X^%d + ", head.coef, head.expn);
head = head.next;
}
System.out.println();
}
/**
* 計算兩個連結串列的和
* @param nodeA
* @param nodeB
* @return 最終和的連結串列
*/
public Node addLink(Node nodeA, Node nodeB){
Node nodeC = new Node(-1, -1);
nodeC.next = null;
//始終指向連結串列的當前需要處理的節點(剛開始要除去開頭的(-1,-1)節點)
Node pA = nodeA.next, pB = nodeB.next, pC = nodeC;
//當前指向的兩個連結串列的指數
int valueAExpn = 0, valueBExpn = 0;
while(pA != null && pB != null){
valueAExpn = pA.expn;
valueBExpn = pB.expn;
if(valueAExpn < valueBExpn){
//將A中的該節點加入C中, 同時移動A和C的指標指向下個元素
pC.next = pA;
pC = pC.next;
pA = pA.next;
}else if(valueAExpn > valueBExpn){
//將B中的該節點加入C中,同時移動B和C的指標指向下個元素
pC.next = pB;
pC = pC.next;
pB = pB.next;
}else{
//兩節點指數相同
//現將係數相加放到A節點的係數中,然後將A節點加入C中
float sum = pA.coef + pB.coef;
if(sum != 0.0f){
//係數和不為0,將A節點的係數改變為sum,然後將A節點加入C中
pA.coef = sum;
pC.next = pA;
pC = pC.next;
}else{
//係數和為0,必須將A連結串列中的該節點從連結串列中刪除掉,如果只移動指標,會在輸出時也輸出該項
//在整個A連結串列中依次查詢,必須找到要刪除節點的前驅節點才能將其刪除
Node s = nodeA;
while(s != pA){
s = s.next;
}
//刪除該節點
s.next = pA.next;
}
//對於係數相同的情況,A和B節點指標都往後移動
pA = pA.next;
pB = pB.next;
}
}
if(pA != null){
pC.next = pA;
}
if(pB != null){
pC.next = pB;
}
return nodeC;
}
}
package cn.codekong;
import cn.codekong.MyLink.Node;
public class MyTest {
public static void main(String[] args) {
MyLink myLink = new MyLink();
System.out.println("請依次輸入第一個多項式的係數和指數");
Node nodea = myLink.createLink();
System.out.println("請依次輸入第二個多項式的係數和指數");
Node nodeb = myLink.createLink();
System.out.println("輸入的第一個多項式是: ");
myLink.printLink(nodea.next);
System.out.println("輸入的第二個多項式是: ");
myLink.printLink(nodeb.next);
Node nodec = myLink.addLink(nodea, nodeb);
System.out.println("兩個多項式的和為: ");
myLink.printLink(nodec.next);
}
}
其實Java的連結串列實現和C語言很類似,只是C語言中的節點是定義結構體,而此處是使用一個內部類來定義連結串列的每個節點,C語言中的指標其實就是Java中的引用,我們生命的Java物件是儲存在堆中,而物件的引用則是儲存在堆疊中。
上面為了保持Java和C在控制檯輸入的一致性,我通過使用Scanner讀入一行,然後通過正則表示式分割出係數和指數進行後續處理。
四、程式碼執行驗證
C語言執行結果:
Java執行結果: