0Pricing
C Academy · บทเรียน

การแทรกและการลบ

แก้ไขรายการ

การแทรกและการลบ เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

การแก้ไขรายการ

จุดเด่นของรายการเชื่อมโยงคือการแทรกและลบที่ใช้ต้นทุนต่ำ คุณจัดเรียงตัวชี้ใหม่แทนการเลื่อนสมาชิกเหมือนในอาร์เรย์

บทเรียนนี้ครอบคลุมการแทรกและการนำโหนดออกจากตำแหน่งต่าง ๆ

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

int main(void) {
    struct Node *head = make(2);
    printf("start: %d\n", head->value);
    free(head);
    return 0;
}

แทรกที่ด้านหน้า

การแทรกที่หัวรายการใช้เวลา O(1) สร้างโหนดใหม่ ให้ next ของโหนดนั้นชี้ไปยังหัวรายการปัจจุบัน จากนั้นปรับหัวรายการให้ชี้ไปยังโหนดใหม่

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

int main(void) {
    struct Node *head = make(2);
    struct Node *fresh = make(1);
    fresh->next = head;
    head = fresh;
    printf("%d -> %d\n", head->value, head->next->value);
    return 0;
}

เหตุผลที่ต้องส่งตัวชี้ซ้อน

หากต้องการเปลี่ยนหัวรายการจากภายในฟังก์ชัน คุณต้องส่งที่อยู่ของหัวรายการไปด้วย ซึ่งมีชนิดเป็น struct Node **

มิฉะนั้นฟังก์ชันจะเปลี่ยนแปลงได้เพียงสำเนาเฉพาะที่ และหัวรายการของผู้เรียกจะไม่เปลี่ยนแปลง

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

void push(struct Node **head, int v) {
    struct Node *n = make(v);
    n->next = *head;
    *head = n;
}

int main(void) {
    struct Node *head = NULL;
    push(&head, 5);
    push(&head, 4);
    printf("%d %d\n", head->value, head->next->value);
    return 0;
}

แทรกที่ด้านท้าย

การเพิ่มต่อท้ายจำเป็นต้องเดินไปยังโหนดสุดท้าย แล้วเชื่อมโหนดใหม่เข้ากับ next ของโหนดนั้น

หากรายการว่าง โหนดใหม่จะกลายเป็นหัวรายการ

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

void append(struct Node **head, int v) {
    struct Node *n = make(v);
    if (!*head) { *head = n; return; }
    struct Node *p = *head;
    while (p->next) p = p->next;
    p->next = n;
}

int main(void) {
    struct Node *head = NULL;
    append(&head, 1); append(&head, 2);
    printf("%d %d\n", head->value, head->next->value);
    return 0;
}

แทรกหลังโหนด

หากต้องการแทรกตรงกลาง ให้ค้นหาโหนดที่ต้องการแทรกต่อท้าย แล้วเชื่อมโหนดใหม่ไว้ระหว่างโหนดนั้นกับโหนดถัดไปเดิม

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

void insert_after(struct Node *node, int v) {
    struct Node *n = make(v);
    n->next = node->next;
    node->next = n;
}

int main(void) {
    struct Node *head = make(1);
    head->next = make(3);
    insert_after(head, 2);
    printf("%d %d %d\n", head->value, head->next->value, head->next->next->value);
    return 0;
}

ลำดับการทำงานมีความสำคัญ

ขณะเชื่อมแทรก ต้องกำหนดค่า next ของโหนดใหม่ ก่อน เปลี่ยนค่า next ของโหนดก่อนหน้าเสมอ

หากทำกลับกัน จะสูญเสียการอ้างอิงไปยังส่วนที่เหลือของรายการ

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

int main(void) {
    struct Node *a = make(1), *c = make(3);
    a->next = c;
    struct Node *b = make(2);
    b->next = a->next;
    a->next = b;
    printf("%d %d %d\n", a->value, b->value, c->value);
    return 0;
}

ลบโหนดแรก

การนำหัวรายการออกหมายถึงการบันทึกหัวรายการไว้ เลื่อนหัวรายการไปที่ head->next แล้วคืนหน่วยความจำของหัวรายการเดิม

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

void pop(struct Node **head) {
    if (!*head) return;
    struct Node *old = *head;
    *head = old->next;
    free(old);
}

int main(void) {
    struct Node *head = make(1);
    head->next = make(2);
    pop(&head);
    printf("new head: %d\n", head->value);
    free(head);
    return 0;
}

