วันอังคารที่ 8 มีนาคม พ.ศ. 2559

บทที่ 3 ลิงค์ลิสต์ (Linked List)

บทที่ 3 ลิงค์ลิสต์ (Linked List)
     ลิงค์ลิสต์เป็นการจัดเก็บชุดข้อมูลเชื่อมโยงต่อเนื่องกันไปตามลำดับ ซึ่งอาจอยู่ในลักษณะแบบเชิงเส้น (Linear) หรือ ไม่เป็นเส้นตรง (nonlinear) ก็ได้
ลักษณะของลิงค์ลิสต์    1. เป็นการนำข้อมูลของแต่ละโหนดมาจัดเรียงกัน มีการเชื่อมโยงกัน
    2.ในแต่ละโหนดมี 2ฟิลด์ คือส่วนของข้อมูลและแอดเดรสของโหนดถัดไป
 
    3. ฟิลด์แอดเดรสของแต่ละโหนด เป็นตัวกำหนดลำดับ
   4. มีพอยเตอร์ชี้ไปยังโหนดแรกของลิงค์ลิสต์เสมอโหนดแรกนิยมเรียก Top,  First และ Head

    5. ฟิลด์แอดเดรสของโหนดสุดท้ายจะต่องเป็น null
    6. ลิงค์ลิตส์ที่ไม่มีโหนดอยู่ภายในเรียกว่าลิสต์ว่าง
      Dynamic  Linked  List  เป็นการใช้เนื้อที่หน่วยความจำ แบบไม่ต้องจองเนื้อที่
      Static  Linked  List  เป็นการใช้เนื้อที่หน่วยความจำ แบบอาร์เรย์แทนที่ ลิงค์ลิสต์ในหน่วยความจำ
ประเภทของลิงค์ลิสต์     ลิงค์ลิสต์เดี่ยว (Singly  Linked  List)
         -  ประกอบด้วยฟิลด์แอดเดรส 1 ฟิลด์ ชี้ไปยังโหนดถัดไป
         -  การดำเนินการเริ่มจากโหนดแรกไปโหนดสุดท้ายย้อนกลับไม่ได้
     ลิงค์ลิสต์คู่ (Doubly  Linked  List)
         - แต่ละโหนดมีฟิลด์แอดเดรส 2 ฟิลด์ ชี้ไปยังโหนดถัดไปและก่อนหน้า
         -  การดำเนินการสามารถดำเนินการไปข้างหน้าและย้อนกลับได้
การดำเนินการกับลิงค์ลิสต์    การเพิ่มโหนดให้กับลิงค์ลิสต์
    1.การเพิ่มข้อมูลที่จุดเริ่มต้นของโครงสร้างโหนดที่เพิ่มจะไปอยู่ที่ตำแหน่งแรกในลิสต์
  2. การเพิ่มข้อมูลต่อจากโหนดที่กำหนดโหนดที่เพิ่มสามารถแทรกอยู่ตรงส่วน ใดก็ได้ในลิสต์ตามที่กำหนด
    3. การเพิ่มข้อมูลที่จุดสุดท้ายของโครงสร้าง โหนดที่เพิ่มจะไปอยู่ในตำแหน่งสุดท้ายของลิสต์
    การค้นหาโหนดที่ต้องการในลิสต์
   จะใช้คำสั่ง searchnode เช่น  searchnode(chlist,B); หมายถึง ค้นหาข้อมูล B ในลิสต์ ชื่อ chlist
    การแทรกโหนดในลิสต์
    จะใช้คำสั่ง insafter เช่น insafter(num,20,25); หมายถึง เพิ่มข้อมูล 25 เข้าไปหลังโหนดที่เก็บข้อมูล 20
    การลบโหนดออกจากลิสต์
    จะใช้คำสั่ง delnode เช่น delnode(num,50); หมายถึง ลบข้อมูล 50 ออกจากลิสต์ ชื่อ num
    การท่องลิสต์
    จะใช้คำสั่ง displaynode เช่น displaynode(numlist); หมายถึง แสดงข้อมูลในลิสต์ ชื่อ numlist จะแสดงผลลัพธ์ได้หลังใช้คำสั่ง displaynode(ChL);
