แทรก ค้นหา และลบ
การดำเนินการหลัก
แทรก ค้นหา และลบ เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
การดำเนินการหลักสามอย่าง
ตารางแฮชทุกตารางรองรับการดำเนินการสามอย่าง ได้แก่ การแทรก การค้นหา และการลบ เมื่อใช้แฮชที่ดีและมีอัตราการใช้งานเหมาะสม ทั้งสามการดำเนินการจะใช้เวลาเฉลี่ย O(1)
เราจะสร้างตารางที่ใช้การเชื่อมโยงทีละขั้นตอน
ชนิดของตารางและโหนด
เราจะกำหนดโหนดที่เก็บสตริงคีย์ที่คัดลอกไว้และค่าจำนวนเต็ม รวมถึงโครงสร้างตารางที่เก็บอาร์เรย์ช่องและความจุของตาราง
#include <stdio.h>
typedef struct Node {
char *key;
int value;
struct Node *next;
} Node;
typedef struct {
Node **buckets;
unsigned capacity;
unsigned size;
} HashTable;
int main(void) {
printf("types defined\n");
return 0;
}การสร้างตาราง
จัดสรรตารางและอาร์เรย์ช่องที่ตั้งค่าเป็นศูนย์ด้วย calloc เพื่อให้ทุกช่องเริ่มต้นเป็น NULL
#include <stdio.h>
#include <stdlib.h>
typedef struct Node { char *key; int value; struct Node *next; } Node;
typedef struct { Node **buckets; unsigned capacity, size; } HashTable;
HashTable *ht_create(unsigned cap) {
HashTable *t = malloc(sizeof *t);
t->buckets = calloc(cap, sizeof(Node *));
t->capacity = cap; t->size = 0;
return t;
}
int main(void) {
HashTable *t = ht_create(16);
printf("capacity=%u size=%u\n", t->capacity, t->size);
return 0;
}ตัวช่วยแฮช
เรานำ DJB2 กลับมาใช้และลดค่าลงเป็นดัชนีช่อง ตัวช่วยนี้ถูกใช้โดยการดำเนินการทั้งสามอย่าง
#include <stdio.h>
unsigned long djb2(const char *s) {
unsigned long h = 5381; int c;
while ((c = (unsigned char)*s++)) h = ((h << 5) + h) + c;
return h;
}
unsigned bucket_of(const char *key, unsigned cap) {
return (unsigned)(djb2(key) % cap);
}
int main(void) {
printf("%u\n", bucket_of("name", 16));
return 0;
}การแทรก: อัปเดตหรือนำไปไว้ด้านหน้า
เมื่อแทรก ให้ค้นหาช่องก่อน หากมีคีย์อยู่แล้ว ให้อัปเดตค่า มิฉะนั้นให้จัดสรรโหนดใหม่ (พร้อมคีย์ที่คัดลอกผ่าน strdup) แล้วนำไปไว้ด้านหน้า
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node { char *key; int value; struct Node *next; } Node;
Node *insert(Node *head, const char *key, int val) {
for (Node *p = head; p; p = p->next)
if (strcmp(p->key, key) == 0) { p->value = val; return head; }
Node *n = malloc(sizeof *n);
n->key = strdup(key); n->value = val; n->next = head;
return n;
}
int main(void) {
Node *b = NULL;
b = insert(b, "a", 1);
b = insert(b, "a", 99); /* update */
printf("%s=%d\n", b->key, b->value);
return 0;
}การค้นหา
การค้นหาจะแฮชคีย์ จากนั้นเดินผ่านรายการในช่องโดยเปรียบเทียบคีย์ด้วย strcmp และคืนค่าตัวชี้ไปยังค่า (หรือคืนค่า NULL หากไม่พบ)
#include <stdio.h>
#include <string.h>
typedef struct Node { char *key; int value; struct Node *next; } Node;
int *lookup(Node *head, const char *key) {
for (Node *p = head; p; p = p->next)
if (strcmp(p->key, key) == 0) return &p->value;
return NULL;
}
int main(void) {
Node n2 = {"y", 20, NULL};
Node n1 = {"x", 10, &n2};
int *v = lookup(&n1, "y");
printf("%d\n", v ? *v : -1);
return 0;
}การลบ: เชื่อมรายการใหม่
การลบจะเดินผ่านช่องโดยเก็บตัวชี้ไปยังโหนดก่อนหน้า จากนั้นเชื่อมรายการข้ามโหนดเป้าหมายใหม่และคืนหน่วยความจำของโหนดนั้น (ทั้งคีย์ที่คัดลอกและโหนด)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node { char *key; int value; struct Node *next; } Node;
Node *delete_key(Node *head, const char *key) {
Node *prev = NULL, *cur = head;
while (cur) {
if (strcmp(cur->key, key) == 0) {
if (prev) prev->next = cur->next; else head = cur->next;
free(cur->key); free(cur);
return head;
}
prev = cur; cur = cur->next;
}
return head;
}
int main(void) {
Node *b = malloc(sizeof *b);
b->key = strdup("a"); b->value = 1; b->next = NULL;
b = delete_key(b, "a");
printf("%s\n", b ? "left" : "empty");
return 0;
}นำทุกส่วนมาประกอบกัน
ตารางแบบสมบูรณ์จะรวมสิ่งเหล่านี้เข้าด้วยกัน โดยคำนวณช่องก่อนแล้วมอบหมายงานให้ตัวช่วยจัดการรายการ ในที่นี้คือตารางขนาดเล็กที่ทำงานได้อย่างครบถ้วน
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node { char *key; int value; struct Node *next; } Node;
unsigned long djb2(const char *s){unsigned long h=5381;int c;while((c=(unsigned char)*s++))h=((h<<5)+h)+c;return h;}
#define CAP 16
Node *table[CAP];
void put(const char *k, int v) {
unsigned i = djb2(k) % CAP;
Node *n = malloc(sizeof *n);
n->key = strdup(k); n->value = v; n->next = table[i];
table[i] = n;
}
int get(const char *k) {
for (Node *p = table[djb2(k) % CAP]; p; p = p->next)
if (!strcmp(p->key, k)) return p->value;
return -1;
}
int main(void) {
put("age", 30); put("score", 95);
printf("age=%d score=%d\n", get("age"), get("score"));
return 0;
}เหตุผลที่ต้องคัดลอกคีย์
เราเก็บคีย์ด้วย strdup เพื่อให้ตารางมีสำเนาของตนเอง หากเราเก็บตัวชี้ของผู้เรียก คีย์อาจถูกเปลี่ยนแปลงหรือลบหน่วยความจำไปโดยที่เราไม่ทราบ ทำให้การค้นหาเสียหาย
นั่นหมายความว่าการลบต้องใช้ free กับคีย์ที่คัดลอกไว้ด้วย
ความซับซ้อนด้านเวลา
เมื่อใช้แฮชแบบสม่ำเสมอและควบคุมอัตราการใช้งานให้อยู่ใกล้ 0.75:
- การแทรก: O(1) โดยเฉลี่ย
- การค้นหา: O(1) โดยเฉลี่ย
- การลบ: O(1) โดยเฉลี่ย
กรณีเลวร้ายที่สุดคือ O(n) เมื่อคีย์ทั้งหมดชนกันในช่องเดียว
การคืนหน่วยความจำของทั้งตาราง
เพื่อป้องกันหน่วยความจำรั่ว ให้คืนหน่วยความจำของทุกโหนดในทุกช่อง จากนั้นคืนหน่วยความจำของอาร์เรย์ช่อง และสุดท้ายคืนหน่วยความจำของโครงสร้างตาราง
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node { char *key; int value; struct Node *next; } Node;
void free_bucket(Node *head) {
while (head) { Node *nx = head->next; free(head->key); free(head); head = nx; }
}
int main(void) {
Node *b = malloc(sizeof *b);
b->key = strdup("k"); b->value = 1; b->next = NULL;
free_bucket(b);
printf("freed\n");
return 0;
}ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจของคุณเกี่ยวกับการดำเนินการหลัก
สรุปทบทวน
คุณได้ใช้งานการดำเนินการหลักสามอย่างของตารางแฮชด้วยการเชื่อมโยง
- การแทรกจะอัปเดตหรือนำโหนดไปไว้ด้านหน้า
- การค้นหาจะเดินผ่านรายการในช่องด้วย
strcmp - การลบจะเชื่อมรายการใหม่และคืนหน่วยความจำทั้งคีย์และโหนด
- จัดการคีย์ของคุณเองด้วย
strdupและคืนหน่วยความจำทุกส่วนเมื่อเลิกใช้งาน
คำถามที่พบบ่อย
บทเรียน “แทรก ค้นหา และลบ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “แทรก ค้นหา และลบ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “แทรก ค้นหา และลบ”
การดำเนินการหลัก คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “แทรก ค้นหา และลบ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม
ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ฟังก์ชันแฮช
- การจัดการการชนกัน
- แทรก ค้นหา และลบ
- การปรับขนาดและปัจจัยโหลด