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