ลิสต์เชื่อมโยงเป็นวงกลม (Circular  List)    เป็นลิสต์ที่ไม่มีโหนดแรก และโหนดสุดท้าย
     -  สามารถเข้าถึงโหนดจากจุดใดก็ได้ในลิสต์ 
     -  ต้องมีพอยเตอร์ ชี้ที่โหนดสุดท้ายในลิสต์เพื่อบ่งบอกถึงจุดสิ้นสุด
    - การดำเนินการใด ๆ ใช้คำสั่งเดียวกับลิสต์ทางเดียว แต่ขั้นตอนในการทำงานจะต่างกัน เช่น การลบโหนดใด ๆ ออกจากลิสต์ จะต้องให้พอยเตอร์ ก่อนหน้าตัวที่จะลบ ชี้ไปโหนดที่อยู่หลังตัวที่จะลบ
ตัวอย่างโปรแกรม
การเพิ่ม Node

          #include <stdio.h>
          #include <conio.h>
          struct node
          {    
          int data;
          node *next;
          };
          node *first;
          void addnode (int i)
          {
           node *n;
           n=new node;
           n->data=i;
           if (first->next==NULL) n->next=NULL;
           else
           n->next=first->next;
           first->next=n;
           }
           void main()
           {
           clrscr();
           first=new node;
           first->data=0;
           first->next=NULL;
           addnode(5);
           addnode(9);
           addnode(4);
           addnode(7);
           addnode(3);
           node *p;
           for (p=first->next;p!=NULL;p=p->next)
                {
                      printf("data=%d\n",p->data);
                }
           getch();
           }
ผลรัน          data=3
          data=7
          data=4
          data=9
          data=5
อธิบายโปรแกรม   ลักษณะการทำงานของโปรแกรม สร้าง struct เพื่อใช้เก็บข้อมูลในโหนด และพอยเตอร์ไว้ในชุดเดียวกัน สร้างฟังก์ชั่น addnode ประกาศค่าตัวแปร i มีชนิดข้อมูลเป็นตัวเลขจำนวนเต็ม ในฟังก์ชั่นจะมีการสร้างพอยเตอร์และ โหนดใหม่ คือ n โดยใช้คำสั่ง new จองหน่วยความจำให้กับโหนด และเก็บตัวเลขที่ต้องการเอาไว้ ในที่นี้จะใช้เป็นค่าตามตัวแปร i ถ้าพอยเตอร์ frist ชี้ไปที่ NULL ก็ให้พอยเตอร์ n ชี้ไปยัง NULL แสดงว่าเป็นโหนดสุดท้าย มิฉะนั้นให้พอยเตอร์ n ชี้ไปยังโหนดที่ frist มันชี้อยู่ แล้วให้โหนด n ชี้ไปหาโหนดเดียวกับที่ first ชี้ก็เพื่อไม่ให้ list ขาดช่วง แล้ว first ก็จะมาชี้ที่ตัวใหม่นี้แทน ต่อมาในฟังก์ชั่น main ทำการสร้างโหนดแรก กำหนดค่าให้กับโหนดแรก มีค่า 0  แล้วกำหนดให้พอยเตอร์ของ frist ชี้ไปที่ NULL เพื่อให้เรารู้ว่าไม่มีสมาชิกใด ๆ ใน list  แล้วทำการเพิ่มโหนด 5, 9, 4, 7, 3 หลังจากนั้นสร้างพอยเตอร์ p ใช้คำสั่ง for วนลูป เช็คเงื่อนไข ให้ พอยเตอร์ p ชี้ไปยังตำแหน่งของโหนดที่ frist หรือโหนดล่าสุดที่เราเพิ่งเพิ่มเข้าไปนั่นเอง ถ้า p ยังไม่เป็นค่า NULL ก็ให้ p ชี้ไปยังตำแหน่งโหนดถัดไป เสร็จแสดงค่าในโหนด ตำแหน่งที่ p ชี้อยู่
การลบ Node
          #include <stdio.h>
          #include <conio.h>
          struct node
          {
           int data;
           node *next;
          };
          node *first;
          void addnode (int i)
          {  
           node *n;
           n=new node;
           n->data=i;
           if (first->next==NULL) n->next=NULL;
           else
           n->next=first->next;
           first->next=n;
           }
           void removefromfirst()
          { 
          node *p;
          p=first->next;
          first->next=p->next;
          delete p; }
          void main()
          {
          clrscr();
          first=new node;
          first->data=0;
          first->next=NULL;
          addnode(5);
          addnode(9);
          addnode(4);
          addnode(7);
          addnode(3); 
          addnode(15); 
          addnode(19); 
          addnode(56);
           node *p; printf("Current data\n");
           for (p=first->next;p!=NULL;p=p->next)
                {
                      printf("data=%d\n",p->data);
                }
          removefromfirst ();
          removefromfirst ();
          removefromfirst ();
          removefromfirst ();
          removefromfirst ();
          removefromfirst ();
          printf("\nCurrent data\n");
          for (p=first->next;p!=NULL;p=p->next)
              {
                    printf("data=%d\n",p->data);
              }
         getch();
         }
