一、雙向鏈表(double linked list)如圖26.5,是在單鏈表的每個結點中,再設置一個指向其前驅結點的指針域。雙向鏈表的基本操作與單鏈表基本一樣,除了插入和刪除的時候需要更改兩個指針變量,需要注意的是修改的順序很重要,插入如圖3-14-5,刪除如圖3-14-6。



鏈表的delete操作需要首先找到要摘除的節點的前趨,而在單鏈表中找某個節點的前趨需要從表頭開始依次查找,對於n個節點的鏈表,刪除操作的時間復雜度為O(n)。可以想像得到,如果每個節點再維護一個指向前趨的指針,刪除操作就像插入操作(這里指只在頭部插入)一樣容易了,時間復雜度為O(1)。要實現雙向鏈表只需在《圖示單鏈表的插入和刪除操作》中代碼的基礎上改動兩個地方。
在linkedlist.h中修改鏈表節點的結構體定義:
struct node
{
unsigned char item;
link prev, next;
};
在linkedlist.c中修改insert和delete函數:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
void insert(link p) { p->next = head; if (head) head->prev = p; head = p; p->prev = NULL; } void delete(link p) { if (p->prev) p->prev->next = p->next; else head = p->next; if (p->next) p->next->prev = p->prev; } |
由於引入了prev指針,insert和delete函數中都有一些特殊情況需要用特殊的代碼處理,不能和一般情況用同樣的代碼處理,這非常不爽,如果在表頭和表尾各添加一個Sentinel節點(這兩個節點只用於界定表頭和表尾,不保存數據),就可以把這些特殊情況都轉化為一般情況了。如圖26.6

在《隊列的鏈式存儲結構》中我們使用單鏈表實現隊列的尾進頭出,下面我們演示使用雙向鏈表實現隊列的頭進尾出。
參考:《Linux C編程 一站式學習》
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/*************************************************************************
> File Name: doublylinkedlist.h > Author: Simba > Mail: dameng34@163.com > Created Time: Fri 28 Dec 2012 08:02:35 PM CST ************************************************************************/ #ifndef DOUBLYLINKEDLIST_H #define DOUBLYLINKEDLIST_H typedefstructnode *link; struct node { unsigned char item; link prev; link next; } ; link make_node(unsigned char item); void free_node(link p); link search(unsigned char key); void insert(link p); void deletep(link p); void traverse(void (*visit)(link)); void destroy(void); void enqueue(link p); link dequeue(void); #endif |
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
#include<stdio.h> #include<stdlib.h> #include "doublylinkedlist.h" struct node tailsentinel; struct node headsentinel = {0, NULL, &tailsentinel}; struct node tailsentinel = {0, &headsentinel, NULL}; static link head = &headsentinel; static link tail = &tailsentinel; link make_node(unsigned char item) { link p = malloc(sizeof(*p)); p->item = item;
p->prev = p->next =
NULL; printf("make node from Item %d\n", item); return p; } void free_node(link p) { printf("free node ...\n"); free(p); } link search(unsigned char key) { link p; printf("search by key %d\n", key); for (p = head->next; p != tail; p = p->next) if (p->item == key) return p; return NULL; } void insert(link p) { printf("insert node from head ...\n"); p->next = head->next; head->next->prev = p; head->next = p; p->prev = head; } void deletep(link p) { printf("delete node from ptr ...\n"); p->prev->next = p->next; p->next->prev = p->prev; } void traverse(void (*visit)(link)) { link p; printf("doublylinkedlist traverse ...\n"); for (p = head->next; p != tail; p = p->next) visit(p); printf("\n"); } void destroy(void) { link q, p = head->next; printf("destory doublylinkedlist ...\n"); head->next = tail; tail->prev = head; while (p != tail) { q = p; p = p->next; free_node(q); } } void enqueue(link p) { printf("enqueue from head ...\n"); insert(p); } link dequeue(void) { if (tail->prev == head) return NULL; else { link p = tail->prev; printf("dequeue from tail ...\n"); deletep(p); return p; } } |
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
/************************************************************************* > File Name: main2.c > Author: Simba > Mail: dameng34@163.com > Created Time: Fri 28 Dec 2012 08:18:57 PM CST ************************************************************************/ #include<stdio.h> #include "doublylinkedlist.h" void print_item(link p) { printf("print item %d \n", p->item); } int main(void) { link p = make_node(10); insert(p); p = make_node(5); insert(p); p = make_node(90); insert(p); p = search(5); deletep(p); free_node(p); traverse(print_item); destroy(); printf("..................\n"); p = make_node(100); enqueue(p); p = make_node(200); enqueue(p); p = make_node(250); enqueue(p); while ((p = dequeue())) { print_item(p); free_node(p); } return 0; } |
輸出為:

解決的error:
關於錯誤 error C2275: “XXX”: 將此類型用作表達式非法
在移植c++代碼到c的時候,經常會出現一個奇怪的錯誤, error C2275: “XXX”: 將此類型用作表達式非法,這個錯誤是由於c的編譯器要求將變量的定義放在所有函數調用語句之前,而c++沒有這樣的要求造成的。解決的辦法就是把變量的定義全部放在變量的生存塊的開始。
------------------------------------------------------------------------------------------------------------------------------------
二、將單鏈表中終端結點的指針端由空指針改為指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表就稱為單循環鏈表,
簡稱循環鏈表(circular linked list)。如下圖所示。

其實循環鏈表和單鏈表的主要差異就在於循環的判斷條件上,原來是判斷p->next是否為空,現在則是p->next不等於頭結點,則循環未結束。
我們在《隊列的順序存儲結構(循環隊列)》中使用數組實現了環形隊列,我們還要“假想”它是首尾相接的,而如果基於鏈表實現環形隊列,我們本來就可以用指針串成首尾相接的。把上面的程序改成雙向環形鏈表也非常簡單,只需要將
把doublylinkedlist.c中的
struct node tailsentinel;
struct node headsentinel = {0, NULL, &tailsentinel};
struct node tailsentinel = {0, &headsentinel, NULL};
static link head = &headsentinel;
static link tail = &tailsentinel;
改成:
struct node sentinel = {0, &sentinel, &sentinel};
static link head = &sentinel;
再把doublylinkedlist.c中所有的tail替換成head即可,相當於把head和tail合二為一了。如圖26.7:

