الخوارزميات وهياكل البيانات في JavaScript: تطبيق عملي
في تطوير الويب الحديث، معرفة الخوارزميات وهياكل البيانات ليست مجرد مهارة أكاديمية، بل هي أداة لإنشاء تطبيقات سريعة وقابلة للتوسع. JavaScript، خاصة في بيئة Node.js، تسمح بتنفيذ تراكيب خوارزمية بسيطة ومعقدة. في هذه المقالة، سنشرح هياكل البيانات والخوارزميات الرئيسية عمليًا، مع أمثلة كود يمكن استخدامها فورًا في المشاريع.
1. هياكل البيانات الأساسية: المكدس، الطابور، والقائمة المرتبطة
لنبدأ بالأساسيات. المكدس والطابور هما هيكلان خطيان يُستخدمان غالبًا في التحليل (parsing)، قوائم المهام، وإدارة سجل الإجراءات.
المكدس (LIFO)
يعمل المكدس وفق مبدأ "آخر من يدخل هو أول من يخرج". سنقوم بتنفيذه باستخدام مصفوفة:
class Stack { constructor() { this.items = []; }
push(element) { this.items.push(element); }
pop() { return this.items.pop(); }
peek() { return this.items[this.items.length - 1]; }
isEmpty() { return this.items.length === 0; }}
// مثال على الاستخدامconst stack = new Stack();stack.push('Page 1');stack.push('Page 2');console.log(stack.pop()); // 'Page 2'الطابور (FIFO)
الطابور مفيد لمعالجة المهام حسب ترتيب وصولها. التنفيذ باستخدام class:
class Queue { constructor() { this.items = {}; this.front = 0; this.rear = 0; }
enqueue(element) { this.items[this.rear] = element; this.rear++; }
dequeue() { const item = this.items[this.front]; delete this.items[this.front]; this.front++; return item; }
peek() { return this.items[this.front]; }
size() { return this.rear - this.front; }}
// مثالconst queue = new Queue();queue.enqueue('Task 1');queue.enqueue('Task 2');console.log(queue.dequeue()); // 'Task 1'القائمة المرتبطة
القائمة المرتبطة فعالة لإدراج وحذف العناصر في منتصف القائمة. سنقوم بتنفيذ قائمة مرتبطة أحادية الاتجاه:
class Node { constructor(value) { this.value = value; this.next = null; }}
class LinkedList { constructor() { this.head = null; this.tail = null; this.length = 0; }
append(value) { const newNode = new Node(value); if (!this.head) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; this.tail = newNode; } this.length++; }
prepend(value) { const newNode = new Node(value); newNode.next = this.head; this.head = newNode; if (!this.tail) { this.tail = newNode; } this.length++; }
print() { const arr = []; let current = this.head; while (current) { arr.push(current.value); current = current.next; } return arr; }}
// مثالconst list = new LinkedList();list.append(10);list.append(20);list.prepend(5);console.log(list.print()); // [5, 10, 20]2. خوارزميات البحث: الخطي والثنائي
البحث هو أحد أكثر العمليات شيوعًا. سننظر في طريقتين: البحث الخطي البسيط والبحث الثنائي الفعال.
البحث الخطي
مناسب للبيانات غير المرتبة. التعقيد O(n):
function linearSearch(arr, target) { for (let i = 0; i < arr.length; i++) { if (arr[i] === target) { return i; // إرجاع الفهرس } } return -1;}
console.log(linearSearch([3, 7, 1, 9, 4], 9)); // 3البحث الثنائي
يتطلب مصفوفة مرتبة. التعقيد O(log n). التنفيذ في Node.js:
function binarySearch(arr, target) { let left = 0; let right = arr.length - 1;
while (left <= right) { const mid = M