From 1cc743e97ad0447d4578dfdfb1ccbb377c9506d5 Mon Sep 17 00:00:00 2001 From: sjinzh <99076655+sjinzh@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:27:14 +0800 Subject: [PATCH] add zig codes for Section Quick Sort, Merge Sort, Radix Sort (#282) * add zig codes for Section 'Quick Sort' (quick_sort.zig), 'Merge Sort' (merge_sort.zig) * add zig codes for Section 'Quick Sort' (quick_sort.zig), 'Merge Sort' (merge_sort.zig) * add zig codes for Section 'Quick Sort' (quick_sort.zig), 'Merge Sort' (merge_sort.zig), 'Radix Sort' (radix_sort.zig) * add zig codes for Section 'Quick Sort' (quick_sort.zig), 'Merge Sort' (merge_sort.zig), 'Radix Sort' (radix_sort.zig) --- codes/zig/build.zig | 42 ++++++ codes/zig/chapter_sorting/merge_sort.zig | 67 ++++++++++ codes/zig/chapter_sorting/quick_sort.zig | 160 +++++++++++++++++++++++ codes/zig/chapter_sorting/radix_sort.zig | 78 +++++++++++ 4 files changed, 347 insertions(+) create mode 100644 codes/zig/chapter_sorting/merge_sort.zig create mode 100644 codes/zig/chapter_sorting/quick_sort.zig create mode 100644 codes/zig/chapter_sorting/radix_sort.zig diff --git a/codes/zig/build.zig b/codes/zig/build.zig index 52d2103c7..ee77bd022 100644 --- a/codes/zig/build.zig +++ b/codes/zig/build.zig @@ -297,4 +297,46 @@ pub fn build(b: *std.build.Builder) void { if (b.args) |args| run_cmd_insertion_sort.addArgs(args); const run_step_insertion_sort = b.step("run_insertion_sort", "Run insertion_sort"); run_step_insertion_sort.dependOn(&run_cmd_insertion_sort.step); + + // Section: "Quick Sort" + // Source File: "chapter_sorting/quick_sort.zig" + // Run Command: zig build run_quick_sort + const exe_quick_sort = b.addExecutable("quick_sort", "chapter_sorting/quick_sort.zig"); + exe_quick_sort.addPackagePath("include", "include/include.zig"); + exe_quick_sort.setTarget(target); + exe_quick_sort.setBuildMode(mode); + exe_quick_sort.install(); + const run_cmd_quick_sort = exe_quick_sort.run(); + run_cmd_quick_sort.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_quick_sort.addArgs(args); + const run_step_quick_sort = b.step("run_quick_sort", "Run quick_sort"); + run_step_quick_sort.dependOn(&run_cmd_quick_sort.step); + + // Section: "Merge Sort" + // Source File: "chapter_sorting/merge_sort.zig" + // Run Command: zig build run_merge_sort + const exe_merge_sort = b.addExecutable("merge_sort", "chapter_sorting/merge_sort.zig"); + exe_merge_sort.addPackagePath("include", "include/include.zig"); + exe_merge_sort.setTarget(target); + exe_merge_sort.setBuildMode(mode); + exe_merge_sort.install(); + const run_cmd_merge_sort = exe_merge_sort.run(); + run_cmd_merge_sort.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_merge_sort.addArgs(args); + const run_step_merge_sort = b.step("run_merge_sort", "Run merge_sort"); + run_step_merge_sort.dependOn(&run_cmd_merge_sort.step); + + // Section: "Radix Sort" + // Source File: "chapter_sorting/radix_sort.zig" + // Run Command: zig build run_radix_sort + const exe_radix_sort = b.addExecutable("radix_sort", "chapter_sorting/radix_sort.zig"); + exe_radix_sort.addPackagePath("include", "include/include.zig"); + exe_radix_sort.setTarget(target); + exe_radix_sort.setBuildMode(mode); + exe_radix_sort.install(); + const run_cmd_radix_sort = exe_radix_sort.run(); + run_cmd_radix_sort.step.dependOn(b.getInstallStep()); + if (b.args) |args| run_cmd_radix_sort.addArgs(args); + const run_step_radix_sort = b.step("run_radix_sort", "Run radix_sort"); + run_step_radix_sort.dependOn(&run_cmd_radix_sort.step); } diff --git a/codes/zig/chapter_sorting/merge_sort.zig b/codes/zig/chapter_sorting/merge_sort.zig new file mode 100644 index 000000000..85e1e3754 --- /dev/null +++ b/codes/zig/chapter_sorting/merge_sort.zig @@ -0,0 +1,67 @@ +// File: merge_sort.zig +// Created Time: 2023-01-15 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 合并左子数组和右子数组 +// 左子数组区间 [left, mid] +// 右子数组区间 [mid + 1, right] +fn merge(nums: []i32, left: usize, mid: usize, right: usize) !void { + // 初始化辅助数组 + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + var tmp = try mem_allocator.alloc(i32, right + 1 - left); + std.mem.copy(i32, tmp, nums[left..right+1]); + // 左子数组的起始索引和结束索引 + var leftStart = left - left; + var leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + var rightStart = mid + 1 - left; + var rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + var i = leftStart; + var j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + var k = left; + while (k <= right) : (k += 1) { + // 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) { + nums[k] = tmp[j]; + j += 1; + // 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++ + } else if (j > rightEnd or tmp[i] <= tmp[j]) { + nums[k] = tmp[i]; + i += 1; + // 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + } else { + nums[k] = tmp[j]; + j += 1; + } + } +} + +// 归并排序 +fn mergeSort(nums: []i32, left: usize, right: usize) !void { + // 终止条件 + if (left >= right) return; // 当子数组长度为 1 时终止递归 + // 划分阶段 + var mid = (left + right) / 2; // 计算中点 + try mergeSort(nums, left, mid); // 递归左子数组 + try mergeSort(nums, mid + 1, right); // 递归右子数组 + // 合并阶段 + try merge(nums, left, mid, right); +} + +// Driver Code +pub fn main() !void { + // 归并排序 + var nums = [_]i32{ 7, 3, 2, 6, 0, 1, 5, 4 }; + try mergeSort(&nums, 0, nums.len - 1); + std.debug.print("归并排序完成后 nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file diff --git a/codes/zig/chapter_sorting/quick_sort.zig b/codes/zig/chapter_sorting/quick_sort.zig new file mode 100644 index 000000000..1a62e3879 --- /dev/null +++ b/codes/zig/chapter_sorting/quick_sort.zig @@ -0,0 +1,160 @@ +// File: quick_sort.zig +// Created Time: 2023-01-15 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 快速排序类 +const QuickSort = struct { + // 元素交换 + pub fn swap(nums: []i32, i: usize, j: usize) void { + var tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + // 哨兵划分 + pub fn partition(nums: []i32, left: usize, right: usize) usize { + // 以 nums[left] 作为基准数 + var i = left; + var j = right; + while (i < j) { + while (i < j and nums[j] >= nums[left]) j -= 1; // 从右向左找首个小于基准数的元素 + while (i < j and nums[i] <= nums[left]) i += 1; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + + // 快速排序 + pub fn quickSort(nums: []i32, left: usize, right: usize) void { + // 子数组长度为 1 时终止递归 + if (left >= right) return; + // 哨兵划分 + var pivot = partition(nums, left, right); + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +// 快速排序类(中位基准数优化) +const QuickSortMedian = struct { + // 元素交换 + pub fn swap(nums: []i32, i: usize, j: usize) void { + var tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + // 选取三个元素的中位数 + pub fn medianThree(nums: []i32, left: usize, mid: usize, right: usize) usize { + // 使用了异或操作来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if ((nums[left] < nums[mid]) != (nums[left] < nums[right])) { + return left; + } else if ((nums[mid] < nums[left]) != (nums[mid] < nums[right])) { + return mid; + } else { + return right; + } + } + + // 哨兵划分(三数取中值) + pub fn partition(nums: []i32, left: usize, right: usize) usize { + // 选取三个候选元素的中位数 + var 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 and nums[j] >= nums[left]) j -= 1; // 从右向左找首个小于基准数的元素 + while (i < j and nums[i] <= nums[left]) i += 1; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + + // 快速排序 + pub fn quickSort(nums: []i32, left: usize, right: usize) void { + // 子数组长度为 1 时终止递归 + if (left >= right) return; + // 哨兵划分 + var pivot = partition(nums, left, right); + if (pivot == 0) return; + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } +}; + +// 快速排序类(尾递归优化) +const QuickSortTailCall = struct { + // 元素交换 + pub fn swap(nums: []i32, i: usize, j: usize) void { + var tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + // 哨兵划分 + pub fn partition(nums: []i32, left: usize, right: usize) usize { + // 以 nums[left] 作为基准数 + var i = left; + var j = right; + while (i < j) { + while (i < j and nums[j] >= nums[left]) j -= 1; // 从右向左找首个小于基准数的元素 + while (i < j and nums[i] <= nums[left]) i += 1; // 从左向右找首个大于基准数的元素 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + + // 快速排序(尾递归优化) + pub fn quickSort(nums: []i32, left_: usize, right_: usize) void { + var left = left_; + var right = right_; + // 子数组长度为 1 时终止递归 + while (left < right) { + // 哨兵划分操作 + var pivot = partition(nums, left, right); + // 对两个子数组中较短的那个执行快排 + if (pivot - left < right - pivot) { + quickSort(nums, left, pivot - 1); // 递归排序左子数组 + left = pivot + 1; // 剩余待排序区间为 [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, right); // 递归排序右子数组 + right = pivot - 1; // 剩余待排序区间为 [left, pivot - 1] + } + } + } +}; + +// Driver Code +pub fn main() !void { + // 快速排序 + var nums = [_]i32{ 2, 4, 1, 0, 3, 5 }; + QuickSort.quickSort(&nums, 0, nums.len - 1); + std.debug.print("快速排序完成后 nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + // 快速排序(中位基准数优化) + var nums1 = [_]i32{ 2, 4, 1, 0, 3, 5 }; + QuickSortMedian.quickSort(&nums1, 0, nums1.len - 1); + std.debug.print("\n快速排序(中位基准数优化)完成后 nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums1); + + // 快速排序(尾递归优化) + var nums2 = [_]i32{ 2, 4, 1, 0, 3, 5 }; + QuickSortTailCall.quickSort(&nums2, 0, nums2.len - 1); + std.debug.print("\n快速排序(尾递归优化)完成后 nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums2); + + _ = try std.io.getStdIn().reader().readByte(); +} diff --git a/codes/zig/chapter_sorting/radix_sort.zig b/codes/zig/chapter_sorting/radix_sort.zig new file mode 100644 index 000000000..c9fc2cc9b --- /dev/null +++ b/codes/zig/chapter_sorting/radix_sort.zig @@ -0,0 +1,78 @@ +// File: radix_sort.zig +// Created Time: 2023-01-15 +// Author: sjinzh (sjinzh@gmail.com) + +const std = @import("std"); +const inc = @import("include"); + +// 获取元素 num 的第 k 位,其中 exp = 10^(k-1) +fn digit(num: i32, exp: i32) i32 { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return @mod(@divFloor(num, exp), 10); +} + +// 计数排序(根据 nums 第 k 位排序) +fn countSort(nums: []i32, exp: i32) !void { + // 十进制的各位数字范围为 0~9 ,因此需要长度为 10 的桶 + var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + // defer mem_arena.deinit(); + const mem_allocator = mem_arena.allocator(); + var bucket = try mem_allocator.alloc(usize, 10); + std.mem.set(usize, bucket, 0); + var n = nums.len; + // 借助桶来统计 0~9 各数字的出现次数 + for (nums) |num| { + var d = @bitCast(u32, digit(num, exp)); // 获取 nums[i] 第 k 位,记为 d + bucket[d] += 1; // 统计数字 d 的出现次数 + } + // 求前缀和,将“出现个数”转换为“数组索引” + var i: usize = 1; + while (i < 10) : (i += 1) { + bucket[i] += bucket[i - 1]; + } + // 倒序遍历,根据桶内统计结果,将各元素填入暂存数组 tmp + var tmp = try mem_allocator.alloc(i32, n); + i = n - 1; + while (i >= 0) : (i -= 1) { + var d = @bitCast(u32, digit(nums[i], exp)); + var j = bucket[d] - 1; // 获取 d 在数组中的索引 j + tmp[j] = nums[i]; // 将当前元素填入索引 j + bucket[d] -= 1; // 将 d 的数量减 1 + if (i == 0) break; + } + // 将 tmp 复制到 nums + i = 0; + while (i < n) : (i += 1) { + nums[i] = tmp[i]; + } +} + +// 基数排序 +fn radixSort(nums: []i32) !void { + // 获取数组的最大元素,用于判断最大位数 + var ma: i32 = std.math.minInt(i32); + for (nums) |num| { + if (num > ma) ma = num; + } + // 按照从低位到高位的顺序遍历 + var exp: i32 = 1; + while (ma >= exp) : (exp *= 10) { + // 对数组元素的第 k 位执行「计数排序」 + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // k = 3 -> exp = 100 + // 即 exp = 10^(k-1) + try countSort(nums, exp); + } +} + +// Driver Code +pub fn main() !void { + // 基数排序 + var nums = [_]i32{ 23, 12, 3, 4, 788, 192 }; + try radixSort(&nums); + std.debug.print("基数排序完成后 nums = ", .{}); + inc.PrintUtil.printArray(i32, &nums); + + _ = try std.io.getStdIn().reader().readByte(); +} \ No newline at end of file