ผลรัน          Current Data
          data=56
          data=19
          data=15
          data=3
          data=7
          data=4
          data=9          
          data=5
          Current Data
          data=9
          data=5
อธิบายโปรแกรม    ลักษณะการทำงานของโปรแกรม สร้าง struct เพื่อใช้เก็บข้อมูลในโหนด และพอยเตอร์ไว้ในชุดเดียวกัน สร้างฟังก์ชั่น addnode ประกาศค่าตัวแปร i มีชนิดข้อมูลเป็นตัวเลขจำนวนเต็ม ในฟังก์ชั่น จะมีการสร้างพอยเตอร์และ โหนดใหม่ คือโหนด n โดยใช้คำสั่ง new จองหน่วยความจำให้กับโหนด และเก็บตัวเลขที่ต้องการเอาไว้ ในที่นี้จะใช้เป็นค่าตามตัวแปร i ถ้าพอยเตอร์ frist ชี้ไปที่ NULL ก็ให้พอยเตอร์ n ชี้ไปยัง NULL แสดงว่าเป็นโหนดสุดท้าย มิฉะนั้นให้พอยเตอร์ n ชี้ไปยังโหนดที่frist มันชี้อยู่ แล้วให้โหนด n ชี้ไปหาโหนดเดียวกับที่ first ชี้ก็เพื่อไม่ให้ list ขาดช่วง แล้ว first ก็จะมาชี้ที่ตัวใหม่นี้แทน  สร้างฟังก์ชั่น removefromfirst ในฟังก์ชั่นมีการสร้างพอยเตอร์ p ให้พอยเตอร์ของ frist ชี้ไปยัง โหนดที่ พอยเตอร์ p ชี้อยู่ เพื่อ ลบโหนดในตำแหน่งที่พอยเตอร์ p ชี้อยู่ ในฟังก์ชั่น main สร้างโหนดแรกและกำหนดค่าให้กับโหนดแรกมีค่า 0 แล้วกำหนดให้พอยเตอร์ของ frist ชี้ไปที่ NULL เพื่อให้เรารู้ว่าไม่มีสมาชิกใด ๆ ใน list  แล้วทำการเพิ่มโหนด 5, 9, 4, 7, 3, 15, 19, 56 สร้างพอยเตอร์ p ใช้คำสั่ง for วนลูป เช็คเงื่อนไข ให้ พอยเตอร์ p ชี้ไปยังตำแหน่งของโหนดที่ frist หรือโหนดล่าสุดที่เราเพิ่งเพิ่มเข้าไปนั่นเอง ถ้า p ยังไม่เป็นค่า NULL ก็ให้ p ชี้ไปยังตำแหน่งของโหนดถัดไป เสร็จแสดงค่าในโหนด ตำแหน่งที่ p ชี้อยู่ แล้วทำการลบโหนดจากโหนดแรก 6 โหนด ใช้คำสั่ง for วนลูป เช็คเงื่อนไข ให้ พอยเตอร์ p ชี้ไปยังตำแหน่งของโหนดที่ frist ถ้า p ยังไม่เป็นค่า NULL ก็ให้ p ชี้ไปยังตำแหน่งของโหมดถัดไป เสร็จแสดงค่าในโหนด ตำแหน่งที่ p ชี้อยู่
  1. การแทรกโหนดในลิสต์ว่าง (Insert into Empty List) 
                   กรณีนี้เป็นการแทรกโหนดเพิ่มเข้าไปในลิสต์ในขณะที่ลิสต์ว่างเปล่าหรือไม่มีข้อมูล 
