資料結構與演算法 第四章 鍵結串列 徐熊健.

Slides:



Advertisements
Similar presentations
資料結構 – 鏈結串列 Linked List 綠園. 鏈結串列 -Linked List Linked List 是由許多相同資料型態的項目所組 成的有限序列。 可以把鏈結串列想像成火車,有多少人就只掛多 少節的車廂,需要車廂時再跟系統要一個車廂, 人少了就把車廂還給系統。 鏈結串列是有多少資料用多少記憶體空間,有新.
Advertisements

第一單元 建立java 程式.
第三章 鏈結串列 Linked List.
第三章 鏈結串列 Linked Lists.
第二章 线性表.
陳維魁 博士 儒林圖書公司 第九章 資料抽象化 陳維魁 博士 儒林圖書公司.
Linked List(串列) Why Linked List? Pointer Dynamic Allocation
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
Linked List Operations
單向鏈結串列 Singly Linked Lists.
第6章 佇列(Queues) 6-1 佇列的基礎 6-2 佇列的表示法 6-3 環狀佇列 6-4 雙佇列.
資料結構 第3章 鏈結串列.
結構(struct).
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置 4-2 鏈結串列的基礎 4-3 單向鏈結串列 4-4 環狀鏈結串列
佇列 (Queue).
佇列與推疊 (Queue and Stack)
資料結構設計與C++程式應用 Fundamentals of Data Structures and Their Applications Using C++ 第3章 佇列 資料結構設計與C++程式應用.
鏈結串列 (Linked List).
4.1 單項鏈結串列 4.2 環狀串列 4.3 雙向鏈結串列 4.4 鏈結串列之應用
Chapter 3 鏈結串列結構 資料結構導論 - C語言實作.
資料結構與C++程式設計進階 鏈結串列 講師:林業峻 CSIE, NTU 6/ 10, 2010.
第十五章 Linked List, Stack and Queue
Ch.3 栈和队列 黄刘生 中国科学技术大学计算机系 国家高性能计算中心(合肥)
類別(class) 類別class與物件object.
(Circular Linked Lists)
第十章 C高级程序应用—链表* 10.1链表的基本概念 10.2单向链表 10.3双向链表 10.4应用举例.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
鏈結串列 (Linked List) 註:要會指標(Pointer)
第 六 章 鏈結串列(Link List) 課程名稱:資料結構 授課老師:________ 2019/1/2.
資料結構與C++程式設計進階 堆疊與佇列(Stack & Queue) 講師:林業峻 CSIE, NTU 6/ 21, 2010.
Ch03 鏈結串列結構 淡江大學 周清江.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
學習 2019/1/12. 學習 2019/1/12 Chapter 3 鏈結串列結構 資料結構導論 - C語言實作.
4.1 單向鏈結串列 4.2 堆疊的加入與刪除 4.3 佇列的加入與刪除 4.4 其他型式的佇列
第三章 鏈結串列 3-1  單向鏈結串列 3-2 環狀鏈結串列 3-3 雙向鏈結串列.
Java 程式設計 講師:FrankLin.
JAVA 程式設計與資料結構 第四章 陣列、字串與數學物件.
Chap3 Linked List 鏈結串列.
資料結構 第4章 堆疊.
第一單元 建立java 程式.
資料結構 7-1 陣列 7-2 鏈結串列 7-3 堆疊和佇列 7-4 樹狀結構.
資料結構 優點 缺點 1 陣列 (Array) 沒有額外變量 (例head, next,...) 運作/操作較簡單 更新資料時,若要保持順序,需要移動較大量資料 靜態結構Static (宣告時已決定了陣列元素多少,不能在程式執行期間增減元素) 2 隊列Queue (FIFO) 容易更新 加入enqueue:
第十章 指標.
Linked Lists Prof. Michael Tsai 2013/3/12.
挑戰C++程式語言 ──第8章 進一步談字元與字串
Chap2 Stack & Queue.
第四章 栈和队列 栈 ( Stack ) 队列 ( Queue ) 优先队列 (Priority Queue) 小结.
本教學投影片係屬教科書著作之延伸,亦受著作權法之保護。
資料結構 老師:李崇明 助教:楊斯竣.
樣版.
C qsort.
資料結構使用Java 第6章 鏈結串列(Linked List).
第14章 結構與其他資料形式.
陣列與結構.
指標、串列 (Linked List).
Chapter 4 鏈結串列 Linked List 2019/5/14.
動畫演示 Node規範了一些基本的方法,像是增加節點、刪除節點、讓節點做一些事、取得第n個節點等等
資料結構 – 鏈結串列 Linked List 綠園.
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
第7章 資料結構 7-1 陣列 7-2 鏈結串列 7-3 堆疊和佇列 7-4 樹狀結構.
資料結構與C++程式設計進階 期末考 講師:林業峻 CSIE, NTU 7/ 15, 2010.
第四章 陣列、指標與參考 4-1 物件陣列 4-2 使用物件指標 4-3 this指標 4-4 new 與 delete
鏈結串列 Link List chapter 6 德明科技大學資訊科技系.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
Array(陣列) Anny
鏈結串列 (Linked List).
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
InputStreamReader Console Scanner
Presentation transcript:

