الخوارزميات وهياكل البيانات بلغة Go: دليل عملي
\n\nGo (Golang) هي لغة مترجمة، ثابتة الأنواع، تم إنشاؤها في Google. بساطتها، أداؤها العالي، ودعمها المدمج للتوازي يجعلها خيارًا ممتازًا لتطوير الواجهات الخلفية، الخدمات المصغرة، والبرمجة النظامية. ومع ذلك، لكتابة كود فعال بلغة Go، لا يكفي معرفة بناء الجملة - بل يجب فهم كيفية عمل الخوارزميات وهياكل البيانات. في هذه المقالة، سنشرح هياكل البيانات والخوارزميات الرئيسية بلغة Go، مع تزويد كل قسم بأمثلة عملية.
\n\n1. المكدس (Stack) بلغة Go
\n\nالمكدس هو هيكل بيانات يعمل وفق مبدأ LIFO (آخر دخول أول خروج). لا يوجد في Go نوع مدمج "مكدس"، ولكن يمكن تنفيذه بسهولة باستخدام الشريحة (slice).
\n\nمثال على تنفيذ المكدس:
\n\npackage main\n\nimport "fmt"\n\ntype Stack struct {\n items []int\n}\n\n// Push يضيف عنصرًا إلى قمة المكدس\nfunc (s *Stack) Push(item int) {\n s.items = append(s.items, item)\n}\n\n// Pop يزيل ويعيد العنصر من قمة المكدس\nfunc (s *Stack) Pop() (int, bool) {\n if len(s.items) == 0 {\n return 0, false\n }\n lastIndex := len(s.items) - 1\n item := s.items[lastIndex]\n s.items = s.items[:lastIndex]\n return item, true\n}\n\n// Peek يعيد العنصر من القمة دون إزالته\nfunc (s *Stack) Peek() (int, bool) {\n if len(s.items) == 0 {\n return 0, false\n }\n return s.items[len(s.items)-1], true\n}\n\nfunc main() {\n stack := Stack{}\n stack.Push(10)\n stack.Push(20)\n stack.Push(30)\n\n item, ok := stack.Pop()\n if ok {\n fmt.Println("تم الاستخراج:", item) // 30\n }\n fmt.Println("قمة المكدس:", stack.Peek()) // 20\n}\n\nالمكدس بلغة Go فعال بفضل تعقيده المطفأ O(1) لعمليات Push و Pop. استخدمه في مهام مثل التحقق من تسلسل الأقواس أو التراجع عن الإجراءات (undo).
\n\n2. الطابور (Queue) بلغة Go
\n\nالطابور يعمل وفق مبدأ FIFO (أول دخول أول خروج). سنقوم بتنفيذه باستخدام شريحة مع مؤشرات للبداية والنهاية.
\n\nمثال على تنفيذ الطابور:
\n\npackage main\n\nimport "fmt"\n\ntype Queue struct {\n items []int\n}\n\n// Enqueue يضيف عنصرًا إلى نهاية الطابور\nfunc (q *Queue) Enqueue(item int) {\n q.items = append(q.items, item)\n}\n\n// Dequeue يزيل ويعيد العنصر من بداية الطابور\nfunc (q *Queue) Dequeue() (int, bool) {\n if len(q.items) == 0 {\n return 0, false\n }\n item := q.items[0]\n q.items = q.items[1:]\n return item, true\n}\n\n// Front يعيد العنصر الأول دون إزالته\nfunc (q *Queue) Front() (int, bool) {\n if len(q.items) == 0 {\n return 0, false\n }\n return q.items[0], true\n}\n\nfunc main() {\n queue := Queue{}\n queue.Enqueue(1)\n queue.Enqueue(2)\n queue.Enqueue(3)\n\n item, ok := queue.Dequeue()\n if ok {\n fmt.Println("تم الاستخراج:", item) // 1\n }\n fmt.Println("الأول في الطابور:", queue.Front()) // 2\n}\n\nتنبيه: عملية Dequeue في هذا التنفيذ لها تعقيد O(n)، لأن إزالة العنصر الأول يؤدي إلى إزاحة الشريحة بأكملها. للكود الإنتاجي، استخدم المخزن المؤقت الحلقي (ring buffer) أو القائمة المزدوجة الارتباط (container/list).
3. فرز الفقاعات (Bubble Sort) بلغة Go
\n\nفرز الفقاعات هو أحد أبسط الخوارزميات، ولكنه غير فعال. ومع ذلك، فهو ممتاز للتعلم. التعقيد: O(n²).
\n\npackage main\n\nimport "fmt"\n\nfunc bubbleSort(arr []int) {\n n := len(arr)\n for i := 0; i < n-1; i++ {\n swapped := false\n for j := 0; j < n-i-1; j++ {\n if arr[j] > arr[j+1] {\n arr[j], arr[j+1] = arr[j+1], arr[j]\n swapped = true\n }\n }\n // إذا لم تكن هناك عمليات تبادل - المصفوفة مرتبة بالفعل\n if !swap