วันพุธที่ 9 มีนาคม พ.ศ. 2559

โครงสร้างข้อมูล

โครงสร้างการควบคุมพื้นฐาน 3 รูปแบบ (The Three Basic Control Structures)
           ในการเขียนโปรแกรมคอมพิวเตอร์ ส่วนใหญ่มักใช้โครงสร้างควบคุมพื้นฐาน
(Control Structures)ซึ่งมีอยู่ 3 รูปแบบพื้นฐานดังนี้คือ แบบเรียงลำดับ(Sequence)
แบบกำหนดทางเลือก(Selection) และแบบทำซ้ำ (Repetition)
แบบเรียงลำดับ (Sequence)
           โครงสร้างการควบคุมแบบเรียงลำดับเป็นการทำงานแบบลำดับขั้นตอนต่อเนื่อง
กันไปในลักษณะบนลงล่าง การประมวลผลจะเป็นไปตามแต่ละชุดคำสั่งตามลำดับสำหรับ
การนำเสนอโครงสร้างการควบคุมแบบเรียงลำดับในรูปแบบของซูโดโค้ด
รูปที่ 1.1 รูปแบบโครงสร้างการควบคุมแบบเรียงลำดับ
แบบเลือกการทำงาน (Selection)
           โครงสร้างการควบคุมแบบเลือกการทำงานจะเกี่ยวข้องกับการเปรียบเทียบ
เงื่อนไขเพื่อให้เกิดทางเลือกสองแนวทางด้วยกันเช่น ถ้าเงื่อนไขเป็นจริง ก็จะไป
ทำงานในโมดูลหนึ่ง หรือในกรณีที่เงื่อนไขเป็นเท็จ ก็จะไปทำงานอีกโมดูลหนึ่งหรือ
อาจทำการเปรียบเทียบเงื่อนไขในลำดับขั้นตอนต่อไปก็ได้ ในซูโดโค้ดการกำหนด
เงื่อนไขจะใช้คำเฉพาะคือif …then …else และ end ifประเภทของโครงสร้างแบบ
เลือก
  • เลือกทำงานเฉพาะเมื่อเงื่อนไขเป็นจริง
    
รูปที่ 1.2 รูปแบบโครงสร้างการควบคุมแบบเลือกการทำงาน
  • เลือกทำงานอย่างใดอย่างหนึ่งระหว่างเงื่อนไขจริงและเท็จ

      
รูปที่ 1.3 รูปแบบโครงสร้างการควบคุมแบบเลือกทำงานอย่างใดอย่างหนึ่งระหว่างเงื่อนไข
              จริงและเท็จ
  • คำสั่งเลือกแบบซ้อนกัน
รูปที่ 1.4 รูปแบบโครงสร้างการควบคุมแบบเลือกแบบซ้อนกัน

  • คำสั่งแบบหลายทางเลือก เมื่อมีทางเลือกมากกว่า 2 ทาง
    
รูปที่ 1.5 รูปแบบโครงสร้างการควบคุมแบบหลายทางเลือก

- แบบทำซ้ำ (Repetition)
          โครงสร้างการทำงานแบบทำงานซ้ำ กลุ่มของชุดคำสั่งที่อยู่ภายในบล็อกของวงจร
ลูปจะทำงานซ้ำไปเรื่อยๆ เมื่อตรงกับเงื่อนไข จนกระทั่งเงื่อนไขเป็นเท็จก็จะหลุดออก
จากลูป
                      รูปที่ 1.6 รูปแบบโครงสร้างการควบคุมแบบทำงานซ้ำด้วย dowhile
            ลูปชนิด dowhile นี้จะมีการตรวจสอบเงื่อนไขก่อนที่จะดำเนินการทุกครั้ง โดยหากเงื่อนไขที่
เปรียบเทียบมีผลเป็นจริง (True) ก็จะดำเนินการเอ็กซีคิวต์ชุดประโยคคำสั่งภายในบล็อก จนกระทั่ง
ได้พบกับตัวปิดคือ enddo นั้น ๆ ต่อไปจนกระทั่งที่เปรียบเทียบจะมีผลเป็นเท็จ (False) ก็จะหลุดจาก
วงจรลูปนั้นออกไปทำงานชุดคำสั่งในลำดับถัดจากตัวปิดลูปหรือ enddo ต่อไป ดังนั้นชุดคำสั่งต่างๆ
ภายในลูปชนิด dowhile นั้นอาจจะไม่ถูกเอ็กซีคิวต์เลยก็เป็นได้ในกรณีเงื่อนไขที่ตรวจสอบครั้งแรกมี
ผลเป็นเท็จ

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

สแต็ก (Stacks)

แต็ก (Stacks)

                     สแต็ก (Stacks) เป็นลิสต์แบบเชิงเส้น (Linear Lists) มีโครงสร้างข้อมูลที่จัดเก็บ
ข้อมูลแบบเรียงลำดับต่อเนื่อง การเพิ่มข้อมูลลงในสแต็ก หรือการนำข้อมูลออกจากสแต็กจะกระ
ทำที่จุดปลายด้านหนึ่งซึ่งมีลักษณะเข้าออกได้ทางเดียว โดยเรียกบริเวณนั้นว่าTop ของสแต็กตัว
อย่างเช่น มีการเพิ่มข้อมูลลงในสแต็กตามลำดับดังนี้ {5, 10, 15, 20} เมื่อมีการนำข้อมูลออกก็
จะเป็นลำดับดังนี้คือ {20, 15, 10, 5} และด้วยคุณสมบัติแบบย้อนกลับด้าน (Reversing) ของ
สแต็กนี้เองจึงมักเรียกโครงสร้างข้อมูลแบบสแต็กว่า มาทีหลังแต่ออกก่อน (Last In- First
Out : LIFO)

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


                                      รูปที่ 4.1 สแต็ก (Stacks)

4.1 การดำเนินงานพื้นฐานของสแต็ก (Basic Stack Operations)
              การดำเนินงานเกี่ยวกับโครงสร้างข้อมูลแบบสแต็ก  ประกอบด้วยฟังก์ชันพื้นฐาน 3 ฟังก์
ชันดัวยกันดังนี้

               1. ฟังก์ชัน Push ทำหน้าที่เพิ่มข้อมูลเข้าไปในชั้นบนสุดของแสต็กปัญหาของการ
Push ก็คือต้องมีความมั่นใจว่าภายในสแต้กนั้นมีพื้นที่ว่างพอที่จะบรรจุข้อมูลใหม่ลงไปได้ถ้าหาก
สแต็กหรือมีพื้นที่ว่างไม่เพียงพอ จะทำให้เกิดสถานะโอเวอร์โฟลว์ (Overflow State) ส่งผลให้
ไม่สามารถใส่ข้อมูลใหม่เข้าไปในสแต็กได้อีก ซึ่งโดยทั่วไปเราจะใช้ฟังก์ชัน fullStack ในการ
ตรวจสอบว่าสแต็กเต็มหรือไม่ โดยพิจารณาจากรูปที่ 4.2 ที่แสดงถึงฟังก์ชัน Push ด้วยการใส่
ข้อมูลลงในสแต็ก
รูปที่ 4.2 การPush ข้อมูลลงในสแต็ก
                2. ฟังก์ชัน Pop  เป็นฟังก์ชันคืนค่าข้อมูลที่อยู่บนสุดของสแต็กส่งคืนให้กับผู้ใช้ พร้อม
ทั้งลบข้อมูลรายการนั้นออกไปด้วย ส่งผลให้ข้อมูลรายการถัดไปกลีบมาอยู่ในสถานะบนสุดอีกครั้ง
และเมื่อรายการสุดท้ายในสแต็กได้ถูกนำออกไปทั้งหมด สแต็กดังกล่าวก็จะถูกกำหนดให้อยู่ใน
สถานะว่าง (EmptyState) แต่หากในขณะนั้นมีการเรียกใช้ฟังก์ชัน Pop บนสแต็กว่างเปล่าจะ
ทำให้เกิดสถานะอันเดอร์โฟลว์(Underflow State) ดังนั้นเมื่อต้องการ Pop ข้อมูลออกจาก
สแต็ก จึงจำเป็นต้องตรวจสอบก่อนว่าสแต็กนั้นว่างหรือไม่ ซึ่งโดยทั่วไปเราจะใช้ฟังก์ชัน empty
Stack ในการตรวจสอบว่าสแต็กว่างหรือไม่ โดยพิจารณาจากรูปที่ 4.3 ที่แสดงถึงฟังก์ชัน Pop
ด้วยการนำข้อมูลออกจากสแต็ก
รูปที่ 4.3  การ Pop ข้อมูลออกจากสแต็ก
              3. ฟังก์ชัน Stack Top ฟังก์ชันนี้จะมีความคล้ายคลึงกับฟังก์ชัน Pop ที่คืนค่าข้อมูล
ด้วยการคัดลอกข้อมูลบนสุดของสแต็กส่งคืนให้กับผู้ใช้ แต่จะแตกต่างตรงที่ฟังก์ชัน Stack Top
นั้น จะคืนค่าข้อมูลไปยังผู้ใช้งานเท่านั้น โดยไม่มีการลบข้อมูลออกจากสแต็กแต่อย่างใด กล่าวคือ
หน้าที่ของฟังก์ชัน StackTop นั้นก็คือการอ่านข้อมูลบนสแต็กที่อยู่ลำดับบนสุดของสแต็กนั่นเอง
โดยพิจารณาจากรูปที่ 4.4 ที่แสดงถึงฟังก์ชันStack Topด้วยการอ่านข้อมูลจากสแต็กที่อยู่ใน
ลำดับสุดส่งคืนกลับไปยังผู้ใช้เพื่อนำไปใช้งานต่อไป
รูปที่ 4.4 การอ่านข้อมูลด้วย Stack Top



4.2 การสร้างสแต็ก (Stack Implementation)
                    เนื่องจากสแต็กเป็นโครงสร้างข้อมูลที่ภาษาคอมพิวเตอร์ไม่ได้มีมาให้เหมือนกับ
อาร์เรย์ดังนั้นจึงจำเป็นต้องสร้างขึ้นเอง โดยสามารถสร้างด้วยการแทนที่ข้อมูลสแต็กได้ 2 วิธี
ด้วยกันคือการสร้างสแต็กด้วยอาร์เรย์และการสร้างสแต็กด้วยลิงก์ลิสต์