ลบตามค่า

หากต้องการนำโหนดที่มีค่าที่กำหนดออก ให้ติดตามโหนดก่อนหน้า เพื่อข้ามโหนดเป้าหมายด้วยการกำหนดค่า prev->next = target->next

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

void del(struct Node **head, int v) {
    struct Node *cur = *head, *prev = NULL;
    while (cur && cur->value != v) { prev = cur; cur = cur->next; }
    if (!cur) return;
    if (prev) prev->next = cur->next; else *head = cur->next;
    free(cur);
}

int main(void) {
    struct Node *head = make(1);
    head->next = make(2);
    head->next->next = make(3);
    del(&head, 2);
    printf("%d %d\n", head->value, head->next->value);
    return 0;
}

จัดการกรณีหัวรายการ

การลบมีกรณีพิเศษเมื่อโหนดเป้าหมายเป็นหัวรายการ: จะไม่มีโหนดก่อนหน้า ดังนั้นให้ปรับตัวชี้หัวรายการโดยตรง

ตัวชี้ซ้อนช่วยให้ทำกรณีนี้ได้อย่างสะดวก ดังที่แสดงไว้ข้างต้น

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

int main(void) {
    struct Node *head = make(1);
    head->next = make(2);
    struct Node *old = head;
    head = head->next;
    free(old);
    printf("head now %d\n", head->value);
    free(head);
    return 0;
}

ป้องกันหน่วยความจำรั่ว

โหนดทุกโหนดที่นำออกจากรายการต้องถูกคืนหน่วยความจำด้วย free การนำโหนดออกโดยไม่คืนหน่วยความจำจะทำให้หน่วยความจำที่โหนดนั้นใช้เกิดการรั่วไหล

ในทำนองเดียวกัน อย่าคืนหน่วยความจำของโหนดขณะที่โหนดยังเชื่อมอยู่ มิฉะนั้นจะสร้างตัวชี้ลอย

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

int main(void) {
    struct Node *n = make(7);
    free(n);
    printf("node freed, no leak\n");
    return 0;
}

การแทรกโดยคงลำดับไว้เป็นทางเลือก

การแทรกตามลำดับเรียงเป็นรูปแบบที่พบได้บ่อย ให้เดินไปจนพบตำแหน่งที่ค่าพอดี แล้วเชื่อมโหนดเข้าไปที่ตำแหน่งนั้น

#include <stdio.h>
#include <stdlib.h>

struct Node { int value; struct Node *next; };
struct Node *make(int v){struct Node*n=malloc(sizeof*n);n->value=v;n->next=NULL;return n;}

void insert_sorted(struct Node **head, int v) {
    struct Node *n = make(v);
    if (!*head || (*head)->value >= v) { n->next = *head; *head = n; return; }
    struct Node *p = *head;
    while (p->next && p->next->value < v) p = p->next;
    n->next = p->next; p->next = n;
}

int main(void) {
    struct Node *head = NULL;
    insert_sorted(&head, 3);
    insert_sorted(&head, 1);
    insert_sorted(&head, 2);
    for (struct Node *p = head; p; p = p->next) printf("%d ", p->value);
    printf("\n");
    return 0;
}

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจเกี่ยวกับการแก้ไขรายการ

สรุปทบทวน

คุณได้เรียนรู้การแทรกและลบโหนดแล้ว:

  • การแทรกที่ด้านหน้าใช้เวลา O(1) ส่วนการเพิ่มต่อท้ายหรือการแทรกตามลำดับจำเป็นต้องเดินไปตามรายการ
  • ใช้ตัวชี้ซ้อนเมื่อหัวรายการอาจเปลี่ยนแปลง
  • เชื่อมแทรกอย่างระมัดระวัง: กำหนดค่า next ของโหนดใหม่ก่อนเชื่อมตัวชี้ใหม่
  • ติดตามโหนดก่อนหน้าเมื่อลบ และคืนหน่วยความจำของโหนดที่นำออกด้วย free เสมอ

คำถามที่พบบ่อย

บทเรียน “การแทรกและการลบ” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การแทรกและการลบ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การแทรกและการลบ”

แก้ไขรายการ คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “การแทรกและการลบ” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม

ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ลิงก์ลิสต์แบบทางเดียว
  2. การแทรกและการลบ
  3. การท่องผ่านและการค้นหา
  4. ลิงก์ลิสต์แบบสองทาง
← กลับไปที่ C Academy