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

ค้นหาและคืนหน่วยความจำ

ค้นหาโหนดและคืนหน่วยความจำ

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

การค้นหาใน BST

การค้นหาอาศัยกฎการเรียงลำดับ ในแต่ละโหนด เราจะเปรียบเทียบเป้าหมายกับค่าของโหนด แล้วไปยังต้นไม้ย่อยเพียงด้านเดียว

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

การค้นหาแบบเรียกซ้ำ

การค้นหาแบบเรียกซ้ำมีกรณีฐานสองกรณี ได้แก่ ต้นไม้ย่อยว่างหมายถึงไม่พบ และค่าที่ตรงกันหมายถึงพบ

นอกเหนือจากนั้น เราจะเรียกซ้ำไปทางซ้ายหรือขวาตามผลการเปรียบเทียบ

Node *search(Node *root, int target) {
    if (root == NULL || root->value == target)
        return root;
    if (target < root->value)
        return search(root->left, target);
    return search(root->right, target);
}

การค้นหาแบบวนซ้ำ

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

เราเดินตามพอยน์เตอร์ลงไปในต้นไม้จนกว่าจะพบเป้าหมาย หรือหลุดออกจากปลายต้นไม้ที่ NULL

Node *search_iter(Node *root, int target) {
    while (root != NULL) {
        if (target == root->value) return root;
        root = (target < root->value)
             ? root->left : root->right;
    }
    return NULL;  /* not found */
}

การค้นหาในการทำงานจริง

โปรแกรมนี้สร้าง BST และค้นหาค่าที่มีอยู่กับค่าที่ไม่มีอยู่ในต้นไม้ พร้อมแสดงว่าพบแต่ละค่าหรือไม่

การส่งคืนค่าที่ไม่ใช่ NULL หมายถึงพบ ส่วน NULL หมายถึงค่านั้นไม่ได้อยู่ในต้นไม้

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

typedef struct Node { int value; struct Node *left, *right; } Node;
Node *cn(int v){ Node *n=malloc(sizeof(Node)); n->value=v; n->left=n->right=NULL; return n; }
Node *insert(Node *r,int v){
    if(!r) return cn(v);
    if(v<r->value) r->left=insert(r->left,v);
    else if(v>r->value) r->right=insert(r->right,v);
    return r;
}
Node *search(Node *r,int t){
    if(!r||r->value==t) return r;
    return t<r->value ? search(r->left,t) : search(r->right,t);
}

int main(void){
    Node *root=NULL;
    int d[]={10,5,15,3,7};
    for(int i=0;i<5;i++) root=insert(root,d[i]);
    printf("7:%s 99:%s\n",
        search(root,7)?"found":"no",
        search(root,99)?"found":"no");
    return 0;
}

การหาค่าต่ำสุด

ใน BST ค่าที่น้อยที่สุดคือโหนดซ้ายสุด ให้เดินตาม left ต่อไปจนกว่าจะเป็น NULL

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

Node *find_min(Node *root) {
    if (root == NULL) return NULL;
    while (root->left != NULL)
        root = root->left;
    return root;
}

เหตุใดการคืนหน่วยความจำจึงสำคัญ

โหนดทุกโหนดมาจาก malloc ดังนั้นทุกโหนดจึงต้องคืนกลับด้วย free หากลืมคืนหน่วยความจำ จะทำให้หน่วยความจำรั่ว

แต่คุณไม่สามารถคืนหน่วยความจำของโหนดแล้วอ่านพอยน์เตอร์ของโหนดลูกได้ ดังนั้น ลำดับ การคืนหน่วยความจำจึงสำคัญมาก

คืนหน่วยความจำแบบหลัง

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

วิธีนี้รับประกันว่าเราจะอ่านพอยน์เตอร์ left และ right ของโหนดก่อนที่หน่วยความจำของโหนดนั้นจะถูกคืน