การสร้างสแต็กด้วยอาร์เรย์ 


                   การสร้างสแต็กด้วยโครงสร้างข้อมูลแบบอาร์เรย์นั้น เป็นการจัดสรรพื้นที่หน่วยความ
จำแบบสแตติก (Static)  ซึ่งต้องมีการกำหนดขนาดของสแต็กเพื่อใช้งานล่วงหน้าว่าต้องการขนาด
เท่าไรจากนั้นก็ทำการจัดสรรเนื้อที่ภายในหน่วยความจำแบบคงที่ตายตัวและด้วยโครงสร้างแบบ
อาร์เรย์ที่นำมาแทนที่ข้อมูลของสแต็ก จึงต้องจัดเก็บข้อมูลที่เป็นชนิดเดียวกัน จากรูปที่ 4.5
แสดงถึงการสร้างสแต็กด้วยอาร์เรย์
      
                                 รูปที่ 4.5  การสร้างสแต็กด้วยอาร์เรย์
อย่างไรก็ตาม การเลือกโครงสร้างข้อมูลแบบอาร์เรย์มาสร้างสแต็กนั้น จะมีข้อเสียอยู่หลายประการ
ด้วยกัน คือ
  • การสร้างสแต็กด้วยอาร์เรย์ต้องมีการจัดสรรพื้นที่หน่วยความจำที่แน่นอนไว้ล่วงหน้า
  • กรณีการเพิ่มข้อมูลลงในสแต็กมากเกินกว่าที่กำหนดไว้จะส่งผลให้สแต็กเต็มได้
    แต่ก็สามารถแก้ไขปัญหาได้ด้วยการจัดสรรเนื้อที่ภายในหน่วยความจำจำนวนมากๆ เข้าไว้
    ซึ่งก็จะทวีความสิ้นเปลืองยิ่งขึ้น
  • สำหรับกรณีมีข้อมูลจำนวนน้อยหรือไม่มีข้อมูลในสแต็กเลย นั่นหมายความว่าต้องเสียพื้นที่
    หน่วยความจำไปโดยปริยาย
การสร้างสแต็กด้วยลิงก์ลิสต์
            การสร้างสแต็กด้วยโครงสร้างข้อมูลแบบลิงก์ลิสต์จัดเป็นอีกวิธีหนึ่งที่มีประสิทธิภาพสูงซึ่ง
ลิงก์ลิสต์จะจัดสรรหน่วยความจำแบบไดนามิก (Dynamic) ดังนั้นจึงไม่ต้องกำหนดขนาดคงที่อย่าง
เช่นอาร์เรย์ กล่าวคือหน่วยความจำจะถูกจัดสรรเมื่อมีการใช้งานจริงเท่านั้น อีกทั้งยังสามารถจัดเก็บ
ข้อมูลต่างชนิดกันได้ นอกจากนี้แล้ว การสร้างสแต็กด้วยลิงก์ลิสต์ สแต็กจะไม่มีวันเต็มต่อเมื่อยังมี
เนื้อที่เพียงพอต่อการจัดสรรได้อยู่
               ส่วนประกอบสำคัญของลิงก์ลิสต์ก็คือ โครงสร้างสองส่วนที่มีความแตกต่างกันคือ ส่วนหัว
(Head) และส่วนข้อมูล (Data Node) โดยโครงสร้างส่วนหัวจะประกอบไปด้วย Metadataที่เป็น
ข้อมูลเพื่อใช้อธิบายข้อมูลอีกทีหนึ่ง และพอยน์เตอร์ที่อยู่ส่วนบนสุดของสแต็ก สำหรับส่วนข้อมูลจะ
ประกอบด้วยข้อมูลพอยน์เตอร์ที่ใช้เชื่อมโยงไปยังโหนดถัดไปในสแต็ก พิจารณาจากรูปที่ 4.6 ที่
แสดงถึงการสร้างสแต็กด้วยด้วยลิงก์ลิสต์ โดยรูปที่ 4.6 นั้นคือแนวความคิดของสแต็ก และเป็นรูป
แบบของการสร้างสแต็กด้วยลิงก์ลิสต์ในเชิงกายภาพ
            
                                              รูปที่ 4.6 การสร้างสแต็กด้วยลิงก์ลิสต์


4.3 การนำสแต็กไปประยุกต์ใช้กับโปรแกรม
               เนื้อหาต่อไปนี้ จะกล่าวถึงการนำโครงสร้างข้อมูลแบบสแต็กไปใช้งานกับโปรแกรม
ซึ่งตามปกติการเขียนโปรแกรมมักเขียนในลักษณะโมดูลหรือโปรแกรมย่อยโดยโปรแกรมหลัก
จะทำการเรียกใช้โปรแกรมย่อยต่างๆเพื่อใช้งานดังนั้นโครงสร้างข้อมูลแบบสแต็กจะสามารถ
นำมาประยุกต์ใช้เพื่อทำงานกับกรณีดังกล่าวได้เป็นอย่างดีโดยส่วนโปรแกรมที่มีการเรียกใช้
งานโปรแกรมย่อย จะมีการ Push ลำดับคำสั่งที่ต้องประมวลผลในลำดับถัดไปไว้ในสแต็ก
เพื่อจะได้สามารถรีเทิร์นกลับมาทำงานต่อจากตัวที่เรียกได้และเมื่อมีการประมวลผลชุดคำสั่ง
ในโปรแกรมย่อยที่เรียกใช้งานจนเสร็จ ก็จะทำการ Pop ลำดับคำสั่งออกจากสแต็กเพื่อจะได้
กลับไปทำงานยังลำดับคำสั่งที่ได้เรียกใช้โปรแกรมย่อยนั้นได้ต่อไป
         
                       รูปที่ 4.7 ตัวอย่างโปรแกรมแสดงลำดับการเรียกโปรแกรมย่อยเพื่อใช้งาน
               จากรูปที่ 4.7 เป็นตัวอย่างโปรแกรมแสดงลำดับการเรียกโปรแกรมย่อยเพื่อใช้งานโดย
ที่ Task A ได้เรียกใช้งาน Task B ได้มีการเรียกใช้งานTask C ในขณะที่ Task C ก็ได้เรียก
ใช้งาน Task D จนกระทั่งจบที่ Task D ก็ได้มีการรีเทิร์นกลับไปทำงานยัง Task ก่อนหน้าที่
เรียกใช้งาน จนกระทั่งจบโปรแกรมจากรูปดังกล่าว สามารถอธิบายให้เข้าใจง่ายยิ่งขึ้นได้ดังราย
ละเอียดต่อไปนี้
- Task A
            
ถือเป็นโปรเซสหลัก โดย ณ ขณะนี้โปรแกรมทำงานที่ Task A อยู่ (รูปที่ 4.8 (a))จาก
นั้นก็ได้มีการเรียกใช้งาน Task B ดังนั้นจึงต้องนำข้อมูลของ Task A ใส่ลงในสแต็ก (รูปที่
4.8(b))

Task B
               ในขณะที่ประมวลผลอยู่ที่ Task B ยังไม่เสร็จสิ้น ก็ได้เรียกใช้งาน Task C ดังนั้นเรา
จึงต้องใส่ข้อมูลที่จำเป็นของ Task B ลงในสแต็กเช่นกัน (รูปที่ 4.8(c))
               
Task C
            