資料結構與演算法 第四章 鍵結串列 徐熊健

目錄 4.1 串列與鍵結串列 4.2 動態配置之鍵結串列 4.3 鍵結堆疊 4.4 鍵結佇列 4.5 環狀串列 4.6 雙向鍵結串列控制 4.1 串列與鍵結串列 4.2 動態配置之鍵結串列 4.3 鍵結堆疊 4.4 鍵結佇列 4.5 環狀串列 4.6 雙向鍵結串列控制 4.7 C語言的指標

4.1 串列與鍵結串列 串列 (list) 的抽象觀念是一組相同資料型態元素的有序集合。與陣列不同處在於:它不必要存在唯一的註標與每一元素相互對應 。 「鍵結串列」 (linked list) 則屬於非循序的儲存結構,邏輯上相鄰的二項資料,在記憶體中的實際配置不需要彼此相鄰。如果資料的引用並不強調實體配置中的連續性,或不必要賦予每一元素唯一的註標,鍵結串列可能是比較好的選擇。在作業系統中利用鍵結串列,掌握動態配置 (dynamic allocation) 所需空間的需求,更是重要的技巧。

範例4-2 以陣列存放串列資料的資料搬移狀況 假設該串列為當天水果商家出售水果之類別。 C[0] C[1] C[2] C[3] C[4] 範例4-2 以陣列存放串列資料的資料搬移狀況 假設該串列為當天水果商家出售水果之類別。 C[0] C[1] C[2] C[3] C[4] apple cherry mango peach strawberry 若“apple”賣完,則陣列將會改變如下: C[0] C[1] C[2] C[3] C[4] cherry mango peach strawberry 又若“banana”到貨,則陣列又會有如下之改變: C[0] C[1] C[2] C[3] C[4] banana cherry mango peach strawberry

範例4-3 利用link陣列掌握data陣列的資料順序 使用了二個陣列data和link來存放水果的資訊。data內存放資料,link內則存放該資料的下一筆資料註標位置;另外再用一個額外的註標head,記著第一項資料在data陣列中的位置。 head=0 [0] [1] [2] [3] [4] data apple cherry mango peach strawberry link 1 2 3 4 -1 若刪除“apple”則改變如下: head = link[head]; head=1 [0] [1] [2] [3] [4] data apple cherry mango peach strawberry link 1 2 3 4 -1

範例4-3 (續) 接著刪除“mango”將有以下改變: [0] [1] [2] [3] [4] data apple cherry 範例4-3 (續) 接著刪除“mango”將有以下改變: link[1] = link[2]; head=0 [0] [1] [2] [3] [4] data apple cherry mango peach strawberry link 1 3 4 -1 新增一資料“orange”: data[0] = “orange”;  link[0] = link[1];  link[1] = 0; head=0 [0] [1] [2] [3] [4] data orange cherry mango peach strawberry link 3 4 -1

