690 likes | 782 Views
第三章 鏈結串列. 課前指引 是由許多相同資料型態的項目,依特定順序排列而成的線性串列,但特性是在電腦記憶體中位置是不連續、隨機 (Random) 的存在,優點是資料的插入或刪除都相當方便,有新資料加入就向系統要一塊記憶體空間,資料刪除後,就把空間還給系統。不需要移動大量資料。缺點就是設計資料結構時較為麻煩,另外在搜尋資料時,也無法像靜態資料一般可隨機讀取資料,必須循序找到該資料為止。. 章節大綱. 3-1 單向鏈結串列. 3-2 環狀鏈結串列. 3-3 雙向鏈結串列. 3-4 鏈結串列相關應用簡介. 備註:可依進度點選小節.
E N D
第三章 鏈結串列 課前指引 是由許多相同資料型態的項目,依特定順序排列而成的線性串列,但特性是在電腦記憶體中位置是不連續、隨機(Random)的存在,優點是資料的插入或刪除都相當方便,有新資料加入就向系統要一塊記憶體空間,資料刪除後,就把空間還給系統。不需要移動大量資料。缺點就是設計資料結構時較為麻煩,另外在搜尋資料時,也無法像靜態資料一般可隨機讀取資料,必須循序找到該資料為止。
章節大綱 3-1 單向鏈結串列 3-2 環狀鏈結串列 3-3 雙向鏈結串列 3-4 鏈結串列相關應用簡介 備註:可依進度點選小節
3-1 單向鏈結串列 • 單向鏈結串列: • 一個單向鏈結串列節點由兩個欄位,即資料欄及指標欄組成,而指標欄將會指向下一個元素的記憶體所在位置。如右圖所示: • 在「單向鏈結串列」中第一個節點是「串列指標首」,指向最後一個節點的鏈結欄位設為NULL表示它是「串列指標尾」,不指向任何地方。如下圖所示:
3-1 單向鏈結串列 • 在其它語言中,當配置的記憶體已不再使用時,就必須釋放該記憶體空間,但由於Java語言的記憶體管理有垃圾回收機制,所以沒有記憶體回收的問題。 • 例如在java語言中要模擬鏈結串列中的節點,必須宣告如下的Node類別: class Node { int data; Node next; public Node(int data) //節點宣告的建構子 { this.data=data; this.next=null; } }
3-1 單向鏈結串列 • 接著可以宣告鏈結串列LinkedList類別,該類別定義兩個Node類別節點指標,分別指向鏈結串列的第1節點及最後1個節點,如下所示: class LinkedList { private Node first; private Node last; //定義類別的方法 …………………… …………………… }
3-1 單向鏈結串列 • 在Java中要模擬鏈結串列中的此類節點,其Node類別的語法可以宣告如下: class Node { String name; int no; int score; Node next; public Node(String name,int no,int score) { this.name=name; this.no=no; this.score=score; this.next=null; } }
3-1 單向鏈結串列 • 建立單向鏈結串列 • 現在我們嘗試使用java語言的單向鏈結串列處理以下學生的成績問題。學生成績處理會有以下欄位。
3-1 單向鏈結串列 • 首先各位必須宣告節點的資料型態,讓每一個節點包含一筆資料,並且包含指向下一筆資料的指標,使所有資料能被串在一起而形成一個串列結構,如下圖:
3-1 單向鏈結串列 • 步驟1 • 建立新節點。 • 步驟2 • 將鏈結串列的first及last指標欄指向newNode。 • 步驟3 • 建立另一個新節點。
3-1 單向鏈結串列 • 步驟4 • 將兩個節點串起來。 • last.next=newNode; • last=newNode; • 步驟5 • 依序完成如下圖所示的鏈結串列結構。
3-1 單向鏈結串列 • 接下來各位不妨自行設計此鏈結串列的建立程式。我們建議在此程式中會宣告Node類別及LinkedList類別,在LinkedList類別中,除了定義兩個Node類別節點指標,分別指向鏈結串列的第1個節點及最後1個節點外,並在該類別中宣告了三個方法:
3-1 單向鏈結串列 • 範例 3.1.1 • 請設計一Java程式,可以讓使用者輸入資料來新增學生資料節點,與建立一個單向鏈結串列。接著再利資料宣告來建立這五個學生成績的單向鏈結串列,並走訪每一個節點來列印成績。
3-1 單向鏈結串列 • 單向鏈結串列刪除節點(1/3) • 刪除串列的第一個節點:只要把串列指標首指向第二個節點即可。如下圖所示: if(first.data==delNode.data) first=first.next;
3-1 單向鏈結串列 • 單向鏈結串列刪除節點(2/3) • 刪除串列內的中間節點:只要將刪除節點的前一個節點的指標,指向欲刪除節點的下一個節點即可。如下圖所示: newNode=first; tmp=first; while(newNode.data!=delNode.data) { tmp=newNode; newNode=newNode.next; } tmp.next=delNode.next;
3-1 單向鏈結串列 • 單向鏈結串列刪除節點(3/3) • 刪除串列後的最後一個節點:只要指向最後一個節點ptr的指標,直接指向NULL即可。如下圖所示: if(last.data==delNode.data) { newNode=first; while(newNode.next!=last) newNode=newNode.next; newNode.next=last.next; last=newNode; }
3-1 單向鏈結串列 • 範例 3.1.2 • 請設計一Java程式,來實作建立一組學生成績的單向鏈結串列程式,包含了座號、姓名與成績三種資料。只要輸入想要刪除的成績,就可以走訪該此串列,並前除該位學生的節點。要結束輸,請輸入”-1”,則此時會列出此串列未刪除的所有學生資料。
3-1 單向鏈結串列 • 單向鏈結串列插入新節點(1/3) • 新節點插入第一個節點之前,即成為此串列的首節點:只需把新節點的指標指向串列的原來第一個節點,再把串列指標首移到新節點上即可。
3-1 單向鏈結串列 • 單向鏈結串列插入新節點(2/3) • 新節點插入最後一個節點之後:只需把串列的最後一個節點的指標指向新節點,新節點再指向NULL即可。
3-1 單向鏈結串列 • 單向鏈結串列插入新節點(3/3) • 將新節點插入串列中間的位置:例如插入的節點是在X與Y之間,只要將X節點的指標指向新節點,新節點的指標指向Y節點即可。如下圖所示: • 把插入點指標指向的新節點
3-1 單向鏈結串列 • 範例 3.1.3 • 請設計一Java程式,來實作單向鏈結串列新增節點過程。
3-1 單向鏈結串列 • 單向鏈結串列的反轉 • 在鏈結串列中的節點特性是知道下一個節點的位置,可是卻無從得知它的上一個節點位置,不過如果要將串列反轉,則必須使用三個指標變數。如下圖所示:
3-1 單向鏈結串列 • 範例 3.1.4 • 以下範例將以java語言來設計將前面的學生成績程式中的學生成績依照座號反轉列印出來。
3-1 單向鏈結串列 • 單向鏈結串列的連結 • 對於兩個或以上鏈結串列的連結(Concatenation),其實作法也很容易;只要將串列的首尾相連即可。如下圖所示:
3-1 單向鏈結串列 • Java的連結演算法如下所示: class Node { int data; Node next; public Node(int data) { this.data=data; this.next=null; } } public class LinkeList { public Node first; public Node last; public boolean isEmpty() { return first==null; } public void print() {
3-1 單向鏈結串列 Node current=first; while(current!=null) { System.out.print("["+current.data+"]"); current=current.next; } System.out.println(); } /*連結兩個鏈結串列*/ public LinkeList Concatenate(LinkeList head1,LinkeList head2) { LinkeList ptr; ptr = head1; while(ptr.last.next != null) ptr.last = ptr.last.next; ptr.last.next = head2.first; return head1; }
3-2 環狀鏈結串列 • 環狀鏈結串列插入節點(1/2) • 將新節點插在第一個節點前成為串列首:首先將新節點X的指標指向原串列首節點,並移動整個串列,將串列首指向新節點。圖形如下:
3-2 環狀鏈結串列 • 環狀鏈結串列插入節點(2/2) • 將新節點X插在串列中任意節點I之後:.首先將新節X的指標指向I節點的下一個節點,並將I節點的指標指向X節點。圖形如下:
3-2 環狀鏈結串列 • 以下是利用Java語言寫出的環狀串列節點插入演算法: class Node { int data; Node next; public Node(int data) { this.data=data; this.next=null; } } public class CircleLink { public Node first; public Node last;
3-2 環狀鏈結串列 public boolean isEmpty() { return first==null; } /*插入節點*/ public void insert(Node trp) { Node tmp; Node newNode; if(this.isEmpty()) { first=trp; last=trp; last.next=first; } else if(trp.next==null) { last.next=trp;
3-2 環狀鏈結串列 last=trp; last.next=first; } else { newNode=first; tmp=first; while(newNode.next!=trp.next) { if(tmp.next==first) break; tmp=newNode; newNode=newNode.next; } tmp.next=trp; trp.next=newNode; } }
3-2 環狀鏈結串列 • 環狀鏈結串列的刪除節點(1/2) • 刪除環狀鏈結串列的第一個節點:首先將串列首移到下一個節點,將最後一個節點的指標移到新的串列首,新的串列首是原串列的第二個節點。圖形如下:
3-2 環狀鏈結串列 • 環狀鏈結串列的刪除節點(2/2) • 刪除環狀鏈結串列的中間節點。首先找到節點Y的前一個節點previous,.將previous節點的指標指向節點Y的下一個節點。圖形如下:
3-2 環狀鏈結串列 • 以下為Java語言寫出的環狀串列節點刪除演算法。 class Node { int data; Node next; public Node(int data) { this.data=data; this.next=null; } } public class CircleLink { public Node first; public Node last;
3-2 環狀鏈結串列 public boolean isEmpty() { return first==null; } /*刪除節點*/ public void delete(Node delNode) { Node newNode; Node tmp; if(first.data==delNode.data)//要刪除的節點是串列首 { first=first.next; if (first==null) System.out.print("[環狀串列已經空了]\n"); return; } else if(last.data==delNode.data)//要刪除的節點是串列尾 {
3-2 環狀鏈結串列 newNode=first; while(newNode.next!=last) newNode=newNode.next; newNode.next=last.next; last=newNode; last.next=first; } else { newNode=first; tmp=first; while(newNode.data!=delNode.data) { tmp=newNode; newNode=newNode.next; } tmp.next=delNode.next; } }
3-2 環狀鏈結串列 • 環狀鏈結串列的結合 • 單向鏈結串列的連結只需改變一個指標就可以了,如下圖所示:
3-2 環狀鏈結串列 • 範例 3.2.1 • 試設計一Java程式,將二個學生成績處理環狀鏈結串列連結,並列印連結後新串列中每個學生的成績與座號。
3-3雙向鏈結串列 • 雙向鏈結串列的定義(1/3) • 對每個節點而言,具有三個欄位,中間為資料欄位。左右各有兩個鏈結欄位,分別為LLINK及RLINK,其中RLINK指向下一個節點,LLINK指向上一個節點。如下圖所示:
3-3雙向鏈結串列 • 雙向鏈結串列的定義(2/3) • 每個節點具有三個欄位,中間為資料欄位。左右各有兩個鏈結欄位,分別為LLINK及RLINK。其中RLINK指向下一個節點,LLINK指向上一個節點。 • 通常加上一個串列首,此串列不存任何資料,其左邊鏈結欄指向串列最後一個節點,而右邊鏈結指向第一個節點。 • 假設ptr為一指向此串列上任一節點的鏈結,則有: ptr=RLINK(LLINK(ptr))=LLINK(RLINK(ptr))
3-3雙向鏈結串列 • 雙向鏈結串列的定義(3/3) • 如果使用Java語言來宣告它的結構,方式如下: class Node { int data; Node rnext; Node lnext; public Node(int data) { this.data=data; this.rnext=null; this.lnext=null; } }
3-3雙向鏈結串列 • 雙向鏈結串列插入節點(1/3) • 將新節點加入此串列的第一個節點前,如下圖: 步驟 1. 將新節點的右鏈結(RLINK)指向原串列的第一個節點。 2.將原串列第一個節點的左鏈結(LLINK)指向新節點。 3.將原串列的串列首指標head指向新節點,且新節點的左鏈結指向null。
3-3雙向鏈結串列 • 雙向鏈結串列插入節點(2/3) • 將新節點加入此串列的最後一個節點之後。如下圖: 步驟 1.將原串列的最後一個節點的右鏈結指向新節點。 2.將新節點的左鏈結指向原串列的最後一個節點,並將新節點的右鏈結指向NULL。
3-3雙向鏈結串列 • 雙向鏈結串列插入節點(3/3) • 將新節點加入到ptr節點之後,如下圖 步驟 1.將ptr節點的右鏈結指向新節點。 2.將新節點的左鏈結指向ptr節點。 3.將ptr節點的下一個節點的左鏈結指向新節點。 4.將新節點的右鏈結指向ptr的下一個節點。
3-3雙向鏈結串列 有關雙向鏈結串列節點加入的Java程式演算法如下: 01import java.util.*; 02import java.io.*; 03 04class Node 05{ 06 int data; 07 Node rnext; 08 Node lnext; 09 public Node(int data) 10 { 11 this.data=data; 12 this.rnext=null; 13 this.lnext=null; 14 }
3-3雙向鏈結串列 • 15 } • 16 • 17 public class Doubly • 18 { • 19 public Node first; • 20 public Node last; • 21 public boolean isEmpty() • 22 { • 23 return first==null; • 24 } • 25 • 26 /*插入節點*/ • 27 public void insert(Node newN) • 28 { • 29 Node tmp; • 30 Node newNode; • if(this.isEmpty())/*如果雙向鏈結串列為空串列
3-3雙向鏈結串列 • 32 { • 33 first=newN; • 34 first.rnext=last; • 35 last=newN; • 36last.lnext=first; • 37 } • 38 else • 39 { • 40 if(newN.lnext==null) /*插入串列首的位置*/ • 41 { • 42 first.lnext=newN; • 43 newN.rnext=first; • 44 first=newN; • 45 } • 46 else • 47 { • 48 if(newN.rnext==null)/*插入串列尾的位置*/ • {
3-3雙向鏈結串列 50 last.rnext=newN; 51 newN.lnext=last; 52 last=newN; 53 } 54 else /*插入中間節點的位置*/ 55 { 56 newNode=first; 57 tmp=first; 58 while(newN.rnext!=newNode.rnext) 59 { 60 tmp=newNode; 61 newNode=newNode.rnext; 62 } 63 tmp.rnext=newN; 64 newN.rnext=newNode; 65 newNode.lnext=newN; 66 newN.lnext=tmp; 67 } 68 } 69 } 70 } 71 }
3-3雙向鏈結串列 • 雙向鏈結串列節點刪除(1/3) • 刪除串列的第一個節點。如下圖: 步驟 1.將串列首指標head指到原串列的第二個節點。 2.將新的串列首指標指向NULL。
3-3雙向鏈結串列 • 雙向鏈結串列節點刪除(2/3) • 刪除此串列的最後一個節點。如下圖: 步驟 1.將原串列最後一個節點之前一個節點的右鏈結指向NULL即可。