310-112 · Algorithms & Basic Programming · หน่วยที่ 4

LINKEDLIST

2 พาร์ตที่ไปคู่กันเสมอ — เขียนโค้ด C ให้ได้ กับ วาดภาพให้ได้ว่าแต่ละ statement เกิดอะไรขึ้น · ทุกอย่างในหน้านี้เดินทีละบรรทัดแล้วภาพขยับตาม

SOURCE 04_Linked list.pdf (81 สไลด์) + ch07 Python (131) + Lab4-2-1 test 3.c ต้องทำได้ วาดภาพสุดท้ายของ list
00

ทำไมต้องมี Linked List

สไลด์เปิดมาด้วยคำถามนี้ — ตอบให้ได้ว่า array มีปัญหาอะไร

ปัญหาของ Array / Record
  • ขนาดคงที่ (fixed size) — กำหนดตอน compile เปลี่ยนตอนรันไม่ได้
  • จองหน่วยความจำเป็น บล็อกเดียวติดกัน — ต้องมีที่ว่างยาวพอ
  • คนเขียนมักจอง "เผื่อไว้ใหญ่ๆ" → เปลืองเปล่า
  • ค้นช้าถ้าไม่เรียง · แทรกช้าถ้าเรียงแล้ว (ต้องเลื่อนของทั้งแถว)
Linked List แก้ให้ยังไง
  • ไม่ต้องอยู่ติดกันในหน่วยความจำ — จองทีละ node แยกกัน
  • จองเมื่อจำเป็นเท่านั้น (dynamic) ยาวขึ้น/สั้นลงได้ตอนรัน
  • แทรก/ลบ = แค่เปลี่ยนลูกศร ไม่ต้องเลื่อนของ
  • เหมาะเมื่อไม่รู้ล่วงหน้าว่าจะมีข้อมูลกี่ตัว
◈ แลกมาด้วยอะไร (ข้อสอบชอบถาม)
Array กระโดดตรงถึงช่องไหนก็ได้ ด้วยสูตร Base + i×eSize (หน่วยที่ 3) — แต่ Linked List ทำแบบนั้นไม่ได้ ต้องไล่จากหัวทีละ node เพราะแต่ละ node อยู่คนละที่ในหน่วยความจำ · ได้ความยืดหยุ่น แต่เสียการเข้าถึงแบบสุ่ม + เปลืองที่เก็บ pointer เพิ่มทุก node
01

Node กับ Pointer — พื้นฐานที่ต้องแม่นก่อน

ถ้าตรงนี้ไม่ชัด พาร์ตวาดภาพจะพังทั้งหมด

Node = 2 ช่องเสมอ
// Type DECLARATIONS
struct node{
    int data;          // ช่องข้อมูล (data section)
    struct node *next; // ช่องลูกศร (link section)
};
typedef struct node nodePtr;
nodePtr *ptr;
  • data = ค่าที่เก็บ · next = ที่อยู่ของ node ถัดไป
  • next ไม่ได้เก็บ node — เก็บแค่ เลขที่อยู่ ของ node นั้น
  • node สุดท้าย next = NULL เสมอ — คือป้ายบอกว่า "จบแล้ว"
3 คำที่สับสนกันบ่อยที่สุด
เขียนหมายถึง
ptrที่อยู่ ของ node (ตัวชี้)
*ptrทั้ง node ที่ ptr ชี้อยู่
ptr->data
(*ptr).data
ช่อง data ในnode นั้น (2 อันนี้เหมือนกันเป๊ะ)
ptr->nextช่อง next = ที่อยู่ของ node ถัดไป
ptr->next->dataช่อง data ของ node ถัดไป
อ่านลูกศร -> ว่า "เดินไปที่ node นั้น แล้วเปิดช่อง…"

lab 01 · เดินดูทีละ statement

กด ▶ ทีละบรรทัด
02

สร้าง node + ลิงก์เข้าด้วยกัน

ท่ามาตรฐานจากสไลด์ — malloc → ใส่ data → ตั้ง next = NULL → ค่อยลิงก์