4.2 動態配置之鍵結串列 鍵結串列有順序的關係,包括至少二種類型的資訊: 內含的元素。 下一項元素所在位置的資訊。 4.2 動態配置之鍵結串列 鍵結串列有順序的關係,包括至少二種類型的資訊: 內含的元素。 下一項元素所在位置的資訊。 鍵結是由指標或位址來構成,比起陣列link的使用更具一般性。

程式4-1 結構的宣告 1 define maxchar 20 2 struct FruitType 3 { char name[maxchar]; 4 struct FruitType *link; 5 }; 6 struct FruitType *head; 在程式4-1第2~5行中我們宣告了新的資料型態struct FruitType,它包括了一組長度為20的字元name,和指向struct FruitType此型態的指標(位址)*link,它成為鍵結串列的基本型態,可稱之為節點。

範例4-5 10 20 程式4-2 1 struct node 2 { int data; 3 struct node *next; first 程式4-2 1 struct node 2 { int data; 3 struct node *next; 4 }; 5 struct node *first; 6 void construct() 7 { first = (node *) malloc (sizeof(node)); // 8 first->data = 10; 9 first->next = (node *) malloc (sizeof(node)); 10 first->next->data = 20; 11 first->next->next = NULL; 12 }

4.2.1 新增資料至節點並插入節點至串列中 以下動畫表現出新增資料至節點並插入節點之串列中。 p … x 4.2.1 新增資料至節點並插入節點至串列中 以下動畫表現出新增資料至節點並插入節點之串列中。 p … x 取消p->next(令之為q); 將x->next改為q(原來的p->next); 將p->next改為x。

程式4-3 新增資料element至節點x中並插入節點x至串 列中p節點之後 1 struct node 2 { int data; 3 struct node *next; 4 }; 5 void InsertAfter(struct node *p; int element) 6 { node *x; 7 x =(struct node *) malloc (sizeof(node)); 8 x->data = element; 9 if (p==NULL) 10 { first = x; 11 x->next = NULL; 12 return; 13 } 14 x->link = p->link; 15 p->link = x; 16 return; 17 }

4.2.2 刪除鍵結串列中的節點 以下為刪除鍵結串列中的節點之概念圖: p … 4.2.2 刪除鍵結串列中的節點 以下為刪除鍵結串列中的節點之概念圖: p …  p->next(令之為q)改成q->next; 將當初動態配置而得的節點q(原來的p->next)還給系統。

程式4-4 刪除串列中p節點之後的節點 1 struct node 2 { int data; 3 struct node *next; 4 }; 5 int DeleteAfter(node *p) 6 { node *q; 7 q = p->next; 8 p->next = q->next; 9 int item =q->data; 10 free(q); 11 return item; 12}

4.2.3 尋找鍵結串列中的資料 程式4-5尋找鍵結串列中的資料 4.2.3 尋找鍵結串列中的資料 程式4-5尋找鍵結串列中的資料 1 struct node 2 { int data; 3 struct node *next; 4 }; 5 struct node *first; 6 struct node * SearchData(int target) 7 { node *p; 8 p = first; 9 while ((p!=NULL) && (p->data!=target)) p = p->next; 10 if (p->data == target) return (p); 11 return NULL; 12 } 在鍵結串列中,搜尋某特定資料在最差情況下,得花O(n)的時間,其中n指的是鍵結串列的節點個數。 一旦插入或刪除的節點位置已知,則執行插入或刪除只需O(1)的時間。

4.2.4 在串列最後新增節點 程式4-6在鍵結串列最後新增節點,last指在串列最後的節點 1 struct Node 4.2.4 在串列最後新增節點 程式4-6在鍵結串列最後新增節點,last指在串列最後的節點 1 struct Node 2 { int data; 3 struct Node * link; 4 } 5 struct Node * first, *last; 6 void AttachDataToList(int element) 7 { struct Node *p; 8 p = (struct Node *) malloc (sizeof (struct Node)) 9 p->data = element; p->link = NULL; 10 if (first == NULL) first = last = p ; 11 else 12 { last->link =p; 13 last = p; 14 } 15 }

4.2.5 反向串接一串列 程式4-7反向串接一串列 1 struct Node 2 { int data; 4.2.5 反向串接一串列 「鍵結串列的指標反向連接」將使原來串列內資料的順序完全對調。 程式4-7反向串接一串列 1 struct Node 2 { int data; 3 struct Node *link; 4 } 5 struct Node *first; 6 void Invert() 7 { struct Node *r, *s, *t ; 8 r = first; 9 s = NULL; 10 while (r != NULL) 11 { t = s; 12 s = r; 13 r = r->link; 14 s->link = t; 15 } 16 first = s; 17 }

4.2.6 串接兩個鍵接串列 程式4-8串接兩個鍵接串列 1 struct Node 2 { int data; 4.2.6 串接兩個鍵接串列 程式4-8串接兩個鍵接串列 1 struct Node 2 { int data; 3 struct Node *link; 4 } 5 struct Node *a_first, *b_first, first; 6 struct Node * Concatenate 7 (struct Node * a_first, struct Node * b_first); 8 { struct Node *p ; 9 if (a_first == NULL) 10 resturn b_first ; 11 else 12 { for(p=first; p->link!=NULL; p=p->link); 13 p→link = b_first ; 14 return a_first ; 15 } 16 }

4.3 鍵結堆疊 利用陣列來實作堆疊的確方便,但是陣列在宣告時即得定義大小,宣告太大形成空間的浪費,宣告太小又怕不敷使用。改用動態配置的鍵結堆疊,即可解決使用陣列造成的缺點。 程式4-9鍵結堆疊 1 Struct StackNode 2 { int data; 3 struct StackNode *link; 4 } 5 struct StackNode *top;

程式4-9鍵結堆疊(續) 6 struct Stacknode *NewNode(int element) 7 { struct StackNode *p; 8 p = (struct StackNode *) malloc (sizeof(StacdNode)); 9 p->data = elemeut; 10 p->link = NULL; 11 return p; 12 } 13 void PushStack(int element) 14 { struct StackNode *x ; 15 x = NewNode(element); 16 if (top==NULL) top = x; 17 else 18 { x->link = top; 19 top = x; 20 } 21 }

鍵結堆疊執行push和pop的過程 : (a) push x至空堆疊 (b) push x至堆疊 (c) pop堆疊 top top x

4.4 鍵結佇列 程式4-10鍵結佇列 1 struct QueueNode 2 { char data; 4.4 鍵結佇列 程式4-10鍵結佇列 1 struct QueueNode 2 { char data; 3 struct QueueNode *next; 4 } 5 struct QueueNode *front, *rear; 6 struct QueueNode * NewQNode (char element) 7 { struct QueueNode *p; 8 p = (struct QueueNode *) malloc (sizeof(QueueNode )); 9 p->data = element; 10 P->next = NULL; 11 } 12 void AddQueue(char element) 13 { struct QueueNode *p; 14 p = NewQNode(element); 15 if (rear == NULL) 16 front = p; 17 else

程式4-10鍵結佇列(續) 18 rear->next = p; 19 rear = p; 20 } 20 } 21 char DeleteQueue() 22 { struct QueueNode *p; 23 char element; 24 if (front == NULL) 25 { QueueEmpty( ); 26 return ”#”; 27 } 28 else 29 { p = front; 30 front = front->next; 31 element = p->data; 32 free(p); 33 return element; 34 } 35 }

鍵結佇列執行新增和刪除的過程 front front : (a) 新增節點至空佇列 (b) 新增節點至佇列 (c) 刪除佇列節點 p rear rear p rear rear rear

4.5 環狀串列 環狀串列比起單向串列的好處在於: 從串列中的任何一個節點開始皆可將此串列走訪一次。 4.5 環狀串列 環狀串列比起單向串列的好處在於: 從串列中的任何一個節點開始皆可將此串列走訪一次。 可是這種串列在遇到「在串列最前加入新節奌」的需求時,倒顯得棘手。

程式4-11 在環狀串列中新增最前節點 很顯然地,這個需求得走訪所有節點,時間複雜度為O(n),其中n為環狀串列的節點數。 程式4-11 在環狀串列中新增最前節點 1 struct node 2 { int data; 3 struct node *next; 4 } 5 struct node *first; 6 void InsertFirst(struct node *x) 7 { struct node *p; 8 if (first == NULL) 9 { first = x; 10 first->next =first; 11 } 12 else 13 { p = first; 14 while (p->next!=first) p = p->next; 15 p->next=x; 16 x->next =first; 17 first =x; 18 } 19 } 很顯然地,這個需求得走訪所有節點,時間複雜度為O(n),其中n為環狀串列的節點數。

在環狀串列中新增最前節點(虛線為更動的指標)

程式4-12 在環狀串列中(有last指標指 向結尾節點)新增最前節點 6 void InsertFirst(struct node *x) 7 { if (last == NULL ) 8 { last = x; 9 last->next= last ; 10 } 11 else 12 { x->next =last->next; 13 last->next =x; 14 } 15 }

在環狀串列中(有last指標指向結尾節點)新增最前節點

以空節點為首之環狀串列 這種用一個空節點當做環狀串列的第一個節點的設計,可使「在串列最前加入新節奌」的需求變得簡單,此時「串列最前」實即first指標之後。 在鍵結串列中搭配使用空節點,常可使程式碼更具一般化,省下邊界情況(boundary condition) 的檢測。

程式4-13 在以空節點為首之環狀串列中新增最前節點 程式4-13 在以空節點為首之環狀串列中新增最前節點 6 void InsertFront (struct node *x ) 7 { x->next = first→next; 8 first->next =x; 9 }

4.6 雙向鏈結串列控制 以上所介紹的鏈結串列,不論是線性或是環狀的,每個節點都只有一個鏈結指標,我們只能朝一個方向走訪其他節點; 4.6 雙向鏈結串列控制 以上所介紹的鏈結串列,不論是線性或是環狀的,每個節點都只有一個鏈結指標,我們只能朝一個方向走訪其他節點; 若有反向走回的需求,單向線性或單向環狀串列將無法有效率地解決。於是有雙向鏈節串列 (doubly linked list) 孕育而生。

4.6 雙向鏈結串列控制 (續) 每個節點皆有兩個鏈節指標,分別指向該節點的前一個和下一個節點。 4.6 雙向鏈結串列控制 (續) 每個節點皆有兩個鏈節指標,分別指向該節點的前一個和下一個節點。 第一個節點的『前指標』和最後一個節點的『後指標』皆為NULL表示正是第一個和最後一個節點。 若將第一個節點的『前指標』指向最後一個節點,最後一個節點的『後指標』指向第一個節點,則形成環狀雙向鏈結串列

4.6.1 新增資料至環狀雙向鍵結節點 程式4-14 雙向鏈結串列的節點宣告 1 struct Dnode 4.6.1 新增資料至環狀雙向鍵結節點 程式4-14 雙向鏈結串列的節點宣告 1 struct Dnode 2 { struct Dnode *prev; 3 int data; 4 Dnode *next; 5 } 6 struct Dnode *head

程式4-15 產生新雙向鏈結節點 1 Dnode NewDnode(int element) 2 { Dnode *p; 程式4-15 產生新雙向鏈結節點 1 Dnode NewDnode(int element) 2 { Dnode *p; 3 p=(struct Dnode *) malloc(sizeof(Dnode)); 4 p->data=element; 5 p->prev=NULL; 6 p->next=NULL; 7 return p; 8 }

4.6.2 搜尋環狀雙向鏈結串列中的資料 程式4-16 搜尋環狀雙向鏈結串列中的資料 4.6.2 搜尋環狀雙向鏈結串列中的資料 程式4-16 搜尋環狀雙向鏈結串列中的資料 1 struct Dnode *SearchDnodeList(int element) 2 { Dnode *p; 3 if (head==NULL) 4 SearchEmptyList(); 5 else 6 { p = head; 7 while ((p->data!=element)&& (p->next!=head)) 8 p = p->next; 9 if (p->data==element) return p; 10 } 11 return NULL; 12 }

4.6.3揷入節點至環狀雙向鏈結串列 可知有四個鏈結指標會有異動:  p->prev = x ;  p->next = x->next ;  x->next->prev = p ;  x->next = p ;

程式4-17 揷入節點至環狀雙向鏈結串列中 1 void InsertDList(struct Dnode *x , 程式4-17 揷入節點至環狀雙向鏈結串列中 1 void InsertDList(struct Dnode *x , struct Dnode *p) 2 { if (x==NULL) 3 { p->prev = p; 4 p->next = p; 5 head = p; 6 } 7 else 8 { p->prev = x; 9 p->next = x->next; 10 x->next->prev = p; 11 x->next = p; 12 } 13 }

4.6.4 刪除雙向鏈結串列的一個節點 可知,異動的鏈結指標有兩處: 4.6.4 刪除雙向鏈結串列的一個節點 可知,異動的鏈結指標有兩處:  x->prev->next = x->next ;  x->next->prev = x->prev ;

程式4-18 刪除雙向鏈結串列中的一個節點 1 void DeleteDList(struct Dnode *x) 程式4-18 刪除雙向鏈結串列中的一個節點 1 void DeleteDList(struct Dnode *x) 2 { if (x==NULL) 3 DeleteEmptyList(); 4 else if ((x==head) && (head->next==head)) 5 head = NULL ; 7 else 8 { x->prev->next = x->next ; 9 x->next->prev = x->prev ; 10 } 11 free(x); 12 }

4.6.5 包含開頭空節點的鍵結串列 程式4-19 環狀雙向鍵結串列(含開頭空節點)新增和刪除節點 4.6.5 包含開頭空節點的鍵結串列 程式4-19 環狀雙向鍵結串列(含開頭空節點)新增和刪除節點 1 void InsertHDList(struct Dnode *x, struct Dnode *p) 2 { p->prev = x ; 3 p->next = x->next ; 4 x->next->prev = p ; 5 x->next = p ; 6 } 7 void DeleteHDList(truct Dnode *x) 8 { if (x == first) 9 DeleteEmptyList(); 10 else 11 { x->prev->next = x->next; 12 x->next->prev = x->prev; 13 free(x); 14 } 15 }

4.7 C語言的指標 在C語言中,指標 (pointer) 是個存放位址 (address) 的變數。 指標在運用的時候,可能與位址、程序中的參數以及陣列間有密切的關係。

4.7.1 指標與位址 &x即為整數變數x的位址A0A0,p = &x則把位址A0A0指定給整位址變數p。所以現在x是5,*p也是5。 4.7.1 指標與位址 &x即為整數變數x的位址A0A0,p = &x則把位址A0A0指定給整位址變數p。所以現在x是5,*p也是5。 整數*p可解讀成:「以p的內容為位址,取出該位址的整數內容,令其為*p」。 在此例中,p的內容為A0A0,取出位址A0A0的整數內容即為5。&x則可解讀為:「取出整數變數x在記憶體中的位址,令之為&x」。 在此例中,x為5,而&x是A0A0  。同理p為A0A0,而&p為A0D0。

4.7.2 指標與程序參數 程式4-20 錯誤的資料對調 1 void swap(int x, int y) 2 { int temp; 4.7.2 指標與程序參數 程式4-20 錯誤的資料對調 1 void swap(int x, int y) 2 { int temp; 3 temp = x; 4 x = y; 5 y = temp; 6 } 程式4-21 資料對調 1 void swap(int *px, int *py) 2 { int temp; 3 temp = *px; 4 *px = *py; 5 *py = temp; 6 }

4.7.3 指標與陣列 當陣列名稱要傳入程序中時,其實傳的即為陣列的起始位址。程式4-22是個計算字串長度的程序,它用字元位址指標變數來存放傳入的字元位址。 程式4-22 計算字串長度 1 int strlen(char *s) 2 { int n; 3 for (n=0; *s!=’\0’; s++) n++; 4 return n; 5 }