ใดๆ อยู่นั่นหมายถึงเป็นการแทรกสมาชิกตัวแรกเข้าไป ซึ่งขณะนั้นเฮดพอยน์เตอร์จะมีค่าเป็น  
null เนื่องจากเป็นลิสต์ว่าง หลังจากนั้นมีลิสต์ใหม่ที่ต้องการแทรกเพิ่มเข้ามา (pNew) 
(a) Before add
                  
                                                                       (b) After add

รูป แสดงการแทรกโหนดเมื่อลิสต์ภายในว่าง

           2 การแทรกโหนดที่ตำแหน่งแรก (Insert at Beginning) 
               เป็นการแทรกโหนดเข้าไปไว้ในตำแหน่งโหนดแรก ซึ่งทำให้โหนดที่เคยอยู่ลำดับแรก 
เดิมต้องมาต่อท้ายโหนดใหม่ที่แทรกเข้าไป ขั้นตอนแรกของการแทรกข้อมูลที่โหนดแรกของลิสต์  
จะต้องทราบถึงตัวชี้ของตัว Predecessor ก่อน ซึ่งหากไม่มี หรือมีค่าเป็น nullก็หมายความว่า 
เป็นการแทรกโหนดแรกในลิสต์ว่างเปล่า
                               การแทรกโหนดที่ลำดับแรกจะมีวิธีการคือ ให้นำตัวชี้ของโหนดใหม่ชี้ไปยังโหนดแรก

           ของ ลิสต์หลังจากนั้นก็ทำการกำหนดเฮดพอยน์เตอร์ชี้ไปยังโหนดใหม่ ซึ่งเราจะรู้ตำแหน่งแอด

           เดรสของโหนดใหม่อยู่แล้วหลังจากที่ได้สร้างขึ้นมา

                                                                     (a) Before add
                   
                                                           (b) After add

                     รูป  แสดงการแทรกโหนดไว้ที่ตำแหน่งแรกของลิสต์

        3 การแทรกโหนดที่กึ่งกลางของลิสต์ (Insert in Middle) 
            การเพิ่มโหนดในส่วนกลางของลิสต์หรือการแทรกระหว่างโหนด ในขั้นตอนแรก ต้องรู้ตำ 
แหน่งโหนดก่อนหน้าเสียก่อน ซึ่งก็คือตัว Predecessor (pPre) ความสำคัญของโหนด  
Predecessor ก็คือจะมีลิงก์ที่ใช้เชื่อมโยงไปยังโหนดถัดไป 
            ในการแทรกโหนดระหว่างสองโหนด ตัวชี้หรือลิงก์ฟิลด์ของโหนดใหม่จะชี้ไปยังโหนด  
Successor ในขณะที่ตัวชี้ pPre ก็จะชี้ไปยังโหนดใหม่ 

                                                   (a) Before add
                   
                                                       (b) After add 
รูป  แสดงแทรกโหนดที่กึ่งกลางของลิสต์

          4 การแทรกโหนดที่ท้ายลิสต์ (Insert at End) 
              เมื่อมีการเพิ่มโหนดที่ส่วนท้ายของลิสต์ เราต้องการเพียงแค่ตัวชี้ของ Predecessor  
เพื่อชี้ไปยังโหนดใหม่เท่านั้น ซึ่งในที่นี้จะไม่มีโหนด Successor เนื่องจากเป็นการแทรกที่ท้าย 
ลิสต์ดังนั้นลิงก์ฟิลด์ของโหนดใหม่จึงถูกกำหนดให้เป็นค่า null

              อย่างไรก็ตาม ก็ยังมีตรรกะพิเศษในอีกรูปแบบหนึ่งเพื่อใช้กับอัลกอริทึมการแทรกโหนด
 
ที่ท้ายลิสต์ ซึ่งจัดเป็นข้อดีข้อหนึ่งของโครงสร้างลิงก์ลิสต์ โดยเราทราบอยู่แล้วว่าโหนดสุดท้ายของ 
ลิสต์จะมีตัวชี้ที่เชื่อมโยงไปที่ null นั่นหมายถึงโหนดสุดท้ายที่ไม่มีโหนดตัวถัดไปแล้วนั่นเองแต่ถ้า 
หากเรามีความต้องการใช้พอยน์เตอร์มากกว่าที่จะใช้ค่าคงที่ของ null เป็นตัวกำหนด

a) Before add

            
(b) After add

ไม่มีความคิดเห็น:

แสดงความคิดเห็น