這篇是我搬運自己以前在 GitHub Pages 上 2020 年 11 月的文章,看了很久,完全不知為何這篇被獨立拿出來寫XDD 當時好多冗文贅字,盡量精簡了,希望有好閱讀一些。
關於 linked list 這裡有很好的講解:
有很清楚的示意圖輔助觀念的養成,光看這兩篇便省了不少需要花在理解上的時間,也很適合收藏做為日後複習觀看。
雖然寫下關於 linked list 的介紹文有助於自身觀念釐清,但這次先著重在應用上,剛好再次熟悉並釐清了很多在 linked list 上的觀念誤區。
為何而做
本次要使用 linked list 在稀疏矩陣(sparse matrix)的加法上。
稀疏矩陣是其元素大部分為零的矩陣。為什麼特別指定稀疏矩陣?我們一般要存矩陣第一個優先會想到array 型態,然而今天遇上稀疏矩陣,既然大部分元素為 0,事實上我們可以不用做到「每一個元素都要參與運算」這件事。
用 linked list 儲存那些少數且必須的資料,需要的時候直接由 linked list 遍歷即可。
初步設計
輸入資料
這裡的 input 我們設計成: 先輸入矩陣大小 m, n,再輸入矩陣元素。前面數字輸入 column 的位置,後面數字是那個位置的值,並且用 0 判斷元素輸入結束。
以這個矩陣舉例來說:
$$
\begin{pmatrix}
1 & 6 & 0 & 85\\
5 & 0 & 0 & 0\\
0 & 0 & 2 & 0
\end{pmatrix}
$$
先輸入矩陣大小: 3 4
再輸入元素:> 1 1 2 6 4 85 0> 1 5 0> 3 2 0> 0
Linked list 的設定
Linked list 用比較不精確的話說,就是一個物件有一些自身的性質外,我們會再多存一兩個性質,去指向這個物件的前一個或後一個資料(單向或雙向的)。因為有了這個性質,很巧妙地彼此之間有了連結(link),把每個物件都像是火車一樣都串連起來了。這部分可以講的東西有很多,在此不再贅述。
這次只專注在設計我需要的 linked list。
- 節點(node)
此處的 node 我要存四個資料:值(value)、所在列(row_pos)、所在行(column_pos)以及下個node 的位置(next):
class llNode {
private:
int value;
int row_pos;
int column_pos;
llNode *next;
public:
llNode() : next(NULL){};
llNode(int a) : value(a), next(NULL){};
friend class LinkedList;
};
- Linked list 本身
創建好基本元素 node 後,可以來弄 linked list 了。 這裡我需要的功能有新增 node、列印已存在 node、列印矩陣、矩陣相加。其中要新增一個起頭的node,在這裡我們將它命名為 first。
class LinkedList {
private:
llNode *first;
public:
LinkedList() : first(NULL){};
void PrintList();
void addNode(int newrow_pos, int newcolumn_pos, int newvalue);
void printMatrix(int m, int n);
LinkedList matrixAdd(LinkedList A, LinkedList B);
};
功能設計
addNode
這是最基本的功能,linked list 是由節點所組成的。新增節點所需資料:行位置、列位置、值。在這程式中必須準備好這三項才能申請新節點(公務人員口吻)。
void LinkedList::addNode(int newrow_pos, int newcolumn_pos, int newvalue) {
//content
}
接下來我需要三個 node:
- 存我要存進去的 node(
newNode) - 有資料傳進來必須暫時要有地方放(
temp) temp有時會存放先前資料,需要多個空間放新的 node(tempNew)
llNode *newNode = new llNode(newvalue); llNode *temp = new llNode(); llNode *tempNew = new llNode();
接下來就是要放入資料和做連接。 若是沒有資料的狀況下,linked list 的開頭應該是空的,第一個資料必須放去 first 中。若是已經有了資料,也不需要一一巡覽,只需要確認我們確實到達 linked list 尾端。因此
if (first == NULL) {
//是空的就直接加入
temp->value = newvalue;
temp->row_pos = newrow_pos;
temp->column_pos = newcolumn_pos;
temp->next = NULL;
first = temp;
} else {
//若不是空的就先到達尾端再加入
temp = first;
while (temp->next != NULL) {
temp = temp->next;
}
tempNew->value = newvalue;
tempNew->row_pos = newrow_pos;
tempNew->column_pos = newcolumn_pos;
tempNew->next = NULL;
temp->next = tempNew;
}
再來要進入重頭戲了。
matrixAdd
在 matrixAdd 中我需要兩個 linked list 當變數(也就是兩個矩陣),並利用 linked list 的特性去做巡覽和相加。
LinkedList LinkedList::matrixAdd(LinkedList A, LinkedList B) {
//content
}
首先要設新的 linked list C 去放置我們相加處理過後的元素。 在巡覽前我們必須要先設定好開頭,電腦才會知道我們要從 linked list 的哪裡開始。
LinkedList C; llNode *nodeA = A.first; llNode *nodeB = B.first;
接下來要設立一個迴圈。我使用的方式是先判斷巡覽到的 node 是否都有東西,再去判斷要怎麼透過辨識 row 和 column 的位置去做相加。如果其中一個矩陣已經巡覽完畢沒東西的話,那個位置就是另一個矩陣巡覽到的元素,直接放進去 linked list C 中
while (nodeA != NULL && nodeB != NULL) {
//content
}
while (nodeA != NULL) {
C.addNode(nodeA->row_pos, nodeA->column_pos, nodeA->value);
nodeA = nodeA->next; //要記得巡覽後指向下一個
}
while (nodeB != NULL) {
C.addNode(nodeB->row_pos, nodeB->column_pos, nodeB->value);
nodeB = nodeB->next;
}
接著我們專注在第一個 while 迴圈中。這邊我的方式是先比對 row 的位置一不一樣,接著再比對 column 的位置。
while (nodeA != NULL && nodeB != NULL) {
if (nodeA row 的位置 = nodeB row 的位置) {
if (nodeA column 的位置 = nodeB column 的位置) {
//指定sum = 巡覽到的 nodeA + nodeB;
//加入C中
//nodeA指向下一個
//nodeB指向下一個
} else {
//也就是row一樣但column位置不同,比較column位置大小
if (nodeA column 的位置較小) {
//存nodeA 的資料進去C
//nodeA指向下一個
} else {
//存nodeB 的資料進去C
//nodeB指向下一個
}
}
} else { //兩個row的位置就已經不同的狀況
//先比較row位置的大小
if (nodeA row 的位置較小) {
//存nodeA 的資料進去
//nodeA指向下一個
} else {
//存nodeB 的資料進去C
//nodeB指向下一個
}
}
}
當初在創建 linked list 的元素順序很重要,如果亂了這裡的設置必須要失敗,但是我也懶得寫防呆,用程式碼實現差不多是這樣:
while (nodeA != NULL && nodeB != NULL) {
if (nodeA->row_pos == nodeB->row_pos) {
if (nodeA->column_pos == nodeB->column_pos) {
int sum = nodeA->value + nodeB->value;
C.addNode(nodeA->row_pos, nodeA->column_pos, sum);
nodeA = nodeA->next;
nodeB = nodeB->next;
} else {
if (nodeA->column_pos < nodeB->column_pos) {
C.addNode(nodeA->row_pos, nodeA->column_pos, nodeA->value);
nodeA = nodeA->next;
} else {
C.addNode(nodeB->row_pos, nodeB->column_pos, nodeB->value);
nodeB = nodeB->next;
}
}
} else {
if (nodeA->row_pos < nodeB->row_pos) {
C.addNode(nodeA->row_pos, nodeA->column_pos, nodeA->value);
nodeA = nodeA->next;
} else {
C.addNode(nodeB->row_pos, nodeB->column_pos, nodeB->value);
nodeB = nodeB->next;
}
}
}
在 GitHub 查看完整程式碼
測試結果
input:
Enter the number of row and column:
> 4 5
Enter the element of matrix A:
> 1 2 5 8 0
> 2 3 4 55 0
> 3 7 0
> 1 100 0
Enter the element of matrix B:
> 2 8 3 4 0
> 2 44 0
> 1 9 3 6 0
> 0
output:
The matrix A:
2 0 0 0 8
0 3 0 55 0
0 0 7 0 0
100 0 0 0 0
The matrix B:
0 8 4 0 0
0 44 0 0 0
9 0 6 0 0
0 0 0 0 0
The matrix A+B:
2 8 4 0 8
0 47 0 55 0
9 0 13 0 0
100 0 0 0 0








