الخوارزميات وهياكل البيانات بلغة C++: من النظرية إلى التطبيق
\n\nإتقان الخوارزميات وهياكل البيانات هو مرحلة أساسية في تكوين مطور محترف. بدون فهم عميق لكيفية تنظيم البيانات ومعالجتها بكفاءة، من المستحيل إنشاء تطبيقات عالية الأداء وقابلة للتوسع. توفر لغة C++ إمكانيات فريدة للعمل على مستوى منخفض مع الذاكرة وأداء عالٍ، مما يجعلها لغة مثالية لدراسة هذا الموضوع.
\n\nفي هذه المقالة، سنستعرض الخوارزميات وهياكل البيانات الأساسية بلغة C++ مع أمثلة عملية للكود يمكنك تطبيقها فورًا في مشاريعك. لن نتعمق في الحسابات الرياضية المجردة — تركيزنا على التنفيذ الحقيقي وفهم مبادئ العمل.
\n\n1. المصفوفات الديناميكية والتعامل مع الذاكرة
\n\nلنبدأ بالأساسيات — المصفوفات الديناميكية. على عكس المصفوفات الثابتة التي يكون حجمها ثابتًا في مرحلة التجميع، تسمح المصفوفات الديناميكية بتغيير الحجم أثناء تنفيذ البرنامج. في لغة C++، يُستخدم عامل new[] و delete[] لهذا الغرض.
#include <iostream>\n\nint main() {\n int size = 5;\n int* arr = new int[size]; // تخصيص الذاكرة\n \n // التهيئة\n for(int i = 0; i < size; i++) {\n arr[i] = i * 10;\n }\n \n // الإخراج\n for(int i = 0; i < size; i++) {\n std::cout << arr[i] << " ";\n }\n \n delete[] arr; // تحرير الذاكرة\n return 0;\n}\n\nمن المهم تذكر أنه عند العمل مع الذاكرة الديناميكية، أنت مسؤول عن تحريرها. تسرب الذاكرة هو أحد أكثر المشكلات شيوعًا في لغة C++. يُوصى باستخدام المؤشرات الذكية (std::unique_ptr, std::shared_ptr) من المكتبة القياسية لإدارة الذاكرة تلقائيًا.
2. المكدس والطابور: هياكل البيانات الكلاسيكية
\n\nالمكدس والطابور هما هيكلان أساسيان للبيانات يطبقان مبدأي LIFO (آخر دخول، أول خروج) و FIFO (أول دخول، أول خروج) على التوالي. دعنا نستعرض تنفيذهما بلغة C++ باستخدام مصفوفة ديناميكية.
\n\nتنفيذ المكدس
\n\n#include <iostream>\n#include <stdexcept>\n\nclass Stack {\nprivate:\n int* data;\n int capacity;\n int top;\n \npublic:\n Stack(int size) : capacity(size), top(-1) {\n data = new int[capacity];\n }\n \n ~Stack() {\n delete[] data;\n }\n \n void push(int value) {\n if(top >= capacity - 1) {\n throw std::overflow_error("Stack overflow");\n }\n data[++top] = value;\n }\n \n int pop() {\n if(isEmpty()) {\n throw std::underflow_error("Stack underflow");\n }\n return data[top--];\n }\n \n bool isEmpty() const {\n return top == -1;\n }\n \n int peek() const {\n if(isEmpty()) {\n throw std::underflow_error("Stack is empty");\n }\n return data[top];\n }\n};\n\nint main() {\n Stack stack(5);\n stack.push(10);\n stack.push(20);\n stack.push(30);\n \n std::cout << "العنصر العلوي: " << stack.peek() << std::endl;\n std::cout << "نستخرج: " << stack.pop() << std::endl;\n std::cout << "نستخرج: " << stack.pop() << std::endl;\n \n return 0;\n}\n\nتنفيذ الطابور
\n\n#include <iostream>\n\nclass Queue {\nprivate:\n int* data;\n int capacity;\n int front;\n int rear;\n int count;\n \npublic:\n Queue(int size) : capacity(size), front(0), rear(-1), count(0) {\n data = new int[capacity];\n }\n \n ~Queue() {\n delete[] data;\n }\n \n void enqueue(int value) {\n if(isFull()) {\n throw std::overflow_error("Queue overflow");\n }\n rear = (rear + 1) %