الإدراج والبحث والحذف
العمليات الأساسية
الإدراج والبحث والحذف درس مجاني في C Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C Academy 4 دروس في المجموع.
العمليات الأساسية الثلاث
يدعم كل جدول تجزئة ثلاث عمليات: insert وlookup وdelete. باستخدام دالة تجزئة جيدة ومعامل تحميل مناسب، تعمل العمليات الثلاث بمتوسط زمن 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وحرّروا كل شيء عند الإنهاء
الأسئلة الشائعة
هل درس «الإدراج والبحث والحذف» مجاني؟
نعم — نص درس «الإدراج والبحث والحذف» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C Academy 4 دروس في المجموع.
ماذا ستتعلم في «الإدراج والبحث والحذف»؟
العمليات الأساسية تتمرن على C Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C Academy؟
لا تُشترط خبرة سابقة. C Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «الإدراج والبحث والحذف»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C Academy هذا؟
نعم. كل درس في C Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- دوال التجزئة
- معالجة التصادمات
- الإدراج والبحث والحذف
- إعادة التحجيم ومعامل التحميل