📚 ما هي قوائم الربط (Linked Lists) ولماذا نتعلمها؟
مرحباً بك في درس جديد ومميز! تخيل أن لديك مجموعة من الصناديق، كل صندوق يحتوي على قطعة من البيانات (مثل رقم أو اسم) ومعلومة إضافية واحدة: عنوان الصندوق التالي. هذه الفكرة البسيطة هي جوهر قوائم الربط (Linked Lists) 🧠. على عكس المصفوفات (Lists) العادية التي تخزن البيانات في أماكن متجاورة في الذاكرة، تخزن قوائم الربط البيانات في أماكن متناثرة، لكن كل عنصر "يربطك" بالعنصر الذي يليه.
لماذا هذا مهم؟ لأن هذه البنية تمنحنا مرونة كبيرة! إضافة أو حذف عنصر من بداية القائمة يكون سريعاً جداً، بخلاف المصفوفة التي قد تحتاج لإعادة ترتيب كل العناصر. هيا نبدأ رحلتنا لفهم هذه البنية الرائعة خطوة بخطوة.
🔗 الفرق الأساسي: قوائم الربط vs المصفوفات في بايثون
قبل الغوص في التنفيذ، دعنا نوضح الفرق الجوهري بينهما بمثال بسيط.
- المصفوفة (List): مثل صف من الكراسي في قاعة سينما. لإضافة كرسي في البداية، يجب دفع كل الكراسي خطوة للخلف! 🪑🪑🪑 ➔ 🪑(جديد)🪑🪑🪑
- قائمة الربط (Linked List): مثل لعبة "الصيد" أو "التتبع". كل لاعب (عقدة) يعرف فقط من هو اللاعب التالي. لإضافة لاعب جديد في البداية، فقط قل للاعب الجديد: "اتبع اللاعب الأول الحالي"، ثم اجعل رأس القائمة يشير للاعب الجديد. 🧍(جديد)➡️🧍(1)➡️🧍(2)
المرونة في الإضافة والحذف من البداية أو الوسط هي الميزة الكبرى لقوائم الربط.
🧱 حجر الأساس: فهم العقدة (Node)
كل "صندوق" في قائمة الربط يسمى عقدة (Node). العقدة هي اللبنة الأساسية التي نبني منها القائمة بأكملها. تحتوي كل عقدة على جزأين رئيسيين:
- البيانات (Data): القيمة التي نريد تخزينها (رقم، نص، كائن، إلخ).
- الرابط (Next): مؤشر أو عنوان يخبرنا: "أين توجد العقدة التالية في القائمة؟".
لننشئ أول عقدة بسيطة في بايثون:
# تعريف فئة العقدة (Node)
class Node:
def __init__(self, data):
self.data = data # تخزين البيانات
self.next = None # الرابط للعقدة التالية، في البداية لا يوجد عقدة تالية
# إنشاء عقدة جديدة تحتوي على الرقم 10
first_node = Node(10)
print(first_node.data) # الإخراج: 10
print(first_node.next) # الإخراج: None
في الكود أعلاه، أنشأنا عقدة معزولة. data هي 10، و next هي None لأنها لا تشير لأي عقدة أخرى بعد.
🏗️ بناء قائمة الربط (LinkedList) الفعلية
الآن، لنبني القائمة نفسها. القائمة تحتاج إلى معرفة نقطة البداية فقط، والتي نسميها رأس القائمة (Head). من خلال الرأس، يمكننا الوصول إلى كل العقد الأخرى عن طريق اتباع روابط next.
class LinkedList:
def __init__(self):
self.head = None # في البداية، القائمة فارغة ورأسها لا يشير إلى شيء
# دالة مساعدة لطباعة محتويات القائمة
def print_list(self):
current_node = self.head # نبدأ من الرأس
while current_node is not None: # طالما أن العقدة الحالية موجودة
print(current_node.data, end=" -> ") # اطبع بياناتها
current_node = current_node.next # انتقل للعقدة التالية
print("None") # نهاية القائمة
# إنشاء قائمة ربط فارغة
my_list = LinkedList()
my_list.print_list() # الإخراج: None (لأن القائمة فارغة)
✨ العمليات الأساسية على قوائم الربط
1. إضافة عقدة في البداية (Add at Head)
هذه أبسط عملية. ننشئ عقدة جديدة، ونجعل رابطها (next) يشير إلى العقدة التي كانت هي الرأس سابقاً، ثم نجعلها هي الرأس الجديد.
class LinkedList:
# ... (الكود السابق) ...
def add_at_head(self, data):
new_node = Node(data) # 1. إنشاء عقدة جديدة
new_node.next = self.head # 2. جعل العقدة الجديدة تشير إلى الرأس الحالي
self.head = new_node # 3. جعل العقدة الجديدة هي رأس القائمة
# لنختبرها
my_list = LinkedList()
my_list.add_at_head(30)
my_list.add_at_head(20)
my_list.add_at_head(10)
my_list.print_list() # الإخراج: 10 -> 20 -> 30 -> None
2. إضافة عقدة في النهاية (Add at Tail)
لإضافة عقدة في النهاية، نحتاج للوصول إلى آخر عقدة في القائمة (التي يكون رابطها next مساوياً لـ None)، ثم نجعل رابطها يشير إلى العقدة الجديدة.
class LinkedList:
# ... (الكود السابق) ...
def add_at_tail(self, data):
new_node = Node(data)
if self.head is None: # إذا كانت القائمة فارغة
self.head = new_node # العقدة الجديدة تصبح الرأس
return
# إذا كانت القائمة غير فارغة، نبحث عن آخر عقدة
last_node = self.head
while last_node.next: # بينما العقدة الحالية لها عقدة تالية
last_node = last_node.next # انتقل للعقدة التالية
# الآن last_node هي آخر عقدة
last_node.next = new_node # اجعل آخر عقدة تشير للعقدة الجديدة
my_list = LinkedList()
my_list.add_at_tail(1)
my_list.add_at_tail(2)
my_list.add_at_tail(3)
my_list.print_list() # الإخراج: 1 -> 2 -> 3 -> None
🧪 مثال عملي شامل
لنجمع كل ما تعلمناه في مثال واحد:
# تعريف فئة Node و LinkedList كما سبق
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def add_at_head(self, data):
new_node = Node(data)
new_node.next = self.head
self.head = new_node
def print_list(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# إنشاء قائمة وتجربة العمليات
tasks = LinkedList()
tasks.add_at_head("اشرب الماء") # إضافة في البداية
tasks.add_at_head("اكتب الكود")
tasks.add_at_head("افتح المحرر")
print("قائمة المهام اليومية:")
tasks.print_list() # الإخراج: افتح المحرر -> اكتب الكود -> اشرب الماء -> None
🎯 ملخص الدرس
تعلمنا اليوم الأساسيات القوية لقوائم الربط:
- قائمة الربط هي بنية بيانات تتكون من عقد (Nodes) متصلة ببعضها بواسطة روابط (Links).
- العقدة (Node) تحتوي على جزأين:
dataوnext. - رأس القائمة (Head) هو مؤشر للعقدة الأولى، ومنه نبدأ أي عملية.
- الإضافة في البداية سريعة ومباشرة.
- الإضافة في النهاية تتطلب اجتياز القائمة للوصول للعقدة الأخيرة.
تذكر أن قوائم الربط تتفوق في العمليات الديناميكية مثل الإضافة والحذف المتكرر، خاصة من بداية القائمة.
🎓 اختبر نفسك
التعليقات
شاركنا رأيك أو أسئلتك حول هذا المقال