สูตรสร้าง node 1 ตัว — ท่องให้ขึ้นใจ 3 บรรทัด
current = (struct node *)malloc(sizeof(struct node));  // 1. ขอที่ในหน่วยความจำ
current->data = i;                                     // 2. ใส่ข้อมูล
current->next = NULL;                                  // 3. ปิดท้ายด้วย NULL เสมอ
  • ลืมข้อ 3 = บั๊ค — node ใหม่จะมี next เป็นค่าขยะ ชี้ไปมั่วในหน่วยความจำ
  • malloc คืนค่าเป็นที่อยู่ของก้อนใหม่ — ถ้าไม่เก็บใส่ตัวแปรไว้ = หาไม่เจออีกเลย (memory leak)

lab 02 · สร้าง 3 node แล้วต่อกัน

ตรงกับสไลด์หน้า 20–23
03

Traverse — เดินอ่านทั้งลิสต์

ลูปนี้โผล่ในทุกฟังก์ชัน (แสดงผล / ค้นหา / หาท้ายลิสต์) — จำโครงให้ได้

ptr = head;                     // เริ่มที่หัวเสมอ ห้ามใช้ head เดินเอง!
while (ptr != NULL) {
    printf("%d\n", ptr->data);
    ptr = ptr->next;            // ก้าวไปตัวถัดไป
}
  • ห้ามใช้ head เดินเอง — เดินเสร็จ head จะกลายเป็น NULL แล้วหาลิสต์ไม่เจออีกเลย ต้องใช้ตัวชี้ชั่วคราว
  • เงื่อนไข ptr != NULL = "ยังไม่หมดลิสต์" · ถ้าเขียน ptr->next != NULL จะหยุดที่ตัวสุดท้าย (ใช้ตอนอยากได้ท้ายลิสต์)

lab 03 · ลูป traverse

ดู ptr ขยับทีละก้าว
04

Insert — แทรก 3 แบบ

จากไฟล์ lab ของ Beer เอง (Lab4-2-1 test 3.c case 2) — ต้องทำได้ทั้ง 3 เคส

◈ กฎเหล็กของการแทรก — ผิดลำดับ = ลิสต์ขาด
ต่อปลายใหม่ก่อน แล้วค่อยตัดของเดิม
current->next = ptr->next; ← ให้ node ใหม่จับตัวถัดไปไว้ก่อน
ptr->next = current; ← แล้วค่อยให้ตัวหน้าชี้มาที่ node ใหม่

ถ้าสลับ 2 บรรทัดนี้ptr->next ถูกทับก่อน = ส่วนหางหลุดหายทั้งก้อน หาไม่เจออีกเลย

ยกเว้น แทรกท้าย — ตัวสุดท้ายมี next = NULL อยู่แล้ว เลยเขียน ptr->next = current; ตรงๆ ได้เลย ไม่ต้องเก็บปลายทางไว้ก่อน

lab 04a · Add Beginning

แทรกหัวลิสต์

lab 04b · Add End

แทรกท้าย — ต้องเดินหาตัวสุดท้ายก่อน

lab 04c · Add After Given Element

แทรกกลาง — ออกสอบบ่อยสุด

lab 04d · ถ้าสลับลำดับ 2 บรรทัด — ดูลิสต์ขาด

⚠ ตัวอย่างที่ผิด
✦ ปิดตัวชี้ที่ใช้เสร็จแล้ว — ptr = NULL;
  • ทำงานเสร็จแล้ว ptr กับ current ไม่มีหน้าที่แล้ว แต่ยังค้างชี้กลางลิสต์อยู่ — เวลาวาดภาพสอบจะรกและสับสน
  • ปิดด้วย ptr = NULL; → ภาพเหลือแค่ head ที่จำเป็นจริง อ่านง่ายขึ้นมาก
  • สำคัญกว่านั้น — หลัง free() ต้อง = NULL เสมอ
    ไม่งั้นตัวชี้ยังเก็บที่อยู่เดิมที่คืนไปแล้ว = dangling pointer เผลอใช้ต่อ = โปรแกรมพังแบบหาสาเหตุยาก
  • ในแล็บทุกอันข้างบนผมต่อบรรทัดปิดไว้ให้แล้ว — กด จนจบจะเห็นตัวชี้เด้งลงไปอยู่โซน "ยังไม่ได้ใช้" ข้างล่าง
current->next = ptr->next;
ptr->next = current;
ptr = NULL;        // ปิดตัวที่ใช้เสร็จ — ภาพสะอาด
current = NULL;    // เหลือแต่ head ที่จำเป็น
05

Delete — ลบ 3 แบบ

