الخوارزميات وهياكل البيانات بلغة Kotlin: من النظرية إلى التطبيق
\n\nKotlin هي لغة حديثة وموجزة وآمنة، تُستخدم بنشاط في تطوير تطبيقات Android، والخوادم الخلفية، وحتى التطبيقات الأصلية. ومع ذلك، فإن قوتها تتجلى بالكامل فقط عند التطبيق الصحيح للخوارزميات وهياكل البيانات. في هذه المقالة، سنستعرض المفاهيم الأساسية من خلال أمثلة عملية ستساعدك على كتابة كود فعال وقابل للصيانة.
\n\n1. هياكل البيانات الأساسية: القوائم، الأكوام، والطوابير
\n\nلنبدأ بالأساسيات. توفر Kotlin مكتبة قياسية غنية، لكن فهم كيفية عمل هياكل البيانات "تحت الغطاء" أمر بالغ الأهمية.
\n\nالقائمة المرتبطة (LinkedList)
\nسنقوم بتنفيذ قائمة مرتبطة بسيطة أحادية الاتجاه يدويًا. سيساعدنا هذا على فهم مبادئ العمل مع الذاكرة والمراجع.
\n\nclass Node<T>(var value: T, var next: Node<T>? = null)\n\nclass LinkedList<T> {\n private var head: Node<T>? = null\n private var tail: Node<T>? = null\n\n fun add(value: T) {\n val node = Node(value)\n if (head == null) {\n head = node\n tail = node\n } else {\n tail?.next = node\n tail = node\n }\n }\n\n fun printAll() {\n var current = head\n while (current != null) {\n print("${current.value} ")\n current = current.next\n }\n println()\n }\n}\n\n// الاستخدام\nfun main() {\n val list = LinkedList<Int>()\n list.add(1)\n list.add(2)\n list.add(3)\n list.printAll() // الإخراج: 1 2 3\n}\n\nالكومة (Stack) المبنية على مصفوفة
\nتعمل الكومة وفق مبدأ LIFO (Last In, First Out). سنقوم بتنفيذها باستخدام قائمة قابلة للتغيير في Kotlin.
\n\nclass Stack<T> {\n private val elements = mutableListOf<T>()\n\n fun push(item: T) = elements.add(item)\n fun pop(): T? = if (elements.isNotEmpty()) elements.removeAt(elements.size - 1) else null\n fun peek(): T? = elements.lastOrNull()\n fun isEmpty() = elements.isEmpty()\n}\n\n// مثال: التحقق من توازن الأقواس\nfun isBalanced(expression: String): Boolean {\n val stack = Stack<Char>()\n for (char in expression) {\n when (char) {\n '(', '{', '[' -> stack.push(char)\n ')' -> if (stack.pop() != '(') return false\n '}' -> if (stack.pop() != '{') return false\n ']' -> if (stack.pop() != '[') return false\n }\n }\n return stack.isEmpty()\n}\n\nfun main() {\n println(isBalanced("({[]})")) // true\n println(isBalanced("({[})")) // false\n}\n\n2. خوارزميات الفرز: من الفقاعي إلى السريع
\n\nالفرز هو أحد أكثر المواضيع التي يتم دراستها. سنستعرض ثلاث خوارزميات كلاسيكية يسهل تنفيذها بلغة Kotlin.
\n\nالفرز الفقاعي (Bubble Sort)
\nخوارزمية بسيطة ولكنها غير فعالة للمصفوفات الكبيرة (O(n²)).
\n\nfun bubbleSort(arr: IntArray) {\n val n = arr.size\n for (i in 0 until n - 1) {\n for (j in 0 until n - i - 1) {\n if (arr[j] > arr[j + 1]) {\n val temp = arr[j]\n arr[j] = arr[j + 1]\n arr[j + 1] = temp\n }\n }\n }\n}\n\nfun main() {\n val array = intArrayOf(64, 34, 25, 12, 22, 11, 90)\n bubbleSort(array)\n println(array.joinToString()) // 11, 12, 22, 25, 34, 64, 90\n}\n\nالفرز السريع (Quick Sort)
\nواحدة من أسرع الخوارزميات في الحالة المتوسطة (O(n log n)). تستخدم العودية وتقسيم المصفوفة.
\n\nfun quickSort(arr: IntArray, low: Int, high: Int) {\n if (low < high) {\n val pi = partition(arr, low, high)\n quickSort(arr, low, pi - 1)\n quickSort(arr, pi + 1, high)\n }\n}\n\nfun partition(arr: IntArray, low: Int, high: Int): Int {\n val pivot = arr[high]\n var i = low - 1\n for (j in low until high) {\n