เมื่อ Task C เริ่มทำงานไปสักพักหนึ่ง ซึ่งยังไม่เสร็จสมบูรณ์ ก็ได้มีการรียกใช้งาน
Task D ดังนั้นจึงต้องดำเนินการใส่ข้อมูลของ Task C ลงในสแต็ก (รูปที่ 4.8 (d) ทำให้ ณ
ขณะนี้มีงานอยู่สามงานด้วยกันที่อยู่ในสแต็ก

Task D
            
ในขณะที่ Task D  ได้เริ่มทำงานจนกระทั่งเสร็จสมบูรณ์ ก็จะต้องรีเทิร์นกลับไปยัง
โปรเซสที่เรียกก่อนหน้า ซึ่งก็คือ Task C นั่นเอง ดังนั้นการกลับไปทำงานยัง Task C  ต่อ ก็
สามารถดำเนินการได้ด้วยการ Pop สแต็กส่วนบนสุดออกมาใช้งาน (รูปที่ 4.8(e))และเมื่อ
TaskC ทำงานจนเสร็จสิ้น ก็จะทำการ Pop สแต็กTask B ที่อยู่ส่วนบนสุดของสแต็กออกมา
(รูปที่ 4.8(f)) โดยขณะนี้ลำดับโปรเซสการทำงานได้กลับมายัง Task B จนกระทั่ง Task B
ได้ทำงานต่อจนเสร็จสิ้น จึงได้ทำการPop สแต็ก Task A ที่อยู่ส่วนบนสุดออกมา (รูปที่ 4.8
(g)) ซึ่งเป็นโปรแกรมหลักออกมาทำงานต่อจนกระทั่งจบโปรแกรม
                รูปที่ 4.8 แสดงถึงโปรแกรมย่อยต่างๆ ที่ Push ลงในสแต็กและ Pop 
                 ออกจากสแต็กและสถานการณ์




4.4 การประยุกต์ใช้งานสแต็ก (Stack Applications)
                  ด้วยคุณสมบัติของสแต็กที่มีหลักการทำงานในรูปแบบของ LIFO นี่เอง จึงสามารถนำสแต็ก
ไปประยุกต์ใช้กับงานต่อไปนี้
การเรียงลำดับข้อมูลแบบย้อนกลับ (Reversing  Data)

                  คือการจัดเรียงลำดับข้อมูลใหม่ ตัวอย่างเช่น ข้อมูลในสแต็กที่มีข้อมูลตัวเลขลำดับจาก Topลง
ไปคือ {4 3 2 1 } เมื่อมีการจัดเรียงใหม่ลงในอีกสแต็กหนึ่งก็จะได้ลำดับใหม่มาเป็น {1 2 3 4} เป็นต้น
                              
                          รูปที่ 4.9 การเรียงลำดับข้อมูลย้อนกลับด้วยสแต็ก
                  เป็นการแตกข้อมูลออกเป็นส่วนๆ ให้เป็นอิสระต่อกัน เพื่อส่งไปประมวลผล ตัวอย่างเช่น การ
แปลซอร์สโค้ดให้เป็นภาษาเครื่อง ซึ่งคอมไพเลอร์จะดำเนินการแตกโปรแกรมออกเป็นส่วนๆ ให้เป็นอิส
ระกัน เช่นคำสงวน ตัวแปร และโทเค็น (Tokens) เป็นต้น  นอกจากนี้แล้ว การแตกข้อมูลออกเป็นส่วนยัง
สามารถนำไปใช้กับการตรวจสอบการจับคู่ของเครื่องหมายวงเล็บในนิพจน์คณิตศาสตร์ได้อีก ด้วย
                
         รูปที่ 4.10 การแตกข้อมูลออกเป็นส่วนๆ เพื่อตรวจสอบการจับคู่ของเครื่อง
                        หมายวงเล็บ
การหน่วงเวลา  (Postponement)
                   เป็นการหน่วงเวลาของข้อมูลไว้ชั่วขณะหนึ่ง เพื่อรอการประมวลผลในช่วงจังหวะเวลาทีเหมาะ
สม ซึ่งกรณีดังกล่าว จะนำไปใช้กับการแปลงนิพจน์ Infix มาเป็นนิพจน์ Postfix โดยรายละเอียดจะกล่าวใน
หัวข้อถัดไป                     
การย้อนรอย (Backtracking  Steps)
                  เป็นการย้อนรอยเพื่อไปยังสถานะก่อนหน้า รวมถึงการค้นหาเส้นทางการเดินทางเพื่อไปยังเป้า
หมายปลายทาง (Goal  Seeking)
            
                  รูปที่ 4.11 การค้นหาเส้นทางการเดินทางเพื่อไปยังเป้าหมาย
                  และด้วยตรรกะดังกล่าวสแต็กจึงมีประโยชน์มากมายต่อการนำไปประยุกต์ใช้งานได้อย่าง
หลาก หลายโดยรายละเอียดต่อไปนี้ เป็นการนำเสนอเรื่องราวเกี่ยวกับการนำสแต็กไปใช้เพื่อแปลงนิ
พจน์ Infix มาเป็นPostfix โดยก่อนที่จะเข้าสู่เนื้อหา ควรเข้าใจถึงความแตกต่างระหว่างโอเปอเรเตอร์
(Operator)ซึ่งคือตัวดำเนินการและโอเปอแรนด์ (Operand) ซึ่งคือตัวถูกดำเนินการเสียก่อนด้วย
การพิจารณานิพจน์คณิตศาสตร์จากรูปที่ 5.19
     
         รูปที่ 4.12 นิพจน์คณิตศาสตร์ที่ประกอบไปด้วยโอเปอเรเตอร์และโอเปอร์แรนด์
                  โดยโอเปอเรเตอร์ก็คือเครื่องหมายการคำนวณต่างๆ เช่น + - * / ในขณะที่ตัวโอเปอแรนด์
ซึ่งเป็นตัวถูกดำเนินการนั้น อาจเป็นได้ทั้งตัวแปรหรือค่าคงที่ใดๆโดยนิพจน์ทางคณิตศาสตร์สามารถนำ
เสนอให้แตกต่างกันได้ถึง 3 รูปแบบด้วยกันดังนี้
      1. นิพจน์ Infix
                  นิพจน์คณิตศาสตร์ที่อยู่ในรูปแบบของ Infixนั้นก็คือนิพจน์โดยทั่วไปที่เรามักใช้กับการคำนวณ
สูตรตัวเลขต่างๆ 
โดยโอเปอเรเตอร์จอยู่ระหว่างตัวโอเปอร์แรนด์ A+B
     2. นิพจน์ Postfix
                  นิพจน์คณิตศาสตร์ที่อยู่ในรูปแบบของ Postfix นั้น คือนิพจน์ในรูปแบบที่โอเปอเรเตอร์จะอยู่
ข้างหลังตัวโอเปอแรนด์ AB+
     3. นิพจน์ Prefix
                  นิพจน์คณิตศาสตร์ที่อยู่ในรูปแบบของ Prefix จะตรงกันข้ามกับ Postfixโดยจะนำโอเปอเร
เตอร์ไปไว้อยู่ข้างหน้าตัวโอเปอแรนด์ + AB

                  ข้อเสียประการหนึ่งของนิพจน์แบบ Infix ก็คือ จำเป็นต้องใช้เครื่องหมายวงเล็บเปิดและปิดเพื่อ
ควบคุมค่าของโอเปอเรเตอร์ ซึ่งการใช้เครื่องหมายวงเล็บ ก็เพื่อต้องการกำหนดลำดับความสำคัญของ
นิพจน์ว่าควรดำเนินการคำนวณภายในวงเล็บก่อนนั่นเอง แต่สำหรับในกรณีนิพจน์ในรูปแบบของPostfix
และ Prefix นั้นจะไม่มีความจำเป็นต้องใช้เครื่องหมายวงเล็บดังกล่าวแต่อย่างใด ซึ่งกฏเกณฑ์ในการแปลง
นิพจน์ Infix มาเป็นนิพจน์ Prefix หรือ Postfix นั้นจะใช้หลักการเดียวกัน เพียงแต่นิพจน์ Prefix จะนำ
โอเปอเรเตอร์ไว้หน้าโอเปอแรนด์ ในขณะที่นิพจน์ Postfix จะนำไปไว้ข้างหลัง
การแปลงนิพจน์ Infix มาเป็น Postfix ด้วยมือ (Manual  Transformation)
                   คอมไพเลอร์หรือตัวแปลภาษายุคใหม่ในปัจจุบัน จะไม่สามารถแปลนิพจน์ Infixไปเป็นภาษา
เครื่องได้โดยตรง อันเนื่องมาจากการจัดลำดับความสำคัญของโอเปอเรเตอร์ที่แตกต่างกันในนิพจน์ Infix
จึงทำให้การคำนวณแต่ละเทอมในนิพจน์ไม่เป็นไปตามลำดับ ทำให้คอมไพเลอร์คำนวณหาผลลัพธ์จาก
นิพจน์ Infix ค่อนข้างยากลำบาก โดยพิจารณาจากลำดับการคำนวณของนิพจน์ Infix ด้านล่างต่อไปนี้
ก็จะรับทราบถึงเหตุผลที่คอมไพเลอร์จะไม่นำนิพจน์ Infix ไปใช้งานและยิ่งเป็นสูตรคำนวณที่ซับซ้อนก็ยิ่ง
ทวีความยุ่งยากให้กับคอมไพเลอร์ในการคำนวณหาผลลัพธ์มากเท่านั้น
                   อย่างไรก็ตาม  คอมไพเลอร์สามารถแก้ไขปัญหาข้างต้นได้ด้วยการแปลงนิพจน์ Infix ให้มา
เป็นนิพจน์แบบ Postfix หรือ Prefix เสียก่อน โดยต่อไปนี้เราจะสาธิตหลักวิธีการแปลงนิพจน์ Infix มาเป็น
นิพจน์แบบPostfix ด้วยมือ และหลังจากนั้นจึงค่อยกล่าวรายละเอียดในส่วนของอัลกอริทึมที่แปลงนิพจน์ Infix
มาเป็น Postfix ที่สามารถนำไปใช้บนคอมพิวเตอร์ได้ต่อไป

                   กฎเกณฑ์สำหรับการแปลงนิพจน์ Infix มาเป็นนิพจน์ Postfix ด้วยมือ สามารถดำเนินการดัง
ขั้นตอนต่อไปนี้
1.  ให้ทำการใส่เครื่องหมายวงเล็บให้กับทุกๆนิพจน์ด้วยการคำนึงถึงลำดับการคำนวณ เช่น เครื่องหมายคูณ
และหารต้องมาก่อนเครื่องหมายบวกและลบ เป็นต้น

2.  ทำการเปลี่ยนสัญลักษณ์ Infix ในแต่ละวงเล็บให้มาเป็นสัญลักษณ์แบบ Postfix โดยเริ่มต้นจากนิพจน์
ที่อยู่วงเล็บในสุดก่อน จากนั้นก็ดำเนินการแปลงให้เป็นนิพจน์ Postfix ด้วยการย้ายโอเปอเรเตอร์ตรงตำ
แหน่งวงเล็บปิดของคู่นั้นๆ

3.  ถอดเครื่องหมายวงเล็บทิ้งออกไปให้หมด จากกฎการแปลงนิพจน์ Infix มาเป็นนิพจน์ Postfix ด้วยมือ
ตามรายละเอียดข้างต้น สามารถแสดงให้เกิดความเข้าใจได้ยิ่งขึ้นดังตัวอย่างที่ 5.1

ตัวอย่างที่ 5.1
 จงแปลงนิพจน์ A + B * C มาเป็นนิพจน์ Postfix  ด้วยมือ

  นิพจน์ Infix ที่ให้มา A + B * C
  ขั้นตอนที่  1          ใส่วงเล็บให้ทั้งหมดตามลำดับความสำคัญ
                           (A +( B * C ))
  ขั้นตอนที่  2          พิจารณานิพจน์ที่อยู่วงเล็บในสุด โดยให้ย้ายเครื่องหมาย *
                          ไปข้างหลัง C
                           (A +( B C *))
                           จากนั้นให้ย้ายโอเปอร์เรเตอร์+ซึ่งอยู่ที่ตำแหน่งวงเล็บเปิด
                           ภายนอกไปยังตำแหน่งวงเล็บปิดภายนอกของคู่ตัวเอง
                           (A +( B C *) + )
  ขั้นตอนที่ 3           ถอดเครื่องหมายวงเล็บทิ้งออกให้หมด
                           A B C * +









ตัวอย่างที่ 5.2 จงแปลงนิพจน์ 5 * 6 – 10  มาเป็นนิพจน์ Postfix  ด้วยมือ

   5 * 6 – 10  = ((5*6)-10)
  = ((5 6 *) -10)
  =  ((5 6 *) 10-)
  =  5 6 * 10-


               และจากนิพจน์ Postfix  ที่ได้จากตัวอย่างที่ 5.2 นั้น คือผลลัพธ์จากการเรียงลำดับตาม
กฎของ Postfixดังนั้นคอมไพเลอร์ก็สามารถทำการคำนวณหาผลลัพธ์จากนิพจน์ Postfix   ได้อย่าง
ง่ายดาย นักศึกษาลองพิจารณาและทำการวิเคราะห์รายละเอียดจากรูปที่ 5.20 ต่อไปนี้ ก็จะทราบว่า
ผลจากการเรียงลำดับของนิพจน์ Postfix  จะทำให้คอมไพเลอร์สามารถคำนวณหาผลลัพธ์จากสูตร
ตัวเลขตามลำดับได้ง่ายกว่านิพจน์ Infix อย่างไรโดยรายละเอียดของการหาผลลัพธ์จากนิพจน์
Postfix  จะกล่าวในหัวข้อ “การหาผลลัพธ์จากนิพจน์ Postfix” ของบทนี้อีกครั้งหนึ่ง
           
  
Expression                   Action                                 Stack
                  Push 5                            
                   Push 6                             
                Pop 6   and 5                   
                                   Evaluate 5 * 6
                                   Push 30
           Push 10                            
              Pop 10   and 30                
                                  Evaluate 30 – 10
                                  Push 20
             Pop 20                                                                Stack is empty                                 Result is 20
                 
            รูปที่ 4.13 แสดงการคำนวณหาผลลัพธ์นิพจน์ Postfix ในสแต็ก

 4.5 อัลกอริทึมสำหรับแปลงนิพจน์  Infix มาเป็นนิพจน์ Postfix (Algorithmic 
             Transformation)
               การดำเนินงานด้วยการแปลงนิพจน์ Infix มาเป็น Postfixที่กระทำด้วยมือนั้นยากต่อ
การนำไปใช้บนคอมพิวเตอร์ ดังนั้นด้วยเทคนิคต่อไปนี้จะทำให้เราสามารถนำไปสร้างโปรแกรม
เพื่อใช้งานบนสแต็กได้ง่ายทีเดียวอันเนื่องมาจากคุณสมบัติการหน่วงเวลาของข้อมูลที่นำเข้าสู่
สแต็กเพื่อรอการประมวลผลในช่วงจังหวะเวลาที่เหมาะสมภายในสแต็กนี่เองที่ช่วยให้การแปลง
นิพจน์จาก Infix มาเป็น Postfix ได้โดยไม่ยาก
อัลกอริทึมการแปลงนิพจน์ Infix มาเป็น Postfix มีขั้นตอนดังนี้
1. ถ้าข้อมูลเข้าเป็นโอเปอรแรนด์ ให้เอาพุตไป Postfix
2. ถ้าข้อมูลเป็นโอเปอเรเตอร์
     - ถ้าสแต็กว่าง ให้ Push ลงในสแต็ก
     - ถ้าภายในสแต็กมีข้อมูลอยู่ ให้ทำการเปรียบเทียบดังนี้       
     - ถ้าโอเปอเรเตอร์ที่อินพุตเข้ามามีลำดับความสำคัญน้อยกว่า หรือเท่ากับโอเปอเรเตอร์ที่อยู่
ส่วนบนของสแต็ก ให้ดำเนินการ Pop สแต็กออกไปที่ Postfix โดยทำการเปรียบเทียบกับโอเปอ
เรเตอร์ที่มีอยู่ในสแต็กไปเรื่อยๆ จนกระทั่งโอเปอเรเตอร์ทีอินพุตเข้ามามีลำดับความสำคัญมาก
กว่าโอเปอเรเตอร์ในสแต็กจากนั้นให้ดำเนินการ Push โอเปอเรเตอร์ที่อินพุตเข้ามาลงในสแต็ก
     - ถ้าโอเปอเรเตอร์ที่อินพุตเข้ามามีลำดับความสำคัญมากกว่าโอเปอเรเตอร์ที่อยู่ส่วนบนของ
สแต็ก ให้ดำเนินการ Push โอเปอเรเตอร์นั้นลงในสแต็ก

3. ถ้าข้อมูลเข้าเป็นเครื่องหมายวงเล็บเปิด ให้ดำเนินการPush ลงในสแต็ก

4. ถ้าข้อมูลเข้าเป็นเครื่องหมายวงเล็บปิด ให้ดำเนินการ Pop สแต็กไปยัง Postfix จนกระทั่งพบ
เครื่องหมายวงเล็บเปิด จากนั้นให้นำเครื่องหมายวงเล็บทั้งสองทิ้งไป

5. หากดำเนินการจนเสร็จสิ้นแล้วยังคงมีข้อมูลอยู่ในสแต็ก ให้ดำเนินการ Pop สแต็กที่เหลืออยู่ทั้ง
หมดไปที่ Postfix
รูปที่ 4.14 การแปลงนิพจน์ Infix มาเป็นนิพจน์ Postfix
            จากรูปที่ 4.14 เป็นภาพแสดงขั้นตอนการแปลงนิพจน์  A + B * C – D /Eมาเป็น
นิพจน์Postfix ซึ่งสามารถอธิบายกระบวนการทำงานได้ดังต่อไปนี้
  1. เริ่มต้นด้วยการเตรียมนิพจน์ Infix สแต็กว่าง และนิพจน์ Postfix
  2. คัดลอกโอเปอแรนด์ตัวแรก ซึ่งก็คือ A ไปยังนิพจน์ Postfix (รูปที่ 4.15(a))
  3. โอเปอเรเตอร์ + ถูก Push ลงในสแต็ก (รูปที่ 4.15(b))
  4. โอเปอเรเตอร์ B ถูกคัดลอกไปยัง Postfix (รูปที่ 4.15 ( c))
  5. โอเปอเรเตอร์ * ถูกนำไปเปรียบเทียบกับโอเปอเรเตอร์ที่อยู่บนสุดของสแต็ก แต่
    ด้วยลำดับ ความสำคัญของโอเปอเรเตอร์ * นั้นมีมากกว่า + ดังนั้นโอเปอเรเตอร์
    * จึงถูก Push ลงในสแต็ก (รูปที่ 4.15(d))
  6. โอเปอเรเตอร์ตัวถัดไปคือ C ถูกคัดลอกไปยังนิพจน์ Postfix (รูปที่ 4.15(e))
  7. โอเปอเรเตอร์- ถูกนำไปเปรียบเทียบกับโอเปอเรชันในสแต็ก โดยลำดับความ
    สำคัญของโอเปอเรชัน – น้อยกว่าโอเปอเรชัน * ดังนั้นจึง Pop สแต็กออก
    ไปยัง Postfix จากนั้นก็ทำการ เปรียบเทียบลำดับความสำคัญระหว่างโอเปอเร
    เตอร์–กับ +ซึ่งต่างก็มีระดับความสำคัญ เท่ากัน จึงทำการ Pop สแต็กออกไปยัง
    postfix หลังากนั้นสแต็กก็จะว่าง ก็ดำเนินการ Pushโอเปอเรเตอร์ – ลงในสแต็ก
    (รูปที่4.15 (f))
  8. นิพจน์ Infix ตัวถัดไปคือโอเปอเรแรนด์ D ให้คัดลอกไปยัง Postfix (รูปที่ 4.15 (g))
  9. ตัวถัดไปคือโอเปอเรเตอร์ / ให้ทำการ Push ลงในสแต็ก (รูปที่ 4.15 (h))
  10. นิพจน์ Infix ตัวถัดไปคือโอเปอเรแรนด์ E ให้คัดลอกไปยัง Postfix (รูปที่ 4.15 (i))
  11. ได้กระทำการจนกระทั่งไม่มีนิพจน์ Infix แล้ว ให้ทำการ Pop สแต็กที่เหลืออยู่ไปยัง
    Postfix (รูปที่ 4.15(j))
             จากขั้นตอนการแปลงนิพจน์ Infix มาเป็นนิพจน์ Postfix ข้างต้น ณ ขณะนี้เรา
พร้อมที่จะทำการพัฒนาอัลกอริทึมการแปลงนิพจน์ Infix มาเป็นนิพจน์ Postfix โดยในที่นี้
สมมติว่ามีเพียงโอเอเรเตอร์ 3 ตัว ซึ่งทั้งสามโอปเอเรเตอร์นั้นมีลำดับความสำคัญดังนี้
      
         ลำดับความสำคัญ (Priority)              
โอเปอเรเตอร์
                        2                                     */
                        1                                     + -
                        0                                     (


ตัวอย่างที่ 4.3 จงแปลงนิพจน์ A*B-(C+D)+E ให้เป็นนิพจน์ Postfix
การหาผลลัพธ์จากนิพจน์ Postfix (Evaluating  Postfix  Expressions)

                   เราได้ทราบรายละเอียดแล้วว่า การนำสแต็กไปประยุกต์ใช้กับการหน่วงเวลา (Postponement) เพื่อจัดการแปลงนิพจน์ Infix มาเป็นนิพจน์ Postfix ได้อย่างไร และต่อ
ไปนี้จะแสดงให้เห็นถึงการนำนิพจน์ Postfix มาหาผลลัพธ์ เพื่อจะได้ให้ทราบว่าคอมไพ
เลอร์สามารถจัดการหาผลลัพธ์จากนิพจน์ Postfix ได้สะดวกกว่านิพจน์ Infix อย่างไร
โดยในที่นี้ขอยกตัวอย่างนิพจน์ Postfix เพื่อนำไปคำนวณหาผลลัพธ์ดังนี้
     A  B  C  +  *
     โดยที่ A=2   B=4  และ C=6


                  หลักการหาผลลัพธ์จากนิพจน์ Postfix ก็คือ จะทำการสแกนนิพจน์ Postfix จาก
ซ้ายไปขวา โดยโอเปอแรนด์ที่พบจะถูกใส่ไว้ในสแต็กไปเรื่อยๆ จนกระทั่งพบตัวโอเปอเรเตอร์ เมื่อพบแล้วจึงดำเนินการหาผลลัพธ์จากคู่โอเปอแรนด์ที่พบก่อนหน้า ผลการคำนวณจะเป็นไป
ตามขั้นตอนดังรูปที่ 4.15 ส่วนอัลกอริทึมการหาผลลัพธ์จากนิพจน์ Postfix แสดงไว้ในอัลกอริ
ทึมที่ 4.10
  รูปที่ 4.15 การคำนวณหาผลลัพธ์ของนิพจน์ Postfix
รีเคอร์ชัน (Recursion)
                   รีเคอร์ชัน (Recursion) เป็นหลักการที่จะนำมาใช้กับการโปรแกรมแบบ
รีเคอร์ชัน (Recursion) ซึ่งโดยปกติทั่วไป  อัลกอริทึมการทำงานซ้ำๆ สามารถเขียนได้ 2
รูปแบบด้วยกันคือ
1. การวนรอบ (Iteration)
               เป็นเทคนิคที่ใช้หลักการวนรอบหรือลูป (Looping) เพื่อทำกระบวนการนั้นซ้ำๆ
จนกระทั่งตรงตามเงื่อนไขการควบคุมลูปก็จะหลุดออกจากลูป
2.การเรียกตัวเอง (Recursion)
               เป็นเทคนิคการวนซ้ำเพื่อทำกระบวนการหรือชุดคำสั่งนั้นซ้ำๆ ด้วยวิธีการเรียก
ตัวเองเพื่อทำซ้ำ วิธีนี้ผู้เขียนโปรแกรมจำเป็นต้องมีความรู้ในหลักการดังกล่าวเป็นอย่างดี
ซึ่งถือว่าเป็นโปรแกรมที่มีความซับซ้อนมากกว่าวิธีแรก แต่ก็มักได้มาซึ่งโปรแกรมที่มีขนาด
เล็กและกระชับมากกว่า
                   รีเคอร์ชัน คือกระบวนการทำงานซ้ำๆ ด้วยการใช้เทคนิคเรียกตัวเองเพื่อทำงาน
ซึ่งถือว่าเป็นสิ่งที่ค่อนข้างเข้าใจยากสำหรับผู้เริ่มต้นศึกษา โดยประเด็นของรีเคอร์ชันก็คือใน
ทุกๆครั้งของการเรียกตัวเองเพื่อใช้งาน จะต้องสามารถแก้ไขปัญหาในส่วนนั้นได้ รวมถึงการ
ลดขนาดของปัญหาลง  แต่อย่างไรก็ตาม ภาษาคอมพิวเตอร์บางภาษาเท่านั้นที่สนับสนุนหลัก
การทำงานของรีเคอร์ชัน เช่น ภาษา C , JAVA และ PASCAL เป็นต้น ในขณะที่ภาษาเก่า
แก่อย่างภาษา COBOL หรือภาษา FORTRAN จะไม่สนับสนุนหลักการทำงานดังกล่าว

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

ตัวอย่างเช่น ให้ทำการหาค่า Factorial  (4)

 Factorial  (4) =   4 x 3 x 2 x 1   =  24
                   จากตัวอย่างข้างต้นจะเห็นไดว่า มีกระบวนการทำงานแบบซ้ำๆ เกิดขึ้น ซึ่งก็คือ
ค่าของแฟคทอเรียล n จะเกิดจากการคูณกันตั้งแต่ n จนถึง 1 ซึ่งกระบวนการดังกล่าวใช้หลัก
การทำงานเป็นรอบ ดังอัลกอริทึมที่ 4.1
 
อัลกอริทึมที่ 4.1 Iterative   Factorial  Algorithm
                   และต่อไปนี้จะทำการนิยามการทำงานของแฟคทอเรียลตามหลักการของรี
เคอร์ชัน ซึ่งสามารถนิยามได้ดังนี้
  Factorial  (n) = 1 {if n = 0}
  Factorial  (n) = n x (Factorial (n-1)) {if  n > 0}


                  โดยสมมติว่า ต้องการหาค่า Factorial(3) หรือ 3! จากนิยามข้างต้น  ก็จะเป็น
ไปดังรูปที่ 4.16 โดยให้ศึกษาจากรูปที่ 4.16 อย่างระมัดระวัง จะเห็นได้ว่าจะมีการใช้รี
เคอร์ชันในการแก้ไขปัญหา 2 ประการด้วยกันคือ ประการแรกเป็นการแตกปัญหาจากบนลง
ล่าง (topto the bottom) จากนั้นจึงค่อยส่งค่าคืนกลับในลักษณะกลับขั้นตอน คือล่างขึ้นบน (bottom to the top) ซึ่งการทำกลับขั้นตอนนี้ เป็นการทำงานในรูปแบบของรีเคอร์ซีพด้วย
การนำสแต็กมาช่วยใช้งาน โดยสแต็กจะดำเนินการเก็บค่า nและ Factorial(n-1)ไว้ในแต่
ละครั้งที่มีการคำนวณหาค่าของ Factorial(n) กล่าวคือ ในขั้นตอนของการแตกประเด็นปัญ
หา ซึ่งเป็นขั้นตอนที่ยังไม่สามารถคำนวณ.ค่าได้ ดังนั้นจึงต้องทำการ Pushค่า n และ
Factorial(n-1)ไว้ในสแต็กก่อน จนกระทั่งถึงจุดที่สามารถคำนวณได้แล้ว(จุดวกคืนกลับ)
ซึ่งก็คือ Factorial(0)ที่มีค่าเท่ากับ 1 โดยหลังจากนั้นข้อมูลในสแต็กก็จะถูก Pop ออกไป
และนำค่านี้ไปแทนที่ Factorial(n-1) ไปเรื่อยๆจนกระทั่งได้ผลลัพธ์สุดท้ายคือ Factorial(n)
       
          รูปที่ 4.16 การคำนวณหา Factorial(3) ด้วยโปรแกรมรีเคอร์ซีพ
                  ส่วนอัลกอริทึมการหาค่าแฟคทอเรียลด้วยหลักการรีเคอร์ชัน แสดงได้ดัง
อัลกอริทึมที่ 4.2 ซึ่งจะเห็นได้ว่าจะใช้ประโยคคำสั่ง if…else แทนการใช้ประโยคคำสั่ง
ควบคุมลูป ในขณะที่รูปที่ 4.17 จะเป็นการแสดงรายละเอียดการคำนวณค่า Factorial(3)
ด้วยอัลกอริทึมแบบรีเคอร์ซีพ
อัลกอริทึมที่ 4.2 Recursive  Factorial  Algorithm
                                 
                            
รูปที่ 4.17 แสดงการทำงานของ Factorial(3) ด้วยอัลกอริทึมแบบ
               รีเคอร์ชีพที่เรียกตัวเองทำงาน

ลิงก์ลิสต์ (Linked Lists)

แนวคิดพื้นฐานเกี่ยวกับลิสต์แบบเชิงเส้น (Linear List Concepts)
                      การเรียงลำดับของข้อมูลภายในลิสต์ที่มีลักษณะเป็นลำดับต่อเนื่อง สามารถอธิบายได้ โดย
         สมาชิก หรืออิลิเมนต์แต่ละตัวจะเชื่อมโยงอิลิเมนต์ตัวถัดไปลักษณะเป็นรายการต่อเนื่องกันไปตัว
         อย่างเช่น อิลิเมนต์ ลำดับที่สองจะอยู่ถัดจากอิลิเมนต์ลำดับที่หนึ่ง หรืออิลิเมนต์ลำดับที่สามจะอยู่ถัด
         จากอิลิเมนต์ตัวที่สอง ซึ่งจะลำดับเช่นนี้ไปเรื่อยๆ จนกระทั่งถึงอิลิเมนต์ลำดับที่ n+1ซึ่งจะอยู่ถัดจาก
         อิลิเมนต์ลำดับที่ n และด้วยคุณสมบัติดังที่กล่าวมานั้นเราจึงเรียกว่าลิสต์นั่นเอง
                       รูปที่ 3.1 โครงสร้างข้อมูลแบบเชิงเส้น (Linear List)
นอกจากนี้แล้ว ลิสต์แบบเชิงเส้นยังสามารถแบ่งออกเป็น 2 ประเภทด้วยกันคือ

 - ลิสต์แบบทั่วไป (General List) 

           ลักษณะของลิสต์แบบทั่วไป เราสามารถแทรกหรือลบรายการลิสต์ ณ ตำแหน่งใดๆก็ได้ โดย
ปราศจากข้อจำกัดในด้านของการดำเนินงานภายในลิสต์ นอกจากนี้แล้ว ลิสต์แบบทั่วไปยังสามารถ
แบ่งออกเป็น
  1. ลิสต์แบบสุ่ม (Random List) ซึ่งข้อมูลภายในลิสต์จะไม่เรียงลำดับ
  2. ลิสต์แบบเรียงลำดับ (Order List) ที่ข้อมูลภายในลิสต์จะถูกจัดเรียงอย่างเหมาะสมด้วย
    คีย์
  3. ลิสต์แบบมีข้อจำกัด (Restricted List)ข้อมูลที่อยู่ภายในลิสต์แบบมีข้อจำกัด ไม่ว่าจะ
    เป็นการเพิ่มหรือการลบข้อมูลออกจากลิสต์จะต้องกระทำที่จุดปลายด้านใดด้านหนึ่งของลิสต์
    เท่านั้น เช่น ลิสต์แบบ FIFO ก็คือคิว ในขณะที่ลิสต์แบบ LIFO ก็คือสแต็ก
การดำเนินงานพื้นฐานของลิสต์ (Basic Operations)

           การดำเนินงานพื้นฐานของลิสต์ ประกอบด้วยการแทรก (Insertion) การลบ (Delete)
การอ่าน (Retrieval) และการท่องเข้าไปในลิสต์ (Traversal) โดยการแทรกก็คือการเพิ่มสมาชิก
ใหม่เข้าไปในลิสต์ การลบก็คือการนำสมาชิกออกจากลิสต์ การอ่านก็คือการประมวลผลในแต่ละอิลิ
เมนต์ภายในลิสต์ตามลำดับ เช่น การท่องเข้าไปในลิสต์เพื่อหาผลรวมของคะแนนดิบของนักศึกษา
ทั้งหมด และนำมาคิดเป็นคะแนนเฉลี่ย เป็นต้น


3.1 แนวคิดของลิสต์ลิสต์ (Linked List Concepts)
แนวคิดพื้นฐานเกี่ยวกับลิสต์แบบเชิงเส้น (Linear List Concepts)

           
  โครงสร้างข้อมูลแบบอาร์เรย์และโครงสร้างข้อมูลแบบลิงก์ลิสต์ก็ยังสามารถนำไปพัฒนา
โครงสร้างข้อมูลแบบสแต็กและคิว

                              รูปที่ 3.2 โครงสร้างข้อมูลแบบสแต็กและคิว

         ในการใช้อาร์เรย์เพื่อจัดเก็บข้อมูลแบลิสต์นั้นข้อมูลภายในหน่วยความจำจะถูกจัดเก็บเป็นลำ
ดับต่อเนื่องกันไปซึ่งทำให้ง่ายต่อการอ้างอิงข้อมูล เพียงแค่ตำแหน่ง Bass Address ก็สามารถจัด
การกับข้อมูลภายในได้แล้วแต่สำหรับลิงก์ลิสต์นั้นจะมีข้อแตกต่างตรงที่ข้อมูลภายในหน่วยความจำ
จะไม่ได้อยู่ในลำดับต่อเนื่องเหมือนกับอาร์เรย์ แต่จะถูกเชื่อมโยงด้วยลิงก์หรือพอยน์เตอร์ ดังนั้นอิลิ
เมนต์แต่ละตัวภายในลิงก์ลิสต์จะมีการบรรจุแอดเดรสเพื่อชี้ไปยังตำแหน่งโหนดตัวถัดไป ซึ่งแต่ละ
โหนดก็จะบรรจุส่วนสำคัญอยู่ 2 ส่วนด้วยกันคือ
ข้อมูล (Data)
         ในส่วนของข้อมูลจะมีการจัดเก็บสารสนเทศที่สามารถนำไปใช้ในการประมวลผลตามที่ต้อง
การต่อไป
ลิงค์ (Link)
          ในส่วนของลิสต์นั้น จะใช้สำหรับเชื่อมโยงไปยังข้อมูลโดยเริ่มต้นจากเฮดพอยน์เตอร์ที่ชี้ไป
ยังตำแหน่งโหนดแรกของลิสต์ จากนั้นลิงก์ในแต่ละโหนดก็จะเชื่อมโยงไปยังโหนดตัวถัดไปเรื่อยๆ
ส่วนชื่อของลิสต์จะเป็นชื่อเดียวกันกับชื่อตัวแปรพอยน์เตอร์โดยลิงก์ลิสต์อย่างง่ายที่จะกล่าวถึงต่อ
ไปนี้คือ ซิงเกิลลิงก์ลิสต์ (Single-Linked List) ซึ่งจะมีเพียงลิงก์เดียวที่ใช้เชื่อมโยงไปยังโหนด
ตัวถัดไป
ตัวอย่างการแทนลิงก์ลิสต์ในหน่วยความจำ



3.2 โครงสร้างข้อมูลแบบลิงก์ลิสต์ (Linked List Data Structure)
                 สำหรับโครงสร้างข้อมูลลิงก์ลิสต์ ประกอบด้วย
 โครงสร้างโหนดส่วนหัว (Head Node Structure)  
            ภายในโหนดส่วนหัวจะมีเพียงหนึ่งพอยน์เตอร์ที่จะชี้ไปยังลิสต์ คือ เฮดพอยน์เตอร์ ภายใน
โครงสร้างส่วนนี้จะมี Metadata ที่เอาไว้อธิบายข้อมูลภายในลิสต์ ภายในนี้คือฟิลด์ Count เพื่อ
เอาไว้บอกว่าในลิสต์นี้มีจำนวนสมาชิกทั้งหมดเท่าไร ซึ่งสามารถเพิ่มหรือลดลงได้ หากมีการแข้
ไขข้อมูลในลิสต์
โครงสร้างโหนดข้อมูล (Data  Node Structure)
             โครงสร้างโหนดข้อมูลประกอบด้วยส่วนข้อมูลและลิงก์ สำหรับข้อมูล (Data type) ของ
ลิสต์นั้นจะขึ้นอยู่กับการนำไปประยุกต์ใช้ แต่ปกติแล้ว ชนิดข้อมูลจะเป็นไปในลักษณะที่แสดงไว้ที่
ด้านล่างและที่สำคัญ ชนิดข้อมูลจะต้องได้รับการปรับปรุงรายละเอียดอยู่เสมอหลังจากถูกสร้างขึ้น
ความจริงแล้วมีโครงสร้างข้อมูลอยู่หลายชนิดที่สามารถนำมาสร้างลิสต์ แต่หัวข้อต่อไปนี้จะขอ
กล่าวถึงการสร้างลิสต์ด้วยลิงก์ลิสต์เป็นสำคัญ โดยสามารถสรุปคุณสมบัติของลิงก์ลิสต์ได้ดังนี้
คือ
1.ลิงก์ลิสต์จะใช้เฮดพอยน์เตอร์ (pHead) เป็นตัวชี้ไปยังโหนดแรกของลิสต์ ในขณะที่พอยน์
เตอร์หรือลิงก์ของแต่ละโหนดก็จะเชื่อมโยงลิงก์ไปยังโหนดตัวถัดไป โดยโหนดตัวสุดท้ายที่ไม่
มีลิงก์ให้เชื่อมต่อจะถูกกำหนดค่าให้เป็น null ซึ่งในที่นี้ใช้สัญลักษณ์   S  แทน

2. โหนดข้อมูลจะประกอบด้วย Data และ Link โดยที่

- Data คือข้อมูลหรือสารสนเทศที่สามารถนำไปใช้ประโยชน์
- Link คือตัวชี้หรือพอยน์เตอร์ที่ใช้สำหรับเชื่อมโยงไปยังโหนดถัดไป

3. ไม่มีความสัมพันธ์ทางกายภาพระหว่างโหนด

4. ข้อมูลที่จัดเก็บภายในหน่วยความจำไม่จำเป็นต้องอยู่ติดกัน

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

2.ไม่จำเป็นต้องขยับอิลิเมนต์ของลิสต์ไปข้างหน้าเพื่อให้เกิดพื้นที่ว่าง ในกรณีที่มีการลบอิลิเมนต์
    ตรงส่วนหน้าหรือส่วนกลางของลิสต์เช่นเดียวกับอาร์เรย์

3.ใช้พื้นที่หน่วยความจำได้เต็มประสิทธิภาพ เนื่องจากหากข้อมูลภายในลิสต์มีน้อยก็ใช้น้อย
   ซึ่งผิดกับอาร์เรย์ที่ต้องสูญเสียพื้นที่ไปในทันที ถึงแม้จะไม่มีข้อมูลภายในลิสต์ก็ตาม

3.3 อัลกอริทึมของลิงก์ลิสต์ (Linked List Algorithm)
                ในที่นี้จะมีฟังก์ชันการดำเนินงานบนลิงก์ลิสต์ 10 ฟังก์ชันด้วยกัน ซึ่งประกอบด้วยฟังก์ชัน
CreateList, Insert Node, Delete Node, Search List, Retrieve Node, Empty List,
Full List, ListCount, Traverse List และ Destory List ซึ่งถือว่าเพียงพอต่อการนำไปใช้เพื่อ
แก้ไขปัญหาต่างๆ อย่างไรก็ตาม หากแอปพลิเคชันบางตัวจำเป็นต้องมีการใช้ฟังก์ชันอื่นๆ เพิ่ม
เติมนอกเหนือจากฟังก์ชันทั้ง 10 ที่กล่าวมา ก็สามารถดำเนินการสร้างเพิ่มเติมได้อีก โดยแต่ละ
ฟังก์ชันจะมีการกำหนดชื่อเรียกของตัวเองพร้อมรายละเอียดอย่างย่อ รวมถึงพารามิเตอร์ที่เรียก
ใช้ ซึ่งแต่ละอัลกอริทึมสามารถอธิบายรายละเอียดได้ดังต่อไปนี้
1.การสร้างลิสต์ (Create List)
                ฟังก์ชัน Create List เป็นการกำหนดโครงสร้างโหนดส่วนหัวและกำหนดค่าเริ่มต้นให้
กับ  metadata    สำหรับลิสต์โดยในที่นี้จะมี   metadata   อยู่  2  ตัวด้วยกัน  แต่ก็อาจขยายเพิ่ม
เติมได้

2.การแทรกโหนด (Insert Node) 
                เป็นฟังก์ชันที่ใช้สำหรับแทรกโหนดเพิ่มเข้าไปในลิสต์ ณ ขณะนี้ต้องการเพียงว่า โหนด
ก่อนหน้า (Predecessor) ของโหนดใหม่ที่จะแทรกนั้นคือโหนดใดเมื่อได้รับการแจ้งว่าโหนดก่อน
หน้าคือโหนดใดแล้ว ก็จะทำการแทรกข้อมูลเพิ่มตามขั้นตอนต่อไปนี้

1.จัดสรรหน่วยความจำสำหรับโหนดใหม่พร้อมกับข้อมูล
2.กำหนดตัวชี้ให้กับลิงก์ฟิลด์ของโหนดใหม่
3.นำตัวชี้ที่อยู่ก่อนหน้าโหนดใหม่ชี้มายังโหนดใหม่
                 จาก 3 ขั้นตอนของการแทรกโหนดเข้าไปยังลิสต์ข้างต้น เป็นเพียงการนำเสนอขั้นตอน
ในรูปแบบอย่างง่ายเพื่อให้เกิดความเข้าใจในเบื้องต้นเท่านั้น แต่ความเป็นจริงยังมีรายละเอียดอีก
หลายอย่าง
                 ในการแทรกโหนดเข้าไปในลิสต์นั้น ขั้นตอนแรกจำเป็นต้องรู้ตำแหน่งที่อยู่ของโหนด
ก่อนหน้าโหนดใหม่ที่ต้องการจะแทรกเสียก่อน ซึ่งโหนดนี้จะระบุตัวชี้ที่เป็นไปได้ทั้ง 2 สถานะด้วย
กันคือ อาจเป็นแอดเดรสของโหนดถัดไป หรือเป็นค่า null ก็ได้ การที่จำเป็นต้องรู้ตำแหน่งโหนด
ก่อนหน้าก็เพราะว่าโหนดนี้จะมีลิงก์ที่ใช้สำหรับเชื่อมโยงไปยังโหนดถัดไป ซึ่งลิงก์ดังกล่าวนี้จะนำ
มาแทนตำแหน่งลิงก์ของโหนดใหม่เพื่อชี้ไปยังโหนดข้างหลัง (Successor) ที่อยู่ถัดจากโหนด
ใหม่นั่นเอง แต่กรณีดังกล่าวเป็นการประยุกต์ใช้กับการแทรกระหว่างโหนดภายในลิสต์ แต่ถ้าเป็น
กรณีลิงก์ของโหนดก่อนหน้ามีค่าเป็นnull นั่นหมายความว่าเป็นการแทรกโหนดที่ท้ายลิสต์ในการ
แทรกโหนดเพิ่มเข้าไปในลิสต์สามารถกระทำได้ 4 รูปแบบด้วยกันคือ
2.1 การแทรกโหนดในลิสต์ว่าง (Insert into Empty List)
                   กรณีนี้เป็นการแทรกโหนดเพิ่มเข้าไปในลิสต์ในขณะที่ลิสต์ว่างเปล่าหรือไม่มีข้อมูล
ใดๆ อยู่นั่นหมายถึงเป็นการแทรกสมาชิกตัวแรกเข้าไป ซึ่งขณะนั้นเฮดพอยน์เตอร์จะมีค่าเป็น
null เนื่องจากเป็นลิสต์ว่าง หลังจากนั้นมีลิสต์ใหม่ที่ต้องการแทรกเพิ่มเข้ามา (pNew)
(a) Before add
                  
                                                                       (b) After add
รูปที่ 3.4 แสดงการแทรกโหนดเมื่อลิสต์ภายในว่าง
2.2 การแทรกโหนดที่ตำแหน่งแรก (Insert at Beginning)
               เป็นการแทรกโหนดเข้าไปไว้ในตำแหน่งโหนดแรก ซึ่งทำให้โหนดที่เคยอยู่ลำดับแรก
เดิมต้องมาต่อท้ายโหนดใหม่ที่แทรกเข้าไป ขั้นตอนแรกของการแทรกข้อมูลที่โหนดแรกของลิสต์
จะต้องทราบถึงตัวชี้ของตัว Predecessor ก่อน ซึ่งหากไม่มี หรือมีค่าเป็น nullก็หมายความว่า
เป็นการแทรกโหนดแรกในลิสต์ว่างเปล่า
                               การแทรกโหนดที่ลำดับแรกจะมีวิธีการคือ ให้นำตัวชี้ของโหนดใหม่ชี้ไปยังโหนดแรก
           ของ ลิสต์หลังจากนั้นก็ทำการกำหนดเฮดพอยน์เตอร์ชี้ไปยังโหนดใหม่ ซึ่งเราจะรู้ตำแหน่งแอด
           เดรสของโหนดใหม่อยู่แล้วหลังจากที่ได้สร้างขึ้นมา

                                                                     (a) Before add
                   
                                                           (b) After add
                     รูปที่ 3.5 แสดงการแทรกโหนดไว้ที่ตำแหน่งแรกของลิสต์
2.3 การแทรกโหนดที่กึ่งกลางของลิสต์ (Insert in Middle)
            การเพิ่มโหนดในส่วนกลางของลิสต์หรือการแทรกระหว่างโหนด ในขั้นตอนแรก ต้องรู้ตำ
แหน่งโหนดก่อนหน้าเสียก่อน ซึ่งก็คือตัว Predecessor (pPre) ความสำคัญของโหนด
Predecessor ก็คือจะมีลิงก์ที่ใช้เชื่อมโยงไปยังโหนดถัดไป
            ในการแทรกโหนดระหว่างสองโหนด ตัวชี้หรือลิงก์ฟิลด์ของโหนดใหม่จะชี้ไปยังโหนด
Successor ในขณะที่ตัวชี้ pPre ก็จะชี้ไปยังโหนดใหม่
                                                   (a) Before add
                   
                                                       (b) After add
รูปที่ 3.6 แสดงแทรกโหนดที่กึ่งกลางของลิสต์
2.4 การแทรกโหนดที่ท้ายลิสต์ (Insert at End)
              เมื่อมีการเพิ่มโหนดที่ส่วนท้ายของลิสต์ เราต้องการเพียงแค่ตัวชี้ของ Predecessor
เพื่อชี้ไปยังโหนดใหม่เท่านั้น ซึ่งในที่นี้จะไม่มีโหนด Successor เนื่องจากเป็นการแทรกที่ท้าย
ลิสต์ดังนั้นลิงก์ฟิลด์ของโหนดใหม่จึงถูกกำหนดให้เป็นค่า null

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

            
(b) After add
รูปที่ 3.7  การแทรกโหนดไว้ที่ส่วนท้ายของลิสต์
                 จากรายละเอียดการแทรกโหนดเข้าไปในลิสต์ในรูปแบบต่างๆไม่ว่าจะเป็นการแทรก
โหนดในขณะที่ลิสต์ว่าง การแทรกโหนดที่ตำแหน่งแรกของลิสต์ กึ่งกลางหรือท้ายลิสต์ก็ตามและต่อ
ไปนี้ขอกล่าวถึงอัลกอลิทึมที่ใช้สำหรับการแทรกโหนดเข้าไปในลิสต์ โดยจะมีพอยน์เตอร์ชี้ไปยังลิสต์
ตัว Predecessor และข้อมูลที่ต้องการแทรก ซึ่งจะต้องมีการจัดสรรหน่วยความจำสำหรับโหนดใหม่
และทำการปรับเปลี่ยนพอยน์เตอร์เชื่อมโยงที่เหมาะสมต่อไป เมื่ออัลกอริทึมนี้ทำงานจนสัมฤทธิ์ผล
จะรีเทิร์นค่าตรรกะเป็นจริงเมื่อแทรกโหนดใหม่ ซึ่งก็คือข้อผิดพลาดในสถานะ Overflow นั่นเอง
3.การลบโหนด (Delete Node)
                  อัลกอริทึมการลบโหนดออกจากลิสต์นอกจากจะนำโหนดที่ถูกลบส่งคืนแก่หน่วยความ
จำระบบเพื่อจะได้นำไปใช้งานอื่นต่อไปแล้ว ยังต้องมีการปรับเปลี่ยนตัวชี้ใหม่ด้วย สำหรับขั้นตอน
แรกของการลบโหนด จะต้องค้นหาตำแหน่งของโหนดที่ต้องลบ (pLoc) ภายในลิสต์ให้พบก่อน เมื่อ
พบตำแหน่งโหนดที่ต้องการลบภายในลิสต์แล้ว จะทำให้ทราบตำแหน่งแอดเดรสของ Predecessor
(pPre) ซึ่งก็คือโหนดที่อยู่ก่อนหน้าโหนดที่ต้องการลบนั่นเอง หลังจากนั้นก็จะกำหนดลิงก์ฟิลด์ของ
โหนด Predecessorชี้ไปยังโหนด Successor ซึ่งเป็นโหนดที่อยู่ด้านหลังโหนดที่ถูกลบ จากนั้น
ก็จะนำพื้นที่หน่วยความจำที่เก็บโหนดที่ถูกลบไปนั้นส่งคืนแก่ระบบเพื่อนำไปใช้งานอื่นต่อไป
3.1การลบโหนดที่ตำแหน่งแรก  (Delete First Node)

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

3.2 การลบโหนดโดยทั่วไป  (General Delete Case)
                การลบโหนดออกจากลิสต์ในกรณีทั่วไป ซึ่งประกอบด้วยการลบโหนดที่อยู่กึ่งกลางภาย
ในลิสต์ และการลบโหนดที่ท้ายลิสต์ ทั้งสองกรณีต่างก็สามารถใช้ตรรกะเดียวกันในการใช้งานใน
การลบโหนดลบโหนดออกจากลิสต์ ไม่ว่าจะเป็นโหนดที่อยู่กึ่งกลางลิสต์หรือที่ท้ายลิสต์ก็ตามขั้นตอน
แรกจำเป็นต้องรู้ตำแหน่งโหนดที่ลบเสียก่อน จากนั้นก็กำหนดตัวชี้ของโหนด Predecessor ให้ชี้
ไปยังโหนดSuccessor ที่อยู่ถัดจากโหนดในตำแหน่ง pLoc หรือโหนดที่ต้องการลบนั่นเองโดย
แสดงขั้นตอนการกระทำได้ดังรูป

                                          (a) Before delete
      
                                               (b) After delete
รูปที่ 3.8 การลบโหนดออกจากลิสต์โดยทั่วไป
                      ส่วนกรณีการลบโหนดที่ท้ายลิสต์ออกเมื่อโหนดท้ายลิสต์ได้ถูกลบออกไปแล้ว
ค่าของ null pointer จะถูกย้ายไปเก็บไว้ในตำแหน่งฟิลด์ของโหนด Predecessorดังนั้น
โหนดที่เคยอยู่ก่อนหน้าก็จะกลายเป็นโหนดในลำดับสุดท้าย ส่วนโหนดท้ายลิสต์ที่ถูกลบไป
ก็จะส่งคืนกลับไปยังระบบ

3.4 การค้นหาข้อมูลภายในลิสต์ (Search List)
                เป็นฟังก์ชันที่ใช้สำหรับค้นหาข้อมูลภายในลิสต์ซึ่งตามปกติแล้วการค้นหาข้อมูลภายใน
ลิสต์สามารถค้นพบได้จากอัลกอริทึมที่หลากหลายเพื่อใช้งานในรูปแบบต่างๆ ไม่ว่าจะเป็น

การแทรกโหนด  
ที่จำเป็นต้องรู้ตำแหน่งตัว Predecessorหรือโหนดก่อนหน้าของโหนดที่ต้อง
การจะแทรกก่อน

การลบโหนดออกจากลิสต์  ขั้นแรกต้องค้นหาโหนดที่ต้องการลบให้พบก่อน แล้วจึงค่อย กำหนด
ให้โหนด Predecessor ชี้ไปยังตำแหน่งโหนด Successor จากนั้นจึงปลดโหนดที่ลบไปนั้นคืน
แก่ระบบ

การดึงข้อมูลจากลิสต์ ขั้นแรกจำเป็นต้องค้นหาข้อมูลภายในลิสต์ให้พบก่อน จึงสามารถดึงข้อมูล
นั้นออกมาใช้งานได้ เรามีความจำเป็นต้องค้นหาข้อมูลในรูปแบบ Sequential Search กับกรณี
การค้นหาข้อมูลในลิสต์ที่สร้างด้วยลิงก์ลิสต์ เป็นเพราะว่าโหนดต่าง ๆ ที่อยู่ภายในลิสต์นั้นไม่ได้มี
ความสัมพันธ์กันในทางกายภาพเลย ซึ่งแตกต่างจากลิงก์ลิสต์ที่สร้างด้วยอาร์เรย์ ที่สามารถค้นหา
ข้อมูลภายในอาร์เรย์ได้ด้วยวิธี Binary Search ซึ่งมีประสิทธิภาพเหนือกว่า สำหรับการค้นหาข้อ
มูลแบบ Sequential Search ภายในลิงก์ลิสต์ สามารถเรียกอีกชื่อหนึ่งว่า Ordered List
Search


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

ลิสต์ว่าง (Empty List) เป็นฟังก์ชันที่ใช้ตรวจสอบว่าภายในลิสต์ว่างหรือไม่ ซึ่งเป็นโมดูลแบบ
ง่ายที่รีเทิร์นค่าตรรกะ ณ ขณะนั้นกลับไป เช่น รีเทิร์นค่าตรรกะเป็นจริงกลับไปเมื่อลิสต์ว่างหรือใน
ทางตรงกันข้ามก็จะรีเทิร์นค่าตรรกะเท็จกลับไป เป็นต้น

ลิสต์เต็ม 
(Full List) เป็นฟังก์ชันที่ใช้ตรวจสอบว่าภายในลิสต์นั้นเต็มหรือไม่ ซึ่งก็จัดเป็นโมดูล
แบบง่ายเช่นกันด้วยการรีเทิร์นค่าตรรกะในขณะนั้นกลับไป อย่างไรก็ตาม ฟังก์ชันนี้อาจไม่จำเป็น
ต้องใช้ก็ได้โดยเฉพาะในภาษา C เนื่องจากลิงก์ลิสต์ใช้หน่วยความจำแบบไดนามิก

จำนวนสมาชิกในลิสต์  (List  Count) ฟังก์ชัน List  Count ภายในโมดูลจะมีเพียงประโยค
คำสั่งเดียวเท่านั้น แต่ก็เป็นฟังก์ชันที่มีความสำคัญทีเดียว เพราะว่าจะแจ้งจำนวนสมาชิกหรือจำ
นวนอิลิเมนต์ที่มีอยู่ในขณะนั้นให้กับโมดูลที่เรียก แทนที่จะต้องท่องเข้าไปในลิสต์เพื่อนับสมาชิก
แต่ละอิลิเมนต์แทน


3.5 การท่องเข้าไปในลิสต์ (Traverse List)
                  อัลกอริทึมการท่องเข้าไปในลิสต์ จะเริ่มต้นที่โหนดแรกและสแกนไปทีละโหนดจนกระ
ทั่งสิ้นสุดที่โหนดสุดท้าย ตรรกะของการท่องเข้าไปในลิสต์นั้น สามารถใช้อัลกอริทึมเพื่อใช้งานได้
อย่างหลากหลาย เช่น
  1. การเปลี่ยนแปลงค่าในแต่ละโหนด
  2. การพิมพ์ข้อมูลภายในลิสต์
  3. การคำนวณหาผลรวมของฟิลด์ภายในลิสต์
  4. การคำนวณหาค่าเฉลี่ย
                  ซึ่งกรณีข้างต้น ล้วนแต่จำเป็นต้องนำมาใช้เพื่อการประมวลผลงานทางธุรกิจที่จำเป็น
ทั้งสิ้น สำหรับการท่องเข้าไปในลิสต์  จะต้องมีการกำหนดตัว Walking Pointer เพื่อให้พอยน์เตอร์
นี้เคลื่อนที่จากโหนดไปยังโหนดในแต่ละอิลิเมนต์ที่ได้โปรเซส โดยซูโดโค้ดที่กำหนดตัว Walking
Pointer เพื่อใช้ท่องไปยังลิสต์จะมีการกำหนดลูปเพื่อให้พอยน์เตอร์เคลื่อนที่ไปยังโหนดถัดไปใน
ลักษณะเรียงลำดับภายในลิสต์
                   เราจะเริ่มต้นจากการติดตั้ง Walking Pointer นี้ที่โหนดแรกภายในลิสต์แล้วใช้ลูป
เพื่อการประมวลผลด้วยการให้ท่องไปทีละโหนดเป็นลำดับไปเรื่อย ๆจนกระทั่งท่องไปยังโหนดสุด
ท้ายการทำงานในแต่ละลูปจะมีการเรียกใช้โมดูลเพื่อประมวลผลทุกครั้ง และส่งค่าข้อมูลกลับไป
ในขณะที่ Walking Pointerท่องไปยังแต่ละโหนดและเมื่อประมวลผลจนกระทั่งถึงโหนดสุดท้าย
ก็จะสิ้นสุดการทำงานของลูป

                  ในความเป็นจริง เราสามารถออกแบบวิธีการท่องเข้าไปในลิสต์ได้ 2 วิธีด้วยกัน วิธี
แรกก็คือผู้ใช้ทำการควบคุมลูปด้วยการเรียกใช้ฟังก์ชันเพื่อท่องไปยังอิลิเมนต์ถัดไปภายในลิสต์
ส่วนวิธีที่สองคือใช้โมดูลการท่องเข้าไปในลิสต์ด้วยการเรียกใช้อัลกอริทึมที่ได้เตรียมมาให้อยู่
แล้วมาทำการประมาวลผลข้อมูล แต่สำหรับในที่นี้จะเลือกการพัฒนาตามแนวทางของวิธีแรก
เพราะว่าเป็นวิธีที่ค่อนข้างยืดหยุ่น เนื่องจากหากแอปพลิเคชันมีความต้องการประมวลผลข้อมูล
เพียงกึ่งหนึ่งภายในลิสต์ทั้งหมดโปรแกรมเมอร์ก็สามารถสร้างลูปเพื่อควบคุมตามเงื่อนไขได้
    
รูปที่ 3.9 แสดงการท่องเข้าไปในลิสต์ (List Traversal)
การยกเลิกการใช้งานลิสต์ (Destroy List)

เมื่อลิสต์ไม่มีความต้องการที่จะใช้งานอีกต่อไป สมควรที่จะถูกยกเลิกการใช้งานเสีย โดยฟังก์ชัน
Destroy List จะดำเนินการลบโหนดทุกโหนดที่ยังคงอยู่ภายในลิสต์ออกไปทั้งหมด และส่งคืนแก่
หน่วยความจำในระบบเพื่อนำไปใช้งานอื่นต่อไป
 ลิงก์ลิสต์ชนิดอื่นๆ (Others Linked Lists)
ความรู้เกี่ยวกับลิงก์ลิสต์ที่กล่าวมาในข้างต้นเป็นซิงเกิลลิงก์ลิสต์ (Single-Linked List) เนื่องจาก
บรรจุเพียงลิงก์เดียวที่ชี้ไปยังโหนดถัดไปเพียงโหนดเดียว ดังนั้นจึงทำให้ลิงก์ลิสต์ประเภทนี้มีข้อจำ
กัดคือไม่สามารถท่องเข้าไปยังลิงก์ลิสต์ในลักษณะจากหลังไปหน้าได้ การเข้าไปดำเนินการใดๆภาย
ในลิสต์จึงต้องเริ่มต้นจากโหนดแรกไปยังโหนดสุดท้าย และไม่สามารถเดินแบบย้อนกลับรายละเอียด
พื้นฐานเกี่ยวกับลิงก์ลิสต์ชนิดอื่นๆ ซึ่งมีความซับซ้อนมากกว่าซิงเกิลลิงก์ลิสต์ โดยในที่นี้ คือลิงก์ลิสต์
ประเภทต่อไปนี้

1. เซอร์คูลาร์ลิงก์ลิสต์ (Circular-Linked List)
2. ดับเบิลลิงก์ลิสต์ (Double-Linked List)

เซอร์คูลาร์ลิงก์ลิสต์ (Circular-Linked List)
ลิงก์ลิสต์แบบนี้จะให้โหนดสุดท้ายชี้กลับมาทีโหนดแรก และในส่วนของเฮดโหนดก็จะเพิ่ม rear
เข้ามาเพื่อเก็บตำแหน่งของโหนดสุดท้าย เมื่อมีการลบ เพิ่ม โหนดก็จะต้องเข้ามาแก้ไขข้อมูลใน
rear ทุกครั้ง

รูปที่ 3.10 Circular – linked List
ดับเบิลลิงก์ลิสต์ (Double-Linked List)
                 ดับเบิลลิงก์ลิสต์จัดเป็นลิงก์ลิสต์ประเภทหนึ่งที่มีความสามารถสูงทีเดียว กล่าวคือ ดับเบิล
ลิงก์ลิสต์ในแต่ละโหนดจะประกอบไปด้วยพอยน์เตอร์อยู่ 2 ตัว โดยตัวแรกจะใช้สำหรับชี้ไปยังตัวถัดไป
(Successor) และอีกตัวหนึ่งจะชี้ไปยังตัวก่อนหน้า (Predecessor)ภายในโครงสร้างเฮดโหนดหรือ
โหนดส่วนหัวจะประกอบไปด้วยข้อมูลอยู่ 3 ฟิลด์คือ ตัวนับ (count) ตำแหน่งเฮดพอยน์เตอร์ และ
พอยน์เตอร์ฟิลด์ rearโดยถึงแม้ว่าฟิลด์ rear อาจไม่จำเป็นต่อการใช้งานสำหรับดับเบิลลิงก์ลิสต์ก็ตาม
แต่อาจจำเป็นต้องนำมาใช้งานในบางครั้ง โดยเฉพาะการแทรกและการค้นหา ซึ่งจะทำให้เกิดประ
สิทธิภาพยิ่งขึ้น

รูปที่ 3.11 ดับเบิลลิงก์ลิสต์ (Double-Linked List)
                 แต่ละโหนดของดับเบิลลิงก์ลิสต์จะบรรจุตัวชี้หรือพอยน์เตอร์อยู่สองตัวคือ Backward
Pointer ที่ชี้ไปยังข้างหลัง ซึ่งก็คือตัวก่อนหน้า (Perdecessor) ในขณะที่ Forward Pointer
จะชี้ไปยังข้างหน้า ซึ่ง ก็คือตัวถัดไป (Successor) โดยทั้งสองจะใช้อักษรย่อแทนว่า B และ F