鏈表由多個數據節點組成,每個數據節點由數據域和指針域兩部分組成,如下圖
其中數據域的類型取決於節點數據類型,若節點爲字符,則數據域類型爲字符型。指針域類型爲地址,存儲着其他節點的內存地址,在C/C++中表現爲節點類型的指針。
計算機存儲鏈表的過程中以數據節點爲基本單位,每個節點都是不可再分的,每次只申請一個節點大小的內存空間,不同節點之間通過指針域連接。這意味着如果在原鏈表上增加和刪除節點十分方便,不必移動其他數據,只需要更改相應的指針域中的內存地址就好,但是當我們訪問鏈表時將會變得十分不方便,只能通過一個又一個節點的指針域不斷跳轉。
常見鏈表分爲單鏈表和雙鏈表,區別在於指針域中指針的個數。單鏈表節點的指針域中只有一個指向下一個節點的指針,指針域爲空時代表該節點爲最後一個節點。這就意味着如果是一個單鏈表,當我們訪問的時候,就只能“一條路走到黑”,沒有其他標記的時候就沒有辦法返回到上一個節點。爲了彌補單鏈表的這種缺陷,我們可以設計雙鏈表,雙鏈表的指針域中不僅有指向下一個節點的指針,還有一個指向上一個節點的指針。
鏈表的增加與刪除,以單鏈表爲例。假設某單鏈表有三個節點a、 b、 c如下,它們的內存地址分別是0x10、0x20、0x30
如果我們要刪除節點b,只需要修改a節點的指針域地址爲c節點的所在地址(0x30),釋放b節點的內存資源即可。
增加節點則恰恰相反,加入我們需要在a、c兩節點之間在添加一個節點時,只需要申請一個節點b的內存,設置b節點的指針域爲c的節點的地址,然後把a節點的指針域修改爲b節點的地址。一般情況下我們的需求可能是把b節點添加到a節點後面,是沒有c節點的,一定要先將b的指針域設置爲a的指針域的值,然後在設置a的指針域,否則我們將無法找到原本a節點後面的節點。
節點的查詢,修改過程不再贅述。
下面是單鏈表的C++簡單實現。
定義節點數據
#define dataType int //節點數據類型
typedef struct Node {
dataType data; //數據域
struct Node *next; //指針域
} *list;
插值函數,後插法可以改進非遞歸實現,非遞歸實現的時候需要注意因爲參數L是引用,不要更頭部節點L以及判斷L是否爲空
void insert(list &L, dataType value) { //鏈表插值函數,L爲鏈表,value爲插入的值
//以下爲前插法,即每次添加的節點都位於鏈表的第一個
list p = L;
L = (list) malloc(sizeof(list));
L->data = value;
L->next = p;
//以下爲後插法,每次插入的節點位於鏈表結尾
// if(L == NULL) {
// L = (list)malloc(sizeof(list));
// L->data = value;
// L->next = NULL;
// return;
// }
// else {
// insert(L->next, value);
// }
}
從鏈表中刪除指定值,因爲傳參的過程中L是引用,所以刪除節點後鏈表的結構不會被破壞。
void deleteByValue(list &L, dataType value) { //刪除指定值的節點,L爲鏈表,value爲匹配值
if(L == NULL) {
return;
}
if(L->data == value) { //當前節點需要刪除
list p = L;
L = L->next;
free(p);
deleteByValue(L, value);
}
else { //當前節點無需刪除
deleteByValue(L->next, value);
}
}
鏈表的打印以及銷燬
void printList(list L) { //打印鏈表
if(L == NULL) {
return;
}
cout << L->data;
for (L = L->next; L != NULL; L = L->next) {
cout << "--->" << L->data;
}
cout << endl;
}
void destoryList(list &L) { //銷燬鏈表
list p;
while(L != NULL) {
p = L;
L = L->next;
free(p);
}
}
文件下載