หลักการเดียวกัน: ให้ตัวหน้าข้ามตัวที่จะลบ แล้วค่อย free

prev->next = ptr->next;   // 1. ให้ตัวหน้าข้ามไปเลย
free(ptr);                // 2. คืนหน่วยความจำทีหลัง
  • free ก่อนต่อสาย = พัง — พอ free แล้ว ptr->next อ่านไม่ได้แล้ว (dangling pointer)
  • ลบหัวลิสต์ต้องทำต่างออกไป — ไม่มี prev ให้ใช้ ต้องเลื่อน head = head->next แล้วค่อย free ตัวเก่า
  • ลบท้ายต้องเดินหา node ที่ ptr->next == NULL พร้อมจำ prev ไว้
  • ปิดท้ายด้วย ptr = NULL; ทุกครั้งหลัง free() — กัน dangling pointer + ทำให้ภาพที่วาดสอบสะอาด

lab 05a · Delete Beginning

ลบหัว

lab 05b · Delete End

ลบท้าย — เดินจนเจอ ptr->next == NULL

lab 05c · Delete Given Element

ลบกลาง — prev + ptr
06

★ จำลอง statement — พาร์ตที่อาจารย์ให้ทำเป็นหลัก

ให้ statement มาเป็นชุด → ตอบว่าภาพสุดท้ายของลิสต์เป็นยังไง · พิมพ์เองได้ แก้ภาพเริ่มต้นได้

statement ที่รองรับ (ครอบคลุมที่อาจารย์ออก)
  • s = r — ย้ายตัวชี้ให้ชี้ที่เดียวกัน
  • r = r->next — เดินไปข้างหน้า 1 ก้าว
  • p = p->next->next — ก้าว 2 ที
  • r->next = pเปลี่ยนลูกศร ของ node
  • q->info = 7 — เปลี่ยนค่าในช่องข้อมูล
  • q = malloc(nodePtr) — สร้าง node ใหม่
  • free(s) — คืนหน่วยความจำ
  • t->next = NULL — ปิดท้ายลิสต์
ใช้ data แทน info ก็ได้ · ใส่ ; ท้ายบรรทัดหรือไม่ใส่ก็ได้ · // = คอมเมนต์

lab 06 · simulator

← → เปลี่ยน step ได้
โจทย์ (พิมพ์ทับได้)
07

โจทย์วาดภาพ — ทำเองก่อนค่อยกดเฉลย

วาดในกระดาษทด (ปุ่มมุมขวาล่าง) แล้วค่อยกดรันเทียบ

lab 07 · drill

ข้อ 1 / 5
โจทย์

แผ่นโกง

NODE
struct node{int data; struct node *next;}
สร้าง 1 NODE
malloc → ->data = i → ->next = NULL
TRAVERSE
ptr=head; while(ptr!=NULL) ptr=ptr->next;
หาท้ายลิสต์
while(ptr->next!=NULL) ptr=ptr->next;
แทรก (ลำดับห้ามสลับ)
current->next = ptr->next; ptr->next = current;
ลบ (ลำดับห้ามสลับ)
prev->next = ptr->next; free(ptr);
แทรกหัว
current->next = head; head = current;
ลบหัว
temp = head; head = head->next; free(temp);
แทรกท้าย
while(ptr->next!=NULL) ptr=ptr->next; ptr->next = current;
ลบท้าย
เดินจน ptr->next==NULL (จำ prev) → prev->next = NULL; free(ptr);
-> อ่านว่า
เดินไป node นั้น แล้วเปิดช่อง…
ptr / *ptr / ptr->data
ที่อยู่ / ทั้ง node / ช่องข้อมูล
✅ เช็กก่อนเข้าห้องสอบ
  • เขียน struct node + สร้าง node ด้วย malloc ได้จากความจำ
  • ไล่ traverse แล้วบอกได้ว่า ptr อยู่ตรงไหนทุกก้าว
  • แทรก/ลบ ทั้ง หัว-ท้าย-กลาง เขียนได้ครบ 6 เคส (ตรงกับ menu ในไฟล์ lab)
  • อธิบายได้ว่าทำไมสลับ 2 บรรทัดแล้วลิสต์ขาด
  • รับ statement 10-15 บรรทัดแล้ววาดภาพสุดท้ายถูก ภายใน 5 นาที