Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
Linked-list เปนโครงสรางทใชในการจดเกบขอมลอกรปแบบหนงทมการน ามาใชอยางมาก Linked-List เปนโครงสรางทใชงานไดดกวา array เนองจากวามความยดหยนในเรองของเนอทในการเกบขอมล การจดการกบขอมลกท าไดดกวา ถามการออกแบบการใชอยางเหมาะสม ในบทนเราจะมาท าความเขาใจกบการจดเกบขอมลดวย Linked-List หลงจากจบบทเรยนนแลว ผอานจะไดทราบถง o การออกแบบโครงสรางของ Node ทใชเปนตวเกบขอมลและตวเชอมใน Linked-List o การออกแบบโครงสรางของ Linked-List o การน าขอมลเขา (insert) และออกจาก (remove) Linked-List o การใช Linked-List เปนตวจดการขอมลของ Stack และ Queue o การออกแบบและใช Double-Ended Linked-List o การออกแบบและใช Doubly-Linked-List o การใช Linked-List ของ Java 2.1 Linked-List Linked-list เปนการน าเอาโครงสรางท (สวนใหญ) เรยกวา node มาตอเชอมกนเปนลกโซ ซงขอดของการน าเอา node มาตอเชอมกนในลกษณะนท าใหการจดเกบมความยดหยนมากยงข น เราสามารถทจะเพมจ านวนของ node ไดตามใจโดยไมตองค านงถงขดจ ากดเหมอนกบการจดเกบแบบ array (ยกเวนกรณทเราใช memory ของระบบจนหมด!) ถาผอานลองนกภาพถงขบวนรถไฟ ผอานกจะเหนวาทจรงแลวขบวนรถไฟเปนการน าเอาตโบกมาตอเขาดวยกน ขบวน
จะสนหรอยาวกข นอยกบจ านวนโบกเหลาน หากผโดยสารทอยในโบกแรก ๆ ตองการจะไปยงโบกสดทายกตองเดนผานโบกอ น ๆ ระหวางทางดวย เชนเดยวกนกบการจดเกบขอมลในรปแบบของ Linked-List การเขาหา node แตละ node กตองผาน node อน ๆ ดวย link ทเชอมตอ node เหลานไว Linked-list ประกอบดวย node หลาย ๆ node ซงในหนง node จะม field ทเปนขอมล (data) ซงอาจมหลายไดตว แตจะตองม field อยหนง field ทเปนตวเชอม (link) ไปยง node อน เชน
ทเหนในภาพท 2.1
Linked-List บทท 2
34
ภาพท 2.1 linked-list
การน าขอมลเขาส Linked-List ท าไดดวยการสราง node ขนใหมหนง node แลวจงท าการเชอม node ใหมนเขากบ node อน ๆ ทมอยใน Linked-List การเชอม node ตาง ๆ เขาดวยกนนน ทจรงแลวเปนการก าหนดใหตวแปรทมชนดเปน node ไดรบทอย (address) ในหนวยความจ าของ node ทตองการเชอม เชน สมมตวา node1 และ node2 เปน node ทเราสรางขน และเราตองการเชอม node ทงสองตวเขาดวยกน เรากอาจใชค าสง node1.next = node2 โดยทตวแปร next จะมทอยของ node2 เรามาดกนถงโครงสรางของ node ทเราไดพดถงเพอใหเกดความเขาใจในเรองของการเชอม node public class MyNode<T> {
private T data; //data
private MyNode<T> next; //connector (link)
…
}
เราก าหนดให field ทเปน data นนเกบขอมลทเปนอะไรกไดตามใจผใช (ซง field ทเปน data นจะมก field กได ในทนจะยกตวอยางของ node ทม data เพยงหนง field เทานน) สวน field ทสองเปน field ทมชนดเปนชนดเดยวกนกบตวของ class เอง ซงเราใชช อวา MyNode ต าราหลาย ๆ เลมเรยกโครงสรางแบบนวา โครงสรางทอางถงตวเอง (Self-referential Structure) เนองจากวา field next ของเรามชนดเปนตวของมนเองคอ MyNode และ field next ตวนแหละทจะเปนทเกบ address ของ node อนทมนชอย ยอนกลบไปด node1 และ node2 ของเรา ถา node1 อยในหนวยความจ าท 1024 และ node2 อยท 1032 และถาเราเชอม node2 เขากบ node1 field next ของ node1 จะมคาเทากบ 1032 ดงทแสดงใหดในภาพท 2.2
data
next
data
next next
data
null
head
378
next
924
next next
258
null
head
โครงสรางหลกของ Linked-List
ตวอยางของ Linked-List ทมขอมลอย 3 ตว
node
บทท 2 Linked-List
35
ภาพท 2.2 การเชอม node
ในการจดเกบแบบ linked-list น ขอมลหรอ node ทเขามาหลงสดจะเปนขอมลตวแรกเมอมการดงออก ลองมาด code ของ node ทเราไดพดถง และน ามาใชในการสราง Linked-List ของเรา
1: /**
2: Basic structure for a node
3: Java has a Node class, so we use MyNode instead
4: */
5:
6: public class MyNode<T> {
7: private T data; //data
8: private MyNode<T> next; //connector (link)
9:
10: //default constructor
11: public MyNode() {
12: data = null;
13: next = null;
14: }
15:
16: //constructor that assigns data and links
17: //to the given node (n)
18: public MyNode(T obj, MyNode<T> n) {
19: data = obj;
20: next = n;
21: }
22:
23: //set this node to node n
24: public void setNext(MyNode<T> n) {
25: next = n;
26: }
27:
28: //set this node's data to d
29: public void setData(T d) {
30: data = d;
31: }
32:
node1
1032
node2
null
1024
1032
1028
1036
ต าแหนงในหนวยความจ า
ทอยของ node ท node นเช อมอย
(node2)
node1
next null
node2
node1 เชอมกบ node2 ผานทาง field next
ทอยของ node ทงสองในหนวยความจ า
ขอมลทเกบ
Linked-List บทท 2
36
33: //get node next to this node
34: public MyNode<T> getNext() {
35: return next;
36: }
37:
38: //get data of this node
39: public T getData() {
40: return data;
41: }
42: }
เราก าหนดใหม field อยสอง filed โดยก าหนดให field แรกเปน Object และ filed ทสองเปนตวเชอม (next) ซง filed ทสองนจะตองมชนดเดยวกนกบส งทมนจะตองเชอมดวย นนกคอ MyNode
private T data; //data
private MyNode<T> next; //connector (link)
เราก าหนดใหม constructor สองตวรองรบการสราง object จาก class MyNode ของเรา โดยก าหนดใหตวแรกเปน constructor ทไมม parameter เพอการสราง node เปลาทไมตองมขอมล สวนตวทสองเราก าหนดให parameter ตวแรกเปน object ทตองการเกบไวใน node สวน parameter ตวทสองก าหนดใหเปน node ทตองเชอมตอดวย //default constructor
public MyNode() {
data = null;
next = null;
}
//constructor that assigns data and links to the given node (n)
public MyNode(T obj, MyNode<T> n) {
data = obj;
next = n;
}
สวน method ตวอน ๆ 4 ตวทเหนน เราเอาไวใชเพอ setNext() ก าหนดจดเชอมของ node getNext() หา node ถดไป
setData() ก าหนดขอมลทอยใน node getData() ดงขอมลออกจาก node เมอเราได MyNode แลว เรากตองออกแบบ Linked-list ส าหรบการน าขอมลเขา และการดงขอมลออก
1: /**
2: Basic structure for Linked List
3: Java has a LinkedList class, so we use MyLinkedList instead
4: */
5:
6: public class MyLinkedList<T extends Comparable<? super T>> {
7: private MyNode<T> head; //gateway node
8: private int count; //nodes in list
9:
10: //default constructor
11: public MyLinkedList() {
12: head = null;
13: count = 0;
14: }
15:
16: //check if list is empty
17: public boolean empty() {
18: return head == null;
19: }
20:
21: //return number of nodes
22: public int size() {
บทท 2 Linked-List
37
23: return count;
24: }
25:
26: public void insert(T value) {
27: //create new node with given value and link head to
28: //this node, equivalent to the following statements
29: //MyNode temp = new MyNode(value, null);
30: //head.setNext(temp);
31: head = new MyNode<T>(value, head);
32: count++; //one more node added
33: }
34:
35: //remove node where head points to
36: public T remove() {
37: //cannot remove if list is empty
38: if(empty())
39: return null;
40: //point head to the node next to it
41: //using temp to retrieve data
42: MyNode<T> temp = head;
43: head = temp.getNext();
44: count--; //one less node
45: return temp.getData(); //return object removed
46: }
47:
48: //peek at data of node n
49: public T peek(int n) {
50: //traversing the list using temp
51: MyNode<T> temp = head;
52: for(int i = 0; i < n && temp != null; i++)
53: temp = temp.getNext();
54: return temp.getData();
55: }
56:
57: //display contents of list via System.out.println()
58: public String toString() {
59: StringBuffer buf = new StringBuffer();
60: buf.append("head->(" + peek(0));
61: for(int i = 1; i < size(); i++)
62: buf.append(", " + peek(i));
63: buf.append(")");
64: return new String(buf);
65: }
66:
67: //to display addresses of all nodes in list
68: //converting from Hex to Dec
69: //beginning address of MyNode start with MyNode@
70: //follow by Hex digits e.g. MyNode@13f5d07
71: public void address() {
72: MyNode<T> temp = head;
73: String thisNode, nextNode;
74: int numNode = 0, numNext = 0;
75: while(temp != null) {
76: //convert Node to String before
77: //convert it to int - starts at 5th index
78: thisNode = temp.toString();
79: numNode = Integer.parseInt(thisNode.substring(7), 16);
80: //avoiding null at last node
81: if(temp.getNext() != null) {
82: nextNode = temp.getNext().toString();
83: numNext = Integer.parseInt(nextNode.substring(7), 16);
84: }
85: System.out.print("This node: " + numNode);
86: System.out.println("\tNext node: " + numNext);
87: temp = temp.getNext();
88: }
89: }
90: }
method ทส าคญในการสราง linked-list กคอ method insert() และ method remove() ส าหรบ method insert() นน เราอาศย constructor ของ class Node เปน method หลกใน
Linked-List บทท 2
38
การ insert เราก าหนดใหมตวแปร count เพอเอาไวบอกจ านวนของ node ทงหมดทมอยใน linked-list 2.2 การน า node เขาส Linked-List ทางดานหนา public void insert(T value) {
//create new node with given value and link head to
//this node, equivalent to the following statements
//MyNode<T> temp = new MyNode<T>(value, null);
//head.setNext(temp);
head = new MyNode<T>(value, head);
count++; //one more node added
}
สงทเกดข นอนดบแรกใน method insert() คอ การสราง node ดวย object ทสงเขามา ประโยคทวา new MyNode(value, head) จะท าให field ทช อวา data ใน class MyNode มคาเปนคาเดยวกนกบคาของ value สวน field ทสองจะชไปยงทท head ชอย (อางถงส งเดยวกน) ซงถา Linked-List ของเราเปน List ใหมทไมม ขอมลอยเลย head จะมคาเปน null ดงนน ประโยค head = new MyNode<T>(value, head);
กเปนการก าหนดคาเกาท head ชอยใหกบ field ทช อวา next (ครงแรก head มคาเปน null – จงท าให node ใหมนมคา null อยใน field next) หลงจากนนจงก าหนดคาใหกบ head ใหม ซงกคอคาของ node ตวใหมนเอง ดงแสดงในภาพท 2.3
ภาพท 2.3 แสดงเมอสราง Linked-List ครงแรก และ เมอม node อย
และทกครงทมการสราง node ใหม เราจะเพมคาของ count ขนอกหนงคาเพอใชบอกจ านวนของ node ทมอยใน linked-list นน 2.3 การลบ node ออกจาก Linked-List ทางดานหนา การลบ node ออกจาก Linked-List เราตองตรวจสอบดวา linked-list ของเรามขอมลหรอไมถาไมมเรากสง null กลบเพอเปนการบอกวาไมสามารถทจะลบ node ได แตถามขอมล เรากท าการลบ node ออกดวยการก าหนดคาของ head ใหเปนคาของ node ทอยถดไป ดวยค าสง MyNode temp = head และค าสง head = temp.getNext() หลงจากนนเรากลดจ านวนของ node ลงดวย count-- เสรจแลวเรากสงขอมลของ node ทถกลบกลบออกไป ดงทเหนจาก code และภาพท 2.4 //remove node where head points to
public T remove() {
//cannot remove if list is empty
if(empty())
return null;
//point head to the node next to it
//using temp to retrieve data
null head
head 423
null
Linked-List ขณะทไมม node อยเลย
Linked-List ขณะทม node อย 1 node จากประโยค
head = new MyNode(value, head)
บทท 2 Linked-List
39
MyNode<T> temp = head;
head = temp.getNext();
count--; //one less node
return temp.getData(); //return object removed
}
ภาพท 2.4 การลบ node ออกจาก Linked-List
เมอเราลบ node ออกจาก Linked-List เราไมตองกงวลถงการคน memory ของ node ทเราลบออก (temp) เพราะ java จะท าการตรวจสอบสวนของ memory ทไมไดใชงานดวย garbage collector และจะคน memory ทไมมการอางถงคนใหระบบเอง แตเราสามารถทจะท าใหการท างานของระบบดข น ดวยการท าให node ทไมไดใชงานแลวมคาเปน null นนกคอก าหนดให temp = null เพราะฉะนน remove() ของเรากตองเปลยนเปน //remove node where head points to
public T remove() {
//cannot remove if list is empty
if(empty())
return null;
//point head to the node next to it
//using temp to retrieve data
MyNode<T> temp = head;
head = temp.getNext();
count--; //one less node
T value = temp.getData(); //copy data from temp
temp = null; //make it null
return value; //return object removed
}
method peek() เอาไวใชในการดวาขอมล ณ ต าแหนงนน ๆ คออะไร เราจะใช method peek() ในการ display ขอมลของ node ทกตวทมอยใน Linked-list การเขยนกไมยาก เราเพยงแตสราง node ขนใหมหนง node และก าหนดให node ใหมนช ไปทท head ชอย (ดานหนาสดของ Linked-List) พรอมทงใช for loop และ parameter ของ method peek() เปนตวก าหนดการเขาหา เมอเราเจอต าแหนงทตองการ เรากสงขอมล ณ ต าแหนงนนกลบไปยงผเรยก //peek at data of node n
public T peek(int n) {
//traversing the list using temp
MyNode<T> temp = head;
for(int i = 0; i < n && temp != null; i++)
temp = temp.getNext();
return temp.getData();
}
458
null
temp
head 329
MyNode temp = head;
458
null
temp
head 329
head = temp.getNext();
Linked-List บทท 2
40
เราทดสอบ code ของเราดวยการ insert ขอมลทเปน int เขาส Linked-List เปนจ านวน 4 ตว และท าการลบออก 2 ตว
1: /**
2: Testing MyLinkedList
3: */
4:
5: class TestMyLinkedList {
6: public static void main(String[] args) {
7: MyLinkedList<Integer> list = new MyLinkedList<Integer>();
8:
9: //insert 4 ints
10: list.insert(34);
11: list.insert(67);
12: list.insert(12);
13: list.insert(78);
14: list.insert(32);
15: System.out.println(list);
16:
17: //remove first 2 items
18: list.remove();
19: list.remove();
20: System.out.println(list);
21: }
22: }
ผลลพธทไดจากการ run head->(32, 78, 12, 67, 34)
head->(12, 67, 34)
ม method อกหนงตวทเราไดเขยนขนเพอใชในการตรวจสอบทอยในหนวยความจ าของ node ตาง ๆ ทอยใน Linked-List นนกคอ method address() ซงอาจไมคอยมความจ าเปนเทาไร แตผเขยนเหนวาในบางครงถาเราใหมตอเรองของ Self-referential Structure วธการดต าแหนงของตวแปรในหนวยความจ ากชวยท าใหเกดความเขาใจทดข น วธการกไมมอะไรเลย เราเพยงแตสง node นน ๆ ไปใหชองสงขอมลออก (System.out.println()) เทานนเอง Java กจะแสดงทอยของ node นน ๆ ใหเราเอง ดงทเหนจากผลลพธของการเรยกใช address() ดานลางน (เพอใหงายตอการอาน เราจงเปลยนทอยของ node ทเปนเลขฐานสบหก ใหเปนเลขในฐานสบ) public void address() {
MyNode<T> temp = head;
String thisNode, nextNode;
int numNode = 0, numNext = 0;
while(temp != null) {
//convert Node to String before
//convert it to int - starts at 5th index
thisNode = temp.toString();
numNode = Integer.parseInt(thisNode.substring(7), 16);
//avoiding null at last node
if(temp.getNext() != null) {
nextNode = temp.getNext().toString();
numNext = Integer.parseInt(
nextNode.substring(7), 16);
}
System.out.print("This node: " + numNode);
System.out.println("\tNext node: " + numNext);
temp = temp.getNext();
}
}
หลงจากทเรยก address() ในโปรแกรมตรวจสอบทเราเขยนขน เราไดผลลพธดงน Insert 10 random Integers
head->(54)
head->(54, 42)
head->(86, 54, 42)
head->(86, 54, 42, 2)
head->(86, 86, 54, 42, 2)
บทท 2 Linked-List
41
head->(86, 86, 54, 42, 2, 35)
head->(70, 86, 86, 54, 42, 2, 35)
head->(70, 86, 86, 54, 42, 2, 35, 16)
head->(88, 70, 86, 86, 54, 42, 2, 35, 16)
head->(88, 70, 86, 86, 54, 42, 2, 35, 16, 10)
This node: 20929799 Next node: 16032330
This node: 16032330 Next node: 13288040
This node: 13288040 Next node: 27355241
This node: 27355241 Next node: 30269696
This node: 30269696 Next node: 24052850
This node: 24052850 Next node: 26022015
This node: 26022015 Next node: 3541984
This node: 3541984 Next node: 4565111
This node: 4565111 Next node: 20392474
This node: 20392474 Next node: 20392474
จะเหนวาคาทอยใน filed next ของ node จะมคาเปนทอยของ node ถดไป จนกระทงถง node สดทายทมคาเปน null จากผลลพธเราจะเหนคา 20392474 เหมอนกนสามตวโดยเฉพาะ field next ของ node สดทายเราจะเหนวามคาเทากบตวมนเอง(ผเขยนคดวา Java อางถง null ดวยการอางถงตวเองของ node เพอใหรวาไมม node ใด ๆ เชอมตอกบ node นอก ถาเราไมเปลยนทอยของ node ใหเปนเลขฐานสบ เราจะเหนขอมล ณ ต าแหนงนเปน null) การน าขอมลเขาส Linked-List ไมจ าเปนทจะตองน าเขาทางดานหนาเสมอไป เราอาจน าขอมลเขาทางดานหลงของ Linked-List กได เชนเดยวกนกบการน าขอมลออก เรากสามารถทจะดงขอมลออกจากทางดานหลงของ Linked-List ได 2.4 การน า node เขาส Linked-List ทางดานหลง วธการทจะน าขอมลเขาทางดานหลงของ Linked-List นน เราจะตองหาต าแหนงของ node สดทายใหเจอเสยกอน โดยเราสามารถทจะท าไดดวยการใช node ชวคราวท าหนาทเปนตวเดนเขาหา node ทกตว เพอท าการตรวจสอบวา node ทเราเขาหาอยตวนม field next ทมคาเปน null หรอไม ซงถาใช แสดงวาเราเจอ node สดทายแลว เรากท าการเพม node ใหมใหกบ Linked-List ดวยการเชอม node สดทายเขากบ node ใหมน ดงทเหนในภาพท 2.5
ภาพท 2.5 การน าขอมลเขาส Linked-list ทางดานหลง
cur จะชไปท node สดทายทอยใน list กอนการเชอมกบ tmp
458
null
head 329
cur
cur.setNext(tmp);
null
458
head 329
tmp
899
MyNode tmp = new MyNode(obj, cur.getNext());
List กอนการน าขอมลเขา
List หลงจากการน าขอมลเขา
address ทเกดจากการก าหนดของ Java ใหกบ node ทง 10 ตวทเราสรางข น
ผอานควรสงเกตถง address ทอยใน next ของ node กอนหนา กบ address
ของ node ถดไป
Linked-List บทท 2
42
//insert data at the end of list
public void insertEnd(T obj) {
//list empty - just call insert()
if(empty())
insert(obj);
else {
//there is at least one node in the list,
//we must locate the last node
MyNode<T> cur = head;
while(cur.getNext() != null)
cur = cur.getNext();
//create new node with given data
//and set current node to point to this new node
MyNode<T> tmp = new MyNode<T>(obj, cur.getNext());
cur.setNext(tmp);
count++; //one more node added
}
}
เราเรมตนดวยการตรวจสอบวา Linked-List ของเรามขอมลหรอไม ถาไมม เรากเรยก insert() ท าหนาทใสขอมลใหเรา แตถามขอมลอยแลว เรากใช cur เปนตวเดนเขาหา Linked-List จนกวาจะเจอ null ซงถาเจอ cur จะชอยท node สดทายและ filed next ของ cur จะชท null หลงจากนนเรากสราง node ใหมชอ tmp ดวยขอมลทเราตองการใสพรอมทง field next ของ cur หลงจากทเราได node ใหมแลวเรากก าหนดให cur ชไปท node ใหมน เราทดสอบ insertEnd() ดวยการเปลยนโปรแกรม TestMyLinkedList.java ใหสราง Integer 10 ตวโดยจะเรยกใช insert() ถา index ของ for loop เปนเลขค และเรยก insertEnd() ถา index เปนเลขค ดงทเหนดานลางน
1: /**
2: Testing MyLinkedList
3: */
4:
5: class TestMyLinkedList {
6: public static void main(String[] args) {
7: MyLinkedList<Integer> list = new MyLinkedList<Integer>();
8:
9: //insert 10 integers
10: for(int i = 0; i < 10; i++) {
11: //at even location
12: if(i % 2 == 0)
13: list.insert(i);
14: //at odd location
15: else
16: list.insertEnd(i);
17:
18: System.out.println(list);
19: }
20: }
21: }
ผลลพธทไดคอ head->(0)
head->(0, 1)
head->(2, 0, 1)
head->(2, 0, 1, 3)
head->(4, 2, 0, 1, 3)
head->(4, 2, 0, 1, 3, 5)
head->(6, 4, 2, 0, 1, 3, 5)
head->(6, 4, 2, 0, 1, 3, 5, 7)
head->(8, 6, 4, 2, 0, 1, 3, 5, 7)
head->(8, 6, 4, 2, 0, 1, 3, 5, 7, 9)
สงทเราตองค านงถงกคอ ต าแหนงทตองน า node เขาส Linked-List ในการ insert แบบน node
ทวานกคอ node ทายสดของ Linked-List หรอจะพดใหถกตองกคอ node ทมคาเทากบ null
บทท 2 Linked-List
43
เราหาต าแหนงของ node นไดดวยการตรวจสอบ field ทช อวา next ของทก node ใน Linked-List วามคาเทากบ null หรอไม ดวยการใช loop และ method getNext() 2.5 การลบ node ออกจาก Linked-List ทางดานหลง การลบ node ออกจาก Linked-List ทางดานหลงกคลายกบการเพม node เราตองหาต าแหนงทายสดของ Linked-List ใหเจอกอนทจะท าการยาย pointer ทเกยวของ ภาพท 4.6 แสดงการหา node สดทายดวย node ชวคราวทช อ temp พรอมทงการก าหนดคา next ของ temp ใหมคาเทากบ null (ผานทาง temp)
ภาพท 2.6 การลบ node สดทายทอยใน Linked-List
code ของการลบ node ออกจากทางดานหลงมดงน //remove node at end of list
public T removeEnd() {
//list is empty
if(empty())
return null;
//only one object in the list
if(head.getNext() == null)
return remove();
//locate the last node
MyNode<T> temp = head;
while(temp.getNext().getNext() != null)
temp = temp.getNext();
//save value of this object
T obj = temp.getNext().getData();
//set next field of the node before last node to null
temp.setNext(temp.getNext().getNext());
count--; //one less node
return obj;
}
ขนตอนการท างานของ code กไมยาก เราเรมทการตรวจสอบวา Linked-List ของเรามขอมล หรอไม ถาไมมเรากสง null กลบออกไป ขนตอนตอไปเรากตรวจสอบวา Linked-List มขอมลอยเพยงตวเดยวหรอไม ซงถาใช เรากเรยก method remove() แบบปกตทเราเขยนกอนหนาน ถาเราผานสองขนตอนนโดยไมมการประมวลผลเลย แสดงวา Linked-List ของเรามขอมลอยาง
head 345 678
null
123
temp
while/loop จะยตการท างานเมอ
temp.getNext().getNext() == null เปนจรง
head 345 678
null
123
temp
หลงจากทเจอ node กอน node สดทายเรากลบ node สดทายดวยประโยค
temp.setNext(temp.getNext().getNext());
Linked-List บทท 2
44
นอยสองตว เรากใช temp เปนตวพาเราเขาหา node ทตองการ (หลงจากทก าหนดให temp = head แลว) ดวยค าสง while(temp.getNext().getNext() != null)
temp = temp.getNext();
เมอเจอแลวเรากเกบคาของ node นไว ดวยค าสง T obj = temp.getNext().getData();
และท าการยาย pointer ใหไปชท null ซงท าไดดวยการใชค าสง temp.setNext(temp.getNext().getNext());
และเมอทดลอง run ดวยโปรแกรมทเหนน
1: /**
2: Testing MyLinkedList
3: */
4:
5: import static java.lang.System.out;
6:
7: class TestMyLinkedList {
8: public static void main(String[] args) {
9: MyLinkedList<Integer> list = new MyLinkedList<Integer>();
10:
11: //insert 10 integers
12: for(int i = 0; i < 10; i++) {
13: //at even location
14: if(i % 2 == 0)
15: list.insert(i);
16: //at odd location
17: else
18: list.insertEnd(i);
19:
20: out.println(list);
21: }
22:
23: //remove 5 integers
24: for(int i = 0; i < 5; i++) {
25: //at even location
26: if(i % 2 == 0)
27: out.println("Removed: " + list.remove());
28: //at odd location
29: else
30: out.println("Removed: " + list.removeEnd());
31:
32: out.println(list);
33: }
34: }
35: }
ผลลพธทไดคอ head->(0)
head->(0, 1)
head->(2, 0, 1)
head->(2, 0, 1, 3)
head->(4, 2, 0, 1, 3)
head->(4, 2, 0, 1, 3, 5)
head->(6, 4, 2, 0, 1, 3, 5)
head->(6, 4, 2, 0, 1, 3, 5, 7)
head->(8, 6, 4, 2, 0, 1, 3, 5, 7)
head->(8, 6, 4, 2, 0, 1, 3, 5, 7, 9)
Removed: 8
head->(6, 4, 2, 0, 1, 3, 5, 7, 9)
Removed: 9
head->(6, 4, 2, 0, 1, 3, 5, 7)
Removed: 6
head->(4, 2, 0, 1, 3, 5, 7)
บทท 2 Linked-List
45
Removed: 7
head->(4, 2, 0, 1, 3, 5)
Removed: 4
head->(2, 0, 1, 3, 5)
code ของ class MyLinkedList ทเขยนขนนสามารถทจะท างานไดทงทางดานหนาและดานหลง แตมขดจ ากด คอ การเพมหรอลบ node ทางดานหลง จะตองใชเวลาในการเดนทางเขาหา list ดงนนเพอใหการท างานมประสทธภาพมากขน เราจงก าหนดใหม pointer อกหนงตวชไปท node สดทายทอยใน list เราเรยก Linked-List แบบนวา Double-Ended Linked-List 2.6 การสราง Double-Ended Linked-List การออกแบบ Double-Ended Linked-List นน สวนใหญจะใช pointer 2 ตวเปนตวจ าต าแหนงของ node ทอยดานหนาและ node ทอยทายสด (มกจะเรยกวา head และ rear) ท าใหการน าขอมลเขาและดงออกเปนไปไดโดยงาย ภาพท 2.7 แสดงโครงสรางของ Double-Ended Linked-List
ภาพท 2.7 Double-Ended Linked-List
เราดดแปลง class MyLinkedList ใหม node 2 ตวคอ head และ rear พรอมกบก าหนดใหทงสองมคาเปน null ใน constructor เราออกแบบการน าเขาใหมดวยการสราง method insertFirst() และ insertLast() ทท าหนาทในการน าขอมลเขาทางดานหนา และทางดานหลงตามล าดบ สวน method อน ๆ กเหมอนเดม ยกเวน toString() ทเราเพม "<-last" ในตวแปร buf ส าหรบการแสดงต าแหนงของ node สดทายทอยใน list ลองมาด code ของ insertFirst()
และ insertLast() กน public void insertFirst(T item) {
//no item yet in list
if(empty()) {
head = new MyNode<T>(item, head);
rear = head;
}
//some items are already in list
else
head = new MyNode<T>(item, head);
count++; //one more node added
}
การท างานของ insertFirst() กไมยากอะไร หลงจากทตรวจสอบแลววา list ไมมขอมลอย เรากสราง node ขนใหมพรอมทงก าหนดให head ชไปท node น พรอมทงก าหนดให rear มคาเทากบ head (เรมตนทเดยวกน) แตถา list มขอมลอยแลว เรากสราง node ใหมเชนเดยวกบทเราท ากบ Linked-List ทมการเขาออกทางดานหนาดานเดยว สวน code ของ insertLast() กมดงน public void insertLast(T item) {
MyNode<T> temp = new MyNode<T>(item, null);
rear.setNext(temp); //connect current node to new node
rear = temp; //reset rear to this new node
count++;
}
head
345 678
null
123
rear
Linked-List บทท 2
46
เนองจากวาเรารวา rear ชอยท node สดทายเสมอ ดงนนเรากเพยงแตสราง node ใหมทม field next มคาเปน null ตอมาเรากก าหนดให field next ของ node ท rear ชอยมคาเทากบ node ใหมทเราสรางขน พรอมกบก าหนดให rear ชไปท temp หลงจากนน method ตวอน ๆ กเหมอน ๆ กบทผานมา ดงทแสดงไวใน class DoubledEndedLinkesList น
1: /**
2: Doubled-Ended LinkedList
3: */
4:
5: class DoubleEndedLinkedList<T extends Comparable<? super T>> {
6: private MyNode<T> head, rear;
7: private int count;
8:
9: //default constructor
10: DoubleEndedLinkedList() {
11: head = rear = null;
12: count = 0;
13: }
14:
15: //insert where head is
16: public void insertFirst(T item) {
17: //no item yet in list
18: if(empty()) {
19: head = new MyNode<T>(item, head);
20: rear = head;
21: }
22: //some items are already in list
23: else
24: head = new MyNode<T>(item, head);
25: count++; //one more node added
26: }
27:
28: //insert where rear is
29: public void insertLast(T item) {
30: MyNode<T> temp = new MyNode<T>(item, null);
31: rear.setNext(temp); //connect current node to new node
32: rear = temp; //reset rear to this new node
33: count++;
34: }
35:
36: //check if list is empty
37: public boolean empty() {
38: return head == null;
39: }
40:
41: //return number of nodes
42: public int size() {
43: return count;
44: }
45:
46: //peek at data of node n
47: private T peek(int n) {
48: //traversing the list using temp
49: MyNode<T> temp = head;
50: for(int i = 0; i < n && temp != null; i++)
51: temp = temp.getNext();
52: return temp.getData();
53: }
54:
55: //display contents of list via System.out.println()
56: public String toString() {
57: StringBuffer buf = new StringBuffer();
58: buf.append("head->(" + peek(0));
59: for(int i = 1; i < size(); i++)
60: buf.append(", " + peek(i));
61: buf.append(")<-last");
62: return new String(buf);
63: }
64: }
เมอทดสอบ code ทเขยนขนใหมดวยโปรแกรม TestDBEndedLinkedList.java ทเหนน
บทท 2 Linked-List
47
1: /**
2: Testing Double-Ended LinkedList
3: */
4:
5: class TestDBEndedLinkedList {
6: public static void main(String[] args) {
7: DoubleEndedLinkedList<Integer> list = new
DoubleEndedLinkedList<Integer>();
8:
9: //populate linked-list with 10 random Integers
10: System.out.println("Insert 10 random Integers");
11: for(int i = 0; i < 10; i++) {
12: int item = (int)(Math.random() * 100);
13: if(i % 2 == 0) {
14: list.insertFirst(item);
15: System.out.println(list);
16: }
17: else {
18: list.insertLast(item);
19: System.out.println(list);
20: }
21: }
22: }
23: }
ผลลพธทไดคอ Insert 10 random Integers
head->(19)<-last
head->(19, 63)<-last
head->(15, 19, 63)<-last
head->(15, 19, 63, 80)<-last
head->(48, 15, 19, 63, 80)<-last
head->(48, 15, 19, 63, 80, 99)<-last
head->(18, 48, 15, 19, 63, 80, 99)<-last
head->(18, 48, 15, 19, 63, 80, 99, 3)<-last
head->(54, 18, 48, 15, 19, 63, 80, 99, 3)<-last
head->(54, 18, 48, 15, 19, 63, 80, 99, 3, 10)<-last
มกระบวนการอน ๆ ทเราสามารถน ามาประยกตใชกบการท างานกบ Linked-List ไดอกหลายกระบวนการ เชน การลบทางดานหนา และทางดานหลงของ Linked-List การ insert ขอมลเขา
ส Linked-List แบบทตองค านงถงต าแหนงทเหมาะสมของขอมล (ordered list) ในทนจะทงไว เปนแบบฝกหด และกรณศกษาของผอานตอไป ซงอาจดดแปลงจาก method insertInorder() ของ Doubly-Linked-List ทเราจะพดถงตอไป หลงจากทเราไดพดถงการใช Linked-List เปนตวจดการกบขอมลของ Stack และ Queue เรยบรอยแลว 2.7 Doubly-Linked-List
Doubly-Linked-List เปน list ทผใชสามารถทจะเขาและออกไดทงสองทาง ทงทางดานหนาและทางดานหลง การทเขาหา list ไดสองทางท าใหการท างานเกยวกบ list มความยดหยนสงยงข น ในการออกแบบและเขยน code เราจ าเปนทจะตองม pointer สองตวทท าหนาทเปนตวบอกต าแหนงของ node แรก และ node สดทายทอยใน list นอกจากนเรายงจ าเปนทจะตองม link จาก node สดทายไปยง node อน ๆ ทอยทางดานหนาของ list เพอใชเปนสะพานเชอมในการเขาหาขอมลจากดานหลงไปดานหนา ดงนนโครงสรางของ node ของเราตองมการเพมตวเชอมอกหนงตว เราใหชอตวเชอมนวา previous ดงทแสดงใหดในภาพท 2.8
Linked-List บทท 2
48
ภาพท 2.8 โครงสรางของ node ส าหรบ Doubly-Linked-List
ภาพท 2.9 โครงสรางของ Doubly-Linked-List
เราตองการทจะแยกแยะเรองของ Doubly Linked-List ออกจาก Linked-List แบบอน ๆ ดงนนเราจงออกแบบโครงสราง node ใหมโดยใชชอวา DBLNode สวนโครงสรางของ Doubly Linked-List เองเรากใชช อวา DoublyLinkedList กอนอนเรามาดกนถงโครงสรางของ node ทเราไดปรบปรงเพอใชกบ Doubly Linked-List
1: /**
2: basic node structure for doubly linked-list
3: */
4:
5: public class DBLNode<T> {
6: private T data; //data
7: private DBLNode<T> previous; //link to node before
8: private DBLNode<T> next; //link to node after
9:
10: //default constructor
11: public DBLNode() {
12: data = null;
13: previous = next = null;
14: }
15:
16: //constructor that assigns data
17: public DBLNode(T obj) {
18: data = obj;
19: previous = next = null;
20: }
21:
22: //set this node's next link to node n
23: public void setNext(DBLNode<T> n) {
24: next = n;
25: }
26:
27: //set this node's previous link to node n
28: public void setPrevious(DBLNode<T> n) {
29: previous = n;
30: }
31:
32: //set this node's data to d
33: public void setData(T d) {
34: data = d;
next
null
325
null
previous
null
949
previous
next
325
previous
next
first
last
null
789
previous
next
บทท 2 Linked-List
49
35: }
36:
37: //get node next to this node
38: public DBLNode<T> getNext() {
39: return next;
40: }
41:
42: //get node before this node
43: public DBLNode<T> getPrevious() {
44: return previous;
45: }
46: //get data of this node
47: public T getData() {
48: return data;
49: }
50: }
เราม pointer 2 ตวคอ next และ previous ใน class DBLNode เพอใชส าหรบการเขาหาทงทางดานหนาและทางดานหลง สวน method อน ๆ กยงคงคลายกบ class Node ทเราออกแบบมากอนหนาน เพยงแตเปลยนชอ และการท างานบางอยาง เราไดออกแบบ method หลก ๆ ทเราตองการใชกบ Doubly Linked-List ตามความเหมาะสมดงน
insertFirst() น าขอมลเขาทางดานหนา insertLast() น าขอมลเขาทางดานหลง insertAfter() น าขอมลเขาถดจาก node ทก าหนดให insertInorder() น าขอมลเขาดวยการจดเรยงจากนอยไปหามาก removeFirst() ดงขอมลออกทางดานหนา removeLast() ดงขอมลออกทางดานหลง
กอนอนเรามาดการน าขอมลเขาดวย insertFirst() 2.7.1 การน าขอมลเขาทางดานหนา public void insertFirst(T item) {
DBLNode<T> temp = new DBLNode<T>(item);
if(empty()) //empty
last = temp; //last points at temp
else
first.setPrevious(temp); //set previous to temp
temp.setNext(first); //set temp.next to first
first = temp; //set first to temp
count++; //one more node added
}
การ insert ขอมลเขาทางดานหนาของ list ขนตอนแรกเรากสราง node ใหมดวยขอมลทสงมาให หลงจากนนเราตรวจสอบดวา list ของเราม node อยหรอไม ถาไมมเรากให last ชไปท node ใหมน พรอมกบก าหนดให node ใหมนช ไปท first และให first ชไปท node ใหมนดวยเหมอนกน แตถา list ของเราม node อยแลว (อยางนอยหนง node) เรากก าหนดให pointer ทช อวา previous ของ node แรกสดทอยใน list ชไปท node ใหมน และให node ใหมนช ไปทเดยวกนกบท first ชอย (node แรกสด) พรอมกบก าหนดให first ชไปท node ใหมนดวย ดงแสดงในภาพท 2.10
Linked-List บทท 2
50
ภาพท 2.10 การ insert node เขาทางดานหนาของ Doubly Linked-List
2.7.2 การน าขอมลเขาทางดานหลง การ insert ของ node ทางดานหลงกคลาย ๆ กนกบการ insert ทางดานหนา ดงแสดงใน code ทเหนน public void insertLast(T item) {
DBLNode<T> temp = new DBLNode<T>(item);
if(empty()) //empty
first = temp; //set first to temp
else {
last.setNext(temp); //set last.next to temp
temp.setPrevious(last); //set temp.previous to last
}
last = temp; //set last to temp
count++; //one more node added
}
เราเรมดวยการสราง node ใหม แลวจงตรวจสอบดวา list ของเรามขอมลอยหรอไม ถาไมมเรากก าหนดให first และ last ชไปท node ใหมน แตถามเราตองก าหนดให field next ของ last ช
ไปท node ใหมพรอมกบให field previous ของ node ใหมชไปท last หลงจากนนกให last ช
ไปท node ใหมน (ภาพท 2.11)
List กอนทจะมการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
null
temp
458
previous
next null
null
789
previous
next
325
previous
next
first last
null
3 1
2
null
null
789
previous
next
325
previous
next
first
last
temp
458
previous
next 1. first.setPrevious(temp);
2. temp.setNext(first); 3. first = temp;
List หลงจากการโยกยาย pointer ใหมการเชอมตอ
กบ node temp
บทท 2 Linked-List
51
ภาพท 2.11 การ insert ทางดานหลงของ Doubly Linked-List
2.7.3 การลบ node ออกทางดานหนา การลบ node ออกจาก list ทางดานหนานนเราจะตองตรวจสอบวา list มขอมลหรอไม ถา list ของเราไมมขอมลเลยเรากก าหนดให last เทากบ null ซงมคาเทากบการสราง list ครงแรกของเรานนเอง แตถามขอมลอย เรากก าหนดให previous ของ node แรกสดชไปท null และให first ชไปยงทท field next ของ node first เคยชอย ดงทแสดงใหดจาก code ขางลางน (ดภาพท 2.12 ประกอบ) public T removeFirst() {
DBLNode<T> temp = first; //set temp to first
if(first.getNext() == null) //basically empty
last = null;
else
//set previous of node after first to null
first.getNext().setPrevious(null);
first = first.getNext(); //set first to that node
count--;
return temp.getData();
}
List หลงจากการโยกยาย pointer ใหมการเชอมตอกบ node temp
List กอนทจะมการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
null
789
previous
next
325
previous
next
first last
null
temp
458
previous
next null
null
3
null
first last
1. last.setNext(temp);
2. temp.setPrevious(last); 3. last = temp;
temp
2
1
null
789
previous
next
325
previous
next
458
previous
next
Linked-List บทท 2
52
ภาพท 2.12 การลบ node ออกจากดานหนาของ Doubly Linked-List
2.7.4 การลบ node ออกทางดานหลง การลบออกจากทางดานหลงกคลายกบทางดานหนา เราตองค านงถงกรณท list ของเรามทงขอมลและไมมขอมล ถา list ไมมขอมลการลบกเพยงแตก าหนดให first มคาเทากบ null หลงจากนนกก าหนดให last ชไปยง node ทอยกอนหนานน แตถา list มขอมลอยเรากก าหนดให field next ของ node ทอยกอน node สดทายเปน null หลงจากนนกให last ชไปทท field previous ของ last เคยชอย (node ทอยกอนหนา last นนเอง) ด code ทใหพรอมภาพท 2.13 ประกอบ public T removeLast() {
DBLNode<T> temp = last; //set temp to last
if(first.getNext() == null) //empty
first = null;
else
//set next of node before last to null
last.getPrevious().setNext(null);
last = last.getPrevious(); //set last to that node
count--;
return temp.getData();
}
List กอนทจะมการลบ node ออกจากทางดานหนา
null
null
789
previous
next
325
previous
next
first last
2. first = first.getNext();
1. first.getNext().setPrevious(null)
null
789
previous
next
325
previous
next
first
last
null
List หลงจากทมการโยกยาย pointer เพอลบ node
null
บทท 2 Linked-List
53
ภาพท 2.13 การลบ node ออกจากทางดานหลง
เมอทดลอง run ดวยโปรแกรมน
1: /**
2: Testing DoublyLinkedList
3: */
4:
5: class TestDoublyLinkedList {
6: public static void main(String[] args) {
7: DoublyLinkedList<Integer> list = new DoublyLinkedList<Integer>();
8:
9: //insert 10 random Integers
10: System.out.println("Insert 10 random Integers");
11: for(int i = 0; i < 10; i++) {
12: int item = (int)(Math.random() * 100 + 1);
13: if(i % 2 == 0)
14: list.insertFirst(item);
15: else
16: list.insertLast(item);
17: list.displayForward();
18: }
19:
20: //remove 5 items
21: System.out.println("Removed 5 Integers randomly");
22: for(int i = 0; i < 5; i++) {
23: if(i % 2 == 0)
24: list.removeFirst();
25: else
26: list.removeLast();
27: list.displayBackward();
28: }
29: }
30: }
ผลลพธทได คอ Insert 10 random Integers
null
null
789
previous
next
325
previous
next
first last
List กอนทจะมการลบ node ออกจากทางดานหลง
2. last = last.getPrevious(); 1. last.getPrevious().setNext(null);
null
null
789
previous
next
325
previous
next first
last
List หลงจาการลบ node ออกจากทางดานหลง
null
Linked-List บทท 2
54
first->{27}<-last
first->{27, 61}<-last
first->{60, 27, 61}<-last
first->{60, 27, 61, 76}<-last
first->{42, 60, 27, 61, 76}<-last
first->{42, 60, 27, 61, 76, 64}<-last
first->{66, 42, 60, 27, 61, 76, 64}<-last
first->{66, 42, 60, 27, 61, 76, 64, 95}<-last
first->{86, 66, 42, 60, 27, 61, 76, 64, 95}<-last
first->{86, 66, 42, 60, 27, 61, 76, 64, 95, 17}<-last
Removed 5 Integers randomly
last->{17, 95, 64, 76, 61, 27, 60, 42, 66}<-first
last->{95, 64, 76, 61, 27, 60, 42, 66}<-first
last->{95, 64, 76, 61, 27, 60, 42}<-first
last->{64, 76, 61, 27, 60, 42}<-first
last->{64, 76, 61, 27, 60}<-first
2.7.5 การน าขอมลเขาหลง node ทก าหนดให ยงเหลอ method อกสองตวทเราตองพดถง นนกคอ insertAfter() และ insertInorder() ในการน าขอมลเขาของทงสอง method เราตองท าการคนหาต าแหนงท node ใหมจะตองไปอย เพราะฉะนนการน าขอมลเขาแบบนจะเสยเวลาพอสมควรถา list มขนาดใหญ เรามาดกนถงการน าขอมลเขาดวย insertAfter() กอนทเราจะไปดการน าขอมลเขาดวย insertInorder()
public void insertAfter(T key, T obj) {
boolean noItem = false;
//serching for key if found -> noItem = false
//if not found -> noItem = true
DBLNode<T> current = first;
while(current != null) {
if(current.getData().equals(key))
break;
current = current.getNext();
}
noItem = (current == null) ? true : false;
if(noItem)
System.out.println("Sorry - given key doesn't exist!");
else {
DBLNode<T> temp = new DBLNode<T>(obj);
if(current == last) { //if this is the last node
last = temp; //set last to temp
}
else {
//set temp.next to current.next then
//set previous of node after current to temp
temp.setNext(current.getNext());
current.getNext().setPrevious(temp);
}
temp.setPrevious(current);//set temp.previous to current
current.setNext(temp); //set current.next to temp
}
}
สงทเราตองท ากอนคอ คนหา node ทมขอมลตามทก าหนดไวในตวแปร key เราเรมตนดวยการก าหนดให current เปนตวเขาหา list ใหเรา โดยก าหนดให current ชไปทท first ชอยและใช while/loop เปนตวประมวลผล ถาเราเขาหา list จน current มคาเทากบ null แสดงวา node ทเราคนหาไมมอยใน list เรากไมน าขอมลใหมเขา แตถาม node ตามทก าหนดจรงเรากจะน าขอมลใหมเขาส list แตเราจะตองตรวจสอบกอนวา node ทมขอมลอยนเปน node สดทายหรอ node ทอยระหวาง node อน ๆ ในกรณทเปน node สดทายเรากก าหนดให last ชไปท node ใหมน (temp) DBLNode<T> temp = new DBLNode<T>(obj);
if(current == last) { //if this is the last node
last = temp; //set last to temp
}
บทท 2 Linked-List
55
แตถาเปน node ทอยระหวาง node เราตองท าการยาย pointer ใหอยในต าแหนงทเหมาะสมดงทแสดงไวในภาพท 2.14 และ code ทตามมา
ภาพท 2.14 การน า node เขาส list ระหวาง node 2 node
else {
//set temp.next to current.next then
//set previous of node after current to temp
temp.setNext(current.getNext());
current.getNext().setPrevious(temp);
}
temp.setPrevious(current); //set temp.previous to current
current.setNext(temp); //set current.next to temp
เมอเรารวา current ชอยท node ตามทก าหนดไว เรากก าหนดให next ของ temp ชไปท node ทอยถดจาก current หลงจากนนกก าหนดให previous ของ node ทอยหลง current ช
ไปท temp กระบวนการหลงจากนนกเหมอนกนกบทท ากอนหนานคอ ก าหนดให previous ของ temp ชไปท current และก าหนดให next ของ current ชไปท temp 2.7.6 การน าขอมลเขาแบบจดเรยง ในการน าขอมลเขาโดยมการจดเรยงนน เราตองคนหาต าแหนงทเหมาะสมของขอมล ซงหลงจากทหาเจอแลว เรากตองตรวจสอบดวาต าแหนงทหาเจอนนอย ณ ต าแหนงใด ใน 3 กรณน (ยกเวนกรณท list ไมมขอมลใด ๆ อยเลย – เราเรยกใช insertFirst() ไดทนท)
o ต าแหนงทตองใสอยดานหนาของ list
List หลงจาการ insert ระหวาง node 2 node
null
null
789
previous
next
325
previous
next
first last
List กอนทจะมการ insert ระหวาง node 2 node
3 4 2 1
1. temp.setNext(current.getNext());
2. current.getNext().setPrevious(temp); 3. temp.setPrevious(current()); 4. current.setNext(temp);
null
null
789
previous
next
325
previous
next first
last
888
previous
next
temp
current
Linked-List บทท 2
56
o ต าแนงทตองใสอยทายสดของ list o ต าแหนงทตองใสอยระหวาง node เราใชตวแปร cur เปนตวเดนเขาหา list เพอหาต าแหนงทเหมาะสมในการน าขอมลเขา โดยกอนอนเราจะก าหนดให cur = first หลงจากนนเดนเขาหา list ดวยการใช loop //search for proper position to insert
DBLNode<T> cur = first;
T x = item; //for comparison
while(cur != null) {
//if item to insert is greater than item at current
//position, move cur forward
if(x.compareTo(cur.getData()) > 0)
cur = cur.getNext();
//stop moving cur if item is less than or equal to
//item at current position which can be either
//before first node or between 2 nodes
else
break;
//if we exit the loop with cur equals to null
//item must be larger than others in list
}
เราจะยตการท างานและออกจาก loop ถาขอมลทตองการน าเขานอยกวาขอมล ณ ต าแหนงท cur ชอย เพราะฉะนนถาเราหลดออกจาก loop ในขณะท cur = first แสดงวาเราจะตองน าขอมลเขาส list ทางดานหนา (แตกอนทเราจะน าขอมลเขาเราตองสราง node ใหมดวยขอมลนนกอน ดวยประโยค DBLNode<T> temp = new DBLNode<T>(item);
หลงจากนนเรากท าการเชอม node ใหมนเขากบ list ของเราดวยการช first ไปท temp (node
ใหม) พรอมทงก าหนดให field next ของ first ชไปท cur หลงจากนนกก าหนดให field previous ของ cur ชไปท temp ดง code ทเหนน (ดภาพท 2.15 ประกอบ) //insert before first node
if(cur == first) {
first = temp;
first.setNext(cur);
cur.setPrevious(temp);
}
บทท 2 Linked-List
57
ภาพท 2.15 การน าขอมลเขาทางดานหนา
ในกรณท cur = null แสดงวาเราตองน าขอมลเขาทางทายสดของ list ซงท าไดงาย ๆ คอก าหนดให field next ของ last ชไปท temp พรอมกบก าหนดให field previous ของ temp ช
ไปท last หลงจากนนกก าหนดให last ชไปท temp ดงทแสดงไวในสวนของ code ทเหนน (ดภาพท 2.16 ประกอบ) //insert at last
else if(cur == null) {
last.setNext(temp);
temp.setPrevious(last);
last = temp;
}
temp
List กอนทจะมการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
null
null
444
previous
next
789
previous
next
first last
234
previous
next null
null
3 2
1 null
null
444
previous
next
789
previous
next
first
last
temp
234
previous
next
List หลงจากทมการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
cur
1. first = temp; 2. first.setNext(cur);
3. cur.setPrevious(temp);
Linked-List บทท 2
58
ภาพท 2.16 การน าขอมลเขาส List ทางดานหลง
กรณสดทายทเราตองน าขอมลเขากคอ กรณท cur ไมเทากบ first และไมเทากบ null ส าหรบการน าเขาขอมลทตองแทรกระหวาง node น เรารวา cur นนชอยท node หลง node ทตองการน าเขา (temp) เพราะฉะนนเรากก าหนดให field next ของ temp ชไปท cur และก าหนดให field next ของ node ทจะอยกอน temp ใหชไปท temp พรอมกบก าหนดให field previous ของ temp ชไปท node ทจะตองอยกอน temp หลงจากนนกก าหนดให field previous ของ
cur ชไปท temp ดงทแสดงไวใน code ทใหน (ดภาพท 2.17 ประกอบ) //insert between two nodes
else {
temp.setNext(cur);
cur.getPrevious().setNext(temp);
temp.setPrevious(cur.getPrevious());
cur.setPrevious(temp);
}
3
2
1
List หลงจากการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
temp
444
previous
next
789
previous
next
first
last
850
previous
next null
null
cur
null
1. last.setNext(temp); 2. temp.setPrevious(last); 3. last = temp;
List กอนทจะมการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
null
444
previous
next
789
previous
next
first last
temp
null
850
previous
next null
null
บทท 2 Linked-List
59
ภาพท 2.17 การน าขอมลเขาส list ระหวาง node
เราไดพดถงการน าขอมลเขาส list ในรปแบบของการจดเรยงเรยบรอยแลวทงสามกรณ ทงนก
ข นอยกบผอานวาจะเขยน code ในลกษณะใด สาเหตทพดเชนนกเพราะวา Doubly Linked-List ของเรานนม pointer อยสองตวคอ next และ previous ซงท าใหเราสามารถทจะเขาหา node ตาง ๆ ผานทาง pointer ทงสองตว ตวอยางเชน ในกรณของการน าขอมลเขาทางดานหนาของ list เราเรยกใชประโยค first = temp ตามดวย first.setNext(cur) ซงเราอาจเปลยนประโยคทสองใหเปน temp.setNext(cur) กได เพราะทง first และ temp มคาเทากน (ชไปทเดยวกน) ส าหรบ code ทงหมดของ insertInOrder() มดงน
1. temp.setNext(cur); 2. cur.getPrevious().setNext(temp);
3. temp.setPrevious(cur.getPrevious()); 4. cur.setPrevious(temp):
2 3 1 4
temp
List หลงจากทมการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
null
444
previous
next
789
previous
next
first last
537
previous
next
null
cur
List กอนทจะมการโยกยาย pointer ใหมการเชอมตอกบ node ใหมทตองการ insert (temp)
null
444
previous
next
789
previous
next
first last
temp
null
537
previous
next null
null
Linked-List บทท 2
60
public void insertInOrder(T item) {
//list is empty just call insertFirst()
if(empty())
insertFirst(item);
else {
//search for proper position to insert
DBLNode<T> cur = first;
T x = item; //for comparison
while(cur != null) {
//if item to insert is greater than item at current
//position, move cur forward
if(x.compareTo(cur.getData()) > 0)
cur = cur.getNext();
//stop moving cur if item is less than or equal to
//item at current position which can be either
//before first node or between 2 nodes
else
break;
//if we exit the loop with cur equals to null
//item must be larger than others in list
}
DBLNode<T> temp = new DBLNode<T>(item);
//insert before first node
if(cur == first) {
first = temp;
first.setNext(cur);
cur.setPrevious(temp);
}
//insert at last
else if(cur == null) {
last.setNext(temp);
temp.setPrevious(last);
last = temp;
}
//insert between two nodes
else {
temp.setNext(cur);
cur.getPrevious().setNext(temp);
temp.setPrevious(cur.getPrevious());
cur.setPrevious(temp);
}
count++; //one more node added
}
}
โปรแกรมตอไปนทดสอบการน าขอมลเขาดวยการเรยกใช insertInOrder() และ insertAfter()
1: /**
2: Testing DoublyLinkedList
3: */
4:
5: class TestDoublyLinkedList {
6: public static void main(String[] args) {
7: DoublyLinkedList<Integer> list = new DoublyLinkedList<Integer>();
8: int[] num = {23, 45, 10, 89, 75, 99, 94, 34, 54, 5};
9:
10: //insert data in num[] to list
11: for(int i = 0; i < 10; i++) {
12: list.insertInOrder(num[i]);
13: list.displayForward();
14: }
15: //insert 77 after 34
16: list.insertAfter(34, 77);
17: list.displayForward();
18: }
19: }
ผลลพธทไดคอ first->{23}<-last
first->{23, 45}<-last
first->{10, 23, 45}<-last
บทท 2 Linked-List
61
first->{10, 23, 45, 89}<-last
first->{10, 23, 45, 75, 89}<-last
first->{10, 23, 45, 75, 89, 99}<-last
first->{10, 23, 45, 75, 89, 94, 99}<-last
first->{10, 23, 34, 45, 75, 89, 94, 99}<-last
first->{10, 23, 34, 45, 54, 75, 89, 94, 99}<-last
first->{5, 10, 23, 34, 45, 54, 75, 89, 94, 99}<-last
first->{5, 10, 23, 34, 77, 45, 54, 75, 89, 94, 99}<-last
method อกสองตวทเรายงไมไดพดถงกคอ displayForward() และ displayBackward() ซงเปน method ส าหรบการแสดงขอมลทอยใน Linked-List ทงจากดานหนาไปยงดานหลง และจากดานหลงไปดานหนา code ของ method ทงสองกเขยนไมยาก ดงทจะแสดงใหดตอไปน
public void displayForward() {
if(empty())
System.out.println("Sorry - list is empty!");
else {
DBLNode<T> current = first;
System.out.print("first->{" + current.getData());
current = current.getNext();
while(current != null) {
System.out.print(", " + current.getData());
current = current.getNext();
}
System.out.println("}<-last");
}
}
public void displayBackward() {
if(empty())
System.out.println("Sorry - list is empty!");
else {
DBLNode<T> current = last;
System.out.print("last->{" + current.getData());
current = current.getPrevious();
while(current != null) {
System.out.print(", " + current.getData());
current = current.getPrevious();
}
System.out.println("}<-first");
}
}
ผอานควรทดสอบการท างานของ Doubly linked-List เพอท าความเขาใจกบสวนประกอบตาง ๆ ใหมากยงข น Code ทงหมดของ DoublyLinkedList.java
1: /**
2: Basic structure for Doubly LinkedList
3: */
4:
5: public class DoublyLinkedList<T extends Comparable<? super T>> {
6: private DBLNode<T> first; //points to first node
7: private DBLNode<T> last; //points to last node
8: private int count; //items in list
9:
10: //default constructor
11: public DoublyLinkedList() {
12: first = last = null;
13: count = 0;
14: }
15:
16: //empty when first == null or last == null
17: //either case is ok
18: public boolean empty() {
19: return first == null;
20: }
21:
22: //number of nodes in list
23: public int size() {
Linked-List บทท 2
62
24: return count;
25: }
26:
27: //insert at front of list
28: public void insertFirst(T item) {
29: DBLNode<T> temp = new DBLNode<T>(item);
30: if(empty()) //empty
31: last = temp; //last points at temp
32: else
33: first.setPrevious(temp); //set previous to temp
34: temp.setNext(first); //set temp.next to first
35: first = temp; //set first to temp
36: count++; //one more node added
37: }
38:
39: //insert at where last is
40: public void insertLast(T item) {
41: DBLNode<T> temp = new DBLNode<T>(item);
42: if(empty()) //empty
43: first = temp; //set first to temp
44: else {
45: last.setNext(temp); //set last.next to temp
46: temp.setPrevious(last); //set temp.previous to last
47: }
48: last = temp; //set last to temp
49: count++; //one more node added
50: }
51:
52: //insert after given key value
53: public void insertAfter(T key, T obj) {
54: boolean noItem = false;
55: //serching for key if found -> noItem = false
56: //if not found -> noItem = true
57: DBLNode<T> current = first;
58: while(current != null) {
59: if(current.getData().equals(key))
60: break;
61: current = current.getNext();
62: }
63: noItem = (current == null) ? true : false;
64:
65: if(noItem)
66: System.out.println("Sorry - given key doesn't exist!");
67: else {
68: DBLNode<T> temp = new DBLNode<T>(obj);//get new node
69: if(current == last) { //if this is the last node
70: last = temp; //set last to temp
71: }
72: else {
73: //set temp.next to current.next then
74: //set previous of node after current to temp
75: temp.setNext(current.getNext());
76: current.getNext().setPrevious(temp);
77: }
78: temp.setPrevious(current); //set temp.previous to current
79: current.setNext(temp); //set current.next to temp
80: }
81: }
82:
83: //inorder insertion (ascending)
84: public void insertInOrder(T item) {
85: //list is empty just call insertFirst()
86: if(empty())
87: insertFirst(item);
88: else {
89: //search for proper position to insert
90: DBLNode<T> cur = first;
91: T x = item; //for comparison
92: while(cur != null) {
93: //if item to insert is greater than item at current
94: //position, move cur forward
95: if(x.compareTo(cur.getData()) > 0)
96: cur = cur.getNext();
97: //stop moving cur if item is less than or equal to
บทท 2 Linked-List
63
98: //item at current position which can be either
99: //before first node or between 2 nodes
100: else
101: break;
102: //if we exit the loop with cur equals to null
103: //item must be larger than others in list
104: }
105: DBLNode<T> temp = new DBLNode<T>(item);
106: //insert before first node
107: if(cur == first) {
108: first = temp;
109: first.setNext(cur);
110: cur.setPrevious(temp);
111: }
112: //insert at last
113: else if(cur == null) {
114: last.setNext(temp);
115: temp.setPrevious(last);
116: last = temp;
117: }
118: //insert between two nodes
119: else {
120: temp.setNext(cur);
121: cur.getPrevious().setNext(temp);
122: temp.setPrevious(cur.getPrevious());
123: cur.setPrevious(temp);
124: }
125: count++; //one more node added
126: }
127: }
128:
129: //remove from front
130: public T removeFirst() {
131: DBLNode<T> temp = first; //set temp to first
132: if(first.getNext() == null) //basically empty
133: last = null;
134: else
135: //set previous of node after first to null
136: first.getNext().setPrevious(null);
137: first = first.getNext(); //set first to that node
138: count--;
139: return temp.getData();
140: }
141:
142: //remove at last
143: public T removeLast() {
144: DBLNode<T> temp = last; //set temp to last
145: if(first.getNext() == null) //empty
146: first = null;
147: else
148: //set next of node before last to null
149: last.getPrevious().setNext(null);
150: last = last.getPrevious(); //set last to that node
151: count--;
152: return temp.getData();
153: }
154:
155:
156: public void displayForward() {
157: if(empty())
158: System.out.println("Sorry - list is empty!");
159: else {
160: DBLNode<T> current = first;
161: System.out.print("first->{" + current.getData());
162: current = current.getNext();
163: while(current != null) {
164: System.out.print(", " + current.getData());
165: current = current.getNext();
166: }
167: System.out.println("}<-last");
168: }
169: }
170:
171: public void displayBackward() {
Linked-List บทท 2
64
172: if(empty())
173: System.out.println("Sorry - list is empty!");
174: else {
175: DBLNode current = last;
176: System.out.print("last->{" + current.getData());
177: current = current.getPrevious();
178: while(current != null) {
179: System.out.print(", " + current.getData());
180: current = current.getPrevious();
181: }
182: System.out.println("}<-first");
183: }
184: }
185:
186: //search for given object
187: public boolean search(T key) {
188: DBLNode<T> temp = null;
189: if(empty()){
190: System.err.println("ERROR! List is empty.");
191: System.exit(1);
192: }
193: else {
194: temp = first;
195: while(temp != null) {
196: if(temp.getData().equals(key))
197: break;
198: temp = temp.getNext();
199: }
200: }
201: return (temp == null) ? false : true;
202: }
203: }
2.8 การใช class LinkedList ท Java มให
ในการท างานบางครงเราอาจตองใชโครงสรางทไดถกออกแบบไวแลวจากผเขยนภาษาทางดานการโปรแกรม เนองจากสาเหตอะไรกตามแต (เชน ขเกยจเขยนใหม เปนตน) เชนการจดเกบขอมลดวย Linked-List ดงเชนทเราไดท ามาแลวกอนหนาน Java ม class LinkedList ทเราสามารถเรยกใชได ดงตวอยางการใชทเหนน เราจะเรยกใช method บางตวดงน add(Object o) น า Object o เขาส LinkedList ทางดานหลง
addFirst(Object o) น า Object o เขาส LinkedList ทางดานหนา addLast(Object o) น า Object o เขาส LinkedList ทางดานหลง removeFirst() ดง Object o ออกจาก LinkedList ทางดานหนา removeLast() ดง Object o ออกจาก LinkedList ทางดานหลง
remove(index i) ดง Object ออกจาก LinkedList ท index i set(index i, Object O) ก าหนดให Object ท index i มคาเปน Object O
โปรแกรมตวอยางทเหนท าการสราง myList จาก LinkedList โดยก าหนดใหมขอมลแบบ random อย 10 ตว ดวยการใช add() หลงจากนนกเพมขอมลอกสองตวดวย addFirst() และ addLast() และเมอท าการแสดงผลเสรจแลวเรากลบขอมลออกสามตวดวย removeFirst(), removeLast(), และ remove()
1: /**
2: Testing LinkedList class from Java
3: */
4:
5: import java.util.LinkedList;
6:
7: public class TestJavaLinkedList {
8: public static void main(String[] args) {
9: //create an empty LinkedList
10: LinkedList<Integer> myList = new LinkedList<Integer>();
11: //populate LinkedList with random numbers
12: for(int i = 0; i < 10; i++) {
13: int num = (int)(Math.random() * 100 + 1);
14: myList.add(num);
15: }
16: System.out.println("Content: ");
17: print(myList);
บทท 2 Linked-List
65
18:
19: //add 55 to front of LinkedList
20: myList.addFirst(55);
21: //add 99 to end of LinkedList
22: myList.addLast(new Integer(99));
23: System.out.println("Content after added 55 and 99: ");
24: print(myList);
25:
26: //remove first item in LinkedList
27: myList.removeFirst();
28: //remove last item in LinkedList
29: myList.removeLast();
30: System.out.println("Content after removed first and last: ");
31: print(myList);
32:
33: //remove second item from last
34: myList.remove(myList.size() - 2);
35: System.out.println("Content after removed second item from last: ");
36: print(myList);
37:
38: //set first item to 111
39: myList.set(0, new Integer(111));
40: System.out.println("Content after replaced first item to 111: ");
41: print(myList);
42: }
43:
44: //print items in list
45: public static void print(LinkedList<Integer> list) {
46: if(list.size() == 0) {
47: System.err.println("List is empty!");
48: return;
49: }
50: Integer item = list.get(0);
51: System.out.print("(" + item);
52: for(int i = 1; i < list.size(); i++) {
53: item = list.get(i);
54: System.out.print(", " + item);
55: }
56: System.out.println(")");
57: }
58: }
ผลลพธทไดจากการ run โปรแกรม LinkedListTest.java คอ Content:
(76, 79, 19, 89, 39, 10, 75, 17, 76, 59)
Content after added 55 and 99:
(55, 76, 79, 19, 89, 39, 10, 75, 17, 76, 59, 99)
Content after removed first and last:
(76, 79, 19, 89, 39, 10, 75, 17, 76, 59)
Content after removed second item from last:
(76, 79, 19, 89, 39, 10, 75, 17, 59)
Content after replaced first item to 111:
(111, 79, 19, 89, 39, 10, 75, 17, 59)
ยงม method อกหลายตวใน class LinkedList ทมากบ Java ทงนจะทงไวใหผอานไดท าการตรวจสอบการใช method เหลานนดวยตวเอง
Linked-List บทท 2
66
สรป เราไดพดถงโครงสรางหลกของ node ทใชในการสราง linked-list ตงแต list ทางเดยว list ทม pointer คอยจ าต าแหนงของ node สดทาย รวมไปถงการออกแบบและใชงาน Doubly Linked-List เราไดพดถงการน าขอมลเขา การดงขอมลออก การแสดงขอมลทมอยใน list และตวอยางการใชงานในรปแบบตาง ๆ โดยสรปแลวเราไดพดถง การออกแบบ node ของ Linked-List ทางเดยว การออกแบบโครงสรางของ Linked-List ทางเดยว การน าขอมลเขา (insert) และการน าขอมลออก (remove) การออกแบบและใชงาน Double-Ended Linked-List ทม pointer 2 ตวทช ไปยง node
แรก และ node สดทาย
การออกแบบ node ของ Doubly Linked-List การออกแบบโครงสรางของ Doubly Linked-List การน าขอมลเขา และออกจาก Doubly Linked-List ตวอยางการใชงานของ Linked-List ในรปแบบตาง ๆ ตวอยางการใช method ตาง ๆ บางตวจาก class LinkedList ท Java มให แบบฝกหด
1. จงเขยนโปรแกรมทท าหนาทสลบขอมล (node) ทกตวทมอยใน linked-list จากดานหนา
ไปดานหลง 2. จงเขยน method ทสงคา true ใหผเรยกถาขอมลทอยใน linked-list ไดถกจดเรยงไวจาก
นอยไปหามาก และสงคา false ถาไมไดถกจดเรยงไว 3. จงเขยน method ทน าขอมลเขาส linked-list (list ทางเดยว) แบบม order (จากนอยไปหา
มาก) ใหเขยนโปรแกรมทดสอบ 4. จาก concept เดยวกนกบ circular array จงเขยนโปรแกรมทใช circular list ในการจดเกบ
ขอมล 5. จากขอ 4 จงเขยน method ท remove node ออกจาก circular list 6. จงเขยน method ทหาจ านวนของ node ทมอยใน circular list (ทสรางข นในขอ 4) 7. จากขอ 6 ใหเขยน method ทท าการคนหา node ทมขอมลนอยทสด 8. จงเขยน method ทคนหาขอมล ใน list ทางเดยวทมการจดเกบแบบเรยงล าดบจากนอยไป
หามากดวย binary search 9. จงเขยน method ทหา node จากต าแหนงของ node ทก าหนดให ใน circular list เชน ถา
ก าหนดใหต าแหนงของ node เปน 8 method นจะสงคาของ node ณ ต าแหนงท 9 (ต าแหนงถดไป - วนกลบ) ไปใหผเรยก ใหเขยนโปรแกรมทดสอบ
10. จงเขยน method ท insert node ระหวาง node ทก าหนดให 2 node ของ ordered
circular list 11. จงเขยน method ท display ขอมลของ circular list จากดานหลงไปดานหนา 12. จงเขยน method ท display ขอมลของ circular list เฉพาะ node ทอยในต าแหนงค
เทานน 13. จงเขยนโปรแกรมทใชโครงสรางของ Doubly Linked-List ในการบวกเลขทมจ านวนหลก
มาก ๆ เชน 20 – 30 หลกโดยใหแตละ node ของ Doubly Linked-List นนเกบตวเลขเพยงหนงตว (digit เดยว)
บทท 2 Linked-List
67
14. จากขอก าหนดในขอ 13 จงเขยน method ทท าหนาทลบเลข 2 ตว 15. จาก Linked-List ทางเดยวทเราเขยนขน จงเพม method ส าหรบการดงขอมลออกจากทาง
ดานหนาของ list และ method ทดงขอมลออกจากทางดานหลง พรอมทงเขยนโปรแกรมทดสอบ
16. Java ม method toArray() ใน class LinkedList จงเขยน method ทท าหนาทในลกษณะ
เดยวกนใหกบ class LinkedList ทเราไดเขยนขนในบทน ใหเขยนโปรแกรมทดสอบ method ดงกลาว
17. จงเขยน method ทท าการยายขอมลทมคามากทสดใน list ไปยง node สดทายของ list
18. จงออกแบบโครงสรางของ Linked-List ทม node แรกสดไวเกบจ านวนของ node ทงหมดทมอยใน Linked-List สวนการน าขอมลเขาใหเรมจาก node ถดไป ถาไมมขอมลอยเลย คาของ node จะเปนศนย ใหเขยนโปรแกรมทดสอบ
19. จงเขยน method ทลบ node หลงจาก node ทก าหนดให เชน ถา node ทก าหนดใหมคา
เทากบ 13 node ทจะถกลบคอ node ทอยถดไปจาก node 13 และหากวา node 13 เปน node สดทายใน list กไมตองท าอะไร
20. จากขอ 19 ถา node นนเปน node สดทาย ใหท าการลบออกทนท 21. จงออกแบบ Doubly-Linked-List ทเปน Circular List โดยก าหนดให field next ของ
node สดทายชไปท node แรกและ field previous ของ node แรกชไปท node สดทาย ดงภาพ
first
Linked-List บทท 2
68