Abstraction
Introduction
學習資料結構,第一個要學的就是linked list,本文示範最簡單的linked list實現,包含建立與顯示,可把它當成linked list的標準範本,畢竟步驟都差不多。
一個基本的問題:為什麼需要linked list?若要將大量資料存到記憶體,你會想到什麼?第一個想到的就是array,但C語言是個靜態語言,array必須事先宣告大小,這樣compiler才能進行最佳化,若到時候沒用這麼多記憶體,就白白浪費記憶體了。或許你會說array可以配合malloc()變成動態array,但前提是你必須告訴malloc()要建立多大的array,若連要建立多大的陣列也不確定,而是在run-time看有多少資料就建立多大,這時連malloc()的動態array也不能用了,此時就得靠linked list。
linked list是資料結構的基礎,基本上就是靠struct如火車車廂那樣連在一起,run-time有需要時,在動態掛一個struct上去。
C語言
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : DS_linked_list_simple.c
5 Compiler : Visual C++ 8.0
6 Description : Demo how to use malloc for linked list
7 Release : 03/22/2008 1.0
8 */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #define SLEN 255
14
15 struct list {
16 int no;
17 char name[SLEN];
18 struct list *next;
19 };
20
21 int main() {
22 int no;
23 char s[255];
24
25 struct list *head = NULL;
26 struct list *current = NULL;
27 struct list *prev = NULL;
28
29 while(1) {
30 printf("No. = ");
31 scanf("%d", &no);
32
33 if (no == 0)
34 break;
35
36 printf("Name = ");
37 scanf("%s", s);
38
39 current = (struct list *)malloc(sizeof(struct list));
40 if (current == NULL)
41 exit(EXIT_FAILURE);
42
43 current->next = NULL;
44
45 current->no = no;
46 strncpy(current->name, s, SLEN -1);
47 current->name[SLEN -1] = '\0';
48
49 if (head == NULL)
50 head = current;
51 else
52 prev->next = current;
53
54 prev = current;
55 }
56
57 // display linked list
58 current = head;
59 while(current != NULL) {
60 printf("No. = %d, Name = %s\n", current->no, current->name);
61 current = current->next;
62 }
63
64 // free linked list
65 current = head;
66 while(current != NULL) {
67 prev = current;
68 current = current->next;
69 free(prev);
70 }
71 }
執行結果
Name = clare
No. = 2
Name = jessie
No. = 0
No. = 1, Name = clare
No. = 2, Name = jessie
15行
int no;
char name[SLEN];
struct list *next;
};
linked list的基礎就是struct,所以先建立一個自訂的struct型別,因為linked list是靠struct串聯起來,所以最後要多一個struct pointer指向下一個struct。
25行
struct list *current = NULL;
struct list *prev = NULL;
建立linked list最基本需要三個指標,head指向linked list的第一個struct,current指向目前剛建立的struct,prev則指向前一個struct,目的在指向下一個struct,對於未使用的pointer,一律指定為NULL,這是一個好的coding style,可以藉由判斷是否為NULL判斷此pointer是否被使用。
39行
if (current == NULL)
exit(EXIT_FAILURE);
current->next = NULL;
每當有新資料,需要建立一個新的struct時,就用malloc()要一塊記憶體,由於malloc()傳回的是void *,所以要手動轉型成struct list *。但malloc()並不是一定會成功,若記憶體不足時,仍然會失敗,所以必須判斷是否傳回NULL。
由於一個新的node,一定是linked list最後一個node,所以將current->next接null。
45行
strncpy(current->name, s, SLEN -1);
current->name[SLEN -1] = '\0';
正式將輸入的資料填進struct,至於為什麼要用strncpy()而不用strcpy()呢?雖然strcpy()也可以,但strncpy()比較安全,若輸入的字串大小超過struct所定義的字串大小,則會只接受struct所接受的字串大小,而不會因為找不到'\0'而造成程式錯誤。
49行
head = current;
else
prev->next = current;
prev = current;
判斷若是第一個node,則將目前的node當成head,若不是第一個node,則將前一個node指向目前的node,完成linked list的連接。最後將目前的node當成前一個node,以備指向下一個node。
58行
current = head;
while(current != NULL) {
printf("No. = %d, Name = %s\n", current->no, current->name);
current = current->next;
}
要重新顯示linked list,所以將指標再度指向第一個node,每當顯示一個node後,就指向下一個node,直到指到NULL為止。
64行
current = head;
while(current != NULL) {
prev = current;
current = current->next;
free(prev);
}
由於malloc()是將記憶體放在heap,而不是放在stack,所以並不會隨著function的結束而釋放,必須要手動使用free()釋放記憶體,否則會造成memory leak。
再來看C++,由於STL已內建一些容器,所以不需再重新實作linked list,有兩個選擇:std::vector或者std::list。std::vector的優點是non-sequential access超快,新增資料於後端超快,但insert和erase任意資料則超慢;std::list則剛好相反,insert和erase速度超快,但non-sequential access超慢,由於本例只有新增與non-sequential access,所以適合std::vector。不過由於STL使用泛型技術,若將來需求改變,想改用std::list也沒關係,只要將容器改掉即可,剩下的都不用改,因為STL的演算法並不挑容器,這正是泛型偉大之處。
C++
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : DS_linked_list_simple_vector_class.cpp
5 Compiler : Visual C++ 8.0
6 Description : Demo how to use vector instead of linked list
7 Release : 03/22/2008 1.0
8 */
9 #include <iostream>
10 #include <string>
11 #include <vector>
12
13 using namespace std;
14
15 class List {
16 public:
17 int no;
18 string name;
19 };
20
21 int main() {
22 vector<List> vec;
23
24 while(1) {
25 List list;
26 cout << "No. = ";
27 cin >> list.no;
28
29 if (list.no == 0)
30 break;
31
32 cout << "Name = ";
33 cin >> list.name;
34
35 vec.push_back(list);
36 }
37
38 vector<List>::iterator iter = vec.begin();
39 for(; iter != vec.end(); ++iter)
40 cout << "No. = " << iter->no << ", Name = " << iter->name << endl;
41 }
執行結果
Name = clare
No. = 2
Name = jessie
No. = 0
No. = 1, Name = clare
No. = 2, Name = jessie
15行由struct改用了class,不過若繼續使用struct亦可,至於其他的程式都很直觀,就不再多做解釋。
Conclusion
本文主要是討論使用C語言透過malloc()實現資料結構的linked list,以彌補靜態語言的不足,同時亦討論C++使用STL的替代方案與便利性,C與C++各擅勝場,你可自行決定使用C或C++。
使用C語言簡單的實現linked list,並用C++的std::vector實作出相同的功能作比較。