mirror of
https://github.com/krahets/hello-algo.git
synced 2026-09-17 20:17:14 +00:00
feat: Traditional Chinese version (#1163)
* First commit * Update mkdocs.yml * Translate all the docs to traditional Chinese * Translate the code files. * Translate the docker file * Fix mkdocs.yml * Translate all the figures from SC to TC * 二叉搜尋樹 -> 二元搜尋樹 * Update terminology. * Update terminology * 构造函数/构造方法 -> 建構子 异或 -> 互斥或 * 擴充套件 -> 擴展 * constant - 常量 - 常數 * 類 -> 類別 * AVL -> AVL 樹 * 數組 -> 陣列 * 係統 -> 系統 斐波那契數列 -> 費波那契數列 運算元量 -> 運算量 引數 -> 參數 * 聯絡 -> 關聯 * 麵試 -> 面試 * 面向物件 -> 物件導向 歸併排序 -> 合併排序 范式 -> 範式 * Fix 算法 -> 演算法 * 錶示 -> 表示 反碼 -> 一補數 補碼 -> 二補數 列列尾部 -> 佇列尾部 區域性性 -> 區域性 一摞 -> 一疊 * Synchronize with main branch * 賬號 -> 帳號 推匯 -> 推導 * Sync with main branch * First commit * Update mkdocs.yml * Translate all the docs to traditional Chinese * Translate the code files. * Translate the docker file * Fix mkdocs.yml * Translate all the figures from SC to TC * 二叉搜尋樹 -> 二元搜尋樹 * Update terminology * 构造函数/构造方法 -> 建構子 异或 -> 互斥或 * 擴充套件 -> 擴展 * constant - 常量 - 常數 * 類 -> 類別 * AVL -> AVL 樹 * 數組 -> 陣列 * 係統 -> 系統 斐波那契數列 -> 費波那契數列 運算元量 -> 運算量 引數 -> 參數 * 聯絡 -> 關聯 * 麵試 -> 面試 * 面向物件 -> 物件導向 歸併排序 -> 合併排序 范式 -> 範式 * Fix 算法 -> 演算法 * 錶示 -> 表示 反碼 -> 一補數 補碼 -> 二補數 列列尾部 -> 佇列尾部 區域性性 -> 區域性 一摞 -> 一疊 * Synchronize with main branch * 賬號 -> 帳號 推匯 -> 推導 * Sync with main branch * Update terminology.md * 操作数量(num. of operations)-> 操作數量 * 字首和->前綴和 * Update figures * 歸 -> 迴 記憶體洩漏 -> 記憶體流失 * Fix the bug of the file filter * 支援 -> 支持 Add zh-Hant/README.md * Add the zh-Hant chapter covers. Bug fixes. * 外掛 -> 擴充功能 * Add the landing page for zh-Hant version * Unify the font of the chapter covers for the zh, en, and zh-Hant version * Move zh-Hant/ to zh-hant/ * Translate terminology.md to traditional Chinese
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
zig-out/
|
||||
zig-cache/
|
||||
@@ -0,0 +1,221 @@
|
||||
// File: build.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// Zig Version: 0.11.0
|
||||
// Zig Build Command: zig build -Doptimize=ReleaseSafe
|
||||
// Zig Run Command: zig build run_* -Doptimize=ReleaseSafe
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const group_name_path = .{
|
||||
// Source File: "chapter_computational_complexity/time_complexity.zig"
|
||||
// Run Command: zig build run_time_complexity -Doptimize=ReleaseSafe
|
||||
.{ .name = "time_complexity", .path = "chapter_computational_complexity/time_complexity.zig" },
|
||||
|
||||
// Source File: "chapter_computational_complexity/worst_best_time_complexity.zig"
|
||||
// Run Command: zig build run_worst_best_time_complexity -Doptimize=ReleaseSafe
|
||||
.{ .name = "worst_best_time_complexity", .path = "chapter_computational_complexity/worst_best_time_complexity.zig" },
|
||||
|
||||
// Source File: "chapter_computational_complexity/space_complexity.zig"
|
||||
// Run Command: zig build run_space_complexity -Doptimize=ReleaseSafe
|
||||
.{ .name = "space_complexity", .path = "chapter_computational_complexity/space_complexity.zig" },
|
||||
|
||||
// Source File: "chapter_computational_complexity/iteration.zig"
|
||||
// Run Command: zig build run_iteration -Doptimize=ReleaseFast
|
||||
.{ .name = "iteration", .path = "chapter_computational_complexity/iteration.zig" },
|
||||
|
||||
// Source File: "chapter_computational_complexity/recursion.zig"
|
||||
// Run Command: zig build run_recursion -Doptimize=ReleaseFast
|
||||
.{ .name = "recursion", .path = "chapter_computational_complexity/recursion.zig" },
|
||||
|
||||
// Source File: "chapter_array_and_linkedlist/array.zig"
|
||||
// Run Command: zig build run_array -Doptimize=ReleaseSafe
|
||||
.{ .name = "array", .path = "chapter_array_and_linkedlist/array.zig" },
|
||||
|
||||
// Source File: "chapter_array_and_linkedlist/linked_list.zig"
|
||||
// Run Command: zig build run_linked_list -Doptimize=ReleaseSafe
|
||||
.{ .name = "linked_list", .path = "chapter_array_and_linkedlist/linked_list.zig" },
|
||||
|
||||
// Source File: "chapter_array_and_linkedlist/list.zig"
|
||||
// Run Command: zig build run_list -Doptimize=ReleaseSafe
|
||||
.{ .name = "list", .path = "chapter_array_and_linkedlist/list.zig" },
|
||||
|
||||
// Source File: "chapter_array_and_linkedlist/my_list.zig"
|
||||
// Run Command: zig build run_my_list -Doptimize=ReleaseSafe
|
||||
.{ .name = "my_list", .path = "chapter_array_and_linkedlist/my_list.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/stack.zig"
|
||||
// Run Command: zig build run_stack -Doptimize=ReleaseSafe
|
||||
.{ .name = "stack", .path = "chapter_stack_and_queue/stack.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/linkedlist_stack.zig"
|
||||
// Run Command: zig build run_linkedlist_stack -Doptimize=ReleaseSafe
|
||||
.{ .name = "linkedlist_stack", .path = "chapter_stack_and_queue/linkedlist_stack.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/array_stack.zig"
|
||||
// Run Command: zig build run_array_stack -Doptimize=ReleaseSafe
|
||||
.{ .name = "array_stack", .path = "chapter_stack_and_queue/array_stack.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/queue.zig"
|
||||
// Run Command: zig build run_queue -Doptimize=ReleaseSafe
|
||||
.{ .name = "queue", .path = "chapter_stack_and_queue/queue.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/array_queue.zig"
|
||||
// Run Command: zig build run_array_queue -Doptimize=ReleaseSafe
|
||||
.{ .name = "array_queue", .path = "chapter_stack_and_queue/array_queue.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/linkedlist_queue.zig"
|
||||
// Run Command: zig build run_linkedlist_queue -Doptimize=ReleaseSafe
|
||||
.{ .name = "linkedlist_queue", .path = "chapter_stack_and_queue/linkedlist_queue.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/deque.zig"
|
||||
// Run Command: zig build run_deque -Doptimize=ReleaseSafe
|
||||
.{ .name = "deque", .path = "chapter_stack_and_queue/deque.zig" },
|
||||
|
||||
// Source File: "chapter_stack_and_queue/linkedlist_deque.zig"
|
||||
// Run Command: zig build run_linkedlist_deque -Doptimize=ReleaseSafe
|
||||
.{ .name = "linkedlist_deque", .path = "chapter_stack_and_queue/linkedlist_deque.zig" },
|
||||
|
||||
// Source File: "chapter_hashing/hash_map.zig"
|
||||
// Run Command: zig build run_hash_map -Doptimize=ReleaseSafe
|
||||
.{ .name = "hash_map", .path = "chapter_hashing/hash_map.zig" },
|
||||
|
||||
// Source File: "chapter_hashing/array_hash_map.zig"
|
||||
// Run Command: zig build run_array_hash_map -Doptimize=ReleaseSafe
|
||||
.{ .name = "array_hash_map", .path = "chapter_hashing/array_hash_map.zig" },
|
||||
|
||||
// Source File: "chapter_tree/binary_tree.zig"
|
||||
// Run Command: zig build run_binary_tree -Doptimize=ReleaseSafe
|
||||
.{ .name = "binary_tree", .path = "chapter_tree/binary_tree.zig" },
|
||||
|
||||
// Source File: "chapter_tree/binary_tree_bfs.zig"
|
||||
// Run Command: zig build run_binary_tree_bfs -Doptimize=ReleaseSafe
|
||||
.{ .name = "binary_tree_bfs", .path = "chapter_tree/binary_tree_bfs.zig" },
|
||||
|
||||
// Source File: "chapter_tree/binary_tree_dfs.zig"
|
||||
// Run Command: zig build run_binary_tree_dfs -Doptimize=ReleaseSafe
|
||||
.{ .name = "binary_tree_dfs", .path = "chapter_tree/binary_tree_dfs.zig" },
|
||||
|
||||
// Source File: "chapter_tree/binary_search_tree.zig"
|
||||
// Run Command: zig build run_binary_search_tree -Doptimize=ReleaseSafe
|
||||
.{ .name = "binary_search_tree", .path = "chapter_tree/binary_search_tree.zig" },
|
||||
|
||||
// Source File: "chapter_tree/avl_tree.zig"
|
||||
// Run Command: zig build run_avl_tree -Doptimize=ReleaseSafe
|
||||
.{ .name = "avl_tree", .path = "chapter_tree/avl_tree.zig" },
|
||||
|
||||
// Source File: "chapter_heap/heap.zig"
|
||||
// Run Command: zig build run_heap -Doptimize=ReleaseSafe
|
||||
.{ .name = "heap", .path = "chapter_heap/heap.zig" },
|
||||
|
||||
// Source File: "chapter_heap/my_heap.zig"
|
||||
// Run Command: zig build run_my_heap -Doptimize=ReleaseSafe
|
||||
.{ .name = "my_heap", .path = "chapter_heap/my_heap.zig" },
|
||||
|
||||
// Source File: "chapter_searching/linear_search.zig"
|
||||
// Run Command: zig build run_linear_search -Doptimize=ReleaseSafe
|
||||
.{ .name = "linear_search", .path = "chapter_searching/linear_search.zig" },
|
||||
|
||||
// Source File: "chapter_searching/binary_search.zig"
|
||||
// Run Command: zig build run_binary_search -Doptimize=ReleaseSafe
|
||||
.{ .name = "binary_search", .path = "chapter_searching/binary_search.zig" },
|
||||
|
||||
// Source File: "chapter_searching/hashing_search.zig"
|
||||
// Run Command: zig build run_hashing_search -Doptimize=ReleaseSafe
|
||||
.{ .name = "hashing_search", .path = "chapter_searching/hashing_search.zig" },
|
||||
|
||||
// Source File: "chapter_searching/two_sum.zig"
|
||||
// Run Command: zig build run_two_sum -Doptimize=ReleaseSafe
|
||||
.{ .name = "two_sum", .path = "chapter_searching/two_sum.zig" },
|
||||
|
||||
// Source File: "chapter_sorting/bubble_sort.zig"
|
||||
// Run Command: zig build run_bubble_sort -Doptimize=ReleaseSafe
|
||||
.{ .name = "bubble_sort", .path = "chapter_sorting/bubble_sort.zig" },
|
||||
|
||||
// Source File: "chapter_sorting/insertion_sort.zig"
|
||||
// Run Command: zig build run_insertion_sort -Doptimize=ReleaseSafe
|
||||
.{ .name = "insertion_sort", .path = "chapter_sorting/insertion_sort.zig" },
|
||||
|
||||
// Source File: "chapter_sorting/quick_sort.zig"
|
||||
// Run Command: zig build run_quick_sort -Doptimize=ReleaseSafe
|
||||
.{ .name = "quick_sort", .path = "chapter_sorting/quick_sort.zig" },
|
||||
|
||||
// Source File: "chapter_sorting/merge_sort.zig"
|
||||
// Run Command: zig build run_merge_sort -Doptimize=ReleaseSafe
|
||||
.{ .name = "merge_sort", .path = "chapter_sorting/merge_sort.zig" },
|
||||
|
||||
// Source File: "chapter_sorting/radix_sort.zig"
|
||||
// Run Command: zig build run_radix_sort -Doptimize=ReleaseSafe
|
||||
.{ .name = "radix_sort", .path = "chapter_sorting/radix_sort.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/climbing_stairs_backtrack.zig"
|
||||
// Run Command: zig build run_climbing_stairs_backtrack -Doptimize=ReleaseSafe
|
||||
.{ .name = "climbing_stairs_backtrack", .path = "chapter_dynamic_programming/climbing_stairs_backtrack.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/climbing_stairs_constraint_dp.zig"
|
||||
// Run Command: zig build run_climbing_stairs_constraint_dp -Doptimize=ReleaseSafe
|
||||
.{ .name = "climbing_stairs_constraint_dp", .path = "chapter_dynamic_programming/climbing_stairs_constraint_dp.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/climbing_stairs_dfs_mem.zig"
|
||||
// Run Command: zig build run_climbing_stairs_dfs_mem -Doptimize=ReleaseSafe
|
||||
.{ .name = "climbing_stairs_dfs_mem", .path = "chapter_dynamic_programming/climbing_stairs_dfs_mem.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/climbing_stairs_dfs.zig"
|
||||
// Run Command: zig build run_climbing_stairs_dfs -Doptimize=ReleaseSafe
|
||||
.{ .name = "climbing_stairs_dfs", .path = "chapter_dynamic_programming/climbing_stairs_dfs.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/climbing_stairs_dp.zig"
|
||||
// Run Command: zig build run_climbing_stairs_dp -Doptimize=ReleaseSafe
|
||||
.{ .name = "climbing_stairs_dp", .path = "chapter_dynamic_programming/climbing_stairs_dp.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/coin_change_ii.zig"
|
||||
// Run Command: zig build run_coin_change_ii -Doptimize=ReleaseSafe
|
||||
.{ .name = "coin_change_ii", .path = "chapter_dynamic_programming/coin_change_ii.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/coin_change.zig"
|
||||
// Run Command: zig build run_coin_change -Doptimize=ReleaseSafe
|
||||
.{ .name = "coin_change", .path = "chapter_dynamic_programming/coin_change.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/edit_distance.zig"
|
||||
// Run Command: zig build run_edit_distance -Doptimize=ReleaseSafe
|
||||
.{ .name = "edit_distance", .path = "chapter_dynamic_programming/edit_distance.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/knapsack.zig"
|
||||
// Run Command: zig build run_knapsack -Doptimize=ReleaseSafe
|
||||
.{ .name = "knapsack", .path = "chapter_dynamic_programming/knapsack.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/min_cost_climbing_stairs_dp.zig"
|
||||
// Run Command: zig build run_min_cost_climbing_stairs_dp -Doptimize=ReleaseSafe
|
||||
.{ .name = "min_cost_climbing_stairs_dp", .path = "chapter_dynamic_programming/min_cost_climbing_stairs_dp.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/min_path_sum.zig"
|
||||
// Run Command: zig build run_min_path_sum -Doptimize=ReleaseSafe
|
||||
.{ .name = "min_path_sum", .path = "chapter_dynamic_programming/min_path_sum.zig" },
|
||||
|
||||
// Source File: "chapter_dynamic_programming/unbounded_knapsack.zig"
|
||||
// Run Command: zig build run_unbounded_knapsack -Doptimize=ReleaseSafe
|
||||
.{ .name = "unbounded_knapsack", .path = "chapter_dynamic_programming/unbounded_knapsack.zig" },
|
||||
};
|
||||
|
||||
inline for (group_name_path) |name_path| {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = name_path.name,
|
||||
.root_source_file = .{ .path = name_path.path },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe.addModule("include", b.addModule("", .{
|
||||
.source_file = .{ .path = "include/include.zig" },
|
||||
}));
|
||||
b.installArtifact(exe);
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
const run_step = b.step("run_" ++ name_path.name, "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// File: array.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 隨機訪問元素
|
||||
pub fn randomAccess(nums: []i32) i32 {
|
||||
// 在區間 [0, nums.len) 中隨機抽取一個整數
|
||||
var randomIndex = std.crypto.random.intRangeLessThan(usize, 0, nums.len);
|
||||
// 獲取並返回隨機元素
|
||||
var randomNum = nums[randomIndex];
|
||||
return randomNum;
|
||||
}
|
||||
|
||||
// 擴展陣列長度
|
||||
pub fn extend(mem_allocator: std.mem.Allocator, nums: []i32, enlarge: usize) ![]i32 {
|
||||
// 初始化一個擴展長度後的陣列
|
||||
var res = try mem_allocator.alloc(i32, nums.len + enlarge);
|
||||
@memset(res, 0);
|
||||
// 將原陣列中的所有元素複製到新陣列
|
||||
std.mem.copy(i32, res, nums);
|
||||
// 返回擴展後的新陣列
|
||||
return res;
|
||||
}
|
||||
|
||||
// 在陣列的索引 index 處插入元素 num
|
||||
pub fn insert(nums: []i32, num: i32, index: usize) void {
|
||||
// 把索引 index 以及之後的所有元素向後移動一位
|
||||
var i = nums.len - 1;
|
||||
while (i > index) : (i -= 1) {
|
||||
nums[i] = nums[i - 1];
|
||||
}
|
||||
// 將 num 賦給 index 處的元素
|
||||
nums[index] = num;
|
||||
}
|
||||
|
||||
// 刪除索引 index 處的元素
|
||||
pub fn remove(nums: []i32, index: usize) void {
|
||||
// 把索引 index 之後的所有元素向前移動一位
|
||||
var i = index;
|
||||
while (i < nums.len - 1) : (i += 1) {
|
||||
nums[i] = nums[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
// 走訪陣列
|
||||
pub fn traverse(nums: []i32) void {
|
||||
var count: i32 = 0;
|
||||
// 透過索引走訪陣列
|
||||
var i: i32 = 0;
|
||||
while (i < nums.len) : (i += 1) {
|
||||
count += nums[i];
|
||||
}
|
||||
count = 0;
|
||||
// 直接走訪陣列元素
|
||||
for (nums) |num| {
|
||||
count += num;
|
||||
}
|
||||
}
|
||||
|
||||
// 在陣列中查詢指定元素
|
||||
pub fn find(nums: []i32, target: i32) i32 {
|
||||
for (nums, 0..) |num, i| {
|
||||
if (num == target) return @intCast(i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化記憶體分配器
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
|
||||
// 初始化陣列
|
||||
var arr = [_]i32{0} ** 5;
|
||||
std.debug.print("陣列 arr = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &arr);
|
||||
|
||||
var array = [_]i32{ 1, 3, 2, 5, 4 };
|
||||
var known_at_runtime_zero: usize = 0;
|
||||
var nums = array[known_at_runtime_zero..];
|
||||
std.debug.print("\n陣列 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, nums);
|
||||
|
||||
// 隨機訪問
|
||||
var randomNum = randomAccess(nums);
|
||||
std.debug.print("\n在 nums 中獲取隨機元素 {}", .{randomNum});
|
||||
|
||||
// 長度擴展
|
||||
nums = try extend(mem_allocator, nums, 3);
|
||||
std.debug.print("\n將陣列長度擴展至 8 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, nums);
|
||||
|
||||
// 插入元素
|
||||
insert(nums, 6, 3);
|
||||
std.debug.print("\n在索引 3 處插入數字 6 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, nums);
|
||||
|
||||
// 刪除元素
|
||||
remove(nums, 2);
|
||||
std.debug.print("\n刪除索引 2 處的元素,得到 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, nums);
|
||||
|
||||
// 走訪陣列
|
||||
traverse(nums);
|
||||
|
||||
// 查詢元素
|
||||
var index = find(nums, 3);
|
||||
std.debug.print("\n在 nums 中查詢元素 3 ,得到索引 = {}\n", .{index});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// File: linked_list.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 在鏈結串列的節點 n0 之後插入節點 P
|
||||
pub fn insert(n0: ?*inc.ListNode(i32), P: ?*inc.ListNode(i32)) void {
|
||||
var n1 = n0.?.next;
|
||||
P.?.next = n1;
|
||||
n0.?.next = P;
|
||||
}
|
||||
|
||||
// 刪除鏈結串列的節點 n0 之後的首個節點
|
||||
pub fn remove(n0: ?*inc.ListNode(i32)) void {
|
||||
if (n0.?.next == null) return;
|
||||
// n0 -> P -> n1
|
||||
var P = n0.?.next;
|
||||
var n1 = P.?.next;
|
||||
n0.?.next = n1;
|
||||
}
|
||||
|
||||
// 訪問鏈結串列中索引為 index 的節點
|
||||
pub fn access(node: ?*inc.ListNode(i32), index: i32) ?*inc.ListNode(i32) {
|
||||
var head = node;
|
||||
var i: i32 = 0;
|
||||
while (i < index) : (i += 1) {
|
||||
head = head.?.next;
|
||||
if (head == null) return null;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
// 在鏈結串列中查詢值為 target 的首個節點
|
||||
pub fn find(node: ?*inc.ListNode(i32), target: i32) i32 {
|
||||
var head = node;
|
||||
var index: i32 = 0;
|
||||
while (head != null) {
|
||||
if (head.?.val == target) return index;
|
||||
head = head.?.next;
|
||||
index += 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化鏈結串列
|
||||
// 初始化各個節點
|
||||
var n0 = inc.ListNode(i32){.val = 1};
|
||||
var n1 = inc.ListNode(i32){.val = 3};
|
||||
var n2 = inc.ListNode(i32){.val = 2};
|
||||
var n3 = inc.ListNode(i32){.val = 5};
|
||||
var n4 = inc.ListNode(i32){.val = 4};
|
||||
// 構建節點之間的引用
|
||||
n0.next = &n1;
|
||||
n1.next = &n2;
|
||||
n2.next = &n3;
|
||||
n3.next = &n4;
|
||||
std.debug.print("初始化的鏈結串列為", .{});
|
||||
try inc.PrintUtil.printLinkedList(i32, &n0);
|
||||
|
||||
// 插入節點
|
||||
var tmp = inc.ListNode(i32){.val = 0};
|
||||
insert(&n0, &tmp);
|
||||
std.debug.print("插入節點後的鏈結串列為", .{});
|
||||
try inc.PrintUtil.printLinkedList(i32, &n0);
|
||||
|
||||
// 刪除節點
|
||||
remove(&n0);
|
||||
std.debug.print("刪除節點後的鏈結串列為", .{});
|
||||
try inc.PrintUtil.printLinkedList(i32, &n0);
|
||||
|
||||
// 訪問節點
|
||||
var node = access(&n0, 3);
|
||||
std.debug.print("鏈結串列中索引 3 處的節點的值 = {}\n", .{node.?.val});
|
||||
|
||||
// 查詢節點
|
||||
var index = find(&n0, 2);
|
||||
std.debug.print("鏈結串列中值為 2 的節點的索引 = {}\n", .{index});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// File: list.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化串列
|
||||
var nums = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
// 延遲釋放記憶體
|
||||
defer nums.deinit();
|
||||
try nums.appendSlice(&[_]i32{ 1, 3, 2, 5, 4 });
|
||||
std.debug.print("串列 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 訪問元素
|
||||
var num = nums.items[1];
|
||||
std.debug.print("\n訪問索引 1 處的元素,得到 num = {}", .{num});
|
||||
|
||||
// 更新元素
|
||||
nums.items[1] = 0;
|
||||
std.debug.print("\n將索引 1 處的元素更新為 0 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 清空串列
|
||||
nums.clearRetainingCapacity();
|
||||
std.debug.print("\n清空串列後 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 在尾部新增元素
|
||||
try nums.append(1);
|
||||
try nums.append(3);
|
||||
try nums.append(2);
|
||||
try nums.append(5);
|
||||
try nums.append(4);
|
||||
std.debug.print("\n新增元素後 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 在中間插入元素
|
||||
try nums.insert(3, 6);
|
||||
std.debug.print("\n在索引 3 處插入數字 6 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 刪除元素
|
||||
_ = nums.orderedRemove(3);
|
||||
std.debug.print("\n刪除索引 3 處的元素,得到 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 透過索引走訪串列
|
||||
var count: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while (i < nums.items.len) : (i += 1) {
|
||||
count += nums[i];
|
||||
}
|
||||
// 直接走訪串列元素
|
||||
count = 0;
|
||||
for (nums.items) |x| {
|
||||
count += x;
|
||||
}
|
||||
|
||||
// 拼接兩個串列
|
||||
var nums1 = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer nums1.deinit();
|
||||
try nums1.appendSlice(&[_]i32{ 6, 8, 7, 10, 9 });
|
||||
try nums.insertSlice(nums.items.len, nums1.items);
|
||||
std.debug.print("\n將串列 nums1 拼接到 nums 之後,得到 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
// 排序串列
|
||||
std.mem.sort(i32, nums.items, {}, comptime std.sort.asc(i32));
|
||||
std.debug.print("\n排序串列後 nums = ", .{});
|
||||
inc.PrintUtil.printList(i32, nums);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
// File: my_list.zig
|
||||
// Created Time: 2023-01-08
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 串列類別
|
||||
pub fn MyList(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
arr: []T = undefined, // 陣列(儲存串列元素)
|
||||
arrCapacity: usize = 10, // 串列容量
|
||||
numSize: usize = 0, // 串列長度(當前元素數量)
|
||||
extendRatio: usize = 2, // 每次串列擴容的倍數
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 記憶體分配器
|
||||
|
||||
// 建構子(分配記憶體+初始化串列)
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator) !void {
|
||||
if (self.mem_arena == null) {
|
||||
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
self.mem_allocator = self.mem_arena.?.allocator();
|
||||
}
|
||||
self.arr = try self.mem_allocator.alloc(T, self.arrCapacity);
|
||||
@memset(self.arr, @as(T, 0));
|
||||
}
|
||||
|
||||
// 析構函式(釋放記憶體)
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.mem_arena == null) return;
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取串列長度(當前元素數量)
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.numSize;
|
||||
}
|
||||
|
||||
// 獲取串列容量
|
||||
pub fn capacity(self: *Self) usize {
|
||||
return self.arrCapacity;
|
||||
}
|
||||
|
||||
// 訪問元素
|
||||
pub fn get(self: *Self, index: usize) T {
|
||||
// 索引如果越界,則丟擲異常,下同
|
||||
if (index < 0 or index >= self.size()) @panic("索引越界");
|
||||
return self.arr[index];
|
||||
}
|
||||
|
||||
// 更新元素
|
||||
pub fn set(self: *Self, index: usize, num: T) void {
|
||||
// 索引如果越界,則丟擲異常,下同
|
||||
if (index < 0 or index >= self.size()) @panic("索引越界");
|
||||
self.arr[index] = num;
|
||||
}
|
||||
|
||||
// 在尾部新增元素
|
||||
pub fn add(self: *Self, num: T) !void {
|
||||
// 元素數量超出容量時,觸發擴容機制
|
||||
if (self.size() == self.capacity()) try self.extendCapacity();
|
||||
self.arr[self.size()] = num;
|
||||
// 更新元素數量
|
||||
self.numSize += 1;
|
||||
}
|
||||
|
||||
// 在中間插入元素
|
||||
pub fn insert(self: *Self, index: usize, num: T) !void {
|
||||
if (index < 0 or index >= self.size()) @panic("索引越界");
|
||||
// 元素數量超出容量時,觸發擴容機制
|
||||
if (self.size() == self.capacity()) try self.extendCapacity();
|
||||
// 將索引 index 以及之後的元素都向後移動一位
|
||||
var j = self.size() - 1;
|
||||
while (j >= index) : (j -= 1) {
|
||||
self.arr[j + 1] = self.arr[j];
|
||||
}
|
||||
self.arr[index] = num;
|
||||
// 更新元素數量
|
||||
self.numSize += 1;
|
||||
}
|
||||
|
||||
// 刪除元素
|
||||
pub fn remove(self: *Self, index: usize) T {
|
||||
if (index < 0 or index >= self.size()) @panic("索引越界");
|
||||
var num = self.arr[index];
|
||||
// 將索引 index 之後的元素都向前移動一位
|
||||
var j = index;
|
||||
while (j < self.size() - 1) : (j += 1) {
|
||||
self.arr[j] = self.arr[j + 1];
|
||||
}
|
||||
// 更新元素數量
|
||||
self.numSize -= 1;
|
||||
// 返回被刪除的元素
|
||||
return num;
|
||||
}
|
||||
|
||||
// 串列擴容
|
||||
pub fn extendCapacity(self: *Self) !void {
|
||||
// 新建一個長度為 size * extendRatio 的陣列,並將原陣列複製到新陣列
|
||||
var newCapacity = self.capacity() * self.extendRatio;
|
||||
var extend = try self.mem_allocator.alloc(T, newCapacity);
|
||||
@memset(extend, @as(T, 0));
|
||||
// 將原陣列中的所有元素複製到新陣列
|
||||
std.mem.copy(T, extend, self.arr);
|
||||
self.arr = extend;
|
||||
// 更新串列容量
|
||||
self.arrCapacity = newCapacity;
|
||||
}
|
||||
|
||||
// 將串列轉換為陣列
|
||||
pub fn toArray(self: *Self) ![]T {
|
||||
// 僅轉換有效長度範圍內的串列元素
|
||||
var arr = try self.mem_allocator.alloc(T, self.size());
|
||||
@memset(arr, @as(T, 0));
|
||||
for (arr, 0..) |*num, i| {
|
||||
num.* = self.get(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化串列
|
||||
var nums = MyList(i32){};
|
||||
try nums.init(std.heap.page_allocator);
|
||||
// 延遲釋放記憶體
|
||||
defer nums.deinit();
|
||||
|
||||
// 在尾部新增元素
|
||||
try nums.add(1);
|
||||
try nums.add(3);
|
||||
try nums.add(2);
|
||||
try nums.add(5);
|
||||
try nums.add(4);
|
||||
std.debug.print("串列 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try nums.toArray());
|
||||
std.debug.print(" ,容量 = {} ,長度 = {}", .{nums.capacity(), nums.size()});
|
||||
|
||||
// 在中間插入元素
|
||||
try nums.insert(3, 6);
|
||||
std.debug.print("\n在索引 3 處插入數字 6 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try nums.toArray());
|
||||
|
||||
// 刪除元素
|
||||
_ = nums.remove(3);
|
||||
std.debug.print("\n刪除索引 3 處的元素,得到 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try nums.toArray());
|
||||
|
||||
// 訪問元素
|
||||
var num = nums.get(1);
|
||||
std.debug.print("\n訪問索引 1 處的元素,得到 num = {}", .{num});
|
||||
|
||||
// 更新元素
|
||||
nums.set(1, 0);
|
||||
std.debug.print("\n將索引 1 處的元素更新為 0 ,得到 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try nums.toArray());
|
||||
|
||||
// 測試擴容機制
|
||||
var i: i32 = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
// 在 i = 5 時,串列長度將超出串列容量,此時觸發擴容機制
|
||||
try nums.add(i);
|
||||
}
|
||||
std.debug.print("\n擴容後的串列 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try nums.toArray());
|
||||
std.debug.print(" ,容量 = {} ,長度 = {}\n", .{nums.capacity(), nums.size()});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// File: iteration.zig
|
||||
// Created Time: 2023-09-27
|
||||
// Author: QiLOL (pikaqqpika@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// for 迴圈
|
||||
fn forLoop(n: usize) i32 {
|
||||
var res: i32 = 0;
|
||||
// 迴圈求和 1, 2, ..., n-1, n
|
||||
for (1..n+1) |i| {
|
||||
res = res + @as(i32, @intCast(i));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// while 迴圈
|
||||
fn whileLoop(n: i32) i32 {
|
||||
var res: i32 = 0;
|
||||
var i: i32 = 1; // 初始化條件變數
|
||||
// 迴圈求和 1, 2, ..., n-1, n
|
||||
while (i <= n) {
|
||||
res += @intCast(i);
|
||||
i += 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// while 迴圈(兩次更新)
|
||||
fn whileLoopII(n: i32) i32 {
|
||||
var res: i32 = 0;
|
||||
var i: i32 = 1; // 初始化條件變數
|
||||
// 迴圈求和 1, 4, 10, ...
|
||||
while (i <= n) {
|
||||
res += @intCast(i);
|
||||
// 更新條件變數
|
||||
i += 1;
|
||||
i *= 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// 雙層 for 迴圈
|
||||
fn nestedForLoop(allocator: Allocator, n: usize) ![]const u8 {
|
||||
var res = std.ArrayList(u8).init(allocator);
|
||||
defer res.deinit();
|
||||
var buffer: [20]u8 = undefined;
|
||||
// 迴圈 i = 1, 2, ..., n-1, n
|
||||
for (1..n+1) |i| {
|
||||
// 迴圈 j = 1, 2, ..., n-1, n
|
||||
for (1..n+1) |j| {
|
||||
var _str = try std.fmt.bufPrint(&buffer, "({d}, {d}), ", .{i, j});
|
||||
try res.appendSlice(_str);
|
||||
}
|
||||
}
|
||||
return res.toOwnedSlice();
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
const n: i32 = 5;
|
||||
var res: i32 = 0;
|
||||
|
||||
res = forLoop(n);
|
||||
std.debug.print("\nfor 迴圈的求和結果 res = {}\n", .{res});
|
||||
|
||||
res = whileLoop(n);
|
||||
std.debug.print("\nwhile 迴圈的求和結果 res = {}\n", .{res});
|
||||
|
||||
res = whileLoopII(n);
|
||||
std.debug.print("\nwhile 迴圈(兩次更新)求和結果 res = {}\n", .{res});
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
const resStr = try nestedForLoop(allocator, n);
|
||||
std.debug.print("\n雙層 for 迴圈的走訪結果 {s}\n", .{resStr});
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// File: recursion.zig
|
||||
// Created Time: 2023-09-27
|
||||
// Author: QiLOL (pikaqqpika@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 遞迴函式
|
||||
fn recur(n: i32) i32 {
|
||||
// 終止條件
|
||||
if (n == 1) {
|
||||
return 1;
|
||||
}
|
||||
// 遞:遞迴呼叫
|
||||
var res: i32 = recur(n - 1);
|
||||
// 迴:返回結果
|
||||
return n + res;
|
||||
}
|
||||
|
||||
// 使用迭代模擬遞迴
|
||||
fn forLoopRecur(comptime n: i32) i32 {
|
||||
// 使用一個顯式的堆疊來模擬系統呼叫堆疊
|
||||
var stack: [n]i32 = undefined;
|
||||
var res: i32 = 0;
|
||||
// 遞:遞迴呼叫
|
||||
var i: usize = n;
|
||||
while (i > 0) {
|
||||
stack[i - 1] = @intCast(i);
|
||||
i -= 1;
|
||||
}
|
||||
// 迴:返回結果
|
||||
var index: usize = n;
|
||||
while (index > 0) {
|
||||
index -= 1;
|
||||
res += stack[index];
|
||||
}
|
||||
// res = 1+2+3+...+n
|
||||
return res;
|
||||
}
|
||||
|
||||
// 尾遞迴函式
|
||||
fn tailRecur(n: i32, res: i32) i32 {
|
||||
// 終止條件
|
||||
if (n == 0) {
|
||||
return res;
|
||||
}
|
||||
// 尾遞迴呼叫
|
||||
return tailRecur(n - 1, res + n);
|
||||
}
|
||||
|
||||
// 費波那契數列
|
||||
fn fib(n: i32) i32 {
|
||||
// 終止條件 f(1) = 0, f(2) = 1
|
||||
if (n == 1 or n == 2) {
|
||||
return n - 1;
|
||||
}
|
||||
// 遞迴呼叫 f(n) = f(n-1) + f(n-2)
|
||||
var res: i32 = fib(n - 1) + fib(n - 2);
|
||||
// 返回結果 f(n)
|
||||
return res;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
const n: i32 = 5;
|
||||
var res: i32 = 0;
|
||||
|
||||
res = recur(n);
|
||||
std.debug.print("\n遞迴函式的求和結果 res = {}\n", .{recur(n)});
|
||||
|
||||
res = forLoopRecur(n);
|
||||
std.debug.print("\n使用迭代模擬遞迴的求和結果 res = {}\n", .{forLoopRecur(n)});
|
||||
|
||||
res = tailRecur(n, 0);
|
||||
std.debug.print("\n尾遞迴函式的求和結果 res = {}\n", .{tailRecur(n, 0)});
|
||||
|
||||
res = fib(n);
|
||||
std.debug.print("\n費波那契數列的第 {} 項為 {}\n", .{n, fib(n)});
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// File: space_complexity.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 函式
|
||||
fn function() i32 {
|
||||
// 執行某些操作
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 常數階
|
||||
fn constant(n: i32) void {
|
||||
// 常數、變數、物件佔用 O(1) 空間
|
||||
const a: i32 = 0;
|
||||
var b: i32 = 0;
|
||||
var nums = [_]i32{0}**10000;
|
||||
var node = inc.ListNode(i32){.val = 0};
|
||||
var i: i32 = 0;
|
||||
// 迴圈中的變數佔用 O(1) 空間
|
||||
while (i < n) : (i += 1) {
|
||||
var c: i32 = 0;
|
||||
_ = c;
|
||||
}
|
||||
// 迴圈中的函式佔用 O(1) 空間
|
||||
i = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
_ = function();
|
||||
}
|
||||
_ = a;
|
||||
_ = b;
|
||||
_ = nums;
|
||||
_ = node;
|
||||
}
|
||||
|
||||
// 線性階
|
||||
fn linear(comptime n: i32) !void {
|
||||
// 長度為 n 的陣列佔用 O(n) 空間
|
||||
var nums = [_]i32{0}**n;
|
||||
// 長度為 n 的串列佔用 O(n) 空間
|
||||
var nodes = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer nodes.deinit();
|
||||
var i: i32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
try nodes.append(i);
|
||||
}
|
||||
// 長度為 n 的雜湊表佔用 O(n) 空間
|
||||
var map = std.AutoArrayHashMap(i32, []const u8).init(std.heap.page_allocator);
|
||||
defer map.deinit();
|
||||
var j: i32 = 0;
|
||||
while (j < n) : (j += 1) {
|
||||
const string = try std.fmt.allocPrint(std.heap.page_allocator, "{d}", .{j});
|
||||
defer std.heap.page_allocator.free(string);
|
||||
try map.put(i, string);
|
||||
}
|
||||
_ = nums;
|
||||
}
|
||||
|
||||
// 線性階(遞迴實現)
|
||||
fn linearRecur(comptime n: i32) void {
|
||||
std.debug.print("遞迴 n = {}\n", .{n});
|
||||
if (n == 1) return;
|
||||
linearRecur(n - 1);
|
||||
}
|
||||
|
||||
// 平方階
|
||||
fn quadratic(n: i32) !void {
|
||||
// 二維串列佔用 O(n^2) 空間
|
||||
var nodes = std.ArrayList(std.ArrayList(i32)).init(std.heap.page_allocator);
|
||||
defer nodes.deinit();
|
||||
var i: i32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
var tmp = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer tmp.deinit();
|
||||
var j: i32 = 0;
|
||||
while (j < n) : (j += 1) {
|
||||
try tmp.append(0);
|
||||
}
|
||||
try nodes.append(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
// 平方階(遞迴實現)
|
||||
fn quadraticRecur(comptime n: i32) i32 {
|
||||
if (n <= 0) return 0;
|
||||
var nums = [_]i32{0}**n;
|
||||
std.debug.print("遞迴 n = {} 中的 nums 長度 = {}\n", .{n, nums.len});
|
||||
return quadraticRecur(n - 1);
|
||||
}
|
||||
|
||||
// 指數階(建立滿二元樹)
|
||||
fn buildTree(mem_allocator: std.mem.Allocator, n: i32) !?*inc.TreeNode(i32) {
|
||||
if (n == 0) return null;
|
||||
const root = try mem_allocator.create(inc.TreeNode(i32));
|
||||
root.init(0);
|
||||
root.left = try buildTree(mem_allocator, n - 1);
|
||||
root.right = try buildTree(mem_allocator, n - 1);
|
||||
return root;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
const n: i32 = 5;
|
||||
// 常數階
|
||||
constant(n);
|
||||
// 線性階
|
||||
try linear(n);
|
||||
linearRecur(n);
|
||||
// 平方階
|
||||
try quadratic(n);
|
||||
_ = quadraticRecur(n);
|
||||
// 指數階
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
var root = blk_root: {
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
break :blk_root try buildTree(mem_allocator, n);
|
||||
};
|
||||
try inc.PrintUtil.printTree(root, null, false);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
// File: time_complexity.zig
|
||||
// Created Time: 2022-12-28
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 常數階
|
||||
fn constant(n: i32) i32 {
|
||||
_ = n;
|
||||
var count: i32 = 0;
|
||||
const size: i32 = 100_000;
|
||||
var i: i32 = 0;
|
||||
while(i<size) : (i += 1) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 線性階
|
||||
fn linear(n: i32) i32 {
|
||||
var count: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 線性階(走訪陣列)
|
||||
fn arrayTraversal(nums: []i32) i32 {
|
||||
var count: i32 = 0;
|
||||
// 迴圈次數與陣列長度成正比
|
||||
for (nums) |_| {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 平方階
|
||||
fn quadratic(n: i32) i32 {
|
||||
var count: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
// 迴圈次數與資料大小 n 成平方關係
|
||||
while (i < n) : (i += 1) {
|
||||
var j: i32 = 0;
|
||||
while (j < n) : (j += 1) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 平方階(泡沫排序)
|
||||
fn bubbleSort(nums: []i32) i32 {
|
||||
var count: i32 = 0; // 計數器
|
||||
// 外迴圈:未排序區間為 [0, i]
|
||||
var i: i32 = @as(i32, @intCast(nums.len)) - 1;
|
||||
while (i > 0) : (i -= 1) {
|
||||
var j: usize = 0;
|
||||
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交換 nums[j] 與 nums[j + 1]
|
||||
var tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
count += 3; // 元素交換包含 3 個單元操作
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 指數階(迴圈實現)
|
||||
fn exponential(n: i32) i32 {
|
||||
var count: i32 = 0;
|
||||
var bas: i32 = 1;
|
||||
var i: i32 = 0;
|
||||
// 細胞每輪一分為二,形成數列 1, 2, 4, 8, ..., 2^(n-1)
|
||||
while (i < n) : (i += 1) {
|
||||
var j: i32 = 0;
|
||||
while (j < bas) : (j += 1) {
|
||||
count += 1;
|
||||
}
|
||||
bas *= 2;
|
||||
}
|
||||
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
|
||||
return count;
|
||||
}
|
||||
|
||||
// 指數階(遞迴實現)
|
||||
fn expRecur(n: i32) i32 {
|
||||
if (n == 1) return 1;
|
||||
return expRecur(n - 1) + expRecur(n - 1) + 1;
|
||||
}
|
||||
|
||||
// 對數階(迴圈實現)
|
||||
fn logarithmic(n: i32) i32 {
|
||||
var count: i32 = 0;
|
||||
var n_var = n;
|
||||
while (n_var > 1)
|
||||
{
|
||||
n_var = n_var / 2;
|
||||
count +=1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 對數階(遞迴實現)
|
||||
fn logRecur(n: i32) i32 {
|
||||
if (n <= 1) return 0;
|
||||
return logRecur(n / 2) + 1;
|
||||
}
|
||||
|
||||
// 線性對數階
|
||||
fn linearLogRecur(n: i32) i32 {
|
||||
if (n <= 1) return 1;
|
||||
var count: i32 = linearLogRecur(n / 2) + linearLogRecur(n / 2);
|
||||
var i: i32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// 階乘階(遞迴實現)
|
||||
fn factorialRecur(n: i32) i32 {
|
||||
if (n == 0) return 1;
|
||||
var count: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
// 從 1 個分裂出 n 個
|
||||
while (i < n) : (i += 1) {
|
||||
count += factorialRecur(n - 1);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 可以修改 n 執行,體會一下各種複雜度的操作數量變化趨勢
|
||||
const n: i32 = 8;
|
||||
std.debug.print("輸入資料大小 n = {}\n", .{n});
|
||||
|
||||
var count = constant(n);
|
||||
std.debug.print("常數階的操作數量 = {}\n", .{count});
|
||||
|
||||
count = linear(n);
|
||||
std.debug.print("線性階的操作數量 = {}\n", .{count});
|
||||
var nums = [_]i32{0}**n;
|
||||
count = arrayTraversal(&nums);
|
||||
std.debug.print("線性階(走訪陣列)的操作數量 = {}\n", .{count});
|
||||
|
||||
count = quadratic(n);
|
||||
std.debug.print("平方階的操作數量 = {}\n", .{count});
|
||||
for (&nums, 0..) |*num, i| {
|
||||
num.* = n - @as(i32, @intCast(i)); // [n,n-1,...,2,1]
|
||||
}
|
||||
count = bubbleSort(&nums);
|
||||
std.debug.print("平方階(泡沫排序)的操作數量 = {}\n", .{count});
|
||||
|
||||
count = exponential(n);
|
||||
std.debug.print("指數階(迴圈實現)的操作數量 = {}\n", .{count});
|
||||
count = expRecur(n);
|
||||
std.debug.print("指數階(遞迴實現)的操作數量 = {}\n", .{count});
|
||||
|
||||
count = logarithmic(n);
|
||||
std.debug.print("對數階(迴圈實現)的操作數量 = {}\n", .{count});
|
||||
count = logRecur(n);
|
||||
std.debug.print("對數階(遞迴實現)的操作數量 = {}\n", .{count});
|
||||
|
||||
count = linearLogRecur(n);
|
||||
std.debug.print("線性對數階(遞迴實現)的操作數量 = {}\n", .{count});
|
||||
|
||||
count = factorialRecur(n);
|
||||
std.debug.print("階乘階(遞迴實現)的操作數量 = {}\n", .{count});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// File: worst_best_time_complexity.zig
|
||||
// Created Time: 2022-12-28
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 生成一個陣列,元素為 { 1, 2, ..., n },順序被打亂
|
||||
pub fn randomNumbers(comptime n: usize) [n]i32 {
|
||||
var nums: [n]i32 = undefined;
|
||||
// 生成陣列 nums = { 1, 2, 3, ..., n }
|
||||
for (&nums, 0..) |*num, i| {
|
||||
num.* = @as(i32, @intCast(i)) + 1;
|
||||
}
|
||||
// 隨機打亂陣列元素
|
||||
const rand = std.crypto.random;
|
||||
rand.shuffle(i32, &nums);
|
||||
return nums;
|
||||
}
|
||||
|
||||
// 查詢陣列 nums 中數字 1 所在索引
|
||||
pub fn findOne(nums: []i32) i32 {
|
||||
for (nums, 0..) |num, i| {
|
||||
// 當元素 1 在陣列頭部時,達到最佳時間複雜度 O(1)
|
||||
// 當元素 1 在陣列尾部時,達到最差時間複雜度 O(n)
|
||||
if (num == 1) return @intCast(i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
var i: i32 = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
const n: usize = 100;
|
||||
var nums = randomNumbers(n);
|
||||
var index = findOne(&nums);
|
||||
std.debug.print("\n陣列 [ 1, 2, ..., n ] 被打亂後 = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &nums);
|
||||
std.debug.print("數字 1 的索引為 {}\n", .{index});
|
||||
}
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// File: climbing_stairs_backtrack.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 回溯
|
||||
fn backtrack(choices: []i32, state: i32, n: i32, res: std.ArrayList(i32)) void {
|
||||
// 當爬到第 n 階時,方案數量加 1
|
||||
if (state == n) {
|
||||
res.items[0] = res.items[0] + 1;
|
||||
}
|
||||
// 走訪所有選擇
|
||||
for (choices) |choice| {
|
||||
// 剪枝:不允許越過第 n 階
|
||||
if (state + choice > n) {
|
||||
continue;
|
||||
}
|
||||
// 嘗試:做出選擇,更新狀態
|
||||
backtrack(choices, state + choice, n, res);
|
||||
// 回退
|
||||
}
|
||||
}
|
||||
|
||||
// 爬樓梯:回溯
|
||||
fn climbingStairsBacktrack(n: usize) !i32 {
|
||||
var choices = [_]i32{ 1, 2 }; // 可選擇向上爬 1 階或 2 階
|
||||
var state: i32 = 0; // 從第 0 階開始爬
|
||||
var res = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer res.deinit();
|
||||
try res.append(0); // 使用 res[0] 記錄方案數量
|
||||
backtrack(&choices, state, @intCast(n), res);
|
||||
return res.items[0];
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
var n: usize = 9;
|
||||
|
||||
var res = try climbingStairsBacktrack(n);
|
||||
std.debug.print("爬 {} 階樓梯共有 {} 種方案\n", .{ n, res });
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// File: climbing_stairs_constraint_dp.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 帶約束爬樓梯:動態規劃
|
||||
fn climbingStairsConstraintDP(comptime n: usize) i32 {
|
||||
if (n == 1 or n == 2) {
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用於儲存子問題的解
|
||||
var dp = [_][3]i32{ [_]i32{ -1, -1, -1 } } ** (n + 1);
|
||||
// 初始狀態:預設最小子問題的解
|
||||
dp[1][1] = 1;
|
||||
dp[1][2] = 0;
|
||||
dp[2][1] = 0;
|
||||
dp[2][2] = 1;
|
||||
// 狀態轉移:從較小子問題逐步求解較大子問題
|
||||
for (3..n + 1) |i| {
|
||||
dp[i][1] = dp[i - 1][2];
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
|
||||
}
|
||||
return dp[n][1] + dp[n][2];
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var n: usize = 9;
|
||||
|
||||
var res = climbingStairsConstraintDP(n);
|
||||
std.debug.print("爬 {} 階樓梯共有 {} 種方案\n", .{ n, res });
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// File: climbing_stairs_dfs.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 搜尋
|
||||
fn dfs(i: usize) i32 {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if (i == 1 or i == 2) {
|
||||
return @intCast(i);
|
||||
}
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
var count = dfs(i - 1) + dfs(i - 2);
|
||||
return count;
|
||||
}
|
||||
|
||||
// 爬樓梯:搜尋
|
||||
fn climbingStairsDFS(comptime n: usize) i32 {
|
||||
return dfs(n);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var n: usize = 9;
|
||||
|
||||
var res = climbingStairsDFS(n);
|
||||
std.debug.print("爬 {} 階樓梯共有 {} 種方案\n", .{ n, res });
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// File: climbing_stairs_dfs_mem.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 記憶化搜尋
|
||||
fn dfs(i: usize, mem: []i32) i32 {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if (i == 1 or i == 2) {
|
||||
return @intCast(i);
|
||||
}
|
||||
// 若存在記錄 dp[i] ,則直接返回之
|
||||
if (mem[i] != -1) {
|
||||
return mem[i];
|
||||
}
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
var count = dfs(i - 1, mem) + dfs(i - 2, mem);
|
||||
// 記錄 dp[i]
|
||||
mem[i] = count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// 爬樓梯:記憶化搜尋
|
||||
fn climbingStairsDFSMem(comptime n: usize) i32 {
|
||||
// mem[i] 記錄爬到第 i 階的方案總數,-1 代表無記錄
|
||||
var mem = [_]i32{ -1 } ** (n + 1);
|
||||
return dfs(n, &mem);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var n: usize = 9;
|
||||
|
||||
var res = climbingStairsDFSMem(n);
|
||||
std.debug.print("爬 {} 階樓梯共有 {} 種方案\n", .{ n, res });
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// File: climbing_stairs_dp.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 爬樓梯:動態規劃
|
||||
fn climbingStairsDP(comptime n: usize) i32 {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
if (n == 1 or n == 2) {
|
||||
return @intCast(n);
|
||||
}
|
||||
// 初始化 dp 表,用於儲存子問題的解
|
||||
var dp = [_]i32{-1} ** (n + 1);
|
||||
// 初始狀態:預設最小子問題的解
|
||||
dp[1] = 1;
|
||||
dp[2] = 2;
|
||||
// 狀態轉移:從較小子問題逐步求解較大子問題
|
||||
for (3..n + 1) |i| {
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
}
|
||||
return dp[n];
|
||||
}
|
||||
|
||||
// 爬樓梯:空間最佳化後的動態規劃
|
||||
fn climbingStairsDPComp(comptime n: usize) i32 {
|
||||
if (n == 1 or n == 2) {
|
||||
return @intCast(n);
|
||||
}
|
||||
var a: i32 = 1;
|
||||
var b: i32 = 2;
|
||||
for (3..n + 1) |_| {
|
||||
var tmp = b;
|
||||
b = a + b;
|
||||
a = tmp;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var n: usize = 9;
|
||||
|
||||
var res = climbingStairsDP(n);
|
||||
std.debug.print("爬 {} 階樓梯共有 {} 種方案\n", .{ n, res });
|
||||
|
||||
res = climbingStairsDPComp(n);
|
||||
std.debug.print("爬 {} 階樓梯共有 {} 種方案\n", .{ n, res });
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// File: coin_change.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 零錢兌換:動態規劃
|
||||
fn coinChangeDP(comptime coins: []i32, comptime amt: usize) i32 {
|
||||
comptime var n = coins.len;
|
||||
comptime var max = amt + 1;
|
||||
// 初始化 dp 表
|
||||
var dp = [_][amt + 1]i32{[_]i32{0} ** (amt + 1)} ** (n + 1);
|
||||
// 狀態轉移:首行首列
|
||||
for (1..amt + 1) |a| {
|
||||
dp[0][a] = max;
|
||||
}
|
||||
// 狀態轉移:其餘行和列
|
||||
for (1..n + 1) |i| {
|
||||
for (1..amt + 1) |a| {
|
||||
if (coins[i - 1] > @as(i32, @intCast(a))) {
|
||||
// 若超過目標金額,則不選硬幣 i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// 不選和選硬幣 i 這兩種方案的較小值
|
||||
dp[i][a] = @min(dp[i - 1][a], dp[i][a - @as(usize, @intCast(coins[i - 1]))] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dp[n][amt] != max) {
|
||||
return @intCast(dp[n][amt]);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 零錢兌換:空間最佳化後的動態規劃
|
||||
fn coinChangeDPComp(comptime coins: []i32, comptime amt: usize) i32 {
|
||||
comptime var n = coins.len;
|
||||
comptime var max = amt + 1;
|
||||
// 初始化 dp 表
|
||||
var dp = [_]i32{0} ** (amt + 1);
|
||||
@memset(&dp, max);
|
||||
dp[0] = 0;
|
||||
// 狀態轉移
|
||||
for (1..n + 1) |i| {
|
||||
for (1..amt + 1) |a| {
|
||||
if (coins[i - 1] > @as(i32, @intCast(a))) {
|
||||
// 若超過目標金額,則不選硬幣 i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// 不選和選硬幣 i 這兩種方案的較小值
|
||||
dp[a] = @min(dp[a], dp[a - @as(usize, @intCast(coins[i - 1]))] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dp[amt] != max) {
|
||||
return @intCast(dp[amt]);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var coins = [_]i32{ 1, 2, 5 };
|
||||
comptime var amt: usize = 4;
|
||||
|
||||
// 動態規劃
|
||||
var res = coinChangeDP(&coins, amt);
|
||||
std.debug.print("湊到目標金額所需的最少硬幣數量為 {}\n", .{res});
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = coinChangeDPComp(&coins, amt);
|
||||
std.debug.print("湊到目標金額所需的最少硬幣數量為 {}\n", .{res});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// File: coin_change_ii.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 零錢兌換 II:動態規劃
|
||||
fn coinChangeIIDP(comptime coins: []i32, comptime amt: usize) i32 {
|
||||
comptime var n = coins.len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_][amt + 1]i32{[_]i32{0} ** (amt + 1)} ** (n + 1);
|
||||
// 初始化首列
|
||||
for (0..n + 1) |i| {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
// 狀態轉移
|
||||
for (1..n + 1) |i| {
|
||||
for (1..amt + 1) |a| {
|
||||
if (coins[i - 1] > @as(i32, @intCast(a))) {
|
||||
// 若超過目標金額,則不選硬幣 i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// 不選和選硬幣 i 這兩種方案的較小值
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - @as(usize, @intCast(coins[i - 1]))];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt];
|
||||
}
|
||||
|
||||
// 零錢兌換 II:空間最佳化後的動態規劃
|
||||
fn coinChangeIIDPComp(comptime coins: []i32, comptime amt: usize) i32 {
|
||||
comptime var n = coins.len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_]i32{0} ** (amt + 1);
|
||||
dp[0] = 1;
|
||||
// 狀態轉移
|
||||
for (1..n + 1) |i| {
|
||||
for (1..amt + 1) |a| {
|
||||
if (coins[i - 1] > @as(i32, @intCast(a))) {
|
||||
// 若超過目標金額,則不選硬幣 i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// 不選和選硬幣 i 這兩種方案的較小值
|
||||
dp[a] = dp[a] + dp[a - @as(usize, @intCast(coins[i - 1]))];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt];
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var coins = [_]i32{ 1, 2, 5 };
|
||||
comptime var amt: usize = 5;
|
||||
|
||||
// 動態規劃
|
||||
var res = coinChangeIIDP(&coins, amt);
|
||||
std.debug.print("湊出目標金額的硬幣組合數量為 {}\n", .{res});
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = coinChangeIIDPComp(&coins, amt);
|
||||
std.debug.print("湊出目標金額的硬幣組合數量為 {}\n", .{res});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// File: edit_distance.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 編輯距離:暴力搜尋
|
||||
fn editDistanceDFS(comptime s: []const u8, comptime t: []const u8, i: usize, j: usize) i32 {
|
||||
// 若 s 和 t 都為空,則返回 0
|
||||
if (i == 0 and j == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若 s 為空,則返回 t 長度
|
||||
if (i == 0) {
|
||||
return @intCast(j);
|
||||
}
|
||||
// 若 t 為空,則返回 s 長度
|
||||
if (j == 0) {
|
||||
return @intCast(i);
|
||||
}
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
return editDistanceDFS(s, t, i - 1, j - 1);
|
||||
}
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
var insert = editDistanceDFS(s, t, i, j - 1);
|
||||
var delete = editDistanceDFS(s, t, i - 1, j);
|
||||
var replace = editDistanceDFS(s, t, i - 1, j - 1);
|
||||
// 返回最少編輯步數
|
||||
return @min(@min(insert, delete), replace) + 1;
|
||||
}
|
||||
|
||||
// 編輯距離:記憶化搜尋
|
||||
fn editDistanceDFSMem(comptime s: []const u8, comptime t: []const u8, mem: anytype, i: usize, j: usize) i32 {
|
||||
// 若 s 和 t 都為空,則返回 0
|
||||
if (i == 0 and j == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若 s 為空,則返回 t 長度
|
||||
if (i == 0) {
|
||||
return @intCast(j);
|
||||
}
|
||||
// 若 t 為空,則返回 s 長度
|
||||
if (j == 0) {
|
||||
return @intCast(i);
|
||||
}
|
||||
// 若已有記錄,則直接返回之
|
||||
if (mem[i][j] != -1) {
|
||||
return mem[i][j];
|
||||
}
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
return editDistanceDFSMem(s, t, mem, i - 1, j - 1);
|
||||
}
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
var insert = editDistanceDFSMem(s, t, mem, i, j - 1);
|
||||
var delete = editDistanceDFSMem(s, t, mem, i - 1, j);
|
||||
var replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1);
|
||||
// 記錄並返回最少編輯步數
|
||||
mem[i][j] = @min(@min(insert, delete), replace) + 1;
|
||||
return mem[i][j];
|
||||
}
|
||||
|
||||
// 編輯距離:動態規劃
|
||||
fn editDistanceDP(comptime s: []const u8, comptime t: []const u8) i32 {
|
||||
comptime var n = s.len;
|
||||
comptime var m = t.len;
|
||||
var dp = [_][m + 1]i32{[_]i32{0} ** (m + 1)} ** (n + 1);
|
||||
// 狀態轉移:首行首列
|
||||
for (1..n + 1) |i| {
|
||||
dp[i][0] = @intCast(i);
|
||||
}
|
||||
for (1..m + 1) |j| {
|
||||
dp[0][j] = @intCast(j);
|
||||
}
|
||||
// 狀態轉移:其餘行和列
|
||||
for (1..n + 1) |i| {
|
||||
for (1..m + 1) |j| {
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
dp[i][j] = @min(@min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][m];
|
||||
}
|
||||
|
||||
// 編輯距離:空間最佳化後的動態規劃
|
||||
fn editDistanceDPComp(comptime s: []const u8, comptime t: []const u8) i32 {
|
||||
comptime var n = s.len;
|
||||
comptime var m = t.len;
|
||||
var dp = [_]i32{0} ** (m + 1);
|
||||
// 狀態轉移:首行
|
||||
for (1..m + 1) |j| {
|
||||
dp[j] = @intCast(j);
|
||||
}
|
||||
// 狀態轉移:其餘行
|
||||
for (1..n + 1) |i| {
|
||||
// 狀態轉移:首列
|
||||
var leftup = dp[0]; // 暫存 dp[i-1, j-1]
|
||||
dp[0] = @intCast(i);
|
||||
// 狀態轉移:其餘列
|
||||
for (1..m + 1) |j| {
|
||||
var temp = dp[j];
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// 若兩字元相等,則直接跳過此兩字元
|
||||
dp[j] = leftup;
|
||||
} else {
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
dp[j] = @min(@min(dp[j - 1], dp[j]), leftup) + 1;
|
||||
}
|
||||
leftup = temp; // 更新為下一輪的 dp[i-1, j-1]
|
||||
}
|
||||
}
|
||||
return dp[m];
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
const s = "bag";
|
||||
const t = "pack";
|
||||
comptime var n = s.len;
|
||||
comptime var m = t.len;
|
||||
|
||||
// 暴力搜尋
|
||||
var res = editDistanceDFS(s, t, n, m);
|
||||
std.debug.print("將 {s} 更改為 {s} 最少需要編輯 {} 步\n", .{ s, t, res });
|
||||
|
||||
// 記憶搜尋
|
||||
var mem = [_][m + 1]i32{[_]i32{-1} ** (m + 1)} ** (n + 1);
|
||||
res = editDistanceDFSMem(s, t, @constCast(&mem), n, m);
|
||||
std.debug.print("將 {s} 更改為 {s} 最少需要編輯 {} 步\n", .{ s, t, res });
|
||||
|
||||
// 動態規劃
|
||||
res = editDistanceDP(s, t);
|
||||
std.debug.print("將 {s} 更改為 {s} 最少需要編輯 {} 步\n", .{ s, t, res });
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = editDistanceDPComp(s, t);
|
||||
std.debug.print("將 {s} 更改為 {s} 最少需要編輯 {} 步\n", .{ s, t, res });
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// File: knapsack.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 0-1 背包:暴力搜尋
|
||||
fn knapsackDFS(wgt: []i32, val: []i32, i: usize, c: usize) i32 {
|
||||
// 若已選完所有物品或背包無剩餘容量,則返回價值 0
|
||||
if (i == 0 or c == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若超過背包容量,則只能選擇不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, val, i - 1, c);
|
||||
}
|
||||
// 計算不放入和放入物品 i 的最大價值
|
||||
var no = knapsackDFS(wgt, val, i - 1, c);
|
||||
var yes = knapsackDFS(wgt, val, i - 1, c - @as(usize, @intCast(wgt[i - 1]))) + val[i - 1];
|
||||
// 返回兩種方案中價值更大的那一個
|
||||
return @max(no, yes);
|
||||
}
|
||||
|
||||
// 0-1 背包:記憶化搜尋
|
||||
fn knapsackDFSMem(wgt: []i32, val: []i32, mem: anytype, i: usize, c: usize) i32 {
|
||||
// 若已選完所有物品或背包無剩餘容量,則返回價值 0
|
||||
if (i == 0 or c == 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若已有記錄,則直接返回
|
||||
if (mem[i][c] != -1) {
|
||||
return mem[i][c];
|
||||
}
|
||||
// 若超過背包容量,則只能選擇不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFSMem(wgt, val, mem, i - 1, c);
|
||||
}
|
||||
// 計算不放入和放入物品 i 的最大價值
|
||||
var no = knapsackDFSMem(wgt, val, mem, i - 1, c);
|
||||
var yes = knapsackDFSMem(wgt, val, mem, i - 1, c - @as(usize, @intCast(wgt[i - 1]))) + val[i - 1];
|
||||
// 記錄並返回兩種方案中價值更大的那一個
|
||||
mem[i][c] = @max(no, yes);
|
||||
return mem[i][c];
|
||||
}
|
||||
|
||||
// 0-1 背包:動態規劃
|
||||
fn knapsackDP(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 {
|
||||
comptime var n = wgt.len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_][cap + 1]i32{[_]i32{0} ** (cap + 1)} ** (n + 1);
|
||||
// 狀態轉移
|
||||
for (1..n + 1) |i| {
|
||||
for (1..cap + 1) |c| {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超過背包容量,則不選物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[i][c] = @max(dp[i - 1][c], dp[i - 1][c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap];
|
||||
}
|
||||
|
||||
// 0-1 背包:空間最佳化後的動態規劃
|
||||
fn knapsackDPComp(wgt: []i32, val: []i32, comptime cap: usize) i32 {
|
||||
var n = wgt.len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_]i32{0} ** (cap + 1);
|
||||
// 狀態轉移
|
||||
for (1..n + 1) |i| {
|
||||
// 倒序走訪
|
||||
var c = cap;
|
||||
while (c > 0) : (c -= 1) {
|
||||
if (wgt[i - 1] < c) {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[c] = @max(dp[c], dp[c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap];
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var wgt = [_]i32{ 10, 20, 30, 40, 50 };
|
||||
comptime var val = [_]i32{ 50, 120, 150, 210, 240 };
|
||||
comptime var cap = 50;
|
||||
comptime var n = wgt.len;
|
||||
|
||||
// 暴力搜尋
|
||||
var res = knapsackDFS(&wgt, &val, n, cap);
|
||||
std.debug.print("不超過背包容量的最大物品價值為 {}\n", .{res});
|
||||
|
||||
// 記憶搜尋
|
||||
var mem = [_][cap + 1]i32{[_]i32{-1} ** (cap + 1)} ** (n + 1);
|
||||
res = knapsackDFSMem(&wgt, &val, @constCast(&mem), n, cap);
|
||||
std.debug.print("不超過背包容量的最大物品價值為 {}\n", .{res});
|
||||
|
||||
// 動態規劃
|
||||
res = knapsackDP(&wgt, &val, cap);
|
||||
std.debug.print("不超過背包容量的最大物品價值為 {}\n", .{res});
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = knapsackDPComp(&wgt, &val, cap);
|
||||
std.debug.print("不超過背包容量的最大物品價值為 {}\n", .{res});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// File: min_cost_climbing_stairs_dp.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 爬樓梯最小代價:動態規劃
|
||||
fn minCostClimbingStairsDP(comptime cost: []i32) i32 {
|
||||
comptime var n = cost.len - 1;
|
||||
if (n == 1 or n == 2) {
|
||||
return cost[n];
|
||||
}
|
||||
// 初始化 dp 表,用於儲存子問題的解
|
||||
var dp = [_]i32{-1} ** (n + 1);
|
||||
// 初始狀態:預設最小子問題的解
|
||||
dp[1] = cost[1];
|
||||
dp[2] = cost[2];
|
||||
// 狀態轉移:從較小子問題逐步求解較大子問題
|
||||
for (3..n + 1) |i| {
|
||||
dp[i] = @min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
}
|
||||
return dp[n];
|
||||
}
|
||||
|
||||
// 爬樓梯最小代價:空間最佳化後的動態規劃
|
||||
fn minCostClimbingStairsDPComp(cost: []i32) i32 {
|
||||
var n = cost.len - 1;
|
||||
if (n == 1 or n == 2) {
|
||||
return cost[n];
|
||||
}
|
||||
var a = cost[1];
|
||||
var b = cost[2];
|
||||
// 狀態轉移:從較小子問題逐步求解較大子問題
|
||||
for (3..n + 1) |i| {
|
||||
var tmp = b;
|
||||
b = @min(a, tmp) + cost[i];
|
||||
a = tmp;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var cost = [_]i32{ 0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1 };
|
||||
std.debug.print("輸入樓梯的代價串列為 {any}\n", .{cost});
|
||||
|
||||
var res = minCostClimbingStairsDP(&cost);
|
||||
std.debug.print("輸入樓梯的代價串列為 {}\n", .{res});
|
||||
|
||||
res = minCostClimbingStairsDPComp(&cost);
|
||||
std.debug.print("輸入樓梯的代價串列為 {}\n", .{res});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// File: min_path_sum.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 最小路徑和:暴力搜尋
|
||||
fn minPathSumDFS(grid: anytype, i: i32, j: i32) i32 {
|
||||
// 若為左上角單元格,則終止搜尋
|
||||
if (i == 0 and j == 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,則返回 +∞ 代價
|
||||
if (i < 0 or j < 0) {
|
||||
return std.math.maxInt(i32);
|
||||
}
|
||||
// 計算從左上角到 (i-1, j) 和 (i, j-1) 的最小路徑代價
|
||||
var up = minPathSumDFS(grid, i - 1, j);
|
||||
var left = minPathSumDFS(grid, i, j - 1);
|
||||
// 返回從左上角到 (i, j) 的最小路徑代價
|
||||
return @min(left, up) + grid[@as(usize, @intCast(i))][@as(usize, @intCast(j))];
|
||||
}
|
||||
|
||||
// 最小路徑和:記憶化搜尋
|
||||
fn minPathSumDFSMem(grid: anytype, mem: anytype, i: i32, j: i32) i32 {
|
||||
// 若為左上角單元格,則終止搜尋
|
||||
if (i == 0 and j == 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,則返回 +∞ 代價
|
||||
if (i < 0 or j < 0) {
|
||||
return std.math.maxInt(i32);
|
||||
}
|
||||
// 若已有記錄,則直接返回
|
||||
if (mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))] != -1) {
|
||||
return mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))];
|
||||
}
|
||||
// 計算從左上角到 (i-1, j) 和 (i, j-1) 的最小路徑代價
|
||||
var up = minPathSumDFSMem(grid, mem, i - 1, j);
|
||||
var left = minPathSumDFSMem(grid, mem, i, j - 1);
|
||||
// 返回從左上角到 (i, j) 的最小路徑代價
|
||||
// 記錄並返回左上角到 (i, j) 的最小路徑代價
|
||||
mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))] = @min(left, up) + grid[@as(usize, @intCast(i))][@as(usize, @intCast(j))];
|
||||
return mem[@as(usize, @intCast(i))][@as(usize, @intCast(j))];
|
||||
}
|
||||
|
||||
// 最小路徑和:動態規劃
|
||||
fn minPathSumDP(comptime grid: anytype) i32 {
|
||||
comptime var n = grid.len;
|
||||
comptime var m = grid[0].len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_][m]i32{[_]i32{0} ** m} ** n;
|
||||
dp[0][0] = grid[0][0];
|
||||
// 狀態轉移:首行
|
||||
for (1..m) |j| {
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j];
|
||||
}
|
||||
// 狀態轉移:首列
|
||||
for (1..n) |i| {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0];
|
||||
}
|
||||
// 狀態轉移:其餘行和列
|
||||
for (1..n) |i| {
|
||||
for (1..m) |j| {
|
||||
dp[i][j] = @min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
return dp[n - 1][m - 1];
|
||||
}
|
||||
|
||||
// 最小路徑和:空間最佳化後的動態規劃
|
||||
fn minPathSumDPComp(comptime grid: anytype) i32 {
|
||||
comptime var n = grid.len;
|
||||
comptime var m = grid[0].len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_]i32{0} ** m;
|
||||
// 狀態轉移:首行
|
||||
dp[0] = grid[0][0];
|
||||
for (1..m) |j| {
|
||||
dp[j] = dp[j - 1] + grid[0][j];
|
||||
}
|
||||
// 狀態轉移:其餘行
|
||||
for (1..n) |i| {
|
||||
// 狀態轉移:首列
|
||||
dp[0] = dp[0] + grid[i][0];
|
||||
for (1..m) |j| {
|
||||
dp[j] = @min(dp[j - 1], dp[j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
return dp[m - 1];
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var grid = [_][4]i32{
|
||||
[_]i32{ 1, 3, 1, 5 },
|
||||
[_]i32{ 2, 2, 4, 2 },
|
||||
[_]i32{ 5, 3, 2, 1 },
|
||||
[_]i32{ 4, 3, 5, 2 },
|
||||
};
|
||||
comptime var n = grid.len;
|
||||
comptime var m = grid[0].len;
|
||||
|
||||
// 暴力搜尋
|
||||
var res = minPathSumDFS(&grid, n - 1, m - 1);
|
||||
std.debug.print("從左上角到右下角的最小路徑和為 {}\n", .{res});
|
||||
|
||||
// 記憶化搜尋
|
||||
var mem = [_][m]i32{[_]i32{-1} ** m} ** n;
|
||||
res = minPathSumDFSMem(&grid, &mem, n - 1, m - 1);
|
||||
std.debug.print("從左上角到右下角的最小路徑和為 {}\n", .{res});
|
||||
|
||||
// 動態規劃
|
||||
res = minPathSumDP(&grid);
|
||||
std.debug.print("從左上角到右下角的最小路徑和為 {}\n", .{res});
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = minPathSumDPComp(&grid);
|
||||
std.debug.print("從左上角到右下角的最小路徑和為 {}\n", .{res});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// File: unbounded_knapsack.zig
|
||||
// Created Time: 2023-07-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 完全背包:動態規劃
|
||||
fn unboundedKnapsackDP(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 {
|
||||
comptime var n = wgt.len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_][cap + 1]i32{[_]i32{0} ** (cap + 1)} ** (n + 1);
|
||||
// 狀態轉移
|
||||
for (1..n + 1) |i| {
|
||||
for (1..cap + 1) |c| {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超過背包容量,則不選物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[i][c] = @max(dp[i - 1][c], dp[i][c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap];
|
||||
}
|
||||
|
||||
// 完全背包:空間最佳化後的動態規劃
|
||||
fn unboundedKnapsackDPComp(comptime wgt: []i32, val: []i32, comptime cap: usize) i32 {
|
||||
comptime var n = wgt.len;
|
||||
// 初始化 dp 表
|
||||
var dp = [_]i32{0} ** (cap + 1);
|
||||
// 狀態轉移
|
||||
for (1..n + 1) |i| {
|
||||
for (1..cap + 1) |c| {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超過背包容量,則不選物品 i
|
||||
dp[c] = dp[c];
|
||||
} else {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[c] = @max(dp[c], dp[c - @as(usize, @intCast(wgt[i - 1]))] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap];
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
comptime var wgt = [_]i32{ 1, 2, 3 };
|
||||
comptime var val = [_]i32{ 5, 11, 15 };
|
||||
comptime var cap = 4;
|
||||
|
||||
// 動態規劃
|
||||
var res = unboundedKnapsackDP(&wgt, &val, cap);
|
||||
std.debug.print("不超過背包容量的最大物品價值為 {}\n", .{res});
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = unboundedKnapsackDPComp(&wgt, &val, cap);
|
||||
std.debug.print("不超過背包容量的最大物品價值為 {}\n", .{res});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// File: array_hash_map.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 鍵值對
|
||||
const Pair = struct {
|
||||
key: usize = undefined,
|
||||
val: []const u8 = undefined,
|
||||
|
||||
pub fn init(key: usize, val: []const u8) Pair {
|
||||
return Pair {
|
||||
.key = key,
|
||||
.val = val,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 基於陣列實現的雜湊表
|
||||
pub fn ArrayHashMap(comptime T: type) type {
|
||||
return struct {
|
||||
bucket: ?std.ArrayList(?T) = null,
|
||||
mem_allocator: std.mem.Allocator = undefined,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
// 建構子
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator) !void {
|
||||
self.mem_allocator = allocator;
|
||||
// 初始化一個長度為 100 的桶(陣列)
|
||||
self.bucket = std.ArrayList(?T).init(self.mem_allocator);
|
||||
var i: i32 = 0;
|
||||
while (i < 100) : (i += 1) {
|
||||
try self.bucket.?.append(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 析構函式
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.bucket != null) self.bucket.?.deinit();
|
||||
}
|
||||
|
||||
// 雜湊函式
|
||||
fn hashFunc(key: usize) usize {
|
||||
var index = key % 100;
|
||||
return index;
|
||||
}
|
||||
|
||||
// 查詢操作
|
||||
pub fn get(self: *Self, key: usize) []const u8 {
|
||||
var index = hashFunc(key);
|
||||
var pair = self.bucket.?.items[index];
|
||||
return pair.?.val;
|
||||
}
|
||||
|
||||
// 新增操作
|
||||
pub fn put(self: *Self, key: usize, val: []const u8) !void {
|
||||
var pair = Pair.init(key, val);
|
||||
var index = hashFunc(key);
|
||||
self.bucket.?.items[index] = pair;
|
||||
}
|
||||
|
||||
// 刪除操作
|
||||
pub fn remove(self: *Self, key: usize) !void {
|
||||
var index = hashFunc(key);
|
||||
// 置為 null ,代表刪除
|
||||
self.bucket.?.items[index] = null;
|
||||
}
|
||||
|
||||
// 獲取所有鍵值對
|
||||
pub fn pairSet(self: *Self) !std.ArrayList(T) {
|
||||
var entry_set = std.ArrayList(T).init(self.mem_allocator);
|
||||
for (self.bucket.?.items) |item| {
|
||||
if (item == null) continue;
|
||||
try entry_set.append(item.?);
|
||||
}
|
||||
return entry_set;
|
||||
}
|
||||
|
||||
// 獲取所有鍵
|
||||
pub fn keySet(self: *Self) !std.ArrayList(usize) {
|
||||
var key_set = std.ArrayList(usize).init(self.mem_allocator);
|
||||
for (self.bucket.?.items) |item| {
|
||||
if (item == null) continue;
|
||||
try key_set.append(item.?.key);
|
||||
}
|
||||
return key_set;
|
||||
}
|
||||
|
||||
// 獲取所有值
|
||||
pub fn valueSet(self: *Self) !std.ArrayList([]const u8) {
|
||||
var value_set = std.ArrayList([]const u8).init(self.mem_allocator);
|
||||
for (self.bucket.?.items) |item| {
|
||||
if (item == null) continue;
|
||||
try value_set.append(item.?.val);
|
||||
}
|
||||
return value_set;
|
||||
}
|
||||
|
||||
// 列印雜湊表
|
||||
pub fn print(self: *Self) !void {
|
||||
var entry_set = try self.pairSet();
|
||||
defer entry_set.deinit();
|
||||
for (entry_set.items) |item| {
|
||||
std.debug.print("{} -> {s}\n", .{item.key, item.val});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化雜湊表
|
||||
var map = ArrayHashMap(Pair){};
|
||||
try map.init(std.heap.page_allocator);
|
||||
defer map.deinit();
|
||||
|
||||
// 新增操作
|
||||
// 在雜湊表中新增鍵值對 (key, value)
|
||||
try map.put(12836, "小哈");
|
||||
try map.put(15937, "小囉");
|
||||
try map.put(16750, "小算");
|
||||
try map.put(13276, "小法");
|
||||
try map.put(10583, "小鴨");
|
||||
std.debug.print("\n新增完成後,雜湊表為\nKey -> Value\n", .{});
|
||||
try map.print();
|
||||
|
||||
// 查詢操作
|
||||
// 向雜湊表中輸入鍵 key ,得到值 value
|
||||
var name = map.get(15937);
|
||||
std.debug.print("\n輸入學號 15937 ,查詢到姓名 {s}\n", .{name});
|
||||
|
||||
// 刪除操作
|
||||
// 在雜湊表中刪除鍵值對 (key, value)
|
||||
try map.remove(10583);
|
||||
std.debug.print("\n刪除 10583 後,雜湊表為\nKey -> Value\n", .{});
|
||||
try map.print();
|
||||
|
||||
// 走訪雜湊表
|
||||
std.debug.print("\n走訪鍵值對 Key->Value\n", .{});
|
||||
var entry_set = try map.pairSet();
|
||||
for (entry_set.items) |kv| {
|
||||
std.debug.print("{} -> {s}\n", .{kv.key, kv.val});
|
||||
}
|
||||
defer entry_set.deinit();
|
||||
std.debug.print("\n單獨走訪鍵 Key\n", .{});
|
||||
var key_set = try map.keySet();
|
||||
for (key_set.items) |key| {
|
||||
std.debug.print("{}\n", .{key});
|
||||
}
|
||||
defer key_set.deinit();
|
||||
std.debug.print("\n單獨走訪值 value\n", .{});
|
||||
var value_set = try map.valueSet();
|
||||
for (value_set.items) |val| {
|
||||
std.debug.print("{s}\n", .{val});
|
||||
}
|
||||
defer value_set.deinit();
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// File: hash_map.zig
|
||||
// Created Time: 2023-01-13
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化雜湊表
|
||||
var map = std.AutoHashMap(i32, []const u8).init(std.heap.page_allocator);
|
||||
// 延遲釋放記憶體
|
||||
defer map.deinit();
|
||||
|
||||
// 新增操作
|
||||
// 在雜湊表中新增鍵值對 (key, value)
|
||||
try map.put(12836, "小哈");
|
||||
try map.put(15937, "小囉");
|
||||
try map.put(16750, "小算");
|
||||
try map.put(13276, "小法");
|
||||
try map.put(10583, "小鴨");
|
||||
std.debug.print("\n新增完成後,雜湊表為\nKey -> Value\n", .{});
|
||||
inc.PrintUtil.printHashMap(i32, []const u8, map);
|
||||
|
||||
// 查詢操作
|
||||
// 向雜湊表中輸入鍵 key ,得到值 value
|
||||
var name = map.get(15937).?;
|
||||
std.debug.print("\n輸入學號 15937 ,查詢到姓名 {s}\n", .{name});
|
||||
|
||||
// 刪除操作
|
||||
// 在雜湊表中刪除鍵值對 (key, value)
|
||||
_ = map.remove(10583);
|
||||
std.debug.print("\n刪除 10583 後,雜湊表為\nKey -> Value\n", .{});
|
||||
inc.PrintUtil.printHashMap(i32, []const u8, map);
|
||||
|
||||
// 走訪雜湊表
|
||||
std.debug.print("\n走訪鍵值對 Key->Value\n", .{});
|
||||
inc.PrintUtil.printHashMap(i32, []const u8, map);
|
||||
|
||||
std.debug.print("\n單獨走訪鍵 Key\n", .{});
|
||||
var it = map.iterator();
|
||||
while (it.next()) |kv| {
|
||||
std.debug.print("{}\n", .{kv.key_ptr.*});
|
||||
}
|
||||
|
||||
std.debug.print("\n單獨走訪值 value\n", .{});
|
||||
it = map.iterator();
|
||||
while (it.next()) |kv| {
|
||||
std.debug.print("{s}\n", .{kv.value_ptr.*});
|
||||
}
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// File: heap.zig
|
||||
// Created Time: 2023-01-14
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
fn lessThan(context: void, a: i32, b: i32) std.math.Order {
|
||||
_ = context;
|
||||
return std.math.order(a, b);
|
||||
}
|
||||
|
||||
fn greaterThan(context: void, a: i32, b: i32) std.math.Order {
|
||||
return lessThan(context, a, b).invert();
|
||||
}
|
||||
|
||||
fn testPush(comptime T: type, mem_allocator: std.mem.Allocator, heap: anytype, val: T) !void {
|
||||
try heap.add(val); //元素入堆積
|
||||
std.debug.print("\n元素 {} 入堆積後\n", .{val});
|
||||
try inc.PrintUtil.printHeap(T, mem_allocator, heap);
|
||||
}
|
||||
|
||||
fn testPop(comptime T: type, mem_allocator: std.mem.Allocator, heap: anytype) !void {
|
||||
var val = heap.remove(); //堆積頂元素出堆積
|
||||
std.debug.print("\n堆積頂元素 {} 出堆積後\n", .{val});
|
||||
try inc.PrintUtil.printHeap(T, mem_allocator, heap);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化記憶體分配器
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
|
||||
// 初始化堆積
|
||||
// 初始化小頂堆積
|
||||
const PQlt = std.PriorityQueue(i32, void, lessThan);
|
||||
var min_heap = PQlt.init(std.heap.page_allocator, {});
|
||||
defer min_heap.deinit();
|
||||
// 初始化大頂堆積
|
||||
const PQgt = std.PriorityQueue(i32, void, greaterThan);
|
||||
var max_heap = PQgt.init(std.heap.page_allocator, {});
|
||||
defer max_heap.deinit();
|
||||
|
||||
std.debug.print("\n以下測試樣例為大頂堆積", .{});
|
||||
|
||||
// 元素入堆積
|
||||
try testPush(i32, mem_allocator, &max_heap, 1);
|
||||
try testPush(i32, mem_allocator, &max_heap, 3);
|
||||
try testPush(i32, mem_allocator, &max_heap, 2);
|
||||
try testPush(i32, mem_allocator, &max_heap, 5);
|
||||
try testPush(i32, mem_allocator, &max_heap, 4);
|
||||
|
||||
// 獲取堆積頂元素
|
||||
var peek = max_heap.peek().?;
|
||||
std.debug.print("\n堆積頂元素為 {}\n", .{peek});
|
||||
|
||||
// 堆積頂元素出堆積
|
||||
try testPop(i32, mem_allocator, &max_heap);
|
||||
try testPop(i32, mem_allocator, &max_heap);
|
||||
try testPop(i32, mem_allocator, &max_heap);
|
||||
try testPop(i32, mem_allocator, &max_heap);
|
||||
try testPop(i32, mem_allocator, &max_heap);
|
||||
|
||||
// 獲取堆積的大小
|
||||
var size = max_heap.len;
|
||||
std.debug.print("\n堆積元素數量為 {}\n", .{size});
|
||||
|
||||
// 判斷堆積是否為空
|
||||
var is_empty = if (max_heap.len == 0) true else false;
|
||||
std.debug.print("\n堆積是否為空 {}\n", .{is_empty});
|
||||
|
||||
// 輸入串列並建堆積
|
||||
try min_heap.addSlice(&[_]i32{ 1, 3, 2, 5, 4 });
|
||||
std.debug.print("\n輸入串列並建立小頂堆積後\n", .{});
|
||||
try inc.PrintUtil.printHeap(i32, mem_allocator, min_heap);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// File: my_heap.zig
|
||||
// Created Time: 2023-01-14
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 堆積類別簡易實現
|
||||
pub fn MaxHeap(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
max_heap: ?std.ArrayList(T) = null, // 使用串列而非陣列,這樣無須考慮擴容問題
|
||||
|
||||
// 建構子,根據輸入串列建堆積
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator, nums: []const T) !void {
|
||||
if (self.max_heap != null) return;
|
||||
self.max_heap = std.ArrayList(T).init(allocator);
|
||||
// 將串列元素原封不動新增進堆積
|
||||
try self.max_heap.?.appendSlice(nums);
|
||||
// 堆積化除葉節點以外的其他所有節點
|
||||
var i: usize = parent(self.size() - 1) + 1;
|
||||
while (i > 0) : (i -= 1) {
|
||||
try self.siftDown(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 析構方法,釋放記憶體
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.max_heap != null) self.max_heap.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取左子節點的索引
|
||||
fn left(i: usize) usize {
|
||||
return 2 * i + 1;
|
||||
}
|
||||
|
||||
// 獲取右子節點的索引
|
||||
fn right(i: usize) usize {
|
||||
return 2 * i + 2;
|
||||
}
|
||||
|
||||
// 獲取父節點的索引
|
||||
fn parent(i: usize) usize {
|
||||
// return (i - 1) / 2; // 向下整除
|
||||
return @divFloor(i - 1, 2);
|
||||
}
|
||||
|
||||
// 交換元素
|
||||
fn swap(self: *Self, i: usize, j: usize) !void {
|
||||
var tmp = self.max_heap.?.items[i];
|
||||
try self.max_heap.?.replaceRange(i, 1, &[_]T{self.max_heap.?.items[j]});
|
||||
try self.max_heap.?.replaceRange(j, 1, &[_]T{tmp});
|
||||
}
|
||||
|
||||
// 獲取堆積大小
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.max_heap.?.items.len;
|
||||
}
|
||||
|
||||
// 判斷堆積是否為空
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.size() == 0;
|
||||
}
|
||||
|
||||
// 訪問堆積頂元素
|
||||
pub fn peek(self: *Self) T {
|
||||
return self.max_heap.?.items[0];
|
||||
}
|
||||
|
||||
// 元素入堆積
|
||||
pub fn push(self: *Self, val: T) !void {
|
||||
// 新增節點
|
||||
try self.max_heap.?.append(val);
|
||||
// 從底至頂堆積化
|
||||
try self.siftUp(self.size() - 1);
|
||||
}
|
||||
|
||||
// 從節點 i 開始,從底至頂堆積化
|
||||
fn siftUp(self: *Self, i_: usize) !void {
|
||||
var i = i_;
|
||||
while (true) {
|
||||
// 獲取節點 i 的父節點
|
||||
var p = parent(i);
|
||||
// 當“越過根節點”或“節點無須修復”時,結束堆積化
|
||||
if (p < 0 or self.max_heap.?.items[i] <= self.max_heap.?.items[p]) break;
|
||||
// 交換兩節點
|
||||
try self.swap(i, p);
|
||||
// 迴圈向上堆積化
|
||||
i = p;
|
||||
}
|
||||
}
|
||||
|
||||
// 元素出堆積
|
||||
pub fn pop(self: *Self) !T {
|
||||
// 判斷處理
|
||||
if (self.isEmpty()) unreachable;
|
||||
// 交換根節點與最右葉節點(交換首元素與尾元素)
|
||||
try self.swap(0, self.size() - 1);
|
||||
// 刪除節點
|
||||
var val = self.max_heap.?.pop();
|
||||
// 從頂至底堆積化
|
||||
try self.siftDown(0);
|
||||
// 返回堆積頂元素
|
||||
return val;
|
||||
}
|
||||
|
||||
// 從節點 i 開始,從頂至底堆積化
|
||||
fn siftDown(self: *Self, i_: usize) !void {
|
||||
var i = i_;
|
||||
while (true) {
|
||||
// 判斷節點 i, l, r 中值最大的節點,記為 ma
|
||||
var l = left(i);
|
||||
var r = right(i);
|
||||
var ma = i;
|
||||
if (l < self.size() and self.max_heap.?.items[l] > self.max_heap.?.items[ma]) ma = l;
|
||||
if (r < self.size() and self.max_heap.?.items[r] > self.max_heap.?.items[ma]) ma = r;
|
||||
// 若節點 i 最大或索引 l, r 越界,則無須繼續堆積化,跳出
|
||||
if (ma == i) break;
|
||||
// 交換兩節點
|
||||
try self.swap(i, ma);
|
||||
// 迴圈向下堆積化
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
|
||||
fn lessThan(context: void, a: T, b: T) std.math.Order {
|
||||
_ = context;
|
||||
return std.math.order(a, b);
|
||||
}
|
||||
|
||||
fn greaterThan(context: void, a: T, b: T) std.math.Order {
|
||||
return lessThan(context, a, b).invert();
|
||||
}
|
||||
|
||||
// 列印堆積(二元樹)
|
||||
pub fn print(self: *Self, mem_allocator: std.mem.Allocator) !void {
|
||||
const PQgt = std.PriorityQueue(T, void, greaterThan);
|
||||
var queue = PQgt.init(std.heap.page_allocator, {});
|
||||
defer queue.deinit();
|
||||
try queue.addSlice(self.max_heap.?.items);
|
||||
try inc.PrintUtil.printHeap(T, mem_allocator, queue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化記憶體分配器
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
|
||||
// 初始化大頂堆積
|
||||
var max_heap = MaxHeap(i32){};
|
||||
try max_heap.init(std.heap.page_allocator, &[_]i32{ 9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2 });
|
||||
defer max_heap.deinit();
|
||||
std.debug.print("\n輸入串列並建堆積後\n", .{});
|
||||
try max_heap.print(mem_allocator);
|
||||
|
||||
// 獲取堆積頂元素
|
||||
var peek = max_heap.peek();
|
||||
std.debug.print("\n堆積頂元素為 {}\n", .{peek});
|
||||
|
||||
// 元素入堆積
|
||||
const val = 7;
|
||||
try max_heap.push(val);
|
||||
std.debug.print("\n元素 {} 入堆積後\n", .{val});
|
||||
try max_heap.print(mem_allocator);
|
||||
|
||||
// 堆積頂元素出堆積
|
||||
peek = try max_heap.pop();
|
||||
std.debug.print("\n堆積頂元素 {} 出堆積後\n", .{peek});
|
||||
try max_heap.print(mem_allocator);
|
||||
|
||||
// 獲取堆積的大小
|
||||
var size = max_heap.size();
|
||||
std.debug.print("\n堆積元素數量為 {}", .{size});
|
||||
|
||||
// 判斷堆積是否為空
|
||||
var is_empty = max_heap.isEmpty();
|
||||
std.debug.print("\n堆積是否為空 {}\n", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// File: binary_search.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 二分搜尋(雙閉區間)
|
||||
fn binarySearch(comptime T: type, nums: std.ArrayList(T), target: T) T {
|
||||
// 初始化雙閉區間 [0, n-1] ,即 i, j 分別指向陣列首元素、尾元素
|
||||
var i: usize = 0;
|
||||
var j: usize = nums.items.len - 1;
|
||||
// 迴圈,當搜尋區間為空時跳出(當 i > j 時為空)
|
||||
while (i <= j) {
|
||||
var m = i + (j - i) / 2; // 計算中點索引 m
|
||||
if (nums.items[m] < target) { // 此情況說明 target 在區間 [m+1, j] 中
|
||||
i = m + 1;
|
||||
} else if (nums.items[m] > target) { // 此情況說明 target 在區間 [i, m-1] 中
|
||||
j = m - 1;
|
||||
} else { // 找到目標元素,返回其索引
|
||||
return @intCast(m);
|
||||
}
|
||||
}
|
||||
// 未找到目標元素,返回 -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 二分搜尋(左閉右開區間)
|
||||
fn binarySearchLCRO(comptime T: type, nums: std.ArrayList(T), target: T) T {
|
||||
// 初始化左閉右開區間 [0, n) ,即 i, j 分別指向陣列首元素、尾元素+1
|
||||
var i: usize = 0;
|
||||
var j: usize = nums.items.len;
|
||||
// 迴圈,當搜尋區間為空時跳出(當 i = j 時為空)
|
||||
while (i <= j) {
|
||||
var m = i + (j - i) / 2; // 計算中點索引 m
|
||||
if (nums.items[m] < target) { // 此情況說明 target 在區間 [m+1, j) 中
|
||||
i = m + 1;
|
||||
} else if (nums.items[m] > target) { // 此情況說明 target 在區間 [i, m) 中
|
||||
j = m;
|
||||
} else { // 找到目標元素,返回其索引
|
||||
return @intCast(m);
|
||||
}
|
||||
}
|
||||
// 未找到目標元素,返回 -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
var target: i32 = 6;
|
||||
var nums = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer nums.deinit();
|
||||
try nums.appendSlice(&[_]i32{ 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 });
|
||||
|
||||
// 二分搜尋(雙閉區間)
|
||||
var index = binarySearch(i32, nums, target);
|
||||
std.debug.print("目標元素 6 的索引 = {}\n", .{index});
|
||||
|
||||
// 二分搜尋(左閉右開區間)
|
||||
index = binarySearchLCRO(i32, nums, target);
|
||||
std.debug.print("目標元素 6 的索引 = {}\n", .{index});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// File: hashing_search.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 雜湊查詢(陣列)
|
||||
fn hashingSearchArray(comptime T: type, map: std.AutoHashMap(T, T), target: T) T {
|
||||
// 雜湊表的 key: 目標元素,value: 索引
|
||||
// 若雜湊表中無此 key ,返回 -1
|
||||
if (map.getKey(target) == null) return -1;
|
||||
return map.get(target).?;
|
||||
}
|
||||
|
||||
// 雜湊查詢(鏈結串列)
|
||||
fn hashingSearchLinkedList(comptime T: type, map: std.AutoHashMap(T, *inc.ListNode(T)), target: T) ?*inc.ListNode(T) {
|
||||
// 雜湊表的 key: 目標節點值,value: 節點物件
|
||||
// 若雜湊表中無此 key ,返回 null
|
||||
if (map.getKey(target) == null) return null;
|
||||
return map.get(target);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
var target: i32 = 3;
|
||||
|
||||
// 雜湊查詢(陣列)
|
||||
var nums = [_]i32{ 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 };
|
||||
// 初始化雜湊表
|
||||
var map = std.AutoHashMap(i32, i32).init(std.heap.page_allocator);
|
||||
defer map.deinit();
|
||||
for (nums, 0..) |num, i| {
|
||||
try map.put(num, @as(i32, @intCast(i))); // key: 元素,value: 索引
|
||||
}
|
||||
var index = hashingSearchArray(i32, map, target);
|
||||
std.debug.print("目標元素 3 的索引 = {}\n", .{index});
|
||||
|
||||
// 雜湊查詢(鏈結串列)
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
var head = try inc.ListUtil.arrToLinkedList(i32, mem_allocator, &nums);
|
||||
// 初始化雜湊表
|
||||
var map1 = std.AutoHashMap(i32, *inc.ListNode(i32)).init(std.heap.page_allocator);
|
||||
defer map1.deinit();
|
||||
while (head != null) {
|
||||
try map1.put(head.?.val, head.?);
|
||||
head = head.?.next;
|
||||
}
|
||||
var node = hashingSearchLinkedList(i32, map1, target);
|
||||
std.debug.print("目標節點值 3 的對應節點物件為 ", .{});
|
||||
try inc.PrintUtil.printLinkedList(i32, node);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// File: linear_search.zig
|
||||
// Created Time: 2023-01-13
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 線性查詢(陣列)
|
||||
fn linearSearchArray(comptime T: type, nums: std.ArrayList(T), target: T) T {
|
||||
// 走訪陣列
|
||||
for (nums.items, 0..) |num, i| {
|
||||
// 找到目標元素, 返回其索引
|
||||
if (num == target) {
|
||||
return @intCast(i);
|
||||
}
|
||||
}
|
||||
// 未找到目標元素,返回 -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 線性查詢(鏈結串列)
|
||||
pub fn linearSearchLinkedList(comptime T: type, node: ?*inc.ListNode(T), target: T) ?*inc.ListNode(T) {
|
||||
var head = node;
|
||||
// 走訪鏈結串列
|
||||
while (head != null) {
|
||||
// 找到目標節點,返回之
|
||||
if (head.?.val == target) return head;
|
||||
head = head.?.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
var target: i32 = 3;
|
||||
|
||||
// 在陣列中執行線性查詢
|
||||
var nums = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
defer nums.deinit();
|
||||
try nums.appendSlice(&[_]i32{ 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 });
|
||||
var index = linearSearchArray(i32, nums, target);
|
||||
std.debug.print("目標元素 3 的索引 = {}\n", .{index});
|
||||
|
||||
// 在鏈結串列中執行線性查詢
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
var head = try inc.ListUtil.listToLinkedList(i32, mem_allocator, nums);
|
||||
var node = linearSearchLinkedList(i32, head, target);
|
||||
std.debug.print("目標節點值 3 的對應節點物件為 ", .{});
|
||||
try inc.PrintUtil.printLinkedList(i32, node);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// File: two_sum.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 方法一:暴力列舉
|
||||
pub fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 {
|
||||
var size: usize = nums.len;
|
||||
var i: usize = 0;
|
||||
// 兩層迴圈,時間複雜度為 O(n^2)
|
||||
while (i < size - 1) : (i += 1) {
|
||||
var j = i + 1;
|
||||
while (j < size) : (j += 1) {
|
||||
if (nums[i] + nums[j] == target) {
|
||||
return [_]i32{@intCast(i), @intCast(j)};
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 方法二:輔助雜湊表
|
||||
pub fn twoSumHashTable(nums: []i32, target: i32) !?[2]i32 {
|
||||
var size: usize = nums.len;
|
||||
// 輔助雜湊表,空間複雜度為 O(n)
|
||||
var dic = std.AutoHashMap(i32, i32).init(std.heap.page_allocator);
|
||||
defer dic.deinit();
|
||||
var i: usize = 0;
|
||||
// 單層迴圈,時間複雜度為 O(n)
|
||||
while (i < size) : (i += 1) {
|
||||
if (dic.contains(target - nums[i])) {
|
||||
return [_]i32{dic.get(target - nums[i]).?, @intCast(i)};
|
||||
}
|
||||
try dic.put(nums[i], @intCast(i));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
pub fn main() !void {
|
||||
// ======= Test Case =======
|
||||
var nums = [_]i32{ 2, 7, 11, 15 };
|
||||
var target: i32 = 9;
|
||||
|
||||
// ====== Driver Code ======
|
||||
// 方法一
|
||||
var res = twoSumBruteForce(&nums, target).?;
|
||||
std.debug.print("方法一 res = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &res);
|
||||
// 方法二
|
||||
res = (try twoSumHashTable(&nums, target)).?;
|
||||
std.debug.print("\n方法二 res = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &res);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// File: bubble_sort.zig
|
||||
// Created Time: 2023-01-08
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 泡沫排序
|
||||
fn bubbleSort(nums: []i32) void {
|
||||
// 外迴圈:未排序區間為 [0, i]
|
||||
var i: usize = nums.len - 1;
|
||||
while (i > 0) : (i -= 1) {
|
||||
var j: usize = 0;
|
||||
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交換 nums[j] 與 nums[j + 1]
|
||||
var tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 泡沫排序(標誌最佳化)
|
||||
fn bubbleSortWithFlag(nums: []i32) void {
|
||||
// 外迴圈:未排序區間為 [0, i]
|
||||
var i: usize = nums.len - 1;
|
||||
while (i > 0) : (i -= 1) {
|
||||
var flag = false; // 初始化標誌位
|
||||
var j: usize = 0;
|
||||
// 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
|
||||
while (j < i) : (j += 1) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交換 nums[j] 與 nums[j + 1]
|
||||
var tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag) break; // 此輪“冒泡”未交換任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
var nums = [_]i32{ 4, 1, 3, 1, 5, 2 };
|
||||
bubbleSort(&nums);
|
||||
std.debug.print("泡沫排序完成後 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &nums);
|
||||
|
||||
var nums1 = [_]i32{ 4, 1, 3, 1, 5, 2 };
|
||||
bubbleSortWithFlag(&nums1);
|
||||
std.debug.print("\n泡沫排序完成後 nums1 = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &nums1);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// File: insertion_sort.zig
|
||||
// Created Time: 2023-01-08
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 插入排序
|
||||
fn insertionSort(nums: []i32) void {
|
||||
// 外迴圈:已排序區間為 [0, i-1]
|
||||
var i: usize = 1;
|
||||
while (i < nums.len) : (i += 1) {
|
||||
var base = nums[i];
|
||||
var j: usize = i;
|
||||
// 內迴圈:將 base 插入到已排序區間 [0, i-1] 中的正確位置
|
||||
while (j >= 1 and nums[j - 1] > base) : (j -= 1) {
|
||||
nums[j] = nums[j - 1]; // 將 nums[j] 向右移動一位
|
||||
}
|
||||
nums[j] = base; // 將 base 賦值到正確位置
|
||||
}
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
var nums = [_]i32{ 4, 1, 3, 1, 5, 2 };
|
||||
insertionSort(&nums);
|
||||
std.debug.print("插入排序完成後 nums = ", .{});
|
||||
inc.PrintUtil.printArray(i32, &nums);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// File: merge_sort.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@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();
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// File: quick_sort.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@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 {
|
||||
var l = nums[left];
|
||||
var m = nums[mid];
|
||||
var r = nums[right];
|
||||
if ((l <= m && m <= r) || (r <= m && m <= l))
|
||||
return mid; // m 在 l 和 r 之間
|
||||
if ((m <= l && l <= r) || (r <= l && l <= m))
|
||||
return left; // l 在 m 和 r 之間
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// File: radix_sort.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@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 countingSortDigit(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 counter = try mem_allocator.alloc(usize, 10);
|
||||
@memset(counter, 0);
|
||||
var n = nums.len;
|
||||
// 統計 0~9 各數字的出現次數
|
||||
for (nums) |num| {
|
||||
var d: u32 = @bitCast(digit(num, exp)); // 獲取 nums[i] 第 k 位,記為 d
|
||||
counter[d] += 1; // 統計數字 d 的出現次數
|
||||
}
|
||||
// 求前綴和,將“出現個數”轉換為“陣列索引”
|
||||
var i: usize = 1;
|
||||
while (i < 10) : (i += 1) {
|
||||
counter[i] += counter[i - 1];
|
||||
}
|
||||
// 倒序走訪,根據桶內統計結果,將各元素填入 res
|
||||
var res = try mem_allocator.alloc(i32, n);
|
||||
i = n - 1;
|
||||
while (i >= 0) : (i -= 1) {
|
||||
var d: u32 = @bitCast(digit(nums[i], exp));
|
||||
var j = counter[d] - 1; // 獲取 d 在陣列中的索引 j
|
||||
res[j] = nums[i]; // 將當前元素填入索引 j
|
||||
counter[d] -= 1; // 將 d 的數量減 1
|
||||
if (i == 0) break;
|
||||
}
|
||||
// 使用結果覆蓋原陣列 nums
|
||||
i = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 基數排序
|
||||
fn radixSort(nums: []i32) !void {
|
||||
// 獲取陣列的最大元素,用於判斷最大位數
|
||||
var m: i32 = std.math.minInt(i32);
|
||||
for (nums) |num| {
|
||||
if (num > m) m = num;
|
||||
}
|
||||
// 按照從低位到高位的順序走訪
|
||||
var exp: i32 = 1;
|
||||
while (exp <= m) : (exp *= 10) {
|
||||
// 對陣列元素的第 k 位執行計數排序
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// 即 exp = 10^(k-1)
|
||||
try countingSortDigit(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();
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// File: array_queue.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 基於環形陣列實現的佇列
|
||||
pub fn ArrayQueue(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
nums: []T = undefined, // 用於儲存佇列元素的陣列
|
||||
cap: usize = 0, // 佇列容量
|
||||
front: usize = 0, // 佇列首指標,指向佇列首元素
|
||||
queSize: usize = 0, // 尾指標,指向佇列尾 + 1
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 記憶體分配器
|
||||
|
||||
// 建構子(分配記憶體+初始化陣列)
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator, cap: usize) !void {
|
||||
if (self.mem_arena == null) {
|
||||
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
self.mem_allocator = self.mem_arena.?.allocator();
|
||||
}
|
||||
self.cap = cap;
|
||||
self.nums = try self.mem_allocator.alloc(T, self.cap);
|
||||
@memset(self.nums, @as(T, 0));
|
||||
}
|
||||
|
||||
// 析構函式(釋放記憶體)
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.mem_arena == null) return;
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取佇列的容量
|
||||
pub fn capacity(self: *Self) usize {
|
||||
return self.cap;
|
||||
}
|
||||
|
||||
// 獲取佇列的長度
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.queSize;
|
||||
}
|
||||
|
||||
// 判斷佇列是否為空
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.queSize == 0;
|
||||
}
|
||||
|
||||
// 入列
|
||||
pub fn push(self: *Self, num: T) !void {
|
||||
if (self.size() == self.capacity()) {
|
||||
std.debug.print("佇列已滿\n", .{});
|
||||
return;
|
||||
}
|
||||
// 計算佇列尾指標,指向佇列尾索引 + 1
|
||||
// 透過取餘操作實現 rear 越過陣列尾部後回到頭部
|
||||
var rear = (self.front + self.queSize) % self.capacity();
|
||||
// 在尾節點後新增 num
|
||||
self.nums[rear] = num;
|
||||
self.queSize += 1;
|
||||
}
|
||||
|
||||
// 出列
|
||||
pub fn pop(self: *Self) T {
|
||||
var num = self.peek();
|
||||
// 佇列首指標向後移動一位,若越過尾部,則返回到陣列頭部
|
||||
self.front = (self.front + 1) % self.capacity();
|
||||
self.queSize -= 1;
|
||||
return num;
|
||||
}
|
||||
|
||||
// 訪問佇列首元素
|
||||
pub fn peek(self: *Self) T {
|
||||
if (self.isEmpty()) @panic("佇列為空");
|
||||
return self.nums[self.front];
|
||||
}
|
||||
|
||||
// 返回陣列
|
||||
pub fn toArray(self: *Self) ![]T {
|
||||
// 僅轉換有效長度範圍內的串列元素
|
||||
var res = try self.mem_allocator.alloc(T, self.size());
|
||||
@memset(res, @as(T, 0));
|
||||
var i: usize = 0;
|
||||
var j: usize = self.front;
|
||||
while (i < self.size()) : ({ i += 1; j += 1; }) {
|
||||
res[i] = self.nums[j % self.capacity()];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化佇列
|
||||
var capacity: usize = 10;
|
||||
var queue = ArrayQueue(i32){};
|
||||
try queue.init(std.heap.page_allocator, capacity);
|
||||
defer queue.deinit();
|
||||
|
||||
// 元素入列
|
||||
try queue.push(1);
|
||||
try queue.push(3);
|
||||
try queue.push(2);
|
||||
try queue.push(5);
|
||||
try queue.push(4);
|
||||
std.debug.print("佇列 queue = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try queue.toArray());
|
||||
|
||||
// 訪問佇列首元素
|
||||
var peek = queue.peek();
|
||||
std.debug.print("\n佇列首元素 peek = {}", .{peek});
|
||||
|
||||
// 元素出列
|
||||
var pop = queue.pop();
|
||||
std.debug.print("\n出列元素 pop = {},出列後 queue = ", .{pop});
|
||||
inc.PrintUtil.printArray(i32, try queue.toArray());
|
||||
|
||||
// 獲取佇列的長度
|
||||
var size = queue.size();
|
||||
std.debug.print("\n佇列長度 size = {}", .{size});
|
||||
|
||||
// 判斷佇列是否為空
|
||||
var is_empty = queue.isEmpty();
|
||||
std.debug.print("\n佇列是否為空 = {}", .{is_empty});
|
||||
|
||||
// 測試環形陣列
|
||||
var i: i32 = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
try queue.push(i);
|
||||
_ = queue.pop();
|
||||
std.debug.print("\n第 {} 輪入列 + 出列後 queue = ", .{i});
|
||||
inc.PrintUtil.printArray(i32, try queue.toArray());
|
||||
}
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// File: array_stack.zig
|
||||
// Created Time: 2023-01-08
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 基於陣列實現的堆疊
|
||||
pub fn ArrayStack(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
stack: ?std.ArrayList(T) = null,
|
||||
|
||||
// 建構子(分配記憶體+初始化堆疊)
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator) void {
|
||||
if (self.stack == null) {
|
||||
self.stack = std.ArrayList(T).init(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
// 析構方法(釋放記憶體)
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.stack == null) return;
|
||||
self.stack.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取堆疊的長度
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.stack.?.items.len;
|
||||
}
|
||||
|
||||
// 判斷堆疊是否為空
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.size() == 0;
|
||||
}
|
||||
|
||||
// 訪問堆疊頂元素
|
||||
pub fn peek(self: *Self) T {
|
||||
if (self.isEmpty()) @panic("堆疊為空");
|
||||
return self.stack.?.items[self.size() - 1];
|
||||
}
|
||||
|
||||
// 入堆疊
|
||||
pub fn push(self: *Self, num: T) !void {
|
||||
try self.stack.?.append(num);
|
||||
}
|
||||
|
||||
// 出堆疊
|
||||
pub fn pop(self: *Self) T {
|
||||
var num = self.stack.?.pop();
|
||||
return num;
|
||||
}
|
||||
|
||||
// 返回 ArrayList
|
||||
pub fn toList(self: *Self) std.ArrayList(T) {
|
||||
return self.stack.?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化堆疊
|
||||
var stack = ArrayStack(i32){};
|
||||
stack.init(std.heap.page_allocator);
|
||||
// 延遲釋放記憶體
|
||||
defer stack.deinit();
|
||||
|
||||
// 元素入堆疊
|
||||
try stack.push(1);
|
||||
try stack.push(3);
|
||||
try stack.push(2);
|
||||
try stack.push(5);
|
||||
try stack.push(4);
|
||||
std.debug.print("堆疊 stack = ", .{});
|
||||
inc.PrintUtil.printList(i32, stack.toList());
|
||||
|
||||
// 訪問堆疊頂元素
|
||||
var peek = stack.peek();
|
||||
std.debug.print("\n堆疊頂元素 peek = {}", .{peek});
|
||||
|
||||
// 元素出堆疊
|
||||
var top = stack.pop();
|
||||
std.debug.print("\n出堆疊元素 pop = {},出堆疊後 stack = ", .{top});
|
||||
inc.PrintUtil.printList(i32, stack.toList());
|
||||
|
||||
// 獲取堆疊的長度
|
||||
var size = stack.size();
|
||||
std.debug.print("\n堆疊的長度 size = {}", .{size});
|
||||
|
||||
// 判斷堆疊是否為空
|
||||
var is_empty = stack.isEmpty();
|
||||
std.debug.print("\n堆疊是否為空 = {}", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// File: deque.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化雙向佇列
|
||||
const L = std.TailQueue(i32);
|
||||
var deque = L{};
|
||||
|
||||
// 元素入列
|
||||
var node1 = L.Node{ .data = 2 };
|
||||
var node2 = L.Node{ .data = 5 };
|
||||
var node3 = L.Node{ .data = 4 };
|
||||
var node4 = L.Node{ .data = 3 };
|
||||
var node5 = L.Node{ .data = 1 };
|
||||
deque.append(&node1); // 新增至佇列尾
|
||||
deque.append(&node2);
|
||||
deque.append(&node3);
|
||||
deque.prepend(&node4); // 新增至佇列首
|
||||
deque.prepend(&node5);
|
||||
std.debug.print("雙向佇列 deque = ", .{});
|
||||
inc.PrintUtil.printQueue(i32, deque);
|
||||
|
||||
// 訪問元素
|
||||
var peek_first = deque.first.?.data; // 佇列首元素
|
||||
std.debug.print("\n佇列首元素 peek_first = {}", .{peek_first});
|
||||
var peek_last = deque.last.?.data; // 佇列尾元素
|
||||
std.debug.print("\n佇列尾元素 peek_last = {}", .{peek_last});
|
||||
|
||||
// 元素出列
|
||||
var pop_first = deque.popFirst().?.data; // 佇列首元素出列
|
||||
std.debug.print("\n佇列首出列元素 pop_first = {},佇列首出列後 deque = ", .{pop_first});
|
||||
inc.PrintUtil.printQueue(i32, deque);
|
||||
var pop_last = deque.pop().?.data; // 佇列尾元素出列
|
||||
std.debug.print("\n佇列尾出列元素 pop_last = {},佇列尾出列後 deque = ", .{pop_last});
|
||||
inc.PrintUtil.printQueue(i32, deque);
|
||||
|
||||
// 獲取雙向佇列的長度
|
||||
var size = deque.len;
|
||||
std.debug.print("\n雙向佇列長度 size = {}", .{size});
|
||||
|
||||
// 判斷雙向佇列是否為空
|
||||
var is_empty = if (deque.len == 0) true else false;
|
||||
std.debug.print("\n雙向佇列是否為空 = {}", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// File: linkedlist_deque.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 雙向鏈結串列節點
|
||||
pub fn ListNode(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
val: T = undefined, // 節點值
|
||||
next: ?*Self = null, // 後繼節點指標
|
||||
prev: ?*Self = null, // 前驅節點指標
|
||||
|
||||
// Initialize a list node with specific value
|
||||
pub fn init(self: *Self, x: i32) void {
|
||||
self.val = x;
|
||||
self.next = null;
|
||||
self.prev = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 基於雙向鏈結串列實現的雙向佇列
|
||||
pub fn LinkedListDeque(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
front: ?*ListNode(T) = null, // 頭節點 front
|
||||
rear: ?*ListNode(T) = null, // 尾節點 rear
|
||||
que_size: usize = 0, // 雙向佇列的長度
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 記憶體分配器
|
||||
|
||||
// 建構子(分配記憶體+初始化佇列)
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator) !void {
|
||||
if (self.mem_arena == null) {
|
||||
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
self.mem_allocator = self.mem_arena.?.allocator();
|
||||
}
|
||||
self.front = null;
|
||||
self.rear = null;
|
||||
self.que_size = 0;
|
||||
}
|
||||
|
||||
// 析構函式(釋放記憶體)
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.mem_arena == null) return;
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取雙向佇列的長度
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.que_size;
|
||||
}
|
||||
|
||||
// 判斷雙向佇列是否為空
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.size() == 0;
|
||||
}
|
||||
|
||||
// 入列操作
|
||||
pub fn push(self: *Self, num: T, is_front: bool) !void {
|
||||
var node = try self.mem_allocator.create(ListNode(T));
|
||||
node.init(num);
|
||||
// 若鏈結串列為空,則令 front 和 rear 都指向 node
|
||||
if (self.isEmpty()) {
|
||||
self.front = node;
|
||||
self.rear = node;
|
||||
// 佇列首入列操作
|
||||
} else if (is_front) {
|
||||
// 將 node 新增至鏈結串列頭部
|
||||
self.front.?.prev = node;
|
||||
node.next = self.front;
|
||||
self.front = node; // 更新頭節點
|
||||
// 佇列尾入列操作
|
||||
} else {
|
||||
// 將 node 新增至鏈結串列尾部
|
||||
self.rear.?.next = node;
|
||||
node.prev = self.rear;
|
||||
self.rear = node; // 更新尾節點
|
||||
}
|
||||
self.que_size += 1; // 更新佇列長度
|
||||
}
|
||||
|
||||
// 佇列首入列
|
||||
pub fn pushFirst(self: *Self, num: T) !void {
|
||||
try self.push(num, true);
|
||||
}
|
||||
|
||||
// 佇列尾入列
|
||||
pub fn pushLast(self: *Self, num: T) !void {
|
||||
try self.push(num, false);
|
||||
}
|
||||
|
||||
// 出列操作
|
||||
pub fn pop(self: *Self, is_front: bool) T {
|
||||
if (self.isEmpty()) @panic("雙向佇列為空");
|
||||
var val: T = undefined;
|
||||
// 佇列首出列操作
|
||||
if (is_front) {
|
||||
val = self.front.?.val; // 暫存頭節點值
|
||||
// 刪除頭節點
|
||||
var fNext = self.front.?.next;
|
||||
if (fNext != null) {
|
||||
fNext.?.prev = null;
|
||||
self.front.?.next = null;
|
||||
}
|
||||
self.front = fNext; // 更新頭節點
|
||||
// 佇列尾出列操作
|
||||
} else {
|
||||
val = self.rear.?.val; // 暫存尾節點值
|
||||
// 刪除尾節點
|
||||
var rPrev = self.rear.?.prev;
|
||||
if (rPrev != null) {
|
||||
rPrev.?.next = null;
|
||||
self.rear.?.prev = null;
|
||||
}
|
||||
self.rear = rPrev; // 更新尾節點
|
||||
}
|
||||
self.que_size -= 1; // 更新佇列長度
|
||||
return val;
|
||||
}
|
||||
|
||||
// 佇列首出列
|
||||
pub fn popFirst(self: *Self) T {
|
||||
return self.pop(true);
|
||||
}
|
||||
|
||||
// 佇列尾出列
|
||||
pub fn popLast(self: *Self) T {
|
||||
return self.pop(false);
|
||||
}
|
||||
|
||||
// 訪問佇列首元素
|
||||
pub fn peekFirst(self: *Self) T {
|
||||
if (self.isEmpty()) @panic("雙向佇列為空");
|
||||
return self.front.?.val;
|
||||
}
|
||||
|
||||
// 訪問佇列尾元素
|
||||
pub fn peekLast(self: *Self) T {
|
||||
if (self.isEmpty()) @panic("雙向佇列為空");
|
||||
return self.rear.?.val;
|
||||
}
|
||||
|
||||
// 返回陣列用於列印
|
||||
pub fn toArray(self: *Self) ![]T {
|
||||
var node = self.front;
|
||||
var res = try self.mem_allocator.alloc(T, self.size());
|
||||
@memset(res, @as(T, 0));
|
||||
var i: usize = 0;
|
||||
while (i < res.len) : (i += 1) {
|
||||
res[i] = node.?.val;
|
||||
node = node.?.next;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化雙向佇列
|
||||
var deque = LinkedListDeque(i32){};
|
||||
try deque.init(std.heap.page_allocator);
|
||||
defer deque.deinit();
|
||||
try deque.pushLast(3);
|
||||
try deque.pushLast(2);
|
||||
try deque.pushLast(5);
|
||||
std.debug.print("雙向佇列 deque = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try deque.toArray());
|
||||
|
||||
// 訪問元素
|
||||
var peek_first = deque.peekFirst();
|
||||
std.debug.print("\n佇列首元素 peek_first = {}", .{peek_first});
|
||||
var peek_last = deque.peekLast();
|
||||
std.debug.print("\n佇列尾元素 peek_last = {}", .{peek_last});
|
||||
|
||||
// 元素入列
|
||||
try deque.pushLast(4);
|
||||
std.debug.print("\n元素 4 佇列尾入列後 deque = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try deque.toArray());
|
||||
try deque.pushFirst(1);
|
||||
std.debug.print("\n元素 1 佇列首入列後 deque = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try deque.toArray());
|
||||
|
||||
// 元素出列
|
||||
var pop_last = deque.popLast();
|
||||
std.debug.print("\n佇列尾出列元素 = {},佇列尾出列後 deque = ", .{pop_last});
|
||||
inc.PrintUtil.printArray(i32, try deque.toArray());
|
||||
var pop_first = deque.popFirst();
|
||||
std.debug.print("\n佇列首出列元素 = {},佇列首出列後 deque = ", .{pop_first});
|
||||
inc.PrintUtil.printArray(i32, try deque.toArray());
|
||||
|
||||
// 獲取雙向佇列的長度
|
||||
var size = deque.size();
|
||||
std.debug.print("\n雙向佇列長度 size = {}", .{size});
|
||||
|
||||
// 判斷雙向佇列是否為空
|
||||
var is_empty = deque.isEmpty();
|
||||
std.debug.print("\n雙向佇列是否為空 = {}", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// File: linkedlist_queue.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 基於鏈結串列實現的佇列
|
||||
pub fn LinkedListQueue(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
front: ?*inc.ListNode(T) = null, // 頭節點 front
|
||||
rear: ?*inc.ListNode(T) = null, // 尾節點 rear
|
||||
que_size: usize = 0, // 佇列的長度
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 記憶體分配器
|
||||
|
||||
// 建構子(分配記憶體+初始化佇列)
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator) !void {
|
||||
if (self.mem_arena == null) {
|
||||
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
self.mem_allocator = self.mem_arena.?.allocator();
|
||||
}
|
||||
self.front = null;
|
||||
self.rear = null;
|
||||
self.que_size = 0;
|
||||
}
|
||||
|
||||
// 析構函式(釋放記憶體)
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.mem_arena == null) return;
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取佇列的長度
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.que_size;
|
||||
}
|
||||
|
||||
// 判斷佇列是否為空
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.size() == 0;
|
||||
}
|
||||
|
||||
// 訪問佇列首元素
|
||||
pub fn peek(self: *Self) T {
|
||||
if (self.size() == 0) @panic("佇列為空");
|
||||
return self.front.?.val;
|
||||
}
|
||||
|
||||
// 入列
|
||||
pub fn push(self: *Self, num: T) !void {
|
||||
// 在尾節點後新增 num
|
||||
var node = try self.mem_allocator.create(inc.ListNode(T));
|
||||
node.init(num);
|
||||
// 如果佇列為空,則令頭、尾節點都指向該節點
|
||||
if (self.front == null) {
|
||||
self.front = node;
|
||||
self.rear = node;
|
||||
// 如果佇列不為空,則將該節點新增到尾節點後
|
||||
} else {
|
||||
self.rear.?.next = node;
|
||||
self.rear = node;
|
||||
}
|
||||
self.que_size += 1;
|
||||
}
|
||||
|
||||
// 出列
|
||||
pub fn pop(self: *Self) T {
|
||||
var num = self.peek();
|
||||
// 刪除頭節點
|
||||
self.front = self.front.?.next;
|
||||
self.que_size -= 1;
|
||||
return num;
|
||||
}
|
||||
|
||||
// 將鏈結串列轉換為陣列
|
||||
pub fn toArray(self: *Self) ![]T {
|
||||
var node = self.front;
|
||||
var res = try self.mem_allocator.alloc(T, self.size());
|
||||
@memset(res, @as(T, 0));
|
||||
var i: usize = 0;
|
||||
while (i < res.len) : (i += 1) {
|
||||
res[i] = node.?.val;
|
||||
node = node.?.next;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化佇列
|
||||
var queue = LinkedListQueue(i32){};
|
||||
try queue.init(std.heap.page_allocator);
|
||||
defer queue.deinit();
|
||||
|
||||
// 元素入列
|
||||
try queue.push(1);
|
||||
try queue.push(3);
|
||||
try queue.push(2);
|
||||
try queue.push(5);
|
||||
try queue.push(4);
|
||||
std.debug.print("佇列 queue = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try queue.toArray());
|
||||
|
||||
// 訪問佇列首元素
|
||||
var peek = queue.peek();
|
||||
std.debug.print("\n佇列首元素 peek = {}", .{peek});
|
||||
|
||||
// 元素出列
|
||||
var pop = queue.pop();
|
||||
std.debug.print("\n出列元素 pop = {},出列後 queue = ", .{pop});
|
||||
inc.PrintUtil.printArray(i32, try queue.toArray());
|
||||
|
||||
// 獲取佇列的長度
|
||||
var size = queue.size();
|
||||
std.debug.print("\n佇列長度 size = {}", .{size});
|
||||
|
||||
// 判斷佇列是否為空
|
||||
var is_empty = queue.isEmpty();
|
||||
std.debug.print("\n佇列是否為空 = {}", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// File: linkedlist_stack.zig
|
||||
// Created Time: 2023-01-08
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 基於鏈結串列實現的堆疊
|
||||
pub fn LinkedListStack(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
stack_top: ?*inc.ListNode(T) = null, // 將頭節點作為堆疊頂
|
||||
stk_size: usize = 0, // 堆疊的長度
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 記憶體分配器
|
||||
|
||||
// 建構子(分配記憶體+初始化堆疊)
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator) !void {
|
||||
if (self.mem_arena == null) {
|
||||
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
self.mem_allocator = self.mem_arena.?.allocator();
|
||||
}
|
||||
self.stack_top = null;
|
||||
self.stk_size = 0;
|
||||
}
|
||||
|
||||
// 析構函式(釋放記憶體)
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.mem_arena == null) return;
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取堆疊的長度
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.stk_size;
|
||||
}
|
||||
|
||||
// 判斷堆疊是否為空
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
return self.size() == 0;
|
||||
}
|
||||
|
||||
// 訪問堆疊頂元素
|
||||
pub fn peek(self: *Self) T {
|
||||
if (self.size() == 0) @panic("堆疊為空");
|
||||
return self.stack_top.?.val;
|
||||
}
|
||||
|
||||
// 入堆疊
|
||||
pub fn push(self: *Self, num: T) !void {
|
||||
var node = try self.mem_allocator.create(inc.ListNode(T));
|
||||
node.init(num);
|
||||
node.next = self.stack_top;
|
||||
self.stack_top = node;
|
||||
self.stk_size += 1;
|
||||
}
|
||||
|
||||
// 出堆疊
|
||||
pub fn pop(self: *Self) T {
|
||||
var num = self.peek();
|
||||
self.stack_top = self.stack_top.?.next;
|
||||
self.stk_size -= 1;
|
||||
return num;
|
||||
}
|
||||
|
||||
// 將堆疊轉換為陣列
|
||||
pub fn toArray(self: *Self) ![]T {
|
||||
var node = self.stack_top;
|
||||
var res = try self.mem_allocator.alloc(T, self.size());
|
||||
@memset(res, @as(T, 0));
|
||||
var i: usize = 0;
|
||||
while (i < res.len) : (i += 1) {
|
||||
res[res.len - i - 1] = node.?.val;
|
||||
node = node.?.next;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化堆疊
|
||||
var stack = LinkedListStack(i32){};
|
||||
try stack.init(std.heap.page_allocator);
|
||||
// 延遲釋放記憶體
|
||||
defer stack.deinit();
|
||||
|
||||
// 元素入堆疊
|
||||
try stack.push(1);
|
||||
try stack.push(3);
|
||||
try stack.push(2);
|
||||
try stack.push(5);
|
||||
try stack.push(4);
|
||||
std.debug.print("堆疊 stack = ", .{});
|
||||
inc.PrintUtil.printArray(i32, try stack.toArray());
|
||||
|
||||
// 訪問堆疊頂元素
|
||||
var peek = stack.peek();
|
||||
std.debug.print("\n堆疊頂元素 top = {}", .{peek});
|
||||
|
||||
// 元素出堆疊
|
||||
var pop = stack.pop();
|
||||
std.debug.print("\n出堆疊元素 pop = {},出堆疊後 stack = ", .{pop});
|
||||
inc.PrintUtil.printArray(i32, try stack.toArray());
|
||||
|
||||
// 獲取堆疊的長度
|
||||
var size = stack.size();
|
||||
std.debug.print("\n堆疊的長度 size = {}", .{size});
|
||||
|
||||
// 判斷堆疊是否為空
|
||||
var is_empty = stack.isEmpty();
|
||||
std.debug.print("\n堆疊是否為空 = {}", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// File: queue.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化佇列
|
||||
const L = std.TailQueue(i32);
|
||||
var queue = L{};
|
||||
|
||||
// 元素入列
|
||||
var node1 = L.Node{ .data = 1 };
|
||||
var node2 = L.Node{ .data = 3 };
|
||||
var node3 = L.Node{ .data = 2 };
|
||||
var node4 = L.Node{ .data = 5 };
|
||||
var node5 = L.Node{ .data = 4 };
|
||||
queue.append(&node1);
|
||||
queue.append(&node2);
|
||||
queue.append(&node3);
|
||||
queue.append(&node4);
|
||||
queue.append(&node5);
|
||||
std.debug.print("佇列 queue = ", .{});
|
||||
inc.PrintUtil.printQueue(i32, queue);
|
||||
|
||||
// 訪問佇列首元素
|
||||
var peek = queue.first.?.data;
|
||||
std.debug.print("\n佇列首元素 peek = {}", .{peek});
|
||||
|
||||
// 元素出列
|
||||
var pop = queue.popFirst().?.data;
|
||||
std.debug.print("\n出列元素 pop = {},出列後 queue = ", .{pop});
|
||||
inc.PrintUtil.printQueue(i32, queue);
|
||||
|
||||
// 獲取佇列的長度
|
||||
var size = queue.len;
|
||||
std.debug.print("\n佇列長度 size = {}", .{size});
|
||||
|
||||
// 判斷佇列是否為空
|
||||
var is_empty = if (queue.len == 0) true else false;
|
||||
std.debug.print("\n佇列是否為空 = {}", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// File: stack.zig
|
||||
// Created Time: 2023-01-08
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化堆疊
|
||||
// 在 zig 中,推薦將 ArrayList 當作堆疊來使用
|
||||
var stack = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
// 延遲釋放記憶體
|
||||
defer stack.deinit();
|
||||
|
||||
// 元素入堆疊
|
||||
try stack.append(1);
|
||||
try stack.append(3);
|
||||
try stack.append(2);
|
||||
try stack.append(5);
|
||||
try stack.append(4);
|
||||
std.debug.print("堆疊 stack = ", .{});
|
||||
inc.PrintUtil.printList(i32, stack);
|
||||
|
||||
// 訪問堆疊頂元素
|
||||
var peek = stack.items[stack.items.len - 1];
|
||||
std.debug.print("\n堆疊頂元素 peek = {}", .{peek});
|
||||
|
||||
// 元素出堆疊
|
||||
var pop = stack.pop();
|
||||
std.debug.print("\n出堆疊元素 pop = {},出堆疊後 stack = ", .{pop});
|
||||
inc.PrintUtil.printList(i32, stack);
|
||||
|
||||
// 獲取堆疊的長度
|
||||
var size = stack.items.len;
|
||||
std.debug.print("\n堆疊的長度 size = {}", .{size});
|
||||
|
||||
// 判斷堆疊是否為空
|
||||
var is_empty = if (stack.items.len == 0) true else false;
|
||||
std.debug.print("\n堆疊是否為空 = {}", .{is_empty});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
// File: avl_tree.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// AVL 樹
|
||||
pub fn AVLTree(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
root: ?*inc.TreeNode(T) = null, // 根節點
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 記憶體分配器
|
||||
|
||||
// 建構子
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator) void {
|
||||
if (self.mem_arena == null) {
|
||||
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
self.mem_allocator = self.mem_arena.?.allocator();
|
||||
}
|
||||
}
|
||||
|
||||
// 析構方法
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.mem_arena == null) return;
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 獲取節點高度
|
||||
fn height(self: *Self, node: ?*inc.TreeNode(T)) i32 {
|
||||
_ = self;
|
||||
// 空節點高度為 -1 ,葉節點高度為 0
|
||||
return if (node == null) -1 else node.?.height;
|
||||
}
|
||||
|
||||
// 更新節點高度
|
||||
fn updateHeight(self: *Self, node: ?*inc.TreeNode(T)) void {
|
||||
// 節點高度等於最高子樹高度 + 1
|
||||
node.?.height = @max(self.height(node.?.left), self.height(node.?.right)) + 1;
|
||||
}
|
||||
|
||||
// 獲取平衡因子
|
||||
fn balanceFactor(self: *Self, node: ?*inc.TreeNode(T)) i32 {
|
||||
// 空節點平衡因子為 0
|
||||
if (node == null) return 0;
|
||||
// 節點平衡因子 = 左子樹高度 - 右子樹高度
|
||||
return self.height(node.?.left) - self.height(node.?.right);
|
||||
}
|
||||
|
||||
// 右旋操作
|
||||
fn rightRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) {
|
||||
var child = node.?.left;
|
||||
var grandChild = child.?.right;
|
||||
// 以 child 為原點,將 node 向右旋轉
|
||||
child.?.right = node;
|
||||
node.?.left = grandChild;
|
||||
// 更新節點高度
|
||||
self.updateHeight(node);
|
||||
self.updateHeight(child);
|
||||
// 返回旋轉後子樹的根節點
|
||||
return child;
|
||||
}
|
||||
|
||||
// 左旋操作
|
||||
fn leftRotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) {
|
||||
var child = node.?.right;
|
||||
var grandChild = child.?.left;
|
||||
// 以 child 為原點,將 node 向左旋轉
|
||||
child.?.left = node;
|
||||
node.?.right = grandChild;
|
||||
// 更新節點高度
|
||||
self.updateHeight(node);
|
||||
self.updateHeight(child);
|
||||
// 返回旋轉後子樹的根節點
|
||||
return child;
|
||||
}
|
||||
|
||||
// 執行旋轉操作,使該子樹重新恢復平衡
|
||||
fn rotate(self: *Self, node: ?*inc.TreeNode(T)) ?*inc.TreeNode(T) {
|
||||
// 獲取節點 node 的平衡因子
|
||||
var balance_factor = self.balanceFactor(node);
|
||||
// 左偏樹
|
||||
if (balance_factor > 1) {
|
||||
if (self.balanceFactor(node.?.left) >= 0) {
|
||||
// 右旋
|
||||
return self.rightRotate(node);
|
||||
} else {
|
||||
// 先左旋後右旋
|
||||
node.?.left = self.leftRotate(node.?.left);
|
||||
return self.rightRotate(node);
|
||||
}
|
||||
}
|
||||
// 右偏樹
|
||||
if (balance_factor < -1) {
|
||||
if (self.balanceFactor(node.?.right) <= 0) {
|
||||
// 左旋
|
||||
return self.leftRotate(node);
|
||||
} else {
|
||||
// 先右旋後左旋
|
||||
node.?.right = self.rightRotate(node.?.right);
|
||||
return self.leftRotate(node);
|
||||
}
|
||||
}
|
||||
// 平衡樹,無須旋轉,直接返回
|
||||
return node;
|
||||
}
|
||||
|
||||
// 插入節點
|
||||
fn insert(self: *Self, val: T) !void {
|
||||
self.root = (try self.insertHelper(self.root, val)).?;
|
||||
}
|
||||
|
||||
// 遞迴插入節點(輔助方法)
|
||||
fn insertHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) !?*inc.TreeNode(T) {
|
||||
var node = node_;
|
||||
if (node == null) {
|
||||
var tmp_node = try self.mem_allocator.create(inc.TreeNode(T));
|
||||
tmp_node.init(val);
|
||||
return tmp_node;
|
||||
}
|
||||
// 1. 查詢插入位置並插入節點
|
||||
if (val < node.?.val) {
|
||||
node.?.left = try self.insertHelper(node.?.left, val);
|
||||
} else if (val > node.?.val) {
|
||||
node.?.right = try self.insertHelper(node.?.right, val);
|
||||
} else {
|
||||
return node; // 重複節點不插入,直接返回
|
||||
}
|
||||
self.updateHeight(node); // 更新節點高度
|
||||
// 2. 執行旋轉操作,使該子樹重新恢復平衡
|
||||
node = self.rotate(node);
|
||||
// 返回子樹的根節點
|
||||
return node;
|
||||
}
|
||||
|
||||
// 刪除節點
|
||||
fn remove(self: *Self, val: T) void {
|
||||
self.root = self.removeHelper(self.root, val).?;
|
||||
}
|
||||
|
||||
// 遞迴刪除節點(輔助方法)
|
||||
fn removeHelper(self: *Self, node_: ?*inc.TreeNode(T), val: T) ?*inc.TreeNode(T) {
|
||||
var node = node_;
|
||||
if (node == null) return null;
|
||||
// 1. 查詢節點並刪除
|
||||
if (val < node.?.val) {
|
||||
node.?.left = self.removeHelper(node.?.left, val);
|
||||
} else if (val > node.?.val) {
|
||||
node.?.right = self.removeHelper(node.?.right, val);
|
||||
} else {
|
||||
if (node.?.left == null or node.?.right == null) {
|
||||
var child = if (node.?.left != null) node.?.left else node.?.right;
|
||||
// 子節點數量 = 0 ,直接刪除 node 並返回
|
||||
if (child == null) {
|
||||
return null;
|
||||
// 子節點數量 = 1 ,直接刪除 node
|
||||
} else {
|
||||
node = child;
|
||||
}
|
||||
} else {
|
||||
// 子節點數量 = 2 ,則將中序走訪的下個節點刪除,並用該節點替換當前節點
|
||||
var temp = node.?.right;
|
||||
while (temp.?.left != null) {
|
||||
temp = temp.?.left;
|
||||
}
|
||||
node.?.right = self.removeHelper(node.?.right, temp.?.val);
|
||||
node.?.val = temp.?.val;
|
||||
}
|
||||
}
|
||||
self.updateHeight(node); // 更新節點高度
|
||||
// 2. 執行旋轉操作,使該子樹重新恢復平衡
|
||||
node = self.rotate(node);
|
||||
// 返回子樹的根節點
|
||||
return node;
|
||||
}
|
||||
|
||||
// 查詢節點
|
||||
fn search(self: *Self, val: T) ?*inc.TreeNode(T) {
|
||||
var cur = self.root;
|
||||
// 迴圈查詢,越過葉節點後跳出
|
||||
while (cur != null) {
|
||||
// 目標節點在 cur 的右子樹中
|
||||
if (cur.?.val < val) {
|
||||
cur = cur.?.right;
|
||||
// 目標節點在 cur 的左子樹中
|
||||
} else if (cur.?.val > val) {
|
||||
cur = cur.?.left;
|
||||
// 找到目標節點,跳出迴圈
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 返回目標節點
|
||||
return cur;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn testInsert(comptime T: type, tree_: *AVLTree(T), val: T) !void {
|
||||
var tree = tree_;
|
||||
try tree.insert(val);
|
||||
std.debug.print("\n插入節點 {} 後,AVL 樹為\n", .{val});
|
||||
try inc.PrintUtil.printTree(tree.root, null, false);
|
||||
}
|
||||
|
||||
pub fn testRemove(comptime T: type, tree_: *AVLTree(T), val: T) void {
|
||||
var tree = tree_;
|
||||
tree.remove(val);
|
||||
std.debug.print("\n刪除節點 {} 後,AVL 樹為\n", .{val});
|
||||
try inc.PrintUtil.printTree(tree.root, null, false);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化空 AVL 樹
|
||||
var avl_tree = AVLTree(i32){};
|
||||
avl_tree.init(std.heap.page_allocator);
|
||||
defer avl_tree.deinit();
|
||||
|
||||
// 插入節點
|
||||
// 請關注插入節點後,AVL 樹是如何保持平衡的
|
||||
try testInsert(i32, &avl_tree, 1);
|
||||
try testInsert(i32, &avl_tree, 2);
|
||||
try testInsert(i32, &avl_tree, 3);
|
||||
try testInsert(i32, &avl_tree, 4);
|
||||
try testInsert(i32, &avl_tree, 5);
|
||||
try testInsert(i32, &avl_tree, 8);
|
||||
try testInsert(i32, &avl_tree, 7);
|
||||
try testInsert(i32, &avl_tree, 9);
|
||||
try testInsert(i32, &avl_tree, 10);
|
||||
try testInsert(i32, &avl_tree, 6);
|
||||
|
||||
// 插入重複節點
|
||||
try testInsert(i32, &avl_tree, 7);
|
||||
|
||||
// 刪除節點
|
||||
// 請關注刪除節點後,AVL 樹是如何保持平衡的
|
||||
testRemove(i32, &avl_tree, 8); // 刪除度為 0 的節點
|
||||
testRemove(i32, &avl_tree, 5); // 刪除度為 1 的節點
|
||||
testRemove(i32, &avl_tree, 4); // 刪除度為 2 的節點
|
||||
|
||||
// 查詢節點
|
||||
var node = avl_tree.search(7).?;
|
||||
std.debug.print("\n查詢到的節點物件為 {any},節點值 = {}\n", .{node, node.val});
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// File: binary_search_tree.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 二元搜尋樹
|
||||
pub fn BinarySearchTree(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
root: ?*inc.TreeNode(T) = null,
|
||||
mem_arena: ?std.heap.ArenaAllocator = null,
|
||||
mem_allocator: std.mem.Allocator = undefined, // 記憶體分配器
|
||||
|
||||
// 建構子
|
||||
pub fn init(self: *Self, allocator: std.mem.Allocator, nums: []T) !void {
|
||||
if (self.mem_arena == null) {
|
||||
self.mem_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
self.mem_allocator = self.mem_arena.?.allocator();
|
||||
}
|
||||
std.mem.sort(T, nums, {}, comptime std.sort.asc(T)); // 排序陣列
|
||||
self.root = try self.buildTree(nums, 0, nums.len - 1); // 構建二元搜尋樹
|
||||
}
|
||||
|
||||
// 析構方法
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.mem_arena == null) return;
|
||||
self.mem_arena.?.deinit();
|
||||
}
|
||||
|
||||
// 構建二元搜尋樹
|
||||
fn buildTree(self: *Self, nums: []T, i: usize, j: usize) !?*inc.TreeNode(T) {
|
||||
if (i > j) return null;
|
||||
// 將陣列中間節點作為根節點
|
||||
var mid = (i + j) / 2;
|
||||
var node = try self.mem_allocator.create(inc.TreeNode(T));
|
||||
node.init(nums[mid]);
|
||||
// 遞迴建立左子樹和右子樹
|
||||
if (mid >= 1) node.left = try self.buildTree(nums, i, mid - 1);
|
||||
node.right = try self.buildTree(nums, mid + 1, j);
|
||||
return node;
|
||||
}
|
||||
|
||||
// 獲取二元樹根節點
|
||||
fn getRoot(self: *Self) ?*inc.TreeNode(T) {
|
||||
return self.root;
|
||||
}
|
||||
|
||||
// 查詢節點
|
||||
fn search(self: *Self, num: T) ?*inc.TreeNode(T) {
|
||||
var cur = self.root;
|
||||
// 迴圈查詢,越過葉節點後跳出
|
||||
while (cur != null) {
|
||||
// 目標節點在 cur 的右子樹中
|
||||
if (cur.?.val < num) {
|
||||
cur = cur.?.right;
|
||||
// 目標節點在 cur 的左子樹中
|
||||
} else if (cur.?.val > num) {
|
||||
cur = cur.?.left;
|
||||
// 找到目標節點,跳出迴圈
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 返回目標節點
|
||||
return cur;
|
||||
}
|
||||
|
||||
// 插入節點
|
||||
fn insert(self: *Self, num: T) !void {
|
||||
// 若樹為空,則初始化根節點
|
||||
if (self.root == null) {
|
||||
self.root = try self.mem_allocator.create(inc.TreeNode(T));
|
||||
return;
|
||||
}
|
||||
var cur = self.root;
|
||||
var pre: ?*inc.TreeNode(T) = null;
|
||||
// 迴圈查詢,越過葉節點後跳出
|
||||
while (cur != null) {
|
||||
// 找到重複節點,直接返回
|
||||
if (cur.?.val == num) return;
|
||||
pre = cur;
|
||||
// 插入位置在 cur 的右子樹中
|
||||
if (cur.?.val < num) {
|
||||
cur = cur.?.right;
|
||||
// 插入位置在 cur 的左子樹中
|
||||
} else {
|
||||
cur = cur.?.left;
|
||||
}
|
||||
}
|
||||
// 插入節點
|
||||
var node = try self.mem_allocator.create(inc.TreeNode(T));
|
||||
node.init(num);
|
||||
if (pre.?.val < num) {
|
||||
pre.?.right = node;
|
||||
} else {
|
||||
pre.?.left = node;
|
||||
}
|
||||
}
|
||||
|
||||
// 刪除節點
|
||||
fn remove(self: *Self, num: T) void {
|
||||
// 若樹為空,直接提前返回
|
||||
if (self.root == null) return;
|
||||
var cur = self.root;
|
||||
var pre: ?*inc.TreeNode(T) = null;
|
||||
// 迴圈查詢,越過葉節點後跳出
|
||||
while (cur != null) {
|
||||
// 找到待刪除節點,跳出迴圈
|
||||
if (cur.?.val == num) break;
|
||||
pre = cur;
|
||||
// 待刪除節點在 cur 的右子樹中
|
||||
if (cur.?.val < num) {
|
||||
cur = cur.?.right;
|
||||
// 待刪除節點在 cur 的左子樹中
|
||||
} else {
|
||||
cur = cur.?.left;
|
||||
}
|
||||
}
|
||||
// 若無待刪除節點,則直接返回
|
||||
if (cur == null) return;
|
||||
// 子節點數量 = 0 or 1
|
||||
if (cur.?.left == null or cur.?.right == null) {
|
||||
// 當子節點數量 = 0 / 1 時, child = null / 該子節點
|
||||
var child = if (cur.?.left != null) cur.?.left else cur.?.right;
|
||||
// 刪除節點 cur
|
||||
if (pre.?.left == cur) {
|
||||
pre.?.left = child;
|
||||
} else {
|
||||
pre.?.right = child;
|
||||
}
|
||||
// 子節點數量 = 2
|
||||
} else {
|
||||
// 獲取中序走訪中 cur 的下一個節點
|
||||
var tmp = cur.?.right;
|
||||
while (tmp.?.left != null) {
|
||||
tmp = tmp.?.left;
|
||||
}
|
||||
var tmp_val = tmp.?.val;
|
||||
// 遞迴刪除節點 tmp
|
||||
self.remove(tmp.?.val);
|
||||
// 用 tmp 覆蓋 cur
|
||||
cur.?.val = tmp_val;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化二元樹
|
||||
var nums = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
var bst = BinarySearchTree(i32){};
|
||||
try bst.init(std.heap.page_allocator, &nums);
|
||||
defer bst.deinit();
|
||||
std.debug.print("初始化的二元樹為\n", .{});
|
||||
try inc.PrintUtil.printTree(bst.getRoot(), null, false);
|
||||
|
||||
// 查詢節點
|
||||
var node = bst.search(7);
|
||||
std.debug.print("\n查詢到的節點物件為 {any},節點值 = {}\n", .{node, node.?.val});
|
||||
|
||||
// 插入節點
|
||||
try bst.insert(16);
|
||||
std.debug.print("\n插入節點 16 後,二元樹為\n", .{});
|
||||
try inc.PrintUtil.printTree(bst.getRoot(), null, false);
|
||||
|
||||
// 刪除節點
|
||||
bst.remove(1);
|
||||
std.debug.print("\n刪除節點 1 後,二元樹為\n", .{});
|
||||
try inc.PrintUtil.printTree(bst.getRoot(), null, false);
|
||||
bst.remove(2);
|
||||
std.debug.print("\n刪除節點 2 後,二元樹為\n", .{});
|
||||
try inc.PrintUtil.printTree(bst.getRoot(), null, false);
|
||||
bst.remove(4);
|
||||
std.debug.print("\n刪除節點 4 後,二元樹為\n", .{});
|
||||
try inc.PrintUtil.printTree(bst.getRoot(), null, false);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// File: binary_tree.zig
|
||||
// Created Time: 2023-01-14
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化二元樹
|
||||
// 初始化節點
|
||||
var n1 = inc.TreeNode(i32){ .val = 1 };
|
||||
var n2 = inc.TreeNode(i32){ .val = 2 };
|
||||
var n3 = inc.TreeNode(i32){ .val = 3 };
|
||||
var n4 = inc.TreeNode(i32){ .val = 4 };
|
||||
var n5 = inc.TreeNode(i32){ .val = 5 };
|
||||
// 構建節點之間的引用(指標)
|
||||
n1.left = &n2;
|
||||
n1.right = &n3;
|
||||
n2.left = &n4;
|
||||
n2.right = &n5;
|
||||
std.debug.print("初始化二元樹\n", .{});
|
||||
try inc.PrintUtil.printTree(&n1, null, false);
|
||||
|
||||
// 插入與刪除節點
|
||||
var p = inc.TreeNode(i32){ .val = 0 };
|
||||
// 在 n1 -> n2 中間插入節點 P
|
||||
n1.left = &p;
|
||||
p.left = &n2;
|
||||
std.debug.print("插入節點 P 後\n", .{});
|
||||
try inc.PrintUtil.printTree(&n1, null, false);
|
||||
// 刪除節點
|
||||
n1.left = &n2;
|
||||
std.debug.print("刪除節點 P 後\n", .{});
|
||||
try inc.PrintUtil.printTree(&n1, null, false);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// File: binary_tree_bfs.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
// 層序走訪
|
||||
fn levelOrder(comptime T: type, mem_allocator: std.mem.Allocator, root: *inc.TreeNode(T)) !std.ArrayList(T) {
|
||||
// 初始化佇列,加入根節點
|
||||
const L = std.TailQueue(*inc.TreeNode(T));
|
||||
var queue = L{};
|
||||
var root_node = try mem_allocator.create(L.Node);
|
||||
root_node.data = root;
|
||||
queue.append(root_node);
|
||||
// 初始化一個串列,用於儲存走訪序列
|
||||
var list = std.ArrayList(T).init(std.heap.page_allocator);
|
||||
while (queue.len > 0) {
|
||||
var queue_node = queue.popFirst().?; // 隊列出隊
|
||||
var node = queue_node.data;
|
||||
try list.append(node.val); // 儲存節點值
|
||||
if (node.left != null) {
|
||||
var tmp_node = try mem_allocator.create(L.Node);
|
||||
tmp_node.data = node.left.?;
|
||||
queue.append(tmp_node); // 左子節點入列
|
||||
}
|
||||
if (node.right != null) {
|
||||
var tmp_node = try mem_allocator.create(L.Node);
|
||||
tmp_node.data = node.right.?;
|
||||
queue.append(tmp_node); // 右子節點入列
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化記憶體分配器
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
|
||||
// 初始化二元樹
|
||||
// 這裡藉助了一個從陣列直接生成二元樹的函式
|
||||
var nums = [_]i32{1, 2, 3, 4, 5, 6, 7};
|
||||
var root = try inc.TreeUtil.arrToTree(i32, mem_allocator, &nums);
|
||||
std.debug.print("初始化二元樹\n", .{});
|
||||
try inc.PrintUtil.printTree(root, null, false);
|
||||
|
||||
// 層序走訪
|
||||
var list = try levelOrder(i32, mem_allocator, root.?);
|
||||
defer list.deinit();
|
||||
std.debug.print("\n層序走訪的節點列印序列 = ", .{});
|
||||
inc.PrintUtil.printList(i32, list);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// File: binary_tree_dfs.zig
|
||||
// Created Time: 2023-01-15
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
const inc = @import("include");
|
||||
|
||||
var list = std.ArrayList(i32).init(std.heap.page_allocator);
|
||||
|
||||
// 前序走訪
|
||||
fn preOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void {
|
||||
if (root == null) return;
|
||||
// 訪問優先順序:根節點 -> 左子樹 -> 右子樹
|
||||
try list.append(root.?.val);
|
||||
try preOrder(T, root.?.left);
|
||||
try preOrder(T, root.?.right);
|
||||
}
|
||||
|
||||
// 中序走訪
|
||||
fn inOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void {
|
||||
if (root == null) return;
|
||||
// 訪問優先順序:左子樹 -> 根節點 -> 右子樹
|
||||
try inOrder(T, root.?.left);
|
||||
try list.append(root.?.val);
|
||||
try inOrder(T, root.?.right);
|
||||
}
|
||||
|
||||
// 後序走訪
|
||||
fn postOrder(comptime T: type, root: ?*inc.TreeNode(T)) !void {
|
||||
if (root == null) return;
|
||||
// 訪問優先順序:左子樹 -> 右子樹 -> 根節點
|
||||
try postOrder(T, root.?.left);
|
||||
try postOrder(T, root.?.right);
|
||||
try list.append(root.?.val);
|
||||
}
|
||||
|
||||
// Driver Code
|
||||
pub fn main() !void {
|
||||
// 初始化記憶體分配器
|
||||
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
|
||||
// 初始化二元樹
|
||||
// 這裡藉助了一個從陣列直接生成二元樹的函式
|
||||
var nums = [_]i32{1, 2, 3, 4, 5, 6, 7};
|
||||
var root = try inc.TreeUtil.arrToTree(i32, mem_allocator, &nums);
|
||||
std.debug.print("初始化二元樹\n", .{});
|
||||
try inc.PrintUtil.printTree(root, null, false);
|
||||
|
||||
// 前序走訪
|
||||
list.clearRetainingCapacity();
|
||||
try preOrder(i32, root);
|
||||
std.debug.print("\n前序走訪的節點列印序列 = ", .{});
|
||||
inc.PrintUtil.printList(i32, list);
|
||||
|
||||
// 中序走訪
|
||||
list.clearRetainingCapacity();
|
||||
try inOrder(i32, root);
|
||||
std.debug.print("\n中序走訪的節點列印序列 = ", .{});
|
||||
inc.PrintUtil.printList(i32, list);
|
||||
|
||||
// 後序走訪
|
||||
list.clearRetainingCapacity();
|
||||
try postOrder(i32, root);
|
||||
std.debug.print("\n後續走訪的節點列印序列 = ", .{});
|
||||
inc.PrintUtil.printList(i32, list);
|
||||
|
||||
_ = try std.io.getStdIn().reader().readByte();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// File: ListNode.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 鏈結串列節點
|
||||
pub fn ListNode(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
val: T = 0,
|
||||
next: ?*Self = null,
|
||||
|
||||
// Initialize a list node with specific value
|
||||
pub fn init(self: *Self, x: i32) void {
|
||||
self.val = x;
|
||||
self.next = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 將串列反序列化為鏈結串列
|
||||
pub fn listToLinkedList(comptime T: type, mem_allocator: std.mem.Allocator, list: std.ArrayList(T)) !?*ListNode(T) {
|
||||
var dum = try mem_allocator.create(ListNode(T));
|
||||
dum.init(0);
|
||||
var head = dum;
|
||||
for (list.items) |val| {
|
||||
var tmp = try mem_allocator.create(ListNode(T));
|
||||
tmp.init(val);
|
||||
head.next = tmp;
|
||||
head = head.next.?;
|
||||
}
|
||||
return dum.next;
|
||||
}
|
||||
|
||||
// 將陣列反序列化為鏈結串列
|
||||
pub fn arrToLinkedList(comptime T: type, mem_allocator: std.mem.Allocator, arr: []T) !?*ListNode(T) {
|
||||
var dum = try mem_allocator.create(ListNode(T));
|
||||
dum.init(0);
|
||||
var head = dum;
|
||||
for (arr) |val| {
|
||||
var tmp = try mem_allocator.create(ListNode(T));
|
||||
tmp.init(val);
|
||||
head.next = tmp;
|
||||
head = head.next.?;
|
||||
}
|
||||
return dum.next;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// File: PrintUtil.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
pub const ListUtil = @import("ListNode.zig");
|
||||
pub const ListNode = ListUtil.ListNode;
|
||||
pub const TreeUtil = @import("TreeNode.zig");
|
||||
pub const TreeNode = TreeUtil.TreeNode;
|
||||
|
||||
// 列印陣列
|
||||
pub fn printArray(comptime T: type, nums: []T) void {
|
||||
std.debug.print("[", .{});
|
||||
if (nums.len > 0) {
|
||||
for (nums, 0..) |num, j| {
|
||||
std.debug.print("{}{s}", .{num, if (j == nums.len-1) "]" else ", " });
|
||||
}
|
||||
} else {
|
||||
std.debug.print("]", .{});
|
||||
}
|
||||
}
|
||||
|
||||
// 列印串列
|
||||
pub fn printList(comptime T: type, list: std.ArrayList(T)) void {
|
||||
std.debug.print("[", .{});
|
||||
if (list.items.len > 0) {
|
||||
for (list.items, 0..) |value, i| {
|
||||
std.debug.print("{}{s}", .{value, if (i == list.items.len-1) "]" else ", " });
|
||||
}
|
||||
} else {
|
||||
std.debug.print("]", .{});
|
||||
}
|
||||
}
|
||||
|
||||
// 列印鏈結串列
|
||||
pub fn printLinkedList(comptime T: type, node: ?*ListNode(T)) !void {
|
||||
if (node == null) return;
|
||||
var list = std.ArrayList(T).init(std.heap.page_allocator);
|
||||
defer list.deinit();
|
||||
var head = node;
|
||||
while (head != null) {
|
||||
try list.append(head.?.val);
|
||||
head = head.?.next;
|
||||
}
|
||||
for (list.items, 0..) |value, i| {
|
||||
std.debug.print("{}{s}", .{value, if (i == list.items.len-1) "\n" else "->" });
|
||||
}
|
||||
}
|
||||
|
||||
// 列印佇列
|
||||
pub fn printQueue(comptime T: type, queue: std.TailQueue(T)) void {
|
||||
var node = queue.first;
|
||||
std.debug.print("[", .{});
|
||||
var i: i32 = 0;
|
||||
while (node != null) : (i += 1) {
|
||||
var data = node.?.data;
|
||||
std.debug.print("{}{s}", .{data, if (i == queue.len - 1) "]" else ", " });
|
||||
node = node.?.next;
|
||||
}
|
||||
}
|
||||
|
||||
// 列印雜湊表
|
||||
pub fn printHashMap(comptime TKey: type, comptime TValue: type, map: std.AutoHashMap(TKey, TValue)) void {
|
||||
var it = map.iterator();
|
||||
while (it.next()) |kv| {
|
||||
var key = kv.key_ptr.*;
|
||||
var value = kv.value_ptr.*;
|
||||
std.debug.print("{} -> {s}\n", .{key, value});
|
||||
}
|
||||
}
|
||||
|
||||
// 列印堆積
|
||||
pub fn printHeap(comptime T: type, mem_allocator: std.mem.Allocator, queue: anytype) !void {
|
||||
var arr = queue.items;
|
||||
var len = queue.len;
|
||||
std.debug.print("堆積的陣列表示:", .{});
|
||||
printArray(T, arr[0..len]);
|
||||
std.debug.print("\n堆積的樹狀表示:\n", .{});
|
||||
var root = try TreeUtil.arrToTree(T, mem_allocator, arr[0..len]);
|
||||
try printTree(root, null, false);
|
||||
}
|
||||
|
||||
// 列印二元樹
|
||||
// This tree printer is borrowed from TECHIE DELIGHT
|
||||
// https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
const Trunk = struct {
|
||||
prev: ?*Trunk = null,
|
||||
str: []const u8 = undefined,
|
||||
|
||||
pub fn init(self: *Trunk, prev: ?*Trunk, str: []const u8) void {
|
||||
self.prev = prev;
|
||||
self.str = str;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn showTrunks(p: ?*Trunk) void {
|
||||
if (p == null) return;
|
||||
showTrunks(p.?.prev);
|
||||
std.debug.print("{s}", .{p.?.str});
|
||||
}
|
||||
|
||||
// 列印二元樹
|
||||
pub fn printTree(root: ?*TreeNode(i32), prev: ?*Trunk, isRight: bool) !void {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var prev_str = " ";
|
||||
var trunk = Trunk{.prev = prev, .str = prev_str};
|
||||
|
||||
try printTree(root.?.right, &trunk, true);
|
||||
|
||||
if (prev == null) {
|
||||
trunk.str = "———";
|
||||
} else if (isRight) {
|
||||
trunk.str = "/———";
|
||||
prev_str = " |";
|
||||
} else {
|
||||
trunk.str = "\\———";
|
||||
prev.?.str = prev_str;
|
||||
}
|
||||
|
||||
showTrunks(&trunk);
|
||||
std.debug.print(" {}\n", .{root.?.val});
|
||||
|
||||
if (prev) |_| {
|
||||
prev.?.str = prev_str;
|
||||
}
|
||||
trunk.str = " |";
|
||||
|
||||
try printTree(root.?.left, &trunk, false);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// File: TreeNode.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 二元樹節點
|
||||
pub fn TreeNode(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
val: T = undefined, // 節點值
|
||||
height: i32 = undefined, // 節點高度
|
||||
left: ?*Self = null, // 左子節點指標
|
||||
right: ?*Self = null, // 右子節點指標
|
||||
|
||||
// Initialize a tree node with specific value
|
||||
pub fn init(self: *Self, x: i32) void {
|
||||
self.val = x;
|
||||
self.height = 0;
|
||||
self.left = null;
|
||||
self.right = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 將陣列反序列化為二元樹
|
||||
pub fn arrToTree(comptime T: type, mem_allocator: std.mem.Allocator, arr: []T) !?*TreeNode(T) {
|
||||
if (arr.len == 0) return null;
|
||||
var root = try mem_allocator.create(TreeNode(T));
|
||||
root.init(arr[0]);
|
||||
const L = std.TailQueue(*TreeNode(T));
|
||||
var que = L{};
|
||||
var root_node = try mem_allocator.create(L.Node);
|
||||
root_node.data = root;
|
||||
que.append(root_node);
|
||||
var index: usize = 0;
|
||||
while (que.len > 0) {
|
||||
var que_node = que.popFirst().?;
|
||||
var node = que_node.data;
|
||||
index += 1;
|
||||
if (index >= arr.len) break;
|
||||
if (index < arr.len) {
|
||||
var tmp = try mem_allocator.create(TreeNode(T));
|
||||
tmp.init(arr[index]);
|
||||
node.left = tmp;
|
||||
var tmp_node = try mem_allocator.create(L.Node);
|
||||
tmp_node.data = node.left.?;
|
||||
que.append(tmp_node);
|
||||
}
|
||||
index += 1;
|
||||
if (index >= arr.len) break;
|
||||
if (index < arr.len) {
|
||||
var tmp = try mem_allocator.create(TreeNode(T));
|
||||
tmp.init(arr[index]);
|
||||
node.right = tmp;
|
||||
var tmp_node = try mem_allocator.create(L.Node);
|
||||
tmp_node.data = node.right.?;
|
||||
que.append(tmp_node);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// File: include.zig
|
||||
// Created Time: 2023-01-07
|
||||
// Author: codingonion (coderonion@gmail.com)
|
||||
|
||||
pub const PrintUtil = @import("PrintUtil.zig");
|
||||
pub const ListUtil = @import("ListNode.zig");
|
||||
pub const ListNode = ListUtil.ListNode;
|
||||
pub const TreeUtil = @import("TreeNode.zig");
|
||||
pub const TreeNode = TreeUtil.TreeNode;
|
||||
Reference in New Issue
Block a user