รายการ(List) [3] ผู้สอน อาจารย์ ยืนยง กันทะเนตร สาขาวิชาเทคโนโลยีคอมพิวเตอร์เคลื่อนที่ คณะเทคโนโลยีสารสนเทศและการสื่อสาร Website : ict.up.ac.th/yeunyong
หัวข้อวันนี้ Insert & Delete หลักในการเขียนฟังก์ชัน insertFirst() deleteFirst() insertLast() deleteLast()
หลักในการเขียนฟังก์ชัน Insert & Delete แยกออกเป็นกรณีต่างๆ ให้ได้ เงื่อนไขของแต่ละกรณี วาดรูปแสดงการทำงานของแต่ละกรณี คำสั่งภาษาโปรแกรม รวมแต่ละกรณีเข้าด้วยกัน การเช็คเงื่อนไขอาจเปลี่ยนแปลงเล็กน้อย บางกรณีอาจรวมกัน(บางส่วน)ได้ เพื่อให้โปรแกรมกระชับ หรือ ทำงานได้ไว้ขึ้น
void insertFirst (node **ptr_head, int input); parameter ชื่อของตัวแปรลิสต์ที่จะใส่ข้อมูล ข้อมูลที่จะใส่ return value ไม่มี body of function มีกี่กรณี ? แต่ละกรณีจะแทรกอย่างไร ? ++++++++++++++1+++++++++++++ void insertFirst ( node **head , int input ) { node *NewNode ; NewNode = new node ; Newnode->data = input ; if ( *head == NULL ) { // กรณียังไม่มีข้อมูล NewNode->next = NUILL ; *head = NewNode ; } else { NewNode->next = *head ; void insertFirst ( node **head , int input ) { ... เผื่อไว้สำหรับคำสั่งอื่นๆ if ( *head == NULL ) ... กรณียังไม่มีข้อมูล else ... กรณีมีข้อมูลอยู่แล้ว ... เผื่อไว้สำหรับคำสั่งอื่นๆ ดูที่กรณีที่ 1. ยังไม่มีข้อมูล วาดรูปให้ดู สรุปเป็นคำสั่งคือ NewNode->next = NUILL ; *head = NewNode ; ดูที่กรณีที่ 2. มีข้อมูลอยู่แล้ว NewNode->next = *head ; เขียนในทั้ง 2 กรณีในโครงของฟังก์ชันให้นิสิตดู ดัง กล่องข้อความหมายเลข 1 แต่สรุปให้ดูว่ามันสามารถรวมกันได้ เพราะถ้าไม่มีข้อมูล *head == NULL ดัง กล่องข้อความหมายเลข 2 ++++++++++++++2+++++++++++++ void insertFirst ( node **head , int input ) { node *NewNode ; NewNode = new node ; Newnode->data = input ; NewNode->next = *head ; *head = NewNode ; }
insertFirst : Singly-linked list void insertFirst ( node **ptr_head , int input ) { node *n ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = NULL ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 2 ตัว n->next = *ptr_head ; // next ของโหนดใหม่ *ptr_head = n ; // head *** คำสั่งใน if และ else สามารถยุบรวมกันได้
insertFirst : Singly-circularly-linked list void insertFirst ( node **ptr_head , int input ) { node *n , *t ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = n ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 3 ตัว t = *ptr_head ; // ใช้ t ท่องไปยังโหนดสุดท้าย while ( t->next != *ptr_head ) t = t->next ; t->next = n ; // next ของโหนดสุดท้าย n->next = *ptr_head ; // next ของโหนดใหม่ *ptr_head = n ; // head
insertFirst : Doubly-linked list void insertFirst ( node **ptr_head , int input ) { node *n ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = NULL ; n->back = NULL ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 4 ตัว n->next = *ptr_head ; // next ของโหนดใหม่ n->back = NULL ; // back ของโหนดใหม่ *ptr_head->back = n ; // back ของโหนดแรก *ptr_head = n ; // head
insertFirst : Doubly-circularly-linked list void insertFirst ( node **ptr_head , int input ) { node *n , *t ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = n ; n->back = n ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 5 ตัว n->next = *ptr_head ; // next ของโหนดใหม่ n->back = *ptr_head->back ; // back ของโหนดใหม่ *ptr_head->back->next = n ; // next ของโหนดสุดท้าย *ptr_head->back = n ; // back ของโหนดแรก *ptr_head = n ; // head
void deleteFirst ( node **ptr_head ) ; parameter ชื่อของตัวแปรลิสต์ที่จะดึงข้อมูลออก return value ไม่มี body of function มีกี่กรณี ? แต่ละกรณีจะลบอย่างไร ? มีความคล้ายคลึงกันกับ insertFirst() ? void deleteFirst ( node **head) { *head = *head->next; } แต่ให้นิสิตตระหนักถึงการคืนหน่วยความจำ (โหนดที่พึ่งลบไป) ด้วยคำสั่ง delete เป็นคำสั่งในภาษา C++ ที่นำมาประยุกต์ใช้ และ ถ้าจะ delete ก็ต้องมีตัวแปรพอยน์เตอร์มาชี้ที่โหนดที่ลบไปด้วย ดังนั้น ต้องสร้างตัวแปรมาชี้ -> ลบ -> delete ได้เป็นฟังก์ชัน คือ void deleteFirst ( node **head) { node *DelNode ; DelNode = *head ; *head = *head->next ; delete DelNode ; } ไม่ต้องใช้คำสั่ง new สำหรับตัวแปร DelNode เพราะ ไม่ได้จองตำแหน่งหน่วยความจำใหม่ แต่ให้มันไปชี้ที่โหนดที่มีข้อมูลอยู่แล้ว
deleteFirst : Singly-linked list void deleteFirst ( node **ptr_head ) { node *d ; d = *ptr_head ; if ( *ptr_head->next == NULL ) { // กรณีเหลือข้อมูลตัวเดียว *ptr_head = NULL ; } else { // ต้องเปลี่ยน pointer 1 ตัว *ptr_head = *ptr_head->next ; // head delete d ; *** คำสั่งใน if และ else สามารถยุบรวมกันได้
deleteFirst : Singly-circularly-linked list void deleteFirst ( node **ptr_head ) { node *d , *t ; d = *ptr_head ; if ( *ptr_head->next == *ptr_head ) { // กรณีเหลือข้อมูลตัวเดียว *ptr_head = NULL ; } else { // ต้องเปลี่ยน pointer 2 ตัว t = *ptr_head ; // ใช้ t ท่องไปยังโหนดสุดท้าย while ( t->next != *ptr_head ) t = t->next ; t->next = *ptr_head->next ; // next ของโหนดสุดท้าย *ptr_head = *ptr_head->next ; // head delete d ;
deleteFirst : Doubly-linked list void deleteFirst ( node **ptr_head ) { node *d ; d = *ptr_head ; if ( *ptr_head->next == NULL ) { // กรณีเหลือข้อมูลตัวเดียว *ptr_head = NULL ; } else { // ต้องเปลี่ยน pointer 2 ตัว *ptr_head->next->back = NULL ; // back ของโหนดที่ 2 *ptr_head = *ptr_head->next ; // head delete d ;
deleteFirst : Doubly-circularly-linked list void deleteFirst ( node **ptr_head ) { node *d ; d = *ptr_head ; if ( *ptr_head->next == NULL ) { // กรณีเหลือข้อมูลตัวเดียว *ptr_head = NULL ; } else { // ต้องเปลี่ยน pointer 3 ตัว // next ของโหนดสุดท้าย *ptr_head->back->next = *ptr_head->next ; // back ของโหนดที่ 2 *ptr_head->next->back = *ptr_head->back ; // head *ptr_head = *ptr_head->next ; delete d ;
void insertLast (node **ptr_head, int input); parameter ชื่อของตัวแปรลิสต์ที่จะใส่ข้อมูล ข้อมูลที่จะใส่ return value ไม่มี body of function มีกี่กรณี ? แต่ละกรณีจะแทรกอย่างไร ? void insertLast ( node **head , int input ) { node *NewNode , *traversal ; NewNode = new node ; Newnode->data = input ; if ( *head == NULL ) { // กรณียังไม่มีข้อมูล NewNode->next = NUILL ; *head = NewNode ; } else { traversal = *head ; while (traversal->next != NULL) traversal = traversal->next ; NewNode->next = NULL ; traversal->next = NewNode ; void insertLast ( node **head , int input ) { ... เผื่อไว้สำหรับคำสั่งอื่นๆ if ( *head == NULL ) ... กรณียังไม่มีข้อมูล else ... กรณีมีข้อมูลอยู่แล้ว ... เผื่อไว้สำหรับคำสั่งอื่นๆ ดูที่กรณีที่ 1. ยังไม่มีข้อมูล วาดรูปให้ดู สรุปเป็นคำสั่งคือ NewNode->next = NUILL ; *head = NewNode ; ดูที่กรณีที่ 2. มีข้อมูลอยู่แล้ว ตรงนี้คือจุดแตกต่าง -> ต่างตรงต้อง “ท่อง” ไปยังโหนดสุดท้ายเพื่อจะ inseet วาดรูปแสดงวิธีทำให้ดู สรุปเป็น 2 ขั้นตอน -> 1. ท่องไปยังโหนดสุดท้าย , 2. เอาโหนดใหม่ต่อ ทบทวนนิสิตถึงขั้นตอนที่ 1. ได้คำสั่ง -> เอาใส่ในโครงฟังก์ชันเลย วาดรูปขั้นตอนที่ 2 ได้คำสั่ง -> เอาใส่ในโครงฟังก์ชันต่อเลย
insertLast : Singly-linked list void insertLast ( node **ptr_head , int input ) { node *n , *t ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = NULL ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 2 ตัว t = *ptr_head ; // ใช้ t ท่องไปยังโหนดสุดท้าย while ( t->next != NULL ) t = t->next ; n->next = NULL ; // next ของโหนดใหม่ t->next = n ; // next ของโหนดสุดท้าย
insertLast : Singly-circularly-linked list void insertLast ( node **ptr_head , int input ) { node *n , *t ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = n ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 2 ตัว t = *ptr_head ; // ใช้ t ท่องไปยังโหนดสุดท้าย while ( t->next != *ptr_head ) t = t->next ; n->next = *ptr_head ; // next ของโหนดใหม่ t->next = n ; // next ของโหนดสุดท้าย
insertLast : Doubly-linked list void insertLast ( node **ptr_head , int input ) { node *n , *t ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = NULL ; n->back = NULL ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 3 ตัว t = *ptr_head ; // ใช้ t ท่องไปยังโหนดสุดท้าย while ( t->next != NULL ) t = t->next ; n->next = NULL ; // next ของโหนดใหม่ n->back = t ; // back ของโหนดใหม่ t->next = n ; // next ของโหนดสุดท้าย
insertLast : Doubly-circularly-linked list void insertLast ( node **ptr_head , int input ) { node *n , *t ; n = new node ; n->data = input ; if ( *ptr_head == NULL ) { // กรณียังไม่มีข้อมูล n->next = n ; n->back = n ; *ptr_head = n ; } else { // ต้องเปลี่ยน pointer 4 ตัว และไม่ต้องท่อง !!! n->next = *ptr_head ; // next ของโหนดใหม่ n->back = *ptr_head->back ; // back ของโหนดใหม่ *ptr_head->back->next = n ; // next ของโหนดสุดท้าย *ptr_head->back = n ; // back ของโหนดแรก
void deleteLast ( node **ptr_head ) ; parameter ชื่อของตัวแปรลิสต์ที่จะดึงข้อมูลออก return value ไม่มี body of function มีกี่กรณี ? แต่ละกรณีจะลบอย่างไร ? ดูที่กรณีที่ 2. มีมากกว่า 1 โหนด ต้อง “ท่อง” ไปยังโหนดสุดท้ายเพื่อจะ delete (เหมือน insertLast) วาดรูปแสดงวิธีทำให้ดู สรุปเป็น 2 ขั้นตอน -> 1. ท่องไปยังโหนดสุดท้าย , 2. ลบโหนดนั้นออก แต่ ปัญหาที่เกิดขึ้นคือ ถ้าใช้ pointer ท่องตัวเดียว ลบไม่ได้ เพราะไม่มีตัวชี้กลับ ต้อง ใช้ pointer 2 ตัว ท่องตามกัน วาดรูปให้ดู สรุปเป็นคำสั่ง node *p,*t; // t ใช้ delnode แทนได้เพราะยังไงก็ต้องลบทิ้งอยู่แล้ว t = *h; while (t->next != NULL) { p = t; t = t->next; } void deleteLast ( node **head) { ... เผื่อไว้สำหรับคำสั่งอื่นๆ if ( *head->next == NULL ) ... กรณีมีโหนดเดียว else ... กรณีมีหลายโหนด ... เผื่อไว้สำหรับคำสั่งอื่นๆ void deleteLast ( node **head) { node *delnode, *p ; if ( *head->next == NULL ) { delnode = *head ; *head = NULL ; } else{ while (delnode->next != NULL){ p = delnode ; delnode = delnode->next ; p->next = NULL delete delnode ; ดูที่กรณีที่ 1. มี 1 โหนด เมื่อลบก็ต้องไม่เหลือโหนดใดๆ วาดรูปให้ดู สรุปเป็นคำสั่งคือ *head = NULL ; แต่ทิ้งไว้ด้วยว่าอย่าลืมการเอาตัวแปรมาคืนหน่วยความจำก่อนลบ ได้เป็น void deleteLast ( node **head) { node *delnode ; if ( *head->next == NULL ) { delnode = *head ; *head = NULL ; } else ... กรณีมีหลายโหนด delete delnode ;
deleteLast : Singly-linked list void deleteLast ( node **ptr_head) { node *d, *p ; if ( *ptr_head->next == NULL ) { // กรณีเหลือข้อมูลตัวเดียว d = *ptr_head ; * ptr_head = NULL ; } else { while (d->next != NULL){ p = d ; // p ท่องตาม delnode(t) d = d->next ; p->next = NULL ; // ตัดโหนดสุดท้ายทิ้ง delete d ;
deleteLast : Singly-circularly-linked list void deleteLast ( node **ptr_head) { node *d, *p ; if ( *ptr_head->next == *ptr_head ) {// เหลือข้อมูลตัวเดียว d = *ptr_head ; *ptr_head = NULL ; } else { // ต้องเปลี่ยน pointer แค่ 1 ตัว !!! while (d->next != *ptr_head ){ p = d ; d = de->next ; p->next = *ptr_head ; delete d ;
deleteLast : Doubly-linked list void deleteLast ( node **ptr_head) { node *d ; // มี back ชี้กลับโหนดก่อนหน้าแล้ว ตัด p ทิ้งได้ if ( *ptr_head->next == NULL ) { // กรณีเหลือข้อมูลตัวเดียว d = *ptr_head ; *ptr_head = NULL ; } else { // ก็ยังเปลี่ยน pointer แค่ 1 ตัว !!! while (d->next != NULL){ d = d->next ; d->back->next = NULL ; // next ของโหนดก่อนหน้า delete d ;
deleteLast : Doubly-circularly-linked list void deleteLast ( node **ptr_head) { node *d ; if ( *ptr_head->next == *ptr_head ) { // เหลือข้อมูลตัวเดียว d = *ptr_head ; *ptr_head = NULL ; } else { // เปลี่ยน pointer 2 ตัว !!! และไม่ต้องท่อง !!! d = *ptr_head->back ; d->back->next = *ptr_head ; // next ของโหนดก่อนหน้า *ptr_head->back = d->back ; delete d ;