From 1845937c5a38fcfa9289b5d9629133efc550eff5 Mon Sep 17 00:00:00 2001 From: curtishd <131777542+curtishd@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:34:20 +0800 Subject: [PATCH] Add kotlin code for the chapter of sorting (#1145) * feat(kotlin): add kotlin code for chapter sorting. * style(kotlin): fix some indent. * refactor(kotlin): refactor quick_sort.kt files. * style(kotlin): modified medianThree function. --- codes/kotlin/chapter_sorting/bubble_sort.kt | 49 ++++++++ codes/kotlin/chapter_sorting/bucket_sort.kt | 46 +++++++ codes/kotlin/chapter_sorting/counting_sort.kt | 80 ++++++++++++ codes/kotlin/chapter_sorting/heap_sort.kt | 48 +++++++ .../kotlin/chapter_sorting/insertion_sort.kt | 29 +++++ codes/kotlin/chapter_sorting/merge_sort.kt | 54 ++++++++ codes/kotlin/chapter_sorting/quick_sort.kt | 119 ++++++++++++++++++ codes/kotlin/chapter_sorting/radix_sort.kt | 67 ++++++++++ .../kotlin/chapter_sorting/selection_sort.kt | 29 +++++ 9 files changed, 521 insertions(+) create mode 100644 codes/kotlin/chapter_sorting/bubble_sort.kt create mode 100644 codes/kotlin/chapter_sorting/bucket_sort.kt create mode 100644 codes/kotlin/chapter_sorting/counting_sort.kt create mode 100644 codes/kotlin/chapter_sorting/heap_sort.kt create mode 100644 codes/kotlin/chapter_sorting/insertion_sort.kt create mode 100644 codes/kotlin/chapter_sorting/merge_sort.kt create mode 100644 codes/kotlin/chapter_sorting/quick_sort.kt create mode 100644 codes/kotlin/chapter_sorting/radix_sort.kt create mode 100644 codes/kotlin/chapter_sorting/selection_sort.kt diff --git a/codes/kotlin/chapter_sorting/bubble_sort.kt b/codes/kotlin/chapter_sorting/bubble_sort.kt new file mode 100644 index 000000000..427e28d69 --- /dev/null +++ b/codes/kotlin/chapter_sorting/bubble_sort.kt @@ -0,0 +1,49 @@ +/** + * File: bubble_sort.kt + * Created Time: 2024-1-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* 冒泡排序 */ +fun bubbleSort(nums: IntArray) { + // 外循环:未排序区间为 [0, i] + for (i in nums.size - 1 downTo 1) { + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for (j in 0.. nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + nums[j] = nums[j+1].also { nums[j+1] = nums[j] } + } + } + } +} + +/* 冒泡排序(标志优化) */ +fun bubbleSortWithFlag(nums: IntArray) { + // 外循环:未排序区间为 [0, i] + for (i in nums.size - 1 downTo 1) { + var flag = false // 初始化标志位 + // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for (j in 0.. nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + nums[j] = nums[j + 1].also { nums[j] = nums[j + 1] } + flag = true // 记录交换元素 + } + } + if (!flag) break // 此轮“冒泡”未交换任何元素,直接跳出 + } +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(4, 1, 3, 1, 5, 2) + bubbleSort(nums) + println("冒泡排序完成后 nums = ${nums.contentToString()}") + + val nums1 = intArrayOf(4, 1, 3, 1, 5, 2) + bubbleSortWithFlag(nums1) + println("冒泡排序完成后 nums1 = ${nums1.contentToString()}") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_sorting/bucket_sort.kt b/codes/kotlin/chapter_sorting/bucket_sort.kt new file mode 100644 index 000000000..3e1bb8720 --- /dev/null +++ b/codes/kotlin/chapter_sorting/bucket_sort.kt @@ -0,0 +1,46 @@ +/** + * File: bucket_sort.kt + * Created Time: 2024-1-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +import kotlin.collections.ArrayList + +/* 桶排序 */ +fun bucketSort(nums: FloatArray) { + // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + val k = nums.size / 2 + val buckets = ArrayList>() + for (i in 0.. nums[ma]) ma = l + if (r < n && nums[r] > nums[ma]) ma = r + // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 + if (ma == i) break + // 交换两节点 + nums[i] = nums[ma].also { nums[ma] = nums[i] } + // 循环向下堆化 + i = ma + } +} + +/* 堆排序 */ +fun heapSort(nums: IntArray) { + // 建堆操作:堆化除叶节点以外的其他所有节点 + for (i in nums.size / 2 - 1 downTo 0) { + siftDown(nums, nums.size, i) + } + // 从堆中提取最大元素,循环 n-1 轮 + for (i in nums.size - 1 downTo 1) { + // 交换根节点与最右叶节点(交换首元素与尾元素) + nums[0] = nums[i].also { nums[i] = nums[0] } + // 以根节点为起点,从顶至底进行堆化 + siftDown(nums, i, 0) + } +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(4, 1, 3, 1, 5, 2) + heapSort(nums) + println("堆排序完成后 nums = ${nums.contentToString()}") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_sorting/insertion_sort.kt b/codes/kotlin/chapter_sorting/insertion_sort.kt new file mode 100644 index 000000000..a272927a5 --- /dev/null +++ b/codes/kotlin/chapter_sorting/insertion_sort.kt @@ -0,0 +1,29 @@ +/** + * File: insertion_sort.kt + * Created Time: 2024-1-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* 插入排序 */ +fun insertionSort(nums: IntArray) { + //外循环: 已排序元素为 1, 2, ..., n + for (i in nums.indices) { + val base = nums[i] + var j = i - 1 + // 内循环: 将 base 插入到已排序部分的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位 + j-- + } + nums[j + 1] = base // 将 base 赋值到正确位置 + } +} + +/* Driver Code */ +fun main() { + val nums = intArrayOf(4, 1, 3, 1, 5, 2) + insertionSort(nums) + println("插入排序完成后 nums = ${nums.contentToString()}") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_sorting/merge_sort.kt b/codes/kotlin/chapter_sorting/merge_sort.kt new file mode 100644 index 000000000..f9b77ae63 --- /dev/null +++ b/codes/kotlin/chapter_sorting/merge_sort.kt @@ -0,0 +1,54 @@ +/** + * File: merge_sort.kt + * Created Time: 2024-1-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* 合并左子数组和右子数组 */ +fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { + // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right] + // 创建一个临时数组 tmp ,用于存放合并后的结果 + val tmp = IntArray(right - left + 1) + // 初始化左子数组和右子数组的起始索引 + var i = left + var j = mid + 1 + var k = 0 + // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中 + while (i <= mid && j <= right) { + if (nums[i] <= nums[j]) tmp[k++] = nums[i++] + else tmp[k++] = nums[j++] + } + // 将左子数组和右子数组的剩余元素复制到临时数组中 + while (i <= mid) { + tmp[k++] = nums[i++] + } + while (j <= right) { + tmp[k++] = nums[j++] + } + // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 + for (l in tmp.indices) { + nums[left + l] = tmp[l] + } +} + +/* 归并排序 */ +fun mergeSort(nums: IntArray, left: Int, right: Int) { + // 终止条件 + if (left >= right) return // 当子数组长度为 1 时终止递归 + // 划分阶段 + val mid = (left + right) / 2 // 计算中点 + mergeSort(nums, left, mid) // 递归左子数组 + mergeSort(nums, mid + 1, right) // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right) +} + +/* Driver Code */ +fun main() { + /* 归并排序 */ + val nums = intArrayOf(7, 3, 2, 6, 0, 1, 5, 4) + mergeSort(nums, 0, nums.size - 1) + println("归并排序完成后 nums = ${nums.contentToString()}") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_sorting/quick_sort.kt b/codes/kotlin/chapter_sorting/quick_sort.kt new file mode 100644 index 000000000..82ab5c04e --- /dev/null +++ b/codes/kotlin/chapter_sorting/quick_sort.kt @@ -0,0 +1,119 @@ +/** + * File: quick_sort.kt + * Created Time: 2024-1-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* 元素交换 */ +fun swap(nums: IntArray, i: Int, j: Int) { + nums[i] = nums[j].also { nums[j] = nums[i] } +} + +/* 哨兵划分 */ +fun partition(nums: IntArray, left: Int, right: Int): Int { + // 以 nums[left] 为基准数 + var i = left + var j = right + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j-- // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++ // 从左向右找首个大于基准数的元素 + swap(nums, i, j) // 交换这两个元素 + } + swap(nums, i, left) // 将基准数交换至两子数组的分界线 + return i // 返回基准数的索引 +} + +/* 快速排序 */ +fun quickSort(nums: IntArray, left: Int, right: Int) { + // 子数组长度为 1 时终止递归 + if (left >= right) return + // 哨兵划分 + val pivot = partition(nums, left, right) + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1) + quickSort(nums, pivot + 1, right) +} + +/* 选取三个候选元素的中位数 */ +fun medianThree(nums: IntArray, left: Int, mid: Int, right: Int): Int { + val l = nums[left] + val m = nums[mid] + val r = nums[right] + if ((m in l..r) || (m in r..l)) + return mid // m 在 l 和 r 之间 + if ((l in m..r) || (l in r..m)) + return left // l 在 m 和 r 之间 + return right +} + +/* 哨兵划分(三数取中值) */ +fun partitionMedian(nums: IntArray, left: Int, right: Int): Int { + // 选取三个候选元素的中位数 + val med = medianThree(nums, left, (left + right) / 2, right) + // 将中位数交换至数组最左端 + swap(nums, left, med) + // 以 nums[left] 为基准数 + var i = left + var j = right + while (i < j) { + while (i < j && nums[j] >= nums[left]) + j-- // 从右向左找首个小于基准数的元素 + while (i < j && nums[i] <= nums[left]) + i++ // 从左向右找首个大于基准数的元素 + swap(nums, i, j) // 交换这两个元素 + } + swap(nums, i, left) // 将基准数交换至两子数组的分界线 + return i // 返回基准数的索引 +} + +/* 快速排序 */ +fun quickSortMedian(nums: IntArray, left: Int, right: Int) { + // 子数组长度为 1 时终止递归 + if (left >= right) return + // 哨兵划分 + val pivot = partitionMedian(nums, left, right) + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1) + quickSort(nums, pivot + 1, right) +} + +/* 快速排序(尾递归优化) */ +fun quickSortTailCall(nums: IntArray, left: Int, right: Int) { + // 子数组长度为 1 时终止 + var l = left + var r = right + while (l < r) { + // 哨兵划分操作 + val pivot = partition(nums, l, r) + // 对两个子数组中较短的那个执行快速排序 + if (pivot - l < r - pivot) { + quickSort(nums, l, pivot - 1) // 递归排序左子数组 + l = pivot + 1 // 剩余未排序区间为 [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, r) // 递归排序右子数组 + r = pivot - 1 // 剩余未排序区间为 [left, pivot - 1] + } + } +} + +/* Driver Code */ +fun main() { + /* 快速排序 */ + val nums = intArrayOf(2, 4, 1, 0, 3, 5) + quickSort(nums, 0, nums.size - 1) + println("快速排序完成后 nums = ${nums.contentToString()}") + + /* 快速排序(中位基准数优化) */ + val nums1 = intArrayOf(2, 4, 1, 0, 3, 5) + quickSortMedian(nums1, 0, nums1.size - 1) + println("快速排序(中位基准数优化)完成后 nums1 = ${nums1.contentToString()}") + + /* 快速排序(尾递归优化) */ + val nums2 = intArrayOf(2, 4, 1, 0, 3, 5) + quickSortTailCall(nums2, 0, nums2.size - 1) + println("快速排序(尾递归优化)完成后 nums2 = ${nums2.contentToString()}") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_sorting/radix_sort.kt b/codes/kotlin/chapter_sorting/radix_sort.kt new file mode 100644 index 000000000..fa8ee8e84 --- /dev/null +++ b/codes/kotlin/chapter_sorting/radix_sort.kt @@ -0,0 +1,67 @@ +/** + * File: radix_sort.kt + * Created Time: 2024-1-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ +fun digit(num: Int, exp: Int): Int { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return (num / exp) % 10 +} + +/* 计数排序(根据 nums 第 k 位排序) */ +fun countingSortDigit(nums: IntArray, exp: Int) { + // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组 + val counter = IntArray(10) + val n = nums.size + // 统计 0~9 各数字的出现次数 + for (i in 0.. m) m = num + var exp = 1 + // 按照从低位到高位的顺序遍历 + while (exp <= m) { + // 对数组元素的第 k 位执行计数排序 + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // 即 exp = 10^(k-1) + countingSortDigit(nums, exp) + exp *= 10 + } +} + +/* Driver Code */ +fun main() { + // 基数排序 + val nums = intArrayOf( + 10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996 + ) + radixSort(nums) + println("基数排序完成后 nums = ${nums.contentToString()}") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_sorting/selection_sort.kt b/codes/kotlin/chapter_sorting/selection_sort.kt new file mode 100644 index 000000000..6293a4bc7 --- /dev/null +++ b/codes/kotlin/chapter_sorting/selection_sort.kt @@ -0,0 +1,29 @@ +/** + * File: selection_sort.kt + * Created Time: 2024-1-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_sorting + +/* 选择排序 */ +fun selectionSort(nums: IntArray) { + val n = nums.size + // 外循环:未排序区间为 [i, n-1] + for (i in 0..