void free_tree(Node *root) {
    if (root == NULL) return;
    free_tree(root->left);
    free_tree(root->right);
    free(root);
}

ลำดับที่ผิดและอันตราย

หากคืนหน่วยความจำของโหนดก่อนเรียกซ้ำไปยังโหนดลูก จะทำให้เกิดพฤติกรรมที่ไม่กำหนด เนื่องจากคุณจะต้องเข้าถึงหน่วยความจำที่ถูกคืนแล้วเพื่อไปยังต้นไม้ย่อย

นี่คือข้อบกพร่องแบบใช้หน่วยความจำหลังคืนแล้ว ซึ่งพบได้บ่อย ควรคืนหน่วยความจำของโหนดลูกก่อนเสมอ

/* WRONG: use-after-free */
void bad_free(Node *root) {
    if (!root) return;
    free(root);                 /* freed here */
    bad_free(root->left);       /* reads freed memory! */
    bad_free(root->right);
}

หลีกเลี่ยงพอยน์เตอร์ห้อย

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

การกำหนดค่าให้เป็น NULL อีกครั้งในผู้เรียกจะป้องกันการนำพอยน์เตอร์ห้อยกลับมาใช้โดยไม่ตั้งใจ

free_tree(root);
root = NULL;   /* avoid a dangling pointer */

นับโหนดที่คืนหน่วยความจำแล้ว

เราสามารถยืนยันได้ว่าการคืนหน่วยความจำทำงานถูกต้อง โดยนับโหนดระหว่างการท่องแบบหลัง แล้วจึงคืนหน่วยความจำของแต่ละโหนด

โปรแกรมนี้สร้างต้นไม้ คืนหน่วยความจำ แล้วรายงานว่ามีการคืนหน่วยความจำของโหนดไปกี่โหนด

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

typedef struct Node { int value; struct Node *left, *right; } Node;
Node *cn(int v){ Node *n=malloc(sizeof(Node)); n->value=v; n->left=n->right=NULL; return n; }
Node *insert(Node *r,int v){
    if(!r) return cn(v);
    if(v<r->value) r->left=insert(r->left,v);
    else if(v>r->value) r->right=insert(r->right,v);
    return r;
}
int free_count(Node *r){
    if(!r) return 0;
    int c = free_count(r->left) + free_count(r->right);
    free(r);
    return c + 1;
}

int main(void){
    Node *root=NULL;
    int d[]={10,5,15,3,7};
    for(int i=0;i<5;i++) root=insert(root,d[i]);
    printf("freed=%d\n", free_count(root));
    root = NULL;
    return 0;
}

ค้นหาและคืนหน่วยความจำร่วมกัน

วงจรการทำงานที่สมบูรณ์คือ สร้างต้นไม้ ค้นหาในต้นไม้ แล้วคืนหน่วยความจำ การทำทั้งสามขั้นตอนช่วยให้โปรแกรมถูกต้องและไม่มีหน่วยความจำรั่ว

เครื่องมืออย่าง Valgrind สามารถยืนยันได้ว่าการเรียก malloc ทุกครั้งมีการเรียก free ที่สอดคล้องกัน

/* lifecycle
 * 1. insert values     (allocate)
 * 2. search as needed   (read-only)
 * 3. free_tree(root)    (deallocate)
 * 4. root = NULL        (avoid dangling)
 */

ตรวจสอบอย่างรวดเร็ว

วิเคราะห์การคืนหน่วยความจำอย่างปลอดภัย

สรุปทบทวน

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

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

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

บทเรียน “ค้นหาและคืนหน่วยความจำ” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “ค้นหาและคืนหน่วยความจำ”

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

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

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

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

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

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

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

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

  1. โหนดและโครงสร้างต้นไม้
  2. แทรกข้อมูลใน BST
  3. การท่องต้นไม้
  4. ค้นหาและคืนหน่วยความจำ
← กลับไปที่ C Academy