Re-translate the Japanese version (#1871)

* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
This commit is contained in:
Yudong Jin
2026-03-30 07:30:15 +08:00
committed by GitHub
parent fe6443235b
commit d7b2277d2b
1444 changed files with 83312 additions and 8363 deletions
+4
View File
@@ -0,0 +1,4 @@
zig-out
zig-cache
.zig-cache
!/.vscode/
+14
View File
@@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/zig-out/bin/${fileBasenameNoExtension}",
"args": [],
"cwd": "${workspaceFolder}",
"preLaunchTask": "build"
}
]
}
+3
View File
@@ -0,0 +1,3 @@
{
"zig.testArgs": ["build", "test", "-Dtest-filter=${filter}"]
}
+10
View File
@@ -0,0 +1,10 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "zig build",
}
]
}
+169
View File
@@ -0,0 +1,169 @@
// File: build.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
//! Zig Version: 0.14.1
//! Build Command: zig build
//! Run Command: zig build run | zig build run_*
//! Test Command: zig build test | zig build test -Dtest-filter=*
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const chapters = [_][]const u8{
"chapter_computational_complexity",
"chapter_array_and_linkedlist",
"chapter_stack_and_queue",
"chapter_hashing",
"chapter_tree",
"chapter_heap",
"chapter_searching",
"chapter_sorting",
"chapter_dynamic_programming",
};
const test_step = b.step("test", "Run unit tests");
const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{};
buildChapterExeModules(b, target, optimize, &chapters, test_step, test_filters);
buildMainExeModule(b, target, optimize);
}
fn buildChapterExeModules(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
chapter_dirs: []const []const u8,
test_step: *std.Build.Step,
test_filters: []const []const u8,
) void {
for (chapter_dirs) |chapter_dir_name| {
const chapter_dir_path = std.fs.path.join(b.allocator, &[_][]const u8{chapter_dir_name}) catch continue;
var chapter_dir = std.fs.cwd().openDir(chapter_dir_path, .{ .iterate = true }) catch continue;
defer chapter_dir.close();
var it = chapter_dir.iterate();
while (it.next() catch continue) |chapter_dir_entry| {
if (chapter_dir_entry.kind != .file or !std.mem.endsWith(u8, chapter_dir_entry.name, ".zig")) continue;
const exe_mod = buildExeModuleFromChapterDirEntry(b, target, optimize, chapter_dir_name, chapter_dir_entry) catch continue;
addTestStepToExeModule(b, test_step, exe_mod, test_filters);
}
}
}
fn buildExeModuleFromChapterDirEntry(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
chapter_dir_name: []const u8,
chapter_dir_entry: std.fs.Dir.Entry,
) !*std.Build.Module {
const zig_file_path = try std.fs.path.join(b.allocator, &[_][]const u8{ chapter_dir_name, chapter_dir_entry.name });
const zig_file_name = chapter_dir_entry.name[0 .. chapter_dir_entry.name.len - 4]; // abstract zig file name from xxx.zig
// ここでは一時的に配列と連結リストの章のみを追加し、後続の修正が終わったらすべて有効にする
const new_algo_names = [_][]const u8{
"array",
"linked_list",
"list",
"my_list",
"iteration",
"recursion",
"space_complexity",
"time_complexity",
"worst_best_time_complexity",
};
var can_run = false;
for (new_algo_names) |name| {
if (std.mem.eql(u8, zig_file_name, name)) {
can_run = true;
}
}
if (!can_run) {
return error.CanNotRunUseOldZigCodes;
}
// std.debug.print("now run zig file name = {s}\n", .{zig_file_name});
const exe_mod = b.createModule(.{
.root_source_file = b.path(zig_file_path),
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = zig_file_name,
.root_module = exe_mod,
});
const utils_mod = createUtilsModule(b, target, optimize);
exe_mod.addImport("utils", utils_mod);
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const step_name = try std.fmt.allocPrint(b.allocator, "run_{s}", .{zig_file_name});
const step_desc = try std.fmt.allocPrint(b.allocator, "Run {s}/{s}.zig", .{ chapter_dir_name, zig_file_name });
const run_step = b.step(step_name, step_desc);
run_step.dependOn(&run_cmd.step);
return exe_mod;
}
fn buildMainExeModule(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
) void {
const exe_mod = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
const utils_mod = createUtilsModule(b, target, optimize);
exe_mod.addImport("utils", utils_mod);
const exe = b.addExecutable(.{
.name = "main",
.root_module = exe_mod,
});
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", "Run all hello algo zig");
run_step.dependOn(&run_cmd.step);
}
fn createUtilsModule(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Module {
const utils_mod = b.createModule(.{
.root_source_file = b.path("utils/utils.zig"),
.target = target,
.optimize = optimize,
});
return utils_mod;
}
fn addTestStepToExeModule(b: *std.Build, test_step: *std.Build.Step, exe_mod: *std.Build.Module, test_filters: []const []const u8) void {
const exe_unit_tests = b.addTest(.{
.root_module = exe_mod,
.filters = test_filters,
});
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
test_step.dependOn(&run_exe_unit_tests.step);
}
@@ -0,0 +1,131 @@
// File: array.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
const utils = @import("utils");
// 要素へランダムアクセス
pub fn randomAccess(nums: []const i32) i32 {
// 区間 [0, nums.len) からランダムに整数を 1 つ選ぶ
const random_index = std.crypto.random.intRangeLessThan(usize, 0, nums.len);
// ランダムな要素を取得して返す
const randomNum = nums[random_index];
return randomNum;
}
// 配列長を拡張する
pub fn extend(allocator: std.mem.Allocator, nums: []const i32, enlarge: usize) ![]i32 {
// 拡張後の長さを持つ配列を初期化する
const res = try allocator.alloc(i32, nums.len + enlarge);
@memset(res, 0);
// 元の配列の全要素を新しい配列にコピー
std.mem.copyForwards(i32, res, nums);
// 拡張後の新しい配列を返す
return res;
}
// 配列の index 番目に要素 num を挿入
pub fn insert(nums: []i32, num: i32, index: usize) void {
// インデックス index 以降の全要素を 1 つ後ろへ移動する
var i = nums.len - 1;
while (i > index) : (i -= 1) {
nums[i] = nums[i - 1];
}
// index の要素に num を代入する
nums[index] = num;
}
// index の要素を削除する
pub fn remove(nums: []i32, index: usize) void {
// インデックス index より後ろの全要素を 1 つ前へ移動する
var i = index;
while (i < nums.len - 1) : (i += 1) {
nums[i] = nums[i + 1];
}
}
// 配列を走査
pub fn traverse(nums: []const i32) void {
var count: i32 = 0;
// インデックスで配列を走査
var i: usize = 0;
while (i < nums.len) : (i += 1) {
count += nums[i];
}
// 配列要素を直接走査
count = 0;
for (nums) |num| {
count += num;
}
// データのインデックスと要素を同時に走査する
for (nums, 0..) |num, index| {
count += nums[index];
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 run() !void {
// 配列を初期化
const arr = [_]i32{0} ** 5;
std.debug.print("配列 arr = {}\n", .{utils.fmt.slice(&arr)});
// 配列スライス
var array = [_]i32{ 1, 3, 2, 5, 4 };
var known_at_runtime_zero: usize = 0;
_ = &known_at_runtime_zero;
var nums = array[known_at_runtime_zero..array.len]; // 実行時変数 known_at_runtime_zero を用いてポインタをスライスに変換する
std.debug.print("配列 nums = {}\n", .{utils.fmt.slice(nums)});
// ランダムアクセス
const randomNum = randomAccess(nums);
std.debug.print("nums からランダムな要素 {} を取得\n", .{randomNum});
// メモリアロケータを初期化する
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
// 長さを拡張
nums = try extend(allocator, nums, 3);
std.debug.print("配列の長さを 8 に拡張し、nums = {} を得る\n", .{utils.fmt.slice(nums)});
// 要素を挿入する
insert(nums, 6, 3);
std.debug.print("インデックス 3 に数値 6 を挿入し、nums = {} を得る\n", .{utils.fmt.slice(nums)});
// 要素を削除
remove(nums, 2);
std.debug.print("インデックス 2 の要素を削除し、nums = {} を得る\n", .{utils.fmt.slice(nums)});
// 配列を走査
traverse(nums);
// 要素を探索する
const index = find(nums, 3);
std.debug.print("nums で要素 3 を検索し、インデックス = {} を得る\n", .{index});
std.debug.print("\n", .{});
}
pub fn main() !void {
try run();
}
test "array" {
try run();
}
@@ -0,0 +1,106 @@
// File: linked_list.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
const utils = @import("utils");
const ListNode = utils.ListNode;
// 連結リストでノード n0 の後ろにノード P を挿入する
pub fn insert(comptime T: type, n0: *ListNode(T), P: *ListNode(T)) void {
const n1 = n0.next;
P.next = n1;
n0.next = P;
}
// 連結リストでノード n0 の直後のノードを削除する
pub fn remove(comptime T: type, n0: *ListNode(T)) void {
// n0 -> P -> n1 => n0 -> n1
const P = n0.next;
const n1 = P.?.next;
n0.next = n1;
}
// 連結リスト内で index 番目のノードにアクセス
pub fn access(comptime T: type, node: *ListNode(T), index: i32) ?*ListNode(T) {
var head: ?*ListNode(T) = node;
var i: i32 = 0;
while (i < index) : (i += 1) {
if (head) |cur| {
head = cur.next;
} else {
return null;
}
}
return head;
}
// 連結リストで値が target の最初のノードを探す
pub fn find(comptime T: type, node: *ListNode(T), target: T) i32 {
var head: ?*ListNode(T) = node;
var index: i32 = 0;
while (head) |cur| {
if (cur.val == target) return index;
head = cur.next;
index += 1;
}
return -1;
}
// Driver Code
pub fn run() void {
// 各ノードを初期化
var n0 = ListNode(i32){ .val = 1 };
var n1 = ListNode(i32){ .val = 3 };
var n2 = ListNode(i32){ .val = 2 };
var n3 = ListNode(i32){ .val = 5 };
var n4 = ListNode(i32){ .val = 4 };
// ノード間の参照を構築する
n0.next = &n1;
n1.next = &n2;
n2.next = &n3;
n3.next = &n4;
std.debug.print(
"初期化後の連結リストは {}\n",
.{utils.fmt.linkedList(i32, &n0)},
);
// ノードを挿入
var tmp = ListNode(i32){ .val = 0 };
insert(i32, &n0, &tmp);
std.debug.print(
"ノード挿入後の連結リストは {}\n",
.{utils.fmt.linkedList(i32, &n0)},
);
// ノードを削除
remove(i32, &n0);
std.debug.print(
"ノード削除後の連結リストは{}\n",
.{utils.fmt.linkedList(i32, &n0)},
);
// ノードにアクセス
const node = access(i32, &n0, 3);
std.debug.print(
"連結リストのインデックス 3 にあるノードの値 = {}\n",
.{node.?.val},
);
// ノードを探索
const index = find(i32, &n0, 2);
std.debug.print(
"連結リスト内の値 2 のノードのインデックス = {}\n",
.{index},
);
std.debug.print("\n", .{});
}
pub fn main() void {
run();
}
test "linked_list" {
run();
}
@@ -0,0 +1,78 @@
// File: list.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
const utils = @import("utils");
// Driver Code
pub fn run() !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 = {}\n", .{utils.fmt.slice(nums.items)});
// 要素にアクセス
const num = nums.items[1];
std.debug.print("インデックス 1 の要素にアクセスすると、num = {}\n", .{num});
// 要素を更新
nums.items[1] = 0;
std.debug.print("インデックス 1 の要素を 0 に更新すると、nums = {}\n", .{utils.fmt.slice(nums.items)});
// リストを空にする
nums.clearRetainingCapacity();
std.debug.print("リストを空にした後 nums = {}\n", .{utils.fmt.slice(nums.items)});
// 末尾に要素を追加
try nums.append(1);
try nums.append(3);
try nums.append(2);
try nums.append(5);
try nums.append(4);
std.debug.print("要素を追加すると nums = {}\n", .{utils.fmt.slice(nums.items)});
// 中間に要素を挿入
try nums.insert(3, 6);
std.debug.print("インデックス 3 に数値 6 を挿入すると、nums = {}\n", .{utils.fmt.slice(nums.items)});
// 要素を削除
_ = nums.orderedRemove(3);
std.debug.print("インデックス 3 の要素を削除すると、nums = {}\n", .{utils.fmt.slice(nums.items)});
// インデックスでリストを走査
var count: i32 = 0;
var i: usize = 0;
while (i < nums.items.len) : (i += 1) {
count += nums.items[i];
}
// リスト要素を直接走査
count = 0;
for (nums.items) |x| {
count += x;
}
// 2 つのリストを連結する
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("リスト nums1 を nums の後ろに連結すると、nums = {}\n", .{utils.fmt.slice(nums.items)});
// リストをソート
std.mem.sort(i32, nums.items, {}, comptime std.sort.asc(i32));
std.debug.print("リストをソートすると nums = {}\n", .{utils.fmt.slice(nums.items)});
std.debug.print("\n", .{});
}
pub fn main() !void {
try run();
}
test "list" {
try run();
}
@@ -0,0 +1,233 @@
// File: my_list.zig
// Created Time: 2023-01-08
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
const utils = @import("utils");
// リストクラス
const MyList = struct {
const Self = @This();
items: []i32, // 配列(リスト要素を格納)
capacity: usize, // リスト容量
allocator: std.mem.Allocator, // メモリアロケータ
extend_ratio: usize = 2, // リスト拡張時の増加倍率
// コンストラクタ(メモリを確保してリストを初期化)
pub fn init(allocator: std.mem.Allocator) Self {
return Self{
.items = &[_]i32{},
.capacity = 0,
.allocator = allocator,
};
}
// デストラクタ(メモリを解放する)
pub fn deinit(self: Self) void {
self.allocator.free(self.allocatedSlice());
}
// 末尾に要素を追加
pub fn add(self: *Self, item: i32) !void {
// 要素数が容量を超えると、拡張機構が発動する
const newlen = self.items.len + 1;
try self.ensureTotalCapacity(newlen);
// 要素を更新
self.items.len += 1;
const new_item_ptr = &self.items[self.items.len - 1];
new_item_ptr.* = item;
}
// リストの長さを取得(現在の要素数)
pub fn getSize(self: *Self) usize {
return self.items.len;
}
// リスト容量を取得する
pub fn getCapacity(self: *Self) usize {
return self.capacity;
}
// 要素にアクセス
pub fn get(self: *Self, index: usize) i32 {
// インデックスが範囲外なら例外を送出する。以下同様
if (index < 0 or index >= self.items.len) {
@panic("インデックスが範囲外です");
}
return self.items[index];
}
// 要素を更新
pub fn set(self: *Self, index: usize, num: i32) void {
// インデックスが範囲外なら例外を送出する。以下同様
if (index < 0 or index >= self.items.len) {
@panic("インデックスが範囲外です");
}
self.items[index] = num;
}
// 中間に要素を挿入
pub fn insert(self: *Self, index: usize, item: i32) !void {
if (index < 0 or index >= self.items.len) {
@panic("インデックスが範囲外です");
}
// 要素数が容量を超えると、拡張機構が発動する
const newlen = self.items.len + 1;
try self.ensureTotalCapacity(newlen);
// index 以降の要素をすべて 1 つ後ろへずらす
self.items.len += 1;
var i = self.items.len - 1;
while (i >= index) : (i -= 1) {
self.items[i] = self.items[i - 1];
}
self.items[index] = item;
}
// 要素を削除
pub fn remove(self: *Self, index: usize) i32 {
if (index < 0 or index >= self.getSize()) {
@panic("インデックスが範囲外です");
}
// インデックス index より後の要素をすべて 1 つ前に移動する
const item = self.items[index];
var i = index;
while (i < self.items.len - 1) : (i += 1) {
self.items[i] = self.items[i + 1];
}
self.items.len -= 1;
// 削除された要素を返す
return item;
}
// リストを配列に変換する
pub fn toArraySlice(self: *Self) ![]i32 {
return self.toOwnedSlice(false);
}
// 新しいスライスを返し、リストコンテナをリセットまたはクリアするかどうかを設定する
pub fn toOwnedSlice(self: *Self, clear: bool) ![]i32 {
const allocator = self.allocator;
const old_memory = self.allocatedSlice();
if (allocator.remap(old_memory, self.items.len)) |new_items| {
if (clear) {
self.* = init(allocator);
}
return new_items;
}
const new_memory = try allocator.alloc(i32, self.items.len);
@memcpy(new_memory, self.items);
if (clear) {
self.clearAndFree();
}
return new_memory;
}
// リストの拡張
fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void {
if (self.capacity >= new_capacity) return;
const capcacity = if (self.capacity == 0) 10 else self.capacity;
const better_capacity = capcacity * self.extend_ratio;
const old_memory = self.allocatedSlice();
if (self.allocator.remap(old_memory, better_capacity)) |new_memory| {
self.items.ptr = new_memory.ptr;
self.capacity = new_memory.len;
} else {
const new_memory = try self.allocator.alloc(i32, better_capacity);
@memcpy(new_memory[0..self.items.len], self.items);
self.allocator.free(old_memory);
self.items.ptr = new_memory.ptr;
self.capacity = new_memory.len;
}
}
fn clearAndFree(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.allocatedSlice());
self.items.len = 0;
self.capacity = 0;
}
fn allocatedSlice(self: Self) []i32 {
return self.items.ptr[0..self.capacity];
}
};
// Driver Code
pub fn run() !void {
var gpa = std.heap.DebugAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// リストを初期化
var nums = MyList.init(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 = {} ,容量 = {} ,長さ = {}\n", .{
utils.fmt.slice(nums.items),
nums.getCapacity(),
nums.getSize(),
});
// 中間に要素を挿入
try nums.insert(3, 6);
std.debug.print(
"インデックス 3 に数値 6 を挿入すると、nums = {}\n",
.{utils.fmt.slice(nums.items)},
);
// 要素を削除
_ = nums.remove(3);
std.debug.print(
"インデックス 3 の要素を削除すると、nums = {}\n",
.{utils.fmt.slice(nums.items)},
);
// 要素にアクセス
const num = nums.get(1);
std.debug.print("インデックス 1 の要素にアクセスすると、num = {}\n", .{num});
// 要素を更新
nums.set(1, 0);
std.debug.print(
"インデックス 1 の要素を 0 に更新すると、nums = {}\n",
.{utils.fmt.slice(nums.items)},
);
// 拡張機構をテストする
var i: i32 = 0;
while (i < 10) : (i += 1) {
// i = 5 のとき、リスト長が容量を超えるため、この時点で拡張機構が発動する
try nums.add(i);
}
std.debug.print(
"拡張後のリスト nums = {} ,容量 = {} ,長さ = {}\n",
.{
utils.fmt.slice(nums.items),
nums.getCapacity(),
nums.getSize(),
},
);
std.debug.print("\n", .{});
}
pub fn main() !void {
try run();
}
test "my_list" {
try run();
}
@@ -0,0 +1,91 @@
// File: iteration.zig
// Created Time: 2023-09-27
// Author: QiLOL (pikaqqpika@gmail.com), CreatorMetaSky (creator_meta_sky@163.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 += @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) : (i += 1) {
res += @intCast(i);
}
return res;
}
// while ループ(2回更新)
fn whileLoopII(n: i32) i32 {
var res: i32 = 0;
var i: i32 = 1; // 条件変数を初期化する
// 1, 4, 10, ... を順に加算する
while (i <= n) : ({
// 条件変数を更新する
i += 1;
i *= 2;
}) {
res += @intCast(i);
}
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| {
const str = try std.fmt.bufPrint(&buffer, "({d}, {d}), ", .{ i, j });
try res.appendSlice(str);
}
}
return res.toOwnedSlice();
}
// Driver Code
pub fn run() !void {
var gpa = std.heap.DebugAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const n: i32 = 5;
var res: i32 = 0;
res = forLoop(n);
std.debug.print("for ループの合計結果 res = {}\n", .{res});
res = whileLoop(n);
std.debug.print("while ループの合計結果 res = {}\n", .{res});
res = whileLoopII(n);
std.debug.print("while ループ(2 回更新)の合計結果 res = {}\n", .{res});
const resStr = try nestedForLoop(allocator, n);
std.debug.print("二重 for ループの走査結果 {s}\n", .{resStr});
allocator.free(resStr);
std.debug.print("\n", .{});
}
pub fn main() !void {
try run();
}
test "interation" {
try run();
}
@@ -0,0 +1,88 @@
// File: recursion.zig
// Created Time: 2023-09-27
// Author: QiLOL (pikaqqpika@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
// 再帰関数
fn recur(n: i32) i32 {
// 終了条件
if (n == 1) {
return 1;
}
// 再帰:再帰呼び出し
const res = 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) を再帰的に呼び出す
const res: i32 = fib(n - 1) + fib(n - 2);
// 結果 f(n) を返す
return res;
}
// Driver Code
pub fn run() void {
const n: i32 = 5;
var res: i32 = 0;
res = recur(n);
std.debug.print("再帰関数の合計結果 res = {}\n", .{recur(n)});
res = forLoopRecur(n);
std.debug.print("反復で再帰をシミュレートした合計結果 res = {}\n", .{forLoopRecur(n)});
res = tailRecur(n, 0);
std.debug.print("末尾再帰関数の合計結果 res = {}\n", .{tailRecur(n, 0)});
res = fib(n);
std.debug.print("フィボナッチ数列の第 {} 項は {}\n", .{ n, fib(n) });
std.debug.print("\n", .{});
}
pub fn main() void {
run();
}
test "recursion" {
run();
}
@@ -0,0 +1,142 @@
// File: space_complexity.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
const utils = @import("utils");
const ListNode = utils.ListNode;
const TreeNode = utils.TreeNode;
// 関数
fn function() i32 {
// 何らかの処理を行う
return 0;
}
// 定数階
fn constant(n: i32) void {
// 定数、変数、オブジェクトは O(1) の空間を占める
const a: i32 = 0;
const b: i32 = 0;
const nums = [_]i32{0} ** 10000;
const node = ListNode(i32){ .val = 0 };
var i: i32 = 0;
// ループ内の変数は O(1) の空間を占める
while (i < n) : (i += 1) {
const 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) の空間を使用
const 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;
const nums = [_]i32{0} ** n;
std.debug.print("再帰 n = {} における nums の長さ = {}\n", .{ n, nums.len });
return quadraticRecur(n - 1);
}
// 指数時間(完全二分木の構築)
fn buildTree(allocator: std.mem.Allocator, n: i32) !?*TreeNode(i32) {
if (n == 0) return null;
const root = try allocator.create(TreeNode(i32));
root.init(0);
root.left = try buildTree(allocator, n - 1);
root.right = try buildTree(allocator, n - 1);
return root;
}
// 木のメモリを解放する
fn freeTree(allocator: std.mem.Allocator, root: ?*const TreeNode(i32)) void {
if (root == null) return;
freeTree(allocator, root.?.left);
freeTree(allocator, root.?.right);
allocator.destroy(root.?);
}
// Driver Code
pub fn run() !void {
var gpa = std.heap.DebugAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const n: i32 = 5;
// 定数階
constant(n);
// 線形階
try linear(n);
linearRecur(n);
// 二乗階
try quadratic(n);
_ = quadraticRecur(n);
// 指数オーダー
const root = try buildTree(allocator, n);
defer freeTree(allocator, root);
std.debug.print("{}\n", .{utils.fmt.tree(i32, root)});
std.debug.print("\n", .{});
}
pub fn main() !void {
try run();
}
test "space_complexity" {
try run();
}
@@ -0,0 +1,184 @@
// File: time_complexity.zig
// Created Time: 2022-12-28
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.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] を交換
const 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;
// 細胞は各ラウンドで 2 つに分裂し、数列 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: i32 = n;
while (n_var > 1) : (n_var = @divTrunc(n_var, 2)) {
count += 1;
}
return count;
}
// 対数時間(再帰実装)
fn logRecur(n: i32) i32 {
if (n <= 1) return 0;
return logRecur(@divTrunc(n, 2)) + 1;
}
// 線形対数時間
fn linearLogRecur(n: i32) i32 {
if (n <= 1) return 1;
var count: i32 = linearLogRecur(@divTrunc(n, 2)) + linearLogRecur(@divTrunc(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 run() 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});
std.debug.print("\n", .{});
}
pub fn main() !void {
run();
}
test "time_complexity" {
run();
}
@@ -0,0 +1,53 @@
// File: worst_best_time_complexity.zig
// Created Time: 2022-12-28
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
const utils = @import("utils");
// 要素が { 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 run() void {
var i: i32 = 0;
while (i < 10) : (i += 1) {
const n: usize = 100;
var nums = randomNumbers(n);
const index = findOne(&nums);
std.debug.print("配列 [ 1, 2, ..., n ] をシャッフルした後 = ", .{});
std.debug.print("{}\n", .{utils.fmt.slice(nums)});
std.debug.print("数字 1 のインデックスは {}\n", .{index});
}
std.debug.print("\n", .{});
}
pub fn main() !void {
run();
}
test "worst_best_time_complexity" {
run();
}
@@ -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);
}
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
if (s[i - 1] == t[j - 1]) {
return editDistanceDFS(s, t, i - 1, j - 1);
}
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 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];
}
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
if (s[i - 1] == t[j - 1]) {
return editDistanceDFSMem(s, t, mem, i - 1, j - 1);
}
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 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]) {
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
dp[i][j] = dp[i - 1][j - 1];
} else {
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 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]) {
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
dp[j] = leftup;
} else {
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 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];
// 2つの案のうち価値が大きいほうを返す
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];
// 2 つの案のうち価値が大きい方を記録して返す
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("\n10583 を削除した後、ハッシュテーブルは\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();
}
+54
View File
@@ -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("\n10583 を削除した後、ハッシュテーブルは\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();
}
+80
View File
@@ -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();
}
+186
View File
@@ -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;
// 2 つのノードを交換
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;
// 2 つのノードを交換
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");
// 方法 1:総当たり列挙
pub fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 {
var size: usize = nums.len;
var i: usize = 0;
// 2重ループのため、時間計算量は 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;
}
// 方法 2:補助ハッシュテーブル
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 ======
// 方法 1
var res = twoSumBruteForce(&nums, target).?;
std.debug.print("方法1 res = ", .{});
inc.PrintUtil.printArray(i32, &res);
// 方法 2
res = (try twoSumHashTable(&nums, target)).?;
std.debug.print("\n方法2 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] を 1 つ右へ移動する
}
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 - left) / 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();
}
+162
View File
@@ -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); // この 2 つの要素を交換
}
swap(nums, i, left); // 基準値を 2 つの部分配列の境界へ交換する
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;
}
// 3つの候補要素の中央値を選ぶ
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;
}
// 番兵による分割処理(3 点中央値)
pub fn partition(nums: []i32, left: usize, right: usize) usize {
// 3つの候補要素の中央値を選ぶ
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); // この 2 つの要素を交換
}
swap(nums, i, left); // 基準値を 2 つの部分配列の境界へ交換する
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); // この 2 つの要素を交換
}
swap(nums, i, left); // 基準値を 2 つの部分配列の境界へ交換する
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);
// 2 つの部分配列のうち短いほうにクイックソートを適用する
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 {
// ここで高コストな累乗計算を繰り返さないよう、k ではなく exp を渡す
return @mod(@divFloor(num, exp), 10);
}
// 計数ソート(nums の k 桁目でソート)
fn countingSortDigit(nums: []i32, exp: i32) !void {
// 10 進数の各桁は 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();
// 先頭ポインタを1つ後ろへ進め、末尾を越えたら配列先頭に戻す
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();
}
+249
View File
@@ -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 - i) / 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();
}
+39
View File
@@ -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();
}
+42
View File
@@ -0,0 +1,42 @@
// 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 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);
}
+7
View File
@@ -0,0 +1,7 @@
// File: include.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com)
pub const PrintUtil = @import("PrintUtil.zig");
pub const TreeUtil = @import("TreeNode.zig");
pub const TreeNode = TreeUtil.TreeNode;
+25
View File
@@ -0,0 +1,25 @@
const std = @import("std");
const iteration = @import("chapter_computational_complexity/iteration.zig");
const recursion = @import("chapter_computational_complexity/recursion.zig");
const time_complexity = @import("chapter_computational_complexity/time_complexity.zig");
const space_complexity = @import("chapter_computational_complexity/space_complexity.zig");
const worst_best_time_complexity = @import("chapter_computational_complexity/worst_best_time_complexity.zig");
const array = @import("chapter_array_and_linkedlist/array.zig");
const linked_list = @import("chapter_array_and_linkedlist/linked_list.zig");
const list = @import("chapter_array_and_linkedlist/list.zig");
const my_list = @import("chapter_array_and_linkedlist/my_list.zig");
pub fn main() !void {
try iteration.run();
recursion.run();
time_complexity.run();
try space_complexity.run();
worst_best_time_complexity.run();
try array.run();
linked_list.run();
try list.run();
try my_list.run();
}
+49
View File
@@ -0,0 +1,49 @@
// File: ListNode.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.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, allocator: std.mem.Allocator, list: std.ArrayList(T)) !?*ListNode(T) {
var dum = try allocator.create(ListNode(T));
dum.init(0);
var head = dum;
for (list.items) |val| {
var tmp = try 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;
}
+63
View File
@@ -0,0 +1,63 @@
// File: TreeNode.zig
// Created Time: 2023-01-07
// Author: codingonion (coderonion@gmail.com), CreatorMetaSky (creator_meta_sky@163.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, allocator: std.mem.Allocator, arr: []T) !?*TreeNode(T) {
if (arr.len == 0) return null;
var root = try allocator.create(TreeNode(T));
root.init(arr[0]);
const L = std.TailQueue(*TreeNode(T));
var que = L{};
var root_node = try allocator.create(L.Node);
root_node.data = root;
que.append(root_node);
var index: usize = 0;
while (que.len > 0) {
const que_node = que.popFirst().?;
var node = que_node.data;
index += 1;
if (index >= arr.len) break;
if (index < arr.len) {
var tmp = try allocator.create(TreeNode(T));
tmp.init(arr[index]);
node.left = tmp;
var tmp_node = try 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 allocator.create(TreeNode(T));
tmp.init(arr[index]);
node.right = tmp;
var tmp_node = try allocator.create(L.Node);
tmp_node.data = node.right.?;
que.append(tmp_node);
}
}
return root;
}
+140
View File
@@ -0,0 +1,140 @@
// File: format.zig
// Created Time: 2025-07-19
// Author: CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
const ListNode = @import("ListNode.zig").ListNode;
const TreeNode = @import("TreeNode.zig").TreeNode;
pub fn slice(items: anytype) SliceFormatter(@TypeOf(items)) {
return .{ .items = items };
}
pub fn SliceFormatter(comptime SliceType: type) type {
return struct {
const Self = @This();
items: SliceType,
pub fn format(
self: Self,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
try writer.writeAll("[");
if (self.items.len > 0) {
for (self.items, 0..) |item, i| {
try std.fmt.format(writer, "{}", .{item});
if (i != self.items.len - 1) {
try writer.writeAll(", ");
}
}
}
try writer.writeAll("]");
}
};
}
pub fn linkedList(comptime T: type, head: *const ListNode(T)) LinkedListFormatter(T) {
return .{ .head = head };
}
pub fn LinkedListFormatter(comptime T: type) type {
return struct {
const Self = @This();
head: *const ListNode(T),
pub fn format(
self: Self,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
try printLinkedList(self.head, writer);
}
pub fn printLinkedList(head: *const ListNode(T), writer: anytype) !void {
try std.fmt.format(writer, "{}", .{head.val});
if (head.next) |next_node| {
try writer.writeAll("->");
try printLinkedList(next_node, writer);
}
}
};
}
pub fn tree(comptime T: type, root: ?*const TreeNode(T)) TreeFormatter(T) {
return .{ .root = root };
}
pub fn TreeFormatter(comptime T: type) type {
return struct {
const Self = @This();
root: ?*const TreeNode(T),
pub fn format(
self: Self,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
try printTree(self.root, null, false, writer);
}
// 二分木を出力
fn printTree(root: ?*const TreeNode(T), prev: ?*Trunk, isRight: bool, writer: anytype) !void {
if (root == null) {
return;
}
var prev_str = " ";
var trunk = Trunk{ .prev = prev, .str = prev_str };
try printTree(root.?.right, &trunk, true, writer);
if (prev == null) {
trunk.str = "———";
} else if (isRight) {
trunk.str = "/———";
prev_str = " |";
} else {
trunk.str = "\\———";
prev.?.str = prev_str;
}
try showTrunks(&trunk, writer);
try std.fmt.format(writer, "{d}\n", .{root.?.val});
if (prev) |_| {
prev.?.str = prev_str;
}
trunk.str = " |";
try printTree(root.?.left, &trunk, false, writer);
}
// 二分木を出力
// 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, writer: anytype) !void {
if (p == null) return;
try showTrunks(p.?.prev, writer);
try std.fmt.format(writer, "{s}", .{p.?.str});
}
};
}
+8
View File
@@ -0,0 +1,8 @@
// File: format.zig
// Created Time: 2025-07-15
// Author: CreatorMetaSky (creator_meta_sky@163.com)
const std = @import("std");
pub const fmt = @import("format.zig");
pub const ListNode = @import("ListNode.zig").ListNode;
pub const TreeNode = @import("TreeNode.zig").TreeNode;