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
+2
View File
@@ -0,0 +1,2 @@
target/
Cargo.lock
+413
View File
@@ -0,0 +1,413 @@
[package]
name = "hello-algo-rust"
version = "0.1.0"
edition = "2021"
publish = false
# Run Command: cargo run --bin time_complexity
[[bin]]
name = "time_complexity"
path = "chapter_computational_complexity/time_complexity.rs"
# Run Command: cargo run --bin worst_best_time_complexity
[[bin]]
name = "worst_best_time_complexity"
path = "chapter_computational_complexity/worst_best_time_complexity.rs"
# Run Command: cargo run --bin space_complexity
[[bin]]
name = "space_complexity"
path = "chapter_computational_complexity/space_complexity.rs"
# Run Command: cargo run --bin iteration
[[bin]]
name = "iteration"
path = "chapter_computational_complexity/iteration.rs"
# Run Command: cargo run --bin recursion
[[bin]]
name = "recursion"
path = "chapter_computational_complexity/recursion.rs"
# Run Command: cargo run --bin two_sum
[[bin]]
name = "two_sum"
path = "chapter_searching/two_sum.rs"
# Run Command: cargo run --bin array
[[bin]]
name = "array"
path = "chapter_array_and_linkedlist/array.rs"
# Run Command: cargo run --bin linked_list
[[bin]]
name = "linked_list"
path = "chapter_array_and_linkedlist/linked_list.rs"
# Run Command: cargo run --bin list
[[bin]]
name = "list"
path = "chapter_array_and_linkedlist/list.rs"
# Run Command: cargo run --bin my_list
[[bin]]
name = "my_list"
path = "chapter_array_and_linkedlist/my_list.rs"
# Run Command: cargo run --bin stack
[[bin]]
name = "stack"
path = "chapter_stack_and_queue/stack.rs"
# Run Command: cargo run --bin linkedlist_stack
[[bin]]
name = "linkedlist_stack"
path = "chapter_stack_and_queue/linkedlist_stack.rs"
# Run Command: cargo run --bin queue
[[bin]]
name = "queue"
path = "chapter_stack_and_queue/queue.rs"
# Run Command: cargo run --bin linkedlist_queue
[[bin]]
name = "linkedlist_queue"
path = "chapter_stack_and_queue/linkedlist_queue.rs"
# Run Command: cargo run --bin deque
[[bin]]
name = "deque"
path = "chapter_stack_and_queue/deque.rs"
# Run Command: cargo run --bin array_deque
[[bin]]
name = "array_deque"
path = "chapter_stack_and_queue/array_deque.rs"
# Run Command: cargo run --bin linkedlist_deque
[[bin]]
name = "linkedlist_deque"
path = "chapter_stack_and_queue/linkedlist_deque.rs"
# Run Command: cargo run --bin simple_hash
[[bin]]
name = "simple_hash"
path = "chapter_hashing/simple_hash.rs"
# Run Command: cargo run --bin hash_map
[[bin]]
name = "hash_map"
path = "chapter_hashing/hash_map.rs"
# Run Command: cargo run --bin array_hash_map
[[bin]]
name = "array_hash_map"
path = "chapter_hashing/array_hash_map.rs"
# Run Command: cargo run --bin build_in_hash
[[bin]]
name = "build_in_hash"
path = "chapter_hashing/build_in_hash.rs"
# Run Command: cargo run --bin hash_map_chaining
[[bin]]
name = "hash_map_chaining"
path = "chapter_hashing/hash_map_chaining.rs"
# Run Command: cargo run --bin hash_map_open_addressing
[[bin]]
name = "hash_map_open_addressing"
path = "chapter_hashing/hash_map_open_addressing.rs"
# Run Command: cargo run --bin binary_search
[[bin]]
name = "binary_search"
path = "chapter_searching/binary_search.rs"
# Run Command: cargo run --bin binary_search_edge
[[bin]]
name = "binary_search_edge"
path = "chapter_searching/binary_search_edge.rs"
# Run Command: cargo run --bin binary_search_insertion
[[bin]]
name = "binary_search_insertion"
path = "chapter_searching/binary_search_insertion.rs"
# Run Command: cargo run --bin bubble_sort
[[bin]]
name = "bubble_sort"
path = "chapter_sorting/bubble_sort.rs"
# Run Command: cargo run --bin insertion_sort
[[bin]]
name = "insertion_sort"
path = "chapter_sorting/insertion_sort.rs"
# Run Command: cargo run --bin quick_sort
[[bin]]
name = "quick_sort"
path = "chapter_sorting/quick_sort.rs"
# Run Command: cargo run --bin merge_sort
[[bin]]
name = "merge_sort"
path = "chapter_sorting/merge_sort.rs"
# Run Command: cargo run --bin selection_sort
[[bin]]
name = "selection_sort"
path = "chapter_sorting/selection_sort.rs"
# Run Command: cargo run --bin bucket_sort
[[bin]]
name = "bucket_sort"
path = "chapter_sorting/bucket_sort.rs"
# Run Command: cargo run --bin heap_sort
[[bin]]
name = "heap_sort"
path = "chapter_sorting/heap_sort.rs"
# Run Command: cargo run --bin counting_sort
[[bin]]
name = "counting_sort"
path = "chapter_sorting/counting_sort.rs"
# Run Command: cargo run --bin radix_sort
[[bin]]
name = "radix_sort"
path = "chapter_sorting/radix_sort.rs"
# Run Command: cargo run --bin array_stack
[[bin]]
name = "array_stack"
path = "chapter_stack_and_queue/array_stack.rs"
# Run Command: cargo run --bin array_queue
[[bin]]
name = "array_queue"
path = "chapter_stack_and_queue/array_queue.rs"
# Run Command: cargo run --bin array_binary_tree
[[bin]]
name = "array_binary_tree"
path = "chapter_tree/array_binary_tree.rs"
# Run Command: cargo run --bin avl_tree
[[bin]]
name = "avl_tree"
path = "chapter_tree/avl_tree.rs"
# Run Command: cargo run --bin binary_search_tree
[[bin]]
name = "binary_search_tree"
path = "chapter_tree/binary_search_tree.rs"
# Run Command: cargo run --bin binary_tree_bfs
[[bin]]
name = "binary_tree_bfs"
path = "chapter_tree/binary_tree_bfs.rs"
# Run Command: cargo run --bin binary_tree_dfs
[[bin]]
name = "binary_tree_dfs"
path = "chapter_tree/binary_tree_dfs.rs"
# Run Command: cargo run --bin binary_tree
[[bin]]
name = "binary_tree"
path = "chapter_tree/binary_tree.rs"
# Run Command: cargo run --bin heap
[[bin]]
name = "heap"
path = "chapter_heap/heap.rs"
# Run Command: cargo run --bin my_heap
[[bin]]
name = "my_heap"
path = "chapter_heap/my_heap.rs"
# Run Command: cargo run --bin top_k
[[bin]]
name = "top_k"
path = "chapter_heap/top_k.rs"
# Run Command: cargo run --bin graph_adjacency_list
[[bin]]
name = "graph_adjacency_list"
path = "chapter_graph/graph_adjacency_list.rs"
# Run Command: cargo run --bin graph_adjacency_matrix
[[bin]]
name = "graph_adjacency_matrix"
path = "chapter_graph/graph_adjacency_matrix.rs"
# Run Command: cargo run --bin graph_bfs
[[bin]]
name = "graph_bfs"
path = "chapter_graph/graph_bfs.rs"
# Run Command: cargo run --bin graph_dfs
[[bin]]
name = "graph_dfs"
path = "chapter_graph/graph_dfs.rs"
# Run Command: cargo run --bin linear_search
[[bin]]
name = "linear_search"
path = "chapter_searching/linear_search.rs"
# Run Command: cargo run --bin hashing_search
[[bin]]
name = "hashing_search"
path = "chapter_searching/hashing_search.rs"
# Run Command: cargo run --bin climbing_stairs_dfs
[[bin]]
name = "climbing_stairs_dfs"
path = "chapter_dynamic_programming/climbing_stairs_dfs.rs"
# Run Command: cargo run --bin climbing_stairs_dfs_mem
[[bin]]
name = "climbing_stairs_dfs_mem"
path = "chapter_dynamic_programming/climbing_stairs_dfs_mem.rs"
# Run Command: cargo run --bin climbing_stairs_dp
[[bin]]
name = "climbing_stairs_dp"
path = "chapter_dynamic_programming/climbing_stairs_dp.rs"
# Run Command: cargo run --bin min_cost_climbing_stairs_dp
[[bin]]
name = "min_cost_climbing_stairs_dp"
path = "chapter_dynamic_programming/min_cost_climbing_stairs_dp.rs"
# Run Command: cargo run --bin climbing_stairs_constraint_dp
[[bin]]
name = "climbing_stairs_constraint_dp"
path = "chapter_dynamic_programming/climbing_stairs_constraint_dp.rs"
# Run Command: cargo run --bin climbing_stairs_backtrack
[[bin]]
name = "climbing_stairs_backtrack"
path = "chapter_dynamic_programming/climbing_stairs_backtrack.rs"
# Run Command: cargo run --bin subset_sum_i_naive
[[bin]]
name = "subset_sum_i_naive"
path = "chapter_backtracking/subset_sum_i_naive.rs"
# Run Command: cargo run --bin subset_sum_i
[[bin]]
name = "subset_sum_i"
path = "chapter_backtracking/subset_sum_i.rs"
# Run Command: cargo run --bin subset_sum_ii
[[bin]]
name = "subset_sum_ii"
path = "chapter_backtracking/subset_sum_ii.rs"
# Run Command: cargo run --bin coin_change
[[bin]]
name = "coin_change"
path = "chapter_dynamic_programming/coin_change.rs"
# Run Command: cargo run --bin coin_change_ii
[[bin]]
name = "coin_change_ii"
path = "chapter_dynamic_programming/coin_change_ii.rs"
# Run Command: cargo run --bin unbounded_knapsack
[[bin]]
name = "unbounded_knapsack"
path = "chapter_dynamic_programming/unbounded_knapsack.rs"
# Run Command: cargo run --bin knapsack
[[bin]]
name = "knapsack"
path = "chapter_dynamic_programming/knapsack.rs"
# Run Command: cargo run --bin min_path_sum
[[bin]]
name = "min_path_sum"
path = "chapter_dynamic_programming/min_path_sum.rs"
# Run Command: cargo run --bin edit_distance
[[bin]]
name = "edit_distance"
path = "chapter_dynamic_programming/edit_distance.rs"
# Run Command: cargo run --bin n_queens
[[bin]]
name = "n_queens"
path = "chapter_backtracking/n_queens.rs"
# Run Command: cargo run --bin permutations_i
[[bin]]
name = "permutations_i"
path = "chapter_backtracking/permutations_i.rs"
# Run Command: cargo run --bin permutations_ii
[[bin]]
name = "permutations_ii"
path = "chapter_backtracking/permutations_ii.rs"
# Run Command: cargo run --bin preorder_traversal_i_compact
[[bin]]
name = "preorder_traversal_i_compact"
path = "chapter_backtracking/preorder_traversal_i_compact.rs"
# Run Command: cargo run --bin preorder_traversal_ii_compact
[[bin]]
name = "preorder_traversal_ii_compact"
path = "chapter_backtracking/preorder_traversal_ii_compact.rs"
# Run Command: cargo run --bin preorder_traversal_iii_compact
[[bin]]
name = "preorder_traversal_iii_compact"
path = "chapter_backtracking/preorder_traversal_iii_compact.rs"
# Run Command: cargo run --bin preorder_traversal_iii_template
[[bin]]
name = "preorder_traversal_iii_template"
path = "chapter_backtracking/preorder_traversal_iii_template.rs"
# Run Command: cargo run --bin binary_search_recur
[[bin]]
name = "binary_search_recur"
path = "chapter_divide_and_conquer/binary_search_recur.rs"
# Run Command: cargo run --bin hanota
[[bin]]
name = "hanota"
path = "chapter_divide_and_conquer/hanota.rs"
# Run Command: cargo run --bin build_tree
[[bin]]
name = "build_tree"
path = "chapter_divide_and_conquer/build_tree.rs"
# Run Command: cargo run --bin coin_change_greedy
[[bin]]
name = "coin_change_greedy"
path = "chapter_greedy/coin_change_greedy.rs"
# Run Command: cargo run --bin fractional_knapsack
[[bin]]
name = "fractional_knapsack"
path = "chapter_greedy/fractional_knapsack.rs"
# Run Command: cargo run --bin max_capacity
[[bin]]
name = "max_capacity"
path = "chapter_greedy/max_capacity.rs"
# Run Command: cargo run --bin max_product_cutting
[[bin]]
name = "max_product_cutting"
path = "chapter_greedy/max_product_cutting.rs"
[dependencies]
rand = "0.8.5"
@@ -0,0 +1,111 @@
/*
* File: array.rs
* Created Time: 2023-01-15
* Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
use rand::Rng;
/* 要素へランダムアクセス */
fn random_access(nums: &[i32]) -> i32 {
// 区間 [0, nums.len()) からランダムに数字を 1 つ選ぶ
let random_index = rand::thread_rng().gen_range(0..nums.len());
// ランダムな要素を取得して返す
let random_num = nums[random_index];
random_num
}
/* 配列長を拡張する */
fn extend(nums: &[i32], enlarge: usize) -> Vec<i32> {
// 拡張後の長さを持つ配列を初期化する
let mut res: Vec<i32> = vec![0; nums.len() + enlarge];
// 元の配列の全要素を新しい配列にコピー
res[0..nums.len()].copy_from_slice(nums);
// 拡張後の新しい配列を返す
res
}
/* 配列の index 番目に要素 num を挿入 */
fn insert(nums: &mut [i32], num: i32, index: usize) {
// インデックス index 以降の全要素を 1 つ後ろへ移動する
for i in (index + 1..nums.len()).rev() {
nums[i] = nums[i - 1];
}
// index の要素に num を代入する
nums[index] = num;
}
/* index の要素を削除する */
fn remove(nums: &mut [i32], index: usize) {
// インデックス index より後ろの全要素を 1 つ前へ移動する
for i in index..nums.len() - 1 {
nums[i] = nums[i + 1];
}
}
/* 配列を走査 */
fn traverse(nums: &[i32]) {
let mut _count = 0;
// インデックスで配列を走査
for i in 0..nums.len() {
_count += nums[i];
}
// 配列要素を直接走査
_count = 0;
for &num in nums {
_count += num;
}
}
/* 配列内で指定要素を探す */
fn find(nums: &[i32], target: i32) -> Option<usize> {
for i in 0..nums.len() {
if nums[i] == target {
return Some(i);
}
}
None
}
/* Driver Code */
fn main() {
/* 配列を初期化 */
let arr: [i32; 5] = [0; 5];
print!("配列 arr = ");
print_util::print_array(&arr);
// Rust では、長さを指定する場合([i32; 5])は配列、指定しない場合(&[i32])はスライスである
// Rust の配列はコンパイル時に長さが確定するよう設計されているため、長さには定数しか使えない
// Vector は Rust で通常動的配列として使われる型である
// extend() メソッドを実装しやすくするため、以下では vector を配列(array)として扱う
let nums: Vec<i32> = vec![1, 3, 2, 5, 4];
print!("\n配列 nums = ");
print_util::print_array(&nums);
// ランダムアクセス
let random_num = random_access(&nums);
println!("\nnums からランダムな要素 {} を取得", random_num);
// 長さを拡張
let mut nums: Vec<i32> = extend(&nums, 3);
print!("配列の長さを 8 に拡張すると、nums = ");
print_util::print_array(&nums);
// 要素を挿入する
insert(&mut nums, 6, 3);
print!("\nインデックス 3 に数値 6 を挿入すると、nums = ");
print_util::print_array(&nums);
// 要素を削除
remove(&mut nums, 2);
print!("\nインデックス 2 の要素を削除すると、nums = ");
print_util::print_array(&nums);
// 配列を走査
traverse(&nums);
// 要素を探索する
let index = find(&nums, 3).unwrap();
println!("\nnums 内で要素 3 を検索すると、インデックス = {}", index);
}
@@ -0,0 +1,100 @@
/*
* File: linked_list.rs
* Created Time: 2023-03-05
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, ListNode};
use std::cell::RefCell;
use std::rc::Rc;
/* 連結リストでノード n0 の後ろにノード P を挿入する */
#[allow(non_snake_case)]
pub fn insert<T>(n0: &Rc<RefCell<ListNode<T>>>, P: Rc<RefCell<ListNode<T>>>) {
let n1 = n0.borrow_mut().next.take();
P.borrow_mut().next = n1;
n0.borrow_mut().next = Some(P);
}
/* 連結リストでノード n0 の直後のノードを削除する */
#[allow(non_snake_case)]
pub fn remove<T>(n0: &Rc<RefCell<ListNode<T>>>) {
// n0 -> P -> n1
let P = n0.borrow_mut().next.take();
if let Some(node) = P {
let n1 = node.borrow_mut().next.take();
n0.borrow_mut().next = n1;
}
}
/* 連結リスト内で index 番目のノードにアクセス */
pub fn access<T>(head: Rc<RefCell<ListNode<T>>>, index: i32) -> Option<Rc<RefCell<ListNode<T>>>> {
fn dfs<T>(
head: Option<&Rc<RefCell<ListNode<T>>>>,
index: i32,
) -> Option<Rc<RefCell<ListNode<T>>>> {
if index <= 0 {
return head.cloned();
}
if let Some(node) = head {
dfs(node.borrow().next.as_ref(), index - 1)
} else {
None
}
}
dfs(Some(head).as_ref(), index)
}
/* 連結リストで値が target の最初のノードを探す */
pub fn find<T: PartialEq>(head: Rc<RefCell<ListNode<T>>>, target: T) -> i32 {
fn find<T: PartialEq>(head: Option<&Rc<RefCell<ListNode<T>>>>, target: T, idx: i32) -> i32 {
if let Some(node) = head {
if node.borrow().val == target {
return idx;
}
return find(node.borrow().next.as_ref(), target, idx + 1);
} else {
-1
}
}
find(Some(head).as_ref(), target, 0)
}
/* Driver Code */
fn main() {
/* 連結リストを初期化 */
// 各ノードを初期化
let n0 = ListNode::new(1);
let n1 = ListNode::new(3);
let n2 = ListNode::new(2);
let n3 = ListNode::new(5);
let n4 = ListNode::new(4);
// ノード間の参照を構築する
n0.borrow_mut().next = Some(n1.clone());
n1.borrow_mut().next = Some(n2.clone());
n2.borrow_mut().next = Some(n3.clone());
n3.borrow_mut().next = Some(n4.clone());
print!("初期化された連結リストは ");
print_util::print_linked_list(&n0);
/* ノードを挿入 */
insert(&n0, ListNode::new(0));
print!("ノード挿入後の連結リストは ");
print_util::print_linked_list(&n0);
/* ノードを削除 */
remove(&n0);
print!("ノード削除後の連結リストは ");
print_util::print_linked_list(&n0);
/* ノードにアクセス */
let node = access(n0.clone(), 3);
println!("連結リストのインデックス 3 にあるノードの値 = {}", node.unwrap().borrow().val);
/* ノードを探索 */
let index = find(n0.clone(), 2);
println!("連結リスト内で値が 2 のノードのインデックス = {}", index);
}
@@ -0,0 +1,71 @@
/*
* File: list.rs
* Created Time: 2023-01-18
* Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
/* Driver Code */
fn main() {
// リストを初期化
let mut nums: Vec<i32> = vec![1, 3, 2, 5, 4];
print!("リスト nums = ");
print_util::print_array(&nums);
// 要素にアクセス
let num = nums[1];
println!("\nインデックス 1 の要素にアクセスすると、num = {num}");
// 要素を更新
nums[1] = 0;
print!("インデックス 1 の要素を 0 に更新すると、nums = ");
print_util::print_array(&nums);
// リストを空にする
nums.clear();
print!("\nリストを空にした後、nums = ");
print_util::print_array(&nums);
// 末尾に要素を追加
nums.push(1);
nums.push(3);
nums.push(2);
nums.push(5);
nums.push(4);
print!("\n要素を追加した後、nums = ");
print_util::print_array(&nums);
// 中間に要素を挿入
nums.insert(3, 6);
print!("\nインデックス 3 に数値 6 を挿入すると、nums = ");
print_util::print_array(&nums);
// 要素を削除
nums.remove(3);
print!("\nインデックス 3 の要素を削除すると、nums = ");
print_util::print_array(&nums);
// インデックスでリストを走査
let mut _count = 0;
for i in 0..nums.len() {
_count += nums[i];
}
// リスト要素を直接走査
_count = 0;
for x in &nums {
_count += x;
}
// 2 つのリストを連結する
let mut nums1 = vec![6, 8, 7, 10, 9];
nums.append(&mut nums1); // appendmove)の後では nums1 は空になる!
// nums.extend(&nums1); // extend(借用)の後も nums1 は引き続き使える
print!("\nリスト nums1 を nums の後ろに連結すると、nums = ");
print_util::print_array(&nums);
// リストをソート
nums.sort();
print!("\nリストをソートした後、nums = ");
print_util::print_array(&nums);
}
@@ -0,0 +1,164 @@
/*
* File: my_list.rs
* Created Time: 2023-03-11
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
/* リストクラス */
#[allow(dead_code)]
struct MyList {
arr: Vec<i32>, // 配列(リスト要素を格納)
capacity: usize, // リスト容量
size: usize, // リストの長さ(現在の要素数)
extend_ratio: usize, // リスト拡張時の増加倍率
}
#[allow(unused, unused_comparisons)]
impl MyList {
/* コンストラクタ */
pub fn new(capacity: usize) -> Self {
let mut vec = vec![0; capacity];
Self {
arr: vec,
capacity,
size: 0,
extend_ratio: 2,
}
}
/* リストの長さを取得(現在の要素数) */
pub fn size(&self) -> usize {
return self.size;
}
/* リスト容量を取得する */
pub fn capacity(&self) -> usize {
return self.capacity;
}
/* 要素にアクセス */
pub fn get(&self, index: usize) -> i32 {
// インデックスが範囲外なら例外を送出する。以下同様
if index >= self.size {
panic!("インデックスが範囲外です")
};
return self.arr[index];
}
/* 要素を更新 */
pub fn set(&mut self, index: usize, num: i32) {
if index >= self.size {
panic!("インデックスが範囲外です")
};
self.arr[index] = num;
}
/* 末尾に要素を追加 */
pub fn add(&mut self, num: i32) {
// 要素数が容量を超えると、拡張機構が発動する
if self.size == self.capacity() {
self.extend_capacity();
}
self.arr[self.size] = num;
// 要素数を更新
self.size += 1;
}
/* 中間に要素を挿入 */
pub fn insert(&mut self, index: usize, num: i32) {
if index >= self.size() {
panic!("インデックスが範囲外です")
};
// 要素数が容量を超えると、拡張機構が発動する
if self.size == self.capacity() {
self.extend_capacity();
}
// index 以降の要素をすべて 1 つ後ろへずらす
for j in (index..self.size).rev() {
self.arr[j + 1] = self.arr[j];
}
self.arr[index] = num;
// 要素数を更新
self.size += 1;
}
/* 要素を削除 */
pub fn remove(&mut self, index: usize) -> i32 {
if index >= self.size() {
panic!("インデックスが範囲外です")
};
let num = self.arr[index];
// インデックス index より後の要素をすべて 1 つ前に移動する
for j in index..self.size - 1 {
self.arr[j] = self.arr[j + 1];
}
// 要素数を更新
self.size -= 1;
// 削除された要素を返す
return num;
}
/* リストの拡張 */
pub fn extend_capacity(&mut self) {
// 元の配列の extend_ratio 倍の長さを持つ新しい配列を作成し、元の配列をコピーする
let new_capacity = self.capacity * self.extend_ratio;
self.arr.resize(new_capacity, 0);
// リストの容量を更新
self.capacity = new_capacity;
}
/* リストを配列に変換する */
pub fn to_array(&self) -> Vec<i32> {
// 有効長の範囲内のリスト要素のみを変換
let mut arr = Vec::new();
for i in 0..self.size {
arr.push(self.get(i));
}
arr
}
}
/* Driver Code */
fn main() {
/* リストを初期化 */
let mut nums = MyList::new(10);
/* 末尾に要素を追加 */
nums.add(1);
nums.add(3);
nums.add(2);
nums.add(5);
nums.add(4);
print!("リスト nums = ");
print_util::print_array(&nums.to_array());
print!(" 、容量 = {} 、長さ = {}", nums.capacity(), nums.size());
/* 中間に要素を挿入 */
nums.insert(3, 6);
print!("\nインデックス 3 に数値 6 を挿入すると、nums = ");
print_util::print_array(&nums.to_array());
/* 要素を削除 */
nums.remove(3);
print!("\nインデックス 3 の要素を削除すると、nums = ");
print_util::print_array(&nums.to_array());
/* 要素にアクセス */
let num = nums.get(1);
println!("\nインデックス 1 の要素にアクセスすると、num = {num}");
/* 要素を更新 */
nums.set(1, 0);
print!("インデックス 1 の要素を 0 に更新すると、nums = ");
print_util::print_array(&nums.to_array());
/* 拡張機構をテストする */
for i in 0..10 {
// i = 5 のとき、リスト長が容量を超えるため、この時点で拡張機構が発動する
nums.add(i);
}
print!("\n拡張後のリスト nums = ");
print_util::print_array(&nums.to_array());
print!(" 、容量 = {} 、長さ = {}", nums.capacity(), nums.size());
}
@@ -0,0 +1,76 @@
/*
* File: n_queens.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
/* バックトラッキング:N クイーン */
fn backtrack(
row: usize,
n: usize,
state: &mut Vec<Vec<String>>,
res: &mut Vec<Vec<Vec<String>>>,
cols: &mut [bool],
diags1: &mut [bool],
diags2: &mut [bool],
) {
// すべての行への配置が完了したら、解を記録する
if row == n {
res.push(state.clone());
return;
}
// すべての列を走査
for col in 0..n {
// このマスに対応する主対角線と副対角線を計算
let diag1 = row + n - 1 - col;
let diag2 = row + col;
// 枝刈り:そのマスの列、主対角線、副対角線にクイーンがあってはならない
if !cols[col] && !diags1[diag1] && !diags2[diag2] {
// 試行:そのマスにクイーンを置く
state[row][col] = "Q".into();
(cols[col], diags1[diag1], diags2[diag2]) = (true, true, true);
// 次の行に配置する
backtrack(row + 1, n, state, res, cols, diags1, diags2);
// 戻す:そのマスを空きマスに戻す
state[row][col] = "#".into();
(cols[col], diags1[diag1], diags2[diag2]) = (false, false, false);
}
}
}
/* N クイーンを解く */
fn n_queens(n: usize) -> Vec<Vec<Vec<String>>> {
// n*n の盤面を初期化する。'Q' はクイーン、'#' は空きマスを表す
let mut state: Vec<Vec<String>> = vec![vec!["#".to_string(); n]; n];
let mut cols = vec![false; n]; // 列にクイーンがあるか記録
let mut diags1 = vec![false; 2 * n - 1]; // 主対角線にクイーンがあるかを記録
let mut diags2 = vec![false; 2 * n - 1]; // 副対角線にクイーンがあるかを記録
let mut res: Vec<Vec<Vec<String>>> = Vec::new();
backtrack(
0,
n,
&mut state,
&mut res,
&mut cols,
&mut diags1,
&mut diags2,
);
res
}
/* Driver Code */
pub fn main() {
let n: usize = 4;
let res = n_queens(n);
println!("盤面の縦横は {n}");
println!("クイーンの配置方法は全部で {} 通り", res.len());
for state in res.iter() {
println!("--------------------");
for row in state.iter() {
println!("{:?}", row);
}
}
}
@@ -0,0 +1,46 @@
/*
* File: permutations_i.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
/* バックトラッキング:順列 I */
fn backtrack(mut state: Vec<i32>, choices: &[i32], selected: &mut [bool], res: &mut Vec<Vec<i32>>) {
// 状態の長さが要素数に等しければ、解を記録
if state.len() == choices.len() {
res.push(state);
return;
}
// すべての選択肢を走査
for i in 0..choices.len() {
let choice = choices[i];
// 枝刈り:要素の重複選択を許可しない
if !selected[i] {
// 試行: 選択を行い、状態を更新
selected[i] = true;
state.push(choice);
// 次の選択へ進む
backtrack(state.clone(), choices, selected, res);
// バックトラック:選択を取り消し、前の状態に戻す
selected[i] = false;
state.pop();
}
}
}
/* 全順列 I */
fn permutations_i(nums: &mut [i32]) -> Vec<Vec<i32>> {
let mut res = Vec::new(); // 状態(部分集合)
backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res);
res
}
/* Driver Code */
pub fn main() {
let mut nums = [1, 2, 3];
let res = permutations_i(&mut nums);
println!("入力配列 nums = {:?}", &nums);
println!("すべての順列 res = {:?}", &res);
}
@@ -0,0 +1,50 @@
/*
* File: permutations_ii.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
use std::collections::HashSet;
/* バックトラッキング:順列 II */
fn backtrack(mut state: Vec<i32>, choices: &[i32], selected: &mut [bool], res: &mut Vec<Vec<i32>>) {
// 状態の長さが要素数に等しければ、解を記録
if state.len() == choices.len() {
res.push(state);
return;
}
// すべての選択肢を走査
let mut duplicated = HashSet::<i32>::new();
for i in 0..choices.len() {
let choice = choices[i];
// 枝刈り:要素の重複選択を許可せず、同値要素の重複選択も許可しない
if !selected[i] && !duplicated.contains(&choice) {
// 試行: 選択を行い、状態を更新
duplicated.insert(choice); // 選択済みの要素値を記録
selected[i] = true;
state.push(choice);
// 次の選択へ進む
backtrack(state.clone(), choices, selected, res);
// バックトラック:選択を取り消し、前の状態に戻す
selected[i] = false;
state.pop();
}
}
}
/* 全順列 II */
fn permutations_ii(nums: &mut [i32]) -> Vec<Vec<i32>> {
let mut res = Vec::new();
backtrack(Vec::new(), nums, &mut vec![false; nums.len()], &mut res);
res
}
/* Driver Code */
pub fn main() {
let mut nums = [1, 2, 2];
let res = permutations_ii(&mut nums);
println!("入力配列 nums = {:?}", &nums);
println!("すべての順列 res = {:?}", &res);
}
@@ -0,0 +1,41 @@
/*
* File: preorder_traversal_i_compact.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode};
use std::{cell::RefCell, rc::Rc};
/* 前順走査:例題 1 */
fn pre_order(res: &mut Vec<Rc<RefCell<TreeNode>>>, root: Option<&Rc<RefCell<TreeNode>>>) {
if root.is_none() {
return;
}
if let Some(node) = root {
if node.borrow().val == 7 {
// 解を記録
res.push(node.clone());
}
pre_order(res, node.borrow().left.as_ref());
pre_order(res, node.borrow().right.as_ref());
}
}
/* Driver Code */
pub fn main() {
let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec());
println!("二分木を初期化");
print_util::print_tree(root.as_ref().unwrap());
// 先行順走査
let mut res = Vec::new();
pre_order(&mut res, root.as_ref());
println!("\n値が 7 のノードをすべて出力");
let mut vals = Vec::new();
for node in res {
vals.push(node.borrow().val)
}
println!("{:?}", vals);
}
@@ -0,0 +1,52 @@
/*
* File: preorder_traversal_ii_compact.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode};
use std::{cell::RefCell, rc::Rc};
/* 前順走査:例題 2 */
fn pre_order(
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
path: &mut Vec<Rc<RefCell<TreeNode>>>,
root: Option<&Rc<RefCell<TreeNode>>>,
) {
if root.is_none() {
return;
}
if let Some(node) = root {
// 試す
path.push(node.clone());
if node.borrow().val == 7 {
// 解を記録
res.push(path.clone());
}
pre_order(res, path, node.borrow().left.as_ref());
pre_order(res, path, node.borrow().right.as_ref());
// バックトラック
path.pop();
}
}
/* Driver Code */
pub fn main() {
let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec());
println!("二分木を初期化");
print_util::print_tree(root.as_ref().unwrap());
// 先行順走査
let mut path = Vec::new();
let mut res = Vec::new();
pre_order(&mut res, &mut path, root.as_ref());
println!("\n根ノードからノード 7 までのすべての経路を出力");
for path in res {
let mut vals = Vec::new();
for node in path {
vals.push(node.borrow().val)
}
println!("{:?}", vals);
}
}
@@ -0,0 +1,53 @@
/*
* File: preorder_traversal_iii_compact.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode};
use std::{cell::RefCell, rc::Rc};
/* 前順走査:例題 3 */
fn pre_order(
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
path: &mut Vec<Rc<RefCell<TreeNode>>>,
root: Option<&Rc<RefCell<TreeNode>>>,
) {
// 枝刈り
if root.is_none() || root.as_ref().unwrap().borrow().val == 3 {
return;
}
if let Some(node) = root {
// 試す
path.push(node.clone());
if node.borrow().val == 7 {
// 解を記録
res.push(path.clone());
}
pre_order(res, path, node.borrow().left.as_ref());
pre_order(res, path, node.borrow().right.as_ref());
// バックトラック
path.pop();
}
}
/* Driver Code */
pub fn main() {
let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec());
println!("二分木を初期化");
print_util::print_tree(root.as_ref().unwrap());
// 先行順走査
let mut path = Vec::new();
let mut res = Vec::new();
pre_order(&mut res, &mut path, root.as_ref());
println!("\n根ノードからノード 7 までのすべての経路を出力し、経路には値が 3 のノードを含めない");
for path in res {
let mut vals = Vec::new();
for node in path {
vals.push(node.borrow().val)
}
println!("{:?}", vals);
}
}
@@ -0,0 +1,88 @@
/*
* File: preorder_traversal_iii_template.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode};
use std::{cell::RefCell, rc::Rc};
/* 現在の状態が解かどうかを判定 */
fn is_solution(state: &mut Vec<Rc<RefCell<TreeNode>>>) -> bool {
return !state.is_empty() && state.last().unwrap().borrow().val == 7;
}
/* 解を記録 */
fn record_solution(
state: &mut Vec<Rc<RefCell<TreeNode>>>,
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
) {
res.push(state.clone());
}
/* 現在の状態で、この選択が有効かどうかを判定 */
fn is_valid(_: &mut Vec<Rc<RefCell<TreeNode>>>, choice: Option<&Rc<RefCell<TreeNode>>>) -> bool {
return choice.is_some() && choice.unwrap().borrow().val != 3;
}
/* 状態を更新 */
fn make_choice(state: &mut Vec<Rc<RefCell<TreeNode>>>, choice: Rc<RefCell<TreeNode>>) {
state.push(choice);
}
/* 状態を元に戻す */
fn undo_choice(state: &mut Vec<Rc<RefCell<TreeNode>>>, _: Rc<RefCell<TreeNode>>) {
state.pop();
}
/* バックトラッキング:例題 3 */
fn backtrack(
state: &mut Vec<Rc<RefCell<TreeNode>>>,
choices: &Vec<Option<&Rc<RefCell<TreeNode>>>>,
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
) {
// 解かどうかを確認
if is_solution(state) {
// 解を記録
record_solution(state, res);
}
// すべての選択肢を走査
for &choice in choices.iter() {
// 枝刈り:選択が妥当かを確認する
if is_valid(state, choice) {
// 試行: 選択を行い、状態を更新
make_choice(state, choice.unwrap().clone());
// 次の選択へ進む
backtrack(
state,
&vec![
choice.unwrap().borrow().left.as_ref(),
choice.unwrap().borrow().right.as_ref(),
],
res,
);
// バックトラック:選択を取り消し、前の状態に戻す
undo_choice(state, choice.unwrap().clone());
}
}
}
/* Driver Code */
pub fn main() {
let root = vec_to_tree([1, 7, 3, 4, 5, 6, 7].map(|x| Some(x)).to_vec());
println!("二分木を初期化");
print_util::print_tree(root.as_ref().unwrap());
// バックトラッキング法
let mut res = Vec::new();
backtrack(&mut Vec::new(), &mut vec![root.as_ref()], &mut res);
println!("\n根ノードからノード 7 までのすべての経路を出力し、経路に値が 3 のノードを含まないことを条件とする");
for path in res {
let mut vals = Vec::new();
for node in path {
vals.push(node.borrow().val)
}
println!("{:?}", vals);
}
}
@@ -0,0 +1,56 @@
/*
* File: subset_sum_i.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* バックトラッキング:部分和 I */
fn backtrack(
state: &mut Vec<i32>,
target: i32,
choices: &[i32],
start: usize,
res: &mut Vec<Vec<i32>>,
) {
// 部分集合の和が target に等しければ、解を記録
if target == 0 {
res.push(state.clone());
return;
}
// すべての選択肢を走査
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
for i in start..choices.len() {
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
if target - choices[i] < 0 {
break;
}
// 試す:選択を行い、target と start を更新
state.push(choices[i]);
// 次の選択へ進む
backtrack(state, target - choices[i], choices, i, res);
// バックトラック:選択を取り消し、前の状態に戻す
state.pop();
}
}
/* 部分和 I を解く */
fn subset_sum_i(nums: &mut [i32], target: i32) -> Vec<Vec<i32>> {
let mut state = Vec::new(); // 状態(部分集合)
nums.sort(); // nums をソート
let start = 0; // 開始点を走査
let mut res = Vec::new(); // 結果リスト(部分集合のリスト)
backtrack(&mut state, target, nums, start, &mut res);
res
}
/* Driver Code */
pub fn main() {
let mut nums = [3, 4, 5];
let target = 9;
let res = subset_sum_i(&mut nums, target);
println!("入力配列 nums = {:?}, target = {}", &nums, target);
println!("和が {} に等しいすべての部分集合 res = {:?}", target, &res);
}
@@ -0,0 +1,54 @@
/*
* File: subset_sum_i_naive.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* バックトラッキング:部分和 I */
fn backtrack(
state: &mut Vec<i32>,
target: i32,
total: i32,
choices: &[i32],
res: &mut Vec<Vec<i32>>,
) {
// 部分集合の和が target に等しければ、解を記録
if total == target {
res.push(state.clone());
return;
}
// すべての選択肢を走査
for i in 0..choices.len() {
// 枝刈り:部分和が target を超える場合はその選択をスキップする
if total + choices[i] > target {
continue;
}
// 試行:選択を行い、要素と total を更新する
state.push(choices[i]);
// 次の選択へ進む
backtrack(state, target, total + choices[i], choices, res);
// バックトラック:選択を取り消し、前の状態に戻す
state.pop();
}
}
/* 部分和 I を解く(重複部分集合を含む) */
fn subset_sum_i_naive(nums: &[i32], target: i32) -> Vec<Vec<i32>> {
let mut state = Vec::new(); // 状態(部分集合)
let total = 0; // 部分和
let mut res = Vec::new(); // 結果リスト(部分集合のリスト)
backtrack(&mut state, target, total, nums, &mut res);
res
}
/* Driver Code */
pub fn main() {
let nums = [3, 4, 5];
let target = 9;
let res = subset_sum_i_naive(&nums, target);
println!("入力配列 nums = {:?}, target = {}", &nums, target);
println!("和が {} に等しいすべての部分集合 res = {:?}", target, &res);
println!("注意: この方法の出力結果には重複した集合が含まれます");
}
@@ -0,0 +1,61 @@
/*
* File: subset_sum_ii.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* バックトラッキング:部分和 II */
fn backtrack(
state: &mut Vec<i32>,
target: i32,
choices: &[i32],
start: usize,
res: &mut Vec<Vec<i32>>,
) {
// 部分集合の和が target に等しければ、解を記録
if target == 0 {
res.push(state.clone());
return;
}
// すべての選択肢を走査
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
// 枝刈り 3: start から走査し、同じ要素の重複選択を避ける
for i in start..choices.len() {
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
if target - choices[i] < 0 {
break;
}
// 枝刈り4:この要素が左隣の要素と等しければ、その探索分岐は重複しているためスキップする
if i > start && choices[i] == choices[i - 1] {
continue;
}
// 試す:選択を行い、target と start を更新
state.push(choices[i]);
// 次の選択へ進む
backtrack(state, target - choices[i], choices, i + 1, res);
// バックトラック:選択を取り消し、前の状態に戻す
state.pop();
}
}
/* 部分和 II を解く */
fn subset_sum_ii(nums: &mut [i32], target: i32) -> Vec<Vec<i32>> {
let mut state = Vec::new(); // 状態(部分集合)
nums.sort(); // nums をソート
let start = 0; // 開始点を走査
let mut res = Vec::new(); // 結果リスト(部分集合のリスト)
backtrack(&mut state, target, nums, start, &mut res);
res
}
/* Driver Code */
pub fn main() {
let mut nums = [4, 4, 5];
let target = 9;
let res = subset_sum_ii(&mut nums, target);
println!("入力配列 nums = {:?}, target = {}", &nums, target);
println!("和が {} に等しいすべての部分集合 res = {:?}", target, &res);
}
@@ -0,0 +1,74 @@
/*
* File: iteration.rs
* Created Time: 2023-09-02
* Author: night-cruise (2586447362@qq.com)
*/
/* for ループ */
fn for_loop(n: i32) -> i32 {
let mut res = 0;
// 1, 2, ..., n-1, n を順に加算する
for i in 1..=n {
res += i;
}
res
}
/* while ループ */
fn while_loop(n: i32) -> i32 {
let mut res = 0;
let mut i = 1; // 条件変数を初期化する
// 1, 2, ..., n-1, n を順に加算する
while i <= n {
res += i;
i += 1; // 条件変数を更新する
}
res
}
/* while ループ(2回更新) */
fn while_loop_ii(n: i32) -> i32 {
let mut res = 0;
let mut i = 1; // 条件変数を初期化する
// 1, 4, 10, ... を順に加算する
while i <= n {
res += i;
// 条件変数を更新する
i += 1;
i *= 2;
}
res
}
/* 二重 for ループ */
fn nested_for_loop(n: i32) -> String {
let mut res = vec![];
// i = 1, 2, ..., n-1, n とループする
for i in 1..=n {
// j = 1, 2, ..., n-1, n とループする
for j in 1..=n {
res.push(format!("({}, {}), ", i, j));
}
}
res.join("")
}
/* Driver Code */
fn main() {
let n = 5;
let mut res;
res = for_loop(n);
println!("\nfor ループの合計結果 res = {res}");
res = while_loop(n);
println!("\nwhile ループの合計結果 res = {res}");
res = while_loop_ii(n);
println!("\nwhile ループ(2 回更新)の合計結果 res = {}", res);
let res = nested_for_loop(n);
println!("\n二重 for ループの走査結果 {res}");
}
@@ -0,0 +1,76 @@
/*
* File: recursion.rs
* Created Time: 2023-09-02
* Author: night-cruise (2586447362@qq.com)
*/
/* 再帰 */
fn recur(n: i32) -> i32 {
// 終了条件
if n == 1 {
return 1;
}
// 再帰:再帰呼び出し
let res = recur(n - 1);
// 帰りがけ:結果を返す
n + res
}
/* 反復で再帰を模擬する */
fn for_loop_recur(n: i32) -> i32 {
// 明示的なスタックを使ってシステムコールスタックを模擬する
let mut stack = Vec::new();
let mut res = 0;
// 再帰:再帰呼び出し
for i in (1..=n).rev() {
// 「スタックへのプッシュ」で「再帰」を模擬する
stack.push(i);
}
// 帰りがけ:結果を返す
while !stack.is_empty() {
// 「スタックから取り出す操作」で「帰り」をシミュレート
res += stack.pop().unwrap();
}
// res = 1+2+3+...+n
res
}
/* 末尾再帰 */
fn tail_recur(n: i32, res: i32) -> i32 {
// 終了条件
if n == 0 {
return res;
}
// 末尾再帰呼び出し
tail_recur(n - 1, res + n)
}
/* フィボナッチ数列:再帰 */
fn fib(n: i32) -> i32 {
// 終了条件 f(1) = 0, f(2) = 1
if n == 1 || n == 2 {
return n - 1;
}
// f(n) = f(n-1) + f(n-2) を再帰的に呼び出す
let res = fib(n - 1) + fib(n - 2);
// 結果を返す
res
}
/* Driver Code */
fn main() {
let n = 5;
let mut res;
res = recur(n);
println!("\n再帰関数による合計結果 res = {res}");
res = for_loop_recur(n);
println!("\n反復で再帰を模擬した合計結果 res = {res}");
res = tail_recur(n, 0);
println!("\n末尾再帰関数による合計結果 res = {res}");
res = fib(n);
println!("\nフィボナッチ数列の第 {n} 項は {res}");
}
@@ -0,0 +1,114 @@
/*
* File: space_complexity.rs
* Created Time: 2023-03-11
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, ListNode, TreeNode};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
/* 関数 */
fn function() -> i32 {
// 何らかの処理を行う
return 0;
}
/* 定数階 */
#[allow(unused)]
fn constant(n: i32) {
// 定数、変数、オブジェクトは O(1) の空間を占める
const A: i32 = 0;
let b = 0;
let nums = vec![0; 10000];
let node = ListNode::new(0);
// ループ内の変数は O(1) の空間を占める
for i in 0..n {
let c = 0;
}
// ループ内の関数は O(1) の空間を占める
for i in 0..n {
function();
}
}
/* 線形階 */
#[allow(unused)]
fn linear(n: i32) {
// 長さ n の配列は O(n) の空間を使用
let mut nums = vec![0; n as usize];
// 長さ n のリストは O(n) の空間を使用
let mut nodes = Vec::new();
for i in 0..n {
nodes.push(ListNode::new(i))
}
// 長さ n のハッシュテーブルは O(n) の空間を使用
let mut map = HashMap::new();
for i in 0..n {
map.insert(i, i.to_string());
}
}
/* 線形時間(再帰実装) */
fn linear_recur(n: i32) {
println!("再帰 n = {}", n);
if n == 1 {
return;
};
linear_recur(n - 1);
}
/* 二乗階 */
#[allow(unused)]
fn quadratic(n: i32) {
// 行列は O(n^2) の空間を使用する
let num_matrix = vec![vec![0; n as usize]; n as usize];
// 二次元リストは O(n^2) の空間を使用
let mut num_list = Vec::new();
for i in 0..n {
let mut tmp = Vec::new();
for j in 0..n {
tmp.push(0);
}
num_list.push(tmp);
}
}
/* 二次時間(再帰実装) */
fn quadratic_recur(n: i32) -> i32 {
if n <= 0 {
return 0;
};
// 配列 nums の長さは n, n-1, ..., 2, 1
let nums = vec![0; n as usize];
println!("再帰 n = {} における nums の長さ = {}", n, nums.len());
return quadratic_recur(n - 1);
}
/* 指数時間(完全二分木の構築) */
fn build_tree(n: i32) -> Option<Rc<RefCell<TreeNode>>> {
if n == 0 {
return None;
};
let root = TreeNode::new(0);
root.borrow_mut().left = build_tree(n - 1);
root.borrow_mut().right = build_tree(n - 1);
return Some(root);
}
/* Driver Code */
fn main() {
let n = 5;
// 定数階
constant(n);
// 線形階
linear(n);
linear_recur(n);
// 二乗階
quadratic(n);
quadratic_recur(n);
// 指数オーダー
let root = build_tree(n);
print_util::print_tree(&root.unwrap());
}
@@ -0,0 +1,170 @@
/*
* File: time_complexity.rs
* Created Time: 2023-01-10
* Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com)
*/
/* 定数階 */
fn constant(n: i32) -> i32 {
_ = n;
let mut count = 0;
let size = 100_000;
for _ in 0..size {
count += 1;
}
count
}
/* 線形階 */
fn linear(n: i32) -> i32 {
let mut count = 0;
for _ in 0..n {
count += 1;
}
count
}
/* 線形時間(配列を走査) */
fn array_traversal(nums: &[i32]) -> i32 {
let mut count = 0;
// ループ回数は配列長に比例する
for _ in nums {
count += 1;
}
count
}
/* 二乗階 */
fn quadratic(n: i32) -> i32 {
let mut count = 0;
// ループ回数はデータサイズ n の二乗に比例する
for _ in 0..n {
for _ in 0..n {
count += 1;
}
}
count
}
/* 二次時間(バブルソート) */
fn bubble_sort(nums: &mut [i32]) -> i32 {
let mut count = 0; // カウンタ
// 外側のループ:未ソート区間は [0, i]
for i in (1..nums.len()).rev() {
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
for j in 0..i {
if nums[j] > nums[j + 1] {
// nums[j] と nums[j + 1] を交換
let tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
count += 3; // 要素交換には 3 回の単位操作が含まれる
}
}
}
count
}
/* 指数時間(ループ実装) */
fn exponential(n: i32) -> i32 {
let mut count = 0;
let mut base = 1;
// 細胞は各ラウンドで 2 つに分裂し、数列 1, 2, 4, 8, ..., 2^(n-1) を形成する
for _ in 0..n {
for _ in 0..base {
count += 1
}
base *= 2;
}
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
count
}
/* 指数時間(再帰実装) */
fn exp_recur(n: i32) -> i32 {
if n == 1 {
return 1;
}
exp_recur(n - 1) + exp_recur(n - 1) + 1
}
/* 対数時間(ループ実装) */
fn logarithmic(mut n: i32) -> i32 {
let mut count = 0;
while n > 1 {
n = n / 2;
count += 1;
}
count
}
/* 対数時間(再帰実装) */
fn log_recur(n: i32) -> i32 {
if n <= 1 {
return 0;
}
log_recur(n / 2) + 1
}
/* 線形対数時間 */
fn linear_log_recur(n: i32) -> i32 {
if n <= 1 {
return 1;
}
let mut count = linear_log_recur(n / 2) + linear_log_recur(n / 2);
for _ in 0..n {
count += 1;
}
return count;
}
/* 階乗時間(再帰実装) */
fn factorial_recur(n: i32) -> i32 {
if n == 0 {
return 1;
}
let mut count = 0;
// 1個から n 個に分裂
for _ in 0..n {
count += factorial_recur(n - 1);
}
count
}
/* Driver Code */
fn main() {
// n を変えて実行し、各計算量で操作回数がどう変化するかを確認できる
let n: i32 = 8;
println!("入力データサイズ n = {}", n);
let mut count = constant(n);
println!("定数時間の操作回数 = {}", count);
count = linear(n);
println!("線形時間の操作回数 = {}", count);
count = array_traversal(&vec![0; n as usize]);
println!("線形時間(配列の走査)の操作回数 = {}", count);
count = quadratic(n);
println!("二乗時間の操作回数 = {}", count);
let mut nums = (1..=n).rev().collect::<Vec<_>>(); // [n,n-1,...,2,1]
count = bubble_sort(&mut nums);
println!("二乗時間(バブルソート)の操作回数 = {}", count);
count = exponential(n);
println!("指数時間(ループ実装)の操作回数 = {}", count);
count = exp_recur(n);
println!("指数時間(再帰実装)の操作回数 = {}", count);
count = logarithmic(n);
println!("対数時間(ループ実装)の操作回数 = {}", count);
count = log_recur(n);
println!("対数時間(再帰実装)の操作回数 = {}", count);
count = linear_log_recur(n);
println!("線形対数時間(再帰実装)の操作回数 = {}", count);
count = factorial_recur(n);
println!("階乗時間(再帰実装)の操作回数 = {}", count);
}
@@ -0,0 +1,42 @@
/*
* File: worst_best_time_complexity.rs
* Created Time: 2023-01-13
* Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
use rand::seq::SliceRandom;
use rand::thread_rng;
/* 要素が { 1, 2, ..., n } で、順序がシャッフルされた配列を生成 */
fn random_numbers(n: i32) -> Vec<i32> {
// 配列 nums = { 1, 2, 3, ..., n } を生成
let mut nums = (1..=n).collect::<Vec<i32>>();
// 配列要素をランダムにシャッフル
nums.shuffle(&mut thread_rng());
nums
}
/* 配列 nums 内で数値 1 のインデックスを探す */
fn find_one(nums: &[i32]) -> Option<usize> {
for i in 0..nums.len() {
// 要素 1 が配列の先頭にあるとき、最良時間計算量 O(1) となる
// 要素 1 が配列の末尾にあるとき、最悪時間計算量 O(n) となる
if nums[i] == 1 {
return Some(i);
}
}
None
}
/* Driver Code */
fn main() {
for _ in 0..10 {
let n = 100;
let nums = random_numbers(n);
let index = find_one(&nums).unwrap();
print!("\n配列 [ 1, 2, ..., n ] をシャッフルした後 = ");
print_util::print_array(&nums);
println!("\n数字 1 のインデックスは {}", index);
}
}
@@ -0,0 +1,41 @@
/*
* File: binary_search_recur.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
/* 二分探索:問題 f(i, j) */
fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 {
// 区間が空なら対象要素は存在しないので -1 を返す
if i > j {
return -1;
}
let m: i32 = i + (j - i) / 2;
if nums[m as usize] < target {
// 部分問題 f(m+1, j) を再帰的に解く
return dfs(nums, target, m + 1, j);
} else if nums[m as usize] > target {
// 部分問題 f(i, m-1) を再帰的に解く
return dfs(nums, target, i, m - 1);
} else {
// 目標要素が見つかったらそのインデックスを返す
return m;
}
}
/* 二分探索 */
fn binary_search(nums: &[i32], target: i32) -> i32 {
let n = nums.len() as i32;
// 問題 f(0, n-1) を解く
dfs(nums, target, 0, n - 1)
}
/* Driver Code */
pub fn main() {
let target = 6;
let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35];
// 二分探索(両閉区間)
let index = binary_search(&nums, target);
println!("目的の要素 6 のインデックス = {index}");
}
@@ -0,0 +1,56 @@
/*
* File: build_tree.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, TreeNode};
use std::collections::HashMap;
use std::{cell::RefCell, rc::Rc};
/* 二分木を構築:分割統治 */
fn dfs(
preorder: &[i32],
inorder_map: &HashMap<i32, i32>,
i: i32,
l: i32,
r: i32,
) -> Option<Rc<RefCell<TreeNode>>> {
// 部分木区間が空なら終了する
if r - l < 0 {
return None;
}
// ルートノードを初期化する
let root = TreeNode::new(preorder[i as usize]);
// m を求めて左右部分木を分割する
let m = inorder_map.get(&preorder[i as usize]).unwrap();
// 部分問題:左部分木を構築する
root.borrow_mut().left = dfs(preorder, inorder_map, i + 1, l, m - 1);
// 部分問題:右部分木を構築する
root.borrow_mut().right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r);
// 根ノードを返す
Some(root)
}
/* 二分木を構築 */
fn build_tree(preorder: &[i32], inorder: &[i32]) -> Option<Rc<RefCell<TreeNode>>> {
// inorder の要素からインデックスへの対応を格納するハッシュテーブルを初期化する
let mut inorder_map: HashMap<i32, i32> = HashMap::new();
for i in 0..inorder.len() {
inorder_map.insert(inorder[i], i as i32);
}
let root = dfs(preorder, &inorder_map, 0, 0, inorder.len() as i32 - 1);
root
}
/* Driver Code */
fn main() {
let preorder = [3, 9, 2, 1, 7];
let inorder = [9, 3, 1, 2, 7];
println!("中順走査 = {:?}", preorder);
println!("前順走査 = {:?}", inorder);
let root = build_tree(&preorder, &inorder);
println!("構築した二分木は:");
print_util::print_tree(root.as_ref().unwrap());
}
@@ -0,0 +1,55 @@
/*
* File: hanota.rs
* Created Time: 2023-07-15
* Author: codingonion (coderonion@gmail.com)
*/
#![allow(non_snake_case)]
/* 円盤を 1 枚移動 */
fn move_pan(src: &mut Vec<i32>, tar: &mut Vec<i32>) {
// src の上から円盤を1枚取り出す
let pan = src.pop().unwrap();
// 円盤を tar の上に置く
tar.push(pan);
}
/* ハノイの塔の問題 f(i) を解く */
fn dfs(i: i32, src: &mut Vec<i32>, buf: &mut Vec<i32>, tar: &mut Vec<i32>) {
// src に円盤が 1 枚だけ残っている場合は、そのまま tar へ移す
if i == 1 {
move_pan(src, tar);
return;
}
// 部分問題 f(i-1)src の上部 i-1 枚の円盤を tar を補助にして buf へ移す
dfs(i - 1, src, tar, buf);
// 部分問題 f(1):src に残る 1 枚の円盤を tar に移す
move_pan(src, tar);
// 部分問題 f(i-1)buf の上部 i-1 枚の円盤を src を補助にして tar へ移す
dfs(i - 1, buf, src, tar);
}
/* ハノイの塔を解く */
fn solve_hanota(A: &mut Vec<i32>, B: &mut Vec<i32>, C: &mut Vec<i32>) {
let n = A.len() as i32;
// A の上から n 枚の円盤を B を介して C へ移す
dfs(n, A, B, C);
}
/* Driver Code */
pub fn main() {
let mut A = vec![5, 4, 3, 2, 1];
let mut B = Vec::new();
let mut C = Vec::new();
println!("初期状態:");
println!("A = {:?}", A);
println!("B = {:?}", B);
println!("C = {:?}", C);
solve_hanota(&mut A, &mut B, &mut C);
println!("円盤の移動完了後:");
println!("A = {:?}", A);
println!("B = {:?}", B);
println!("C = {:?}", C);
}
@@ -0,0 +1,41 @@
/*
* File: climbing_stairs_backtrack.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* バックトラッキング */
fn backtrack(choices: &[i32], state: i32, n: i32, res: &mut [i32]) {
// 第 n 段に到達したら、方法数を 1 増やす
if state == n {
res[0] = res[0] + 1;
}
// すべての選択肢を走査
for &choice in choices {
// 枝刈り: 第 n 段を超えないようにする
if state + choice > n {
continue;
}
// 試行: 選択を行い、状態を更新
backtrack(choices, state + choice, n, res);
// バックトラック
}
}
/* 階段登り:バックトラッキング */
fn climbing_stairs_backtrack(n: usize) -> i32 {
let choices = vec![1, 2]; // 1 段または 2 段上ることを選べる
let state = 0; // 第 0 段から上り始める
let mut res = Vec::new();
res.push(0); // res[0] を使って方法数を記録する
backtrack(&choices, state, n as i32, &mut res);
res[0]
}
/* Driver Code */
pub fn main() {
let n: usize = 9;
let res = climbing_stairs_backtrack(n);
println!("{n} 段の階段を上る方法は全部で {res} 通り");
}
@@ -0,0 +1,33 @@
/*
* File: climbing_stairs_constraint_dp.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* 制約付き階段登り:動的計画法 */
fn climbing_stairs_constraint_dp(n: usize) -> i32 {
if n == 1 || n == 2 {
return 1;
};
// 部分問題の解を保存するために dp テーブルを初期化
let mut dp = vec![vec![-1; 3]; n + 1];
// 初期状態:最小部分問題の解をあらかじめ設定
dp[1][1] = 1;
dp[1][2] = 0;
dp[2][1] = 0;
dp[2][2] = 1;
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
for i in 3..=n {
dp[i][1] = dp[i - 1][2];
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
}
dp[n][1] + dp[n][2]
}
/* Driver Code */
pub fn main() {
let n: usize = 9;
let res = climbing_stairs_constraint_dp(n);
println!("{n} 段の階段を上る方法は全部で {res} 通り");
}
@@ -0,0 +1,29 @@
/*
* File: climbing_stairs_dfs.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* 検索 */
fn dfs(i: usize) -> i32 {
// dp[1] と dp[2] は既知なので返す
if i == 1 || i == 2 {
return i as i32;
}
// dp[i] = dp[i-1] + dp[i-2]
let count = dfs(i - 1) + dfs(i - 2);
count
}
/* 階段登り:探索 */
fn climbing_stairs_dfs(n: usize) -> i32 {
dfs(n)
}
/* Driver Code */
pub fn main() {
let n: usize = 9;
let res = climbing_stairs_dfs(n);
println!("{n} 段の階段を上る方法は全部で {res} 通り");
}
@@ -0,0 +1,37 @@
/*
* File: climbing_stairs_dfs_mem.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* メモ化探索 */
fn dfs(i: usize, mem: &mut [i32]) -> i32 {
// dp[1] と dp[2] は既知なので返す
if i == 1 || i == 2 {
return i as i32;
}
// dp[i] の記録があれば、それをそのまま返す
if mem[i] != -1 {
return mem[i];
}
// dp[i] = dp[i-1] + dp[i-2]
let count = dfs(i - 1, mem) + dfs(i - 2, mem);
// dp[i] を記録する
mem[i] = count;
count
}
/* 階段登り:メモ化探索 */
fn climbing_stairs_dfs_mem(n: usize) -> i32 {
// mem[i] は第 i 段まで上る方法の総数を記録し、-1 は未記録を表す
let mut mem = vec![-1; n + 1];
dfs(n, &mut mem)
}
/* Driver Code */
pub fn main() {
let n: usize = 9;
let res = climbing_stairs_dfs_mem(n);
println!("{n} 段の階段を上る方法は全部で {res} 通り");
}
@@ -0,0 +1,48 @@
/*
* File: climbing_stairs_dp.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* 階段登り:動的計画法 */
fn climbing_stairs_dp(n: usize) -> i32 {
// dp[1] と dp[2] は既知なので返す
if n == 1 || n == 2 {
return n as i32;
}
// 部分問題の解を保存するために dp テーブルを初期化
let mut dp = vec![-1; n + 1];
// 初期状態:最小部分問題の解をあらかじめ設定
dp[1] = 1;
dp[2] = 2;
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
for i in 3..=n {
dp[i] = dp[i - 1] + dp[i - 2];
}
dp[n]
}
/* 階段登り:空間最適化した動的計画法 */
fn climbing_stairs_dp_comp(n: usize) -> i32 {
if n == 1 || n == 2 {
return n as i32;
}
let (mut a, mut b) = (1, 2);
for _ in 3..=n {
let tmp = b;
b = a + b;
a = tmp;
}
b
}
/* Driver Code */
pub fn main() {
let n: usize = 9;
let res = climbing_stairs_dp(n);
println!("{n} 段の階段を上る方法は全部で {res} 通り");
let res = climbing_stairs_dp_comp(n);
println!("{n} 段の階段を上る方法は全部で {res} 通り");
}
@@ -0,0 +1,75 @@
/*
* File: coin_change.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* コイン両替:動的計画法 */
fn coin_change_dp(coins: &[i32], amt: usize) -> i32 {
let n = coins.len();
let max = amt + 1;
// dp テーブルを初期化
let mut dp = vec![vec![0; amt + 1]; n + 1];
// 状態遷移:先頭行と先頭列
for a in 1..=amt {
dp[0][a] = max;
}
// 状態遷移: 残りの行と列
for i in 1..=n {
for a in 1..=amt {
if coins[i - 1] > a as i32 {
// 目標金額を超えるなら硬貨 i は選ばない
dp[i][a] = dp[i - 1][a];
} else {
// 硬貨 i を選ばない場合と選ぶ場合の小さい方
dp[i][a] = std::cmp::min(dp[i - 1][a], dp[i][a - coins[i - 1] as usize] + 1);
}
}
}
if dp[n][amt] != max {
return dp[n][amt] as i32;
} else {
-1
}
}
/* コイン交換:空間最適化後の動的計画法 */
fn coin_change_dp_comp(coins: &[i32], amt: usize) -> i32 {
let n = coins.len();
let max = amt + 1;
// dp テーブルを初期化
let mut dp = vec![0; amt + 1];
dp.fill(max);
dp[0] = 0;
// 状態遷移
for i in 1..=n {
for a in 1..=amt {
if coins[i - 1] > a as i32 {
// 目標金額を超えるなら硬貨 i は選ばない
dp[a] = dp[a];
} else {
// 硬貨 i を選ばない場合と選ぶ場合の小さい方
dp[a] = std::cmp::min(dp[a], dp[a - coins[i - 1] as usize] + 1);
}
}
}
if dp[amt] != max {
return dp[amt] as i32;
} else {
-1
}
}
/* Driver Code */
pub fn main() {
let coins = [1, 2, 5];
let amt: usize = 4;
// 動的計画法
let res = coin_change_dp(&coins, amt);
println!("目標金額にするために必要な最小硬貨枚数は {res}");
// 空間最適化後の動的計画法
let res = coin_change_dp_comp(&coins, amt);
println!("目標金額にするために必要な最小硬貨枚数は {res}");
}
@@ -0,0 +1,64 @@
/*
* File: coin_change_ii.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* コイン両替 II:動的計画法 */
fn coin_change_ii_dp(coins: &[i32], amt: usize) -> i32 {
let n = coins.len();
// dp テーブルを初期化
let mut dp = vec![vec![0; amt + 1]; n + 1];
// 先頭列を初期化する
for i in 0..=n {
dp[i][0] = 1;
}
// 状態遷移
for i in 1..=n {
for a in 1..=amt {
if coins[i - 1] > a as i32 {
// 目標金額を超えるなら硬貨 i は選ばない
dp[i][a] = dp[i - 1][a];
} else {
// コイン i を選ばない場合と選ぶ場合の和
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1] as usize];
}
}
}
dp[n][amt]
}
/* コイン両替 II:空間最適化した動的計画法 */
fn coin_change_ii_dp_comp(coins: &[i32], amt: usize) -> i32 {
let n = coins.len();
// dp テーブルを初期化
let mut dp = vec![0; amt + 1];
dp[0] = 1;
// 状態遷移
for i in 1..=n {
for a in 1..=amt {
if coins[i - 1] > a as i32 {
// 目標金額を超えるなら硬貨 i は選ばない
dp[a] = dp[a];
} else {
// コイン i を選ばない場合と選ぶ場合の和
dp[a] = dp[a] + dp[a - coins[i - 1] as usize];
}
}
}
dp[amt]
}
/* Driver Code */
pub fn main() {
let coins = [1, 2, 5];
let amt: usize = 5;
// 動的計画法
let res = coin_change_ii_dp(&coins, amt);
println!("目標金額を作る硬貨の組み合わせ数は {res}");
// 空間最適化後の動的計画法
let res = coin_change_ii_dp_comp(&coins, amt);
println!("目標金額を作る硬貨の組み合わせ数は {res}");
}
@@ -0,0 +1,145 @@
/*
* File: edit_distance.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* 編集距離:総当たり探索 */
fn edit_distance_dfs(s: &str, t: &str, i: usize, j: usize) -> i32 {
// s と t がともに空なら 0 を返す
if i == 0 && j == 0 {
return 0;
}
// s が空なら t の長さを返す
if i == 0 {
return j as i32;
}
// t が空なら s の長さを返す
if j == 0 {
return i as i32;
}
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
return edit_distance_dfs(s, t, i - 1, j - 1);
}
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
let insert = edit_distance_dfs(s, t, i, j - 1);
let delete = edit_distance_dfs(s, t, i - 1, j);
let replace = edit_distance_dfs(s, t, i - 1, j - 1);
// 最小編集回数を返す
std::cmp::min(std::cmp::min(insert, delete), replace) + 1
}
/* 編集距離:メモ化探索 */
fn edit_distance_dfs_mem(s: &str, t: &str, mem: &mut Vec<Vec<i32>>, i: usize, j: usize) -> i32 {
// s と t がともに空なら 0 を返す
if i == 0 && j == 0 {
return 0;
}
// s が空なら t の長さを返す
if i == 0 {
return j as i32;
}
// t が空なら s の長さを返す
if j == 0 {
return i as i32;
}
// 記録済みなら、それをそのまま返す
if mem[i][j] != -1 {
return mem[i][j];
}
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1);
}
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
let insert = edit_distance_dfs_mem(s, t, mem, i, j - 1);
let delete = edit_distance_dfs_mem(s, t, mem, i - 1, j);
let replace = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1);
// 最小編集回数を記録して返す
mem[i][j] = std::cmp::min(std::cmp::min(insert, delete), replace) + 1;
mem[i][j]
}
/* 編集距離:動的計画法 */
fn edit_distance_dp(s: &str, t: &str) -> i32 {
let (n, m) = (s.len(), t.len());
let mut dp = vec![vec![0; m + 1]; n + 1];
// 状態遷移:先頭行と先頭列
for i in 1..=n {
dp[i][0] = i as i32;
}
for j in 1..m {
dp[0][j] = j as i32;
}
// 状態遷移: 残りの行と列
for i in 1..=n {
for j in 1..=m {
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
dp[i][j] = dp[i - 1][j - 1];
} else {
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
dp[i][j] =
std::cmp::min(std::cmp::min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
}
}
}
dp[n][m]
}
/* 編集距離:空間最適化した動的計画法 */
fn edit_distance_dp_comp(s: &str, t: &str) -> i32 {
let (n, m) = (s.len(), t.len());
let mut dp = vec![0; m + 1];
// 状態遷移:先頭行
for j in 1..m {
dp[j] = j as i32;
}
// 状態遷移:残りの行
for i in 1..=n {
// 状態遷移:先頭列
let mut leftup = dp[0]; // dp[i-1, j-1] を一時保存する
dp[0] = i as i32;
// 状態遷移:残りの列
for j in 1..=m {
let temp = dp[j];
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
dp[j] = leftup;
} else {
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
dp[j] = std::cmp::min(std::cmp::min(dp[j - 1], dp[j]), leftup) + 1;
}
leftup = temp; // 次の反復の dp[i-1, j-1] に更新する
}
}
dp[m]
}
/* Driver Code */
pub fn main() {
let s = "bag";
let t = "pack";
let (n, m) = (s.len(), t.len());
// 全探索
let res = edit_distance_dfs(s, t, n, m);
println!("{s}{t} に変更するには最小で {res} 回の編集が必要");
// メモ化探索
let mut mem = vec![vec![0; m + 1]; n + 1];
for row in mem.iter_mut() {
row.fill(-1);
}
let res = edit_distance_dfs_mem(s, t, &mut mem, n, m);
println!("{s}{t} に変更するには最小で {res} 回の編集が必要");
// 動的計画法
let res = edit_distance_dp(s, t);
println!("{s}{t} に変更するには最小で {res} 回の編集が必要");
// 空間最適化後の動的計画法
let res = edit_distance_dp_comp(s, t);
println!("{s}{t} に変更するには最小で {res} 回の編集が必要");
}
@@ -0,0 +1,113 @@
/*
* File: knapsack.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* 0-1 ナップサック:総当たり探索 */
fn knapsack_dfs(wgt: &[i32], val: &[i32], i: usize, c: usize) -> i32 {
// すべての品物を選び終えたか、ナップサックに残り容量がなければ、価値 0 を返す
if i == 0 || c == 0 {
return 0;
}
// ナップサック容量を超える場合は、入れない選択しかできない
if wgt[i - 1] > c as i32 {
return knapsack_dfs(wgt, val, i - 1, c);
}
// 品物 i を入れない場合と入れる場合の最大価値を計算する
let no = knapsack_dfs(wgt, val, i - 1, c);
let yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1] as usize) + val[i - 1];
// 2つの案のうち価値が大きいほうを返す
std::cmp::max(no, yes)
}
/* 0-1 ナップサック:メモ化探索 */
fn knapsack_dfs_mem(wgt: &[i32], val: &[i32], mem: &mut Vec<Vec<i32>>, i: usize, c: usize) -> i32 {
// すべての品物を選び終えたか、ナップサックに残り容量がなければ、価値 0 を返す
if i == 0 || c == 0 {
return 0;
}
// 既に記録があればそのまま返す
if mem[i][c] != -1 {
return mem[i][c];
}
// ナップサック容量を超える場合は、入れない選択しかできない
if wgt[i - 1] > c as i32 {
return knapsack_dfs_mem(wgt, val, mem, i - 1, c);
}
// 品物 i を入れない場合と入れる場合の最大価値を計算する
let no = knapsack_dfs_mem(wgt, val, mem, i - 1, c);
let yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i - 1] as usize) + val[i - 1];
// 2 つの案のうち価値が大きい方を記録して返す
mem[i][c] = std::cmp::max(no, yes);
mem[i][c]
}
/* 0-1 ナップサック:動的計画法 */
fn knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
let n = wgt.len();
// dp テーブルを初期化
let mut dp = vec![vec![0; cap + 1]; n + 1];
// 状態遷移
for i in 1..=n {
for c in 1..=cap {
if wgt[i - 1] > c as i32 {
// ナップサック容量を超えるなら品物 i は選ばない
dp[i][c] = dp[i - 1][c];
} else {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[i][c] = std::cmp::max(
dp[i - 1][c],
dp[i - 1][c - wgt[i - 1] as usize] + val[i - 1],
);
}
}
}
dp[n][cap]
}
/* 0-1 ナップサック:空間最適化後の動的計画法 */
fn knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
let n = wgt.len();
// dp テーブルを初期化
let mut dp = vec![0; cap + 1];
// 状態遷移
for i in 1..=n {
// 逆順に走査する
for c in (1..=cap).rev() {
if wgt[i - 1] <= c as i32 {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]);
}
}
}
dp[cap]
}
/* Driver Code */
pub fn main() {
let wgt = [10, 20, 30, 40, 50];
let val = [50, 120, 150, 210, 240];
let cap: usize = 50;
let n = wgt.len();
// 全探索
let res = knapsack_dfs(&wgt, &val, n, cap);
println!("ナップサック容量を超えない最大価値は {res}");
// メモ化探索
let mut mem = vec![vec![0; cap + 1]; n + 1];
for row in mem.iter_mut() {
row.fill(-1);
}
let res = knapsack_dfs_mem(&wgt, &val, &mut mem, n, cap);
println!("ナップサック容量を超えない最大価値は {res}");
// 動的計画法
let res = knapsack_dp(&wgt, &val, cap);
println!("ナップサック容量を超えない最大価値は {res}");
// 空間最適化後の動的計画法
let res = knapsack_dp_comp(&wgt, &val, cap);
println!("ナップサック容量を超えない最大価値は {res}");
}
@@ -0,0 +1,52 @@
/*
* File: min_cost_climbing_stairs_dp.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
use std::cmp;
/* 階段登りの最小コスト:動的計画法 */
fn min_cost_climbing_stairs_dp(cost: &[i32]) -> i32 {
let n = cost.len() - 1;
if n == 1 || n == 2 {
return cost[n];
}
// 部分問題の解を保存するために dp テーブルを初期化
let mut dp = vec![-1; n + 1];
// 初期状態:最小部分問題の解をあらかじめ設定
dp[1] = cost[1];
dp[2] = cost[2];
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
for i in 3..=n {
dp[i] = cmp::min(dp[i - 1], dp[i - 2]) + cost[i];
}
dp[n]
}
/* 階段昇りの最小コスト:空間最適化後の動的計画法 */
fn min_cost_climbing_stairs_dp_comp(cost: &[i32]) -> i32 {
let n = cost.len() - 1;
if n == 1 || n == 2 {
return cost[n];
};
let (mut a, mut b) = (cost[1], cost[2]);
for i in 3..=n {
let tmp = b;
b = cmp::min(a, tmp) + cost[i];
a = tmp;
}
b
}
/* Driver Code */
pub fn main() {
let cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1];
println!("入力された階段コストのリストは {:?}", &cost);
let res = min_cost_climbing_stairs_dp(&cost);
println!("階段を上り切る最小コストは {res}");
let res = min_cost_climbing_stairs_dp_comp(&cost);
println!("階段を上り切る最小コストは {res}");
}
@@ -0,0 +1,120 @@
/*
* File: min_path_sum.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* 最小経路和:全探索 */
fn min_path_sum_dfs(grid: &Vec<Vec<i32>>, i: i32, j: i32) -> i32 {
// 左上のセルなら探索を終了する
if i == 0 && j == 0 {
return grid[0][0];
}
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
if i < 0 || j < 0 {
return i32::MAX;
}
// 左上から (i-1, j) および (i, j-1) までの最小経路コストを計算する
let up = min_path_sum_dfs(grid, i - 1, j);
let left = min_path_sum_dfs(grid, i, j - 1);
// 左上隅から (i, j) までの最小経路コストを返す
std::cmp::min(left, up) + grid[i as usize][j as usize]
}
/* 最小経路和:メモ化探索 */
fn min_path_sum_dfs_mem(grid: &Vec<Vec<i32>>, mem: &mut Vec<Vec<i32>>, i: i32, j: i32) -> i32 {
// 左上のセルなら探索を終了する
if i == 0 && j == 0 {
return grid[0][0];
}
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
if i < 0 || j < 0 {
return i32::MAX;
}
// 既に記録があればそのまま返す
if mem[i as usize][j as usize] != -1 {
return mem[i as usize][j as usize];
}
// 左と上のセルからの最小経路コスト
let up = min_path_sum_dfs_mem(grid, mem, i - 1, j);
let left = min_path_sum_dfs_mem(grid, mem, i, j - 1);
// 左上から (i, j) までの最小経路コストを記録して返す
mem[i as usize][j as usize] = std::cmp::min(left, up) + grid[i as usize][j as usize];
mem[i as usize][j as usize]
}
/* 最小経路和:動的計画法 */
fn min_path_sum_dp(grid: &Vec<Vec<i32>>) -> i32 {
let (n, m) = (grid.len(), grid[0].len());
// dp テーブルを初期化
let mut dp = vec![vec![0; m]; n];
dp[0][0] = grid[0][0];
// 状態遷移:先頭行
for j in 1..m {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
// 状態遷移:先頭列
for i in 1..n {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
// 状態遷移: 残りの行と列
for i in 1..n {
for j in 1..m {
dp[i][j] = std::cmp::min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
}
}
dp[n - 1][m - 1]
}
/* 最小経路和:空間最適化後の動的計画法 */
fn min_path_sum_dp_comp(grid: &Vec<Vec<i32>>) -> i32 {
let (n, m) = (grid.len(), grid[0].len());
// dp テーブルを初期化
let mut dp = vec![0; m];
// 状態遷移:先頭行
dp[0] = grid[0][0];
for j in 1..m {
dp[j] = dp[j - 1] + grid[0][j];
}
// 状態遷移:残りの行
for i in 1..n {
// 状態遷移:先頭列
dp[0] = dp[0] + grid[i][0];
// 状態遷移:残りの列
for j in 1..m {
dp[j] = std::cmp::min(dp[j - 1], dp[j]) + grid[i][j];
}
}
dp[m - 1]
}
/* Driver Code */
pub fn main() {
let grid = vec![
vec![1, 3, 1, 5],
vec![2, 2, 4, 2],
vec![5, 3, 2, 1],
vec![4, 3, 5, 2],
];
let (n, m) = (grid.len(), grid[0].len());
// 全探索
let res = min_path_sum_dfs(&grid, n as i32 - 1, m as i32 - 1);
println!("左上から右下までの最小経路和は {res}");
// メモ化探索
let mut mem = vec![vec![0; m]; n];
for row in mem.iter_mut() {
row.fill(-1);
}
let res = min_path_sum_dfs_mem(&grid, &mut mem, n as i32 - 1, m as i32 - 1);
println!("左上から右下までの最小経路和は {res}");
// 動的計画法
let res = min_path_sum_dp(&grid);
println!("左上から右下までの最小経路和は {res}");
// 空間最適化後の動的計画法
let res = min_path_sum_dp_comp(&grid);
println!("左上から右下までの最小経路和は {res}");
}
@@ -0,0 +1,60 @@
/*
* File: unbounded_knapsack.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
/* 完全ナップサック問題:動的計画法 */
fn unbounded_knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
let n = wgt.len();
// dp テーブルを初期化
let mut dp = vec![vec![0; cap + 1]; n + 1];
// 状態遷移
for i in 1..=n {
for c in 1..=cap {
if wgt[i - 1] > c as i32 {
// ナップサック容量を超えるなら品物 i は選ばない
dp[i][c] = dp[i - 1][c];
} else {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[i][c] = std::cmp::max(dp[i - 1][c], dp[i][c - wgt[i - 1] as usize] + val[i - 1]);
}
}
}
return dp[n][cap];
}
/* 完全ナップサック問題:空間最適化後の動的計画法 */
fn unbounded_knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
let n = wgt.len();
// dp テーブルを初期化
let mut dp = vec![0; cap + 1];
// 状態遷移
for i in 1..=n {
for c in 1..=cap {
if wgt[i - 1] > c as i32 {
// ナップサック容量を超えるなら品物 i は選ばない
dp[c] = dp[c];
} else {
// 品物 i を選ばない場合と選ぶ場合の大きい方
dp[c] = std::cmp::max(dp[c], dp[c - wgt[i - 1] as usize] + val[i - 1]);
}
}
}
dp[cap]
}
/* Driver Code */
pub fn main() {
let wgt = [1, 2, 3];
let val = [5, 11, 15];
let cap: usize = 4;
// 動的計画法
let res = unbounded_knapsack_dp(&wgt, &val, cap);
println!("ナップサック容量を超えない最大価値は {res}");
// 空間最適化後の動的計画法
let res = unbounded_knapsack_dp_comp(&wgt, &val, cap);
println!("ナップサック容量を超えない最大価値は {res}");
}
@@ -0,0 +1,135 @@
/*
* File: graph_adjacency_list.rs
* Created Time: 2023-07-12
* Author: night-cruise (2586447362@qq.com)
*/
pub use hello_algo_rust::include::{vals_to_vets, vets_to_vals, Vertex};
use std::collections::HashMap;
/* 隣接リストに基づく無向グラフ型 */
pub struct GraphAdjList {
// 隣接リスト。key は頂点、value はその頂点に隣接する全頂点
pub adj_list: HashMap<Vertex, Vec<Vertex>>, // maybe HashSet<Vertex> for value part is better?
}
impl GraphAdjList {
/* コンストラクタ */
pub fn new(edges: Vec<[Vertex; 2]>) -> Self {
let mut graph = GraphAdjList {
adj_list: HashMap::new(),
};
// すべての頂点と辺を追加
for edge in edges {
graph.add_vertex(edge[0]);
graph.add_vertex(edge[1]);
graph.add_edge(edge[0], edge[1]);
}
graph
}
/* 頂点数を取得 */
#[allow(unused)]
pub fn size(&self) -> usize {
self.adj_list.len()
}
/* 辺を追加 */
pub fn add_edge(&mut self, vet1: Vertex, vet2: Vertex) {
if vet1 == vet2 {
panic!("value error");
}
// 辺 vet1 - vet2 を追加
self.adj_list.entry(vet1).or_default().push(vet2);
self.adj_list.entry(vet2).or_default().push(vet1);
}
/* 辺を削除 */
#[allow(unused)]
pub fn remove_edge(&mut self, vet1: Vertex, vet2: Vertex) {
if vet1 == vet2 {
panic!("value error");
}
// 辺 vet1 - vet2 を削除
self.adj_list
.entry(vet1)
.and_modify(|v| v.retain(|&e| e != vet2));
self.adj_list
.entry(vet2)
.and_modify(|v| v.retain(|&e| e != vet1));
}
/* 頂点を追加 */
pub fn add_vertex(&mut self, vet: Vertex) {
if self.adj_list.contains_key(&vet) {
return;
}
// 隣接リストに新しいリストを追加
self.adj_list.insert(vet, vec![]);
}
/* 頂点を削除 */
#[allow(unused)]
pub fn remove_vertex(&mut self, vet: Vertex) {
// 隣接リストから頂点 vet に対応するリストを削除
self.adj_list.remove(&vet);
// 他の頂点のリストを走査し、vet を含むすべての辺を削除
for list in self.adj_list.values_mut() {
list.retain(|&v| v != vet);
}
}
/* 隣接リストを出力 */
pub fn print(&self) {
println!("隣接リスト =");
for (vertex, list) in &self.adj_list {
let list = list.iter().map(|vertex| vertex.val).collect::<Vec<i32>>();
println!("{}: {:?},", vertex.val, list);
}
}
}
/* Driver Code */
#[allow(unused)]
fn main() {
/* 無向グラフを初期化 */
let v = vals_to_vets(vec![1, 3, 2, 5, 4]);
let edges = vec![
[v[0], v[1]],
[v[0], v[3]],
[v[1], v[2]],
[v[2], v[3]],
[v[2], v[4]],
[v[3], v[4]],
];
let mut graph = GraphAdjList::new(edges);
println!("\n初期化後、グラフは");
graph.print();
/* 辺を追加 */
// 頂点 1, 2 は v[0], v[2]
graph.add_edge(v[0], v[2]);
println!("\n辺 1-2 を追加した後、グラフは");
graph.print();
/* 辺を削除 */
// 頂点 1, 3 は v[0], v[1]
graph.remove_edge(v[0], v[1]);
println!("\n辺 1-3 を削除した後、グラフは");
graph.print();
/* 頂点を追加 */
let v5 = Vertex { val: 6 };
graph.add_vertex(v5);
println!("\n頂点 6 を追加した後、グラフは");
graph.print();
/* 頂点を削除 */
// 頂点 3 は v[1]
graph.remove_vertex(v[1]);
println!("\n頂点 3 を削除した後、グラフは");
graph.print();
}
@@ -0,0 +1,136 @@
/*
* File: graph_adjacency_matrix.rs
* Created Time: 2023-07-12
* Author: night-cruise (2586447362@qq.com)
*/
/* 隣接行列に基づく無向グラフ型 */
pub struct GraphAdjMat {
// 頂点リスト。要素は「頂点値」、インデックスは「頂点インデックス」を表す
pub vertices: Vec<i32>,
// 隣接行列。行・列のインデックスは「頂点インデックス」に対応
pub adj_mat: Vec<Vec<i32>>,
}
impl GraphAdjMat {
/* コンストラクタ */
pub fn new(vertices: Vec<i32>, edges: Vec<[usize; 2]>) -> Self {
let mut graph = GraphAdjMat {
vertices: vec![],
adj_mat: vec![],
};
// 頂点を追加
for val in vertices {
graph.add_vertex(val);
}
// 辺を追加
// 注意:edges の各要素は頂点インデックスを表し、vertices の要素インデックスに対応する
for edge in edges {
graph.add_edge(edge[0], edge[1])
}
graph
}
/* 頂点数を取得 */
pub fn size(&self) -> usize {
self.vertices.len()
}
/* 頂点を追加 */
pub fn add_vertex(&mut self, val: i32) {
let n = self.size();
// 頂点リストに新しい頂点の値を追加
self.vertices.push(val);
// 隣接行列に 1 行追加
self.adj_mat.push(vec![0; n]);
// 隣接行列に 1 列追加
for row in self.adj_mat.iter_mut() {
row.push(0);
}
}
/* 頂点を削除 */
pub fn remove_vertex(&mut self, index: usize) {
if index >= self.size() {
panic!("index error")
}
// 頂点リストから index の頂点を削除する
self.vertices.remove(index);
// 隣接行列で index 行を削除する
self.adj_mat.remove(index);
// 隣接行列で index 列を削除する
for row in self.adj_mat.iter_mut() {
row.remove(index);
}
}
/* 辺を追加 */
pub fn add_edge(&mut self, i: usize, j: usize) {
// パラメータ i, j は vertices の要素インデックスに対応する
// 範囲外と同値の場合の処理
if i >= self.size() || j >= self.size() || i == j {
panic!("index error")
}
// 無向グラフでは、隣接行列は主対角線に関して対称、すなわち (i, j) == (j, i) を満たす
self.adj_mat[i][j] = 1;
self.adj_mat[j][i] = 1;
}
/* 辺を削除 */
// 引数 i, j は vertices の要素インデックスに対応する
pub fn remove_edge(&mut self, i: usize, j: usize) {
// パラメータ i, j は vertices の要素インデックスに対応する
// 範囲外と同値の場合の処理
if i >= self.size() || j >= self.size() || i == j {
panic!("index error")
}
self.adj_mat[i][j] = 0;
self.adj_mat[j][i] = 0;
}
/* 隣接行列を出力 */
pub fn print(&self) {
println!("頂点リスト = {:?}", self.vertices);
println!("隣接行列 =");
println!("[");
for row in &self.adj_mat {
println!(" {:?},", row);
}
println!("]")
}
}
/* Driver Code */
fn main() {
/* 無向グラフを初期化 */
// edges の要素は頂点インデックス、すなわち vertices の要素インデックスに対応する点に注意
let vertices = vec![1, 3, 2, 5, 4];
let edges = vec![[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]];
let mut graph = GraphAdjMat::new(vertices, edges);
println!("\n初期化後、グラフは");
graph.print();
/* 辺を追加 */
// 頂点 1, 2 のインデックスはそれぞれ 0, 2
graph.add_edge(0, 2);
println!("\n辺 1-2 を追加した後、グラフは");
graph.print();
/* 辺を削除 */
// 頂点 1, 3 のインデックスはそれぞれ 0, 1
graph.remove_edge(0, 1);
println!("\n辺 1-3 を削除した後、グラフは");
graph.print();
/* 頂点を追加 */
graph.add_vertex(6);
println!("\n頂点 6 を追加した後、グラフは");
graph.print();
/* 頂点を削除 */
// 頂点 3 のインデックスは 1
graph.remove_vertex(1);
println!("\n頂点 3 を削除した後、グラフは");
graph.print();
}
+69
View File
@@ -0,0 +1,69 @@
/*
* File: graph_bfs.rs
* Created Time: 2023-07-12
* Author: night-cruise (2586447362@qq.com)
*/
mod graph_adjacency_list;
use graph_adjacency_list::GraphAdjList;
use graph_adjacency_list::{vals_to_vets, vets_to_vals, Vertex};
use std::collections::{HashSet, VecDeque};
/* 幅優先探索 */
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
// 頂点の走査順序
let mut res = vec![];
// 訪問済み頂点を記録するためのハッシュ集合
let mut visited = HashSet::new();
visited.insert(start_vet);
// BFS の実装にキューを用いる
let mut que = VecDeque::new();
que.push_back(start_vet);
// 頂点 vet を起点に、すべての頂点を訪問し終えるまで繰り返す
while let Some(vet) = que.pop_front() {
res.push(vet); // 訪問した頂点を記録
// この頂点のすべての隣接頂点を走査
if let Some(adj_vets) = graph.adj_list.get(&vet) {
for &adj_vet in adj_vets {
if visited.contains(&adj_vet) {
continue; // 訪問済みの頂点をスキップ
}
que.push_back(adj_vet); // 未訪問の頂点のみをキューに追加
visited.insert(adj_vet); // この頂点を訪問済みにする
}
}
}
// 頂点の走査順を返す
res
}
/* Driver Code */
fn main() {
/* 無向グラフを初期化 */
let v = vals_to_vets(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
let edges = vec![
[v[0], v[1]],
[v[0], v[3]],
[v[1], v[2]],
[v[1], v[4]],
[v[2], v[5]],
[v[3], v[4]],
[v[3], v[6]],
[v[4], v[5]],
[v[4], v[7]],
[v[5], v[8]],
[v[6], v[7]],
[v[7], v[8]],
];
let graph = GraphAdjList::new(edges);
println!("\n初期化後、グラフは");
graph.print();
/* 幅優先探索 */
let res = graph_bfs(graph, v[0]);
println!("\n幅優先探索(BFS)の頂点順序は");
println!("{:?}", vets_to_vals(res));
}
+61
View File
@@ -0,0 +1,61 @@
/*
* File: graph_dfs.rs
* Created Time: 2023-07-12
* Author: night-cruise (2586447362@qq.com)
*/
mod graph_adjacency_list;
use graph_adjacency_list::GraphAdjList;
use graph_adjacency_list::{vals_to_vets, vets_to_vals, Vertex};
use std::collections::HashSet;
/* 深さ優先走査の補助関数 */
fn dfs(graph: &GraphAdjList, visited: &mut HashSet<Vertex>, res: &mut Vec<Vertex>, vet: Vertex) {
res.push(vet); // 訪問した頂点を記録
visited.insert(vet); // この頂点を訪問済みにする
// この頂点のすべての隣接頂点を走査
if let Some(adj_vets) = graph.adj_list.get(&vet) {
for &adj_vet in adj_vets {
if visited.contains(&adj_vet) {
continue; // 訪問済みの頂点をスキップ
}
// 隣接頂点を再帰的に訪問
dfs(graph, visited, res, adj_vet);
}
}
}
/* 深さ優先探索 */
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
fn graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
// 頂点の走査順序
let mut res = vec![];
// 訪問済み頂点を記録するためのハッシュ集合
let mut visited = HashSet::new();
dfs(&graph, &mut visited, &mut res, start_vet);
res
}
/* Driver Code */
fn main() {
/* 無向グラフを初期化 */
let v = vals_to_vets(vec![0, 1, 2, 3, 4, 5, 6]);
let edges = vec![
[v[0], v[1]],
[v[0], v[3]],
[v[1], v[2]],
[v[2], v[5]],
[v[4], v[5]],
[v[5], v[6]],
];
let graph = GraphAdjList::new(edges);
println!("\n初期化後、グラフは");
graph.print();
/* 深さ優先探索 */
let res = graph_dfs(graph, v[0]);
println!("\n深さ優先探索(DFS)の頂点順序は");
println!("{:?}", vets_to_vals(res));
}
@@ -0,0 +1,54 @@
/*
* File: coin_change_greedy.rs
* Created Time: 2023-07-22
* Author: night-cruise (2586447362@qq.com)
*/
/* コイン交換:貪欲法 */
fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 {
// coins リストはソート済みと仮定する
let mut i = coins.len() - 1;
let mut count = 0;
// 残額がなくなるまで貪欲選択を繰り返す
while amt > 0 {
// 残額以下で最も近い硬貨を見つける
while i > 0 && coins[i] > amt {
i -= 1;
}
// coins[i] を選択する
amt -= coins[i];
count += 1;
}
// 実行可能な解が見つからなければ -1 を返す
if amt == 0 {
count
} else {
-1
}
}
/* Driver Code */
fn main() {
// 貪欲法:大域最適解を保証できる
let coins = [1, 5, 10, 20, 50, 100];
let amt = 186;
let res = coin_change_greedy(&coins, amt);
println!("\ncoins = {:?}, amt = {}", coins, amt);
println!("{} にするために必要な最小硬貨枚数は {}", amt, res);
// 貪欲法:大域最適解を保証できない
let coins = [1, 20, 50];
let amt = 60;
let res = coin_change_greedy(&coins, amt);
println!("\ncoins = {:?}, amt = {}", coins, amt);
println!("{} にするために必要な最小硬貨枚数は {}", amt, res);
println!("実際に必要な最小枚数は 3、つまり 20 + 20 + 20");
// 貪欲法:大域最適解を保証できない
let coins = [1, 49, 50];
let amt = 98;
let res = coin_change_greedy(&coins, amt);
println!("\ncoins = {:?}, amt = {}", coins, amt);
println!("{} にするために必要な最小硬貨枚数は {}", amt, res);
println!("実際に必要な最小枚数は 2、つまり 49 + 49");
}
@@ -0,0 +1,59 @@
/*
* File: coin_change_greedy.rs
* Created Time: 2023-07-22
* Author: night-cruise (2586447362@qq.com)
*/
/* 品物 */
struct Item {
w: i32, // 品物の重さ
v: i32, // 品物の価値
}
impl Item {
fn new(w: i32, v: i32) -> Self {
Self { w, v }
}
}
/* 分数ナップサック:貪欲法 */
fn fractional_knapsack(wgt: &[i32], val: &[i32], mut cap: i32) -> f64 {
// 重さと価値の 2 属性を持つ品物リストを作成
let mut items = wgt
.iter()
.zip(val.iter())
.map(|(&w, &v)| Item::new(w, v))
.collect::<Vec<Item>>();
// 単位価値 item.v / item.w の高い順にソートする
items.sort_by(|a, b| {
(b.v as f64 / b.w as f64)
.partial_cmp(&(a.v as f64 / a.w as f64))
.unwrap()
});
// 貪欲選択を繰り返す
let mut res = 0.0;
for item in &items {
if item.w <= cap {
// 残り容量が十分なら、現在の品物を丸ごとナップサックに入れる
res += item.v as f64;
cap -= item.w;
} else {
// 残り容量が足りない場合は、現在の品物の一部だけをナップサックに入れる
res += item.v as f64 / item.w as f64 * cap as f64;
// 残り容量がないため、ループを抜ける
break;
}
}
res
}
/* Driver Code */
fn main() {
let wgt = [10, 20, 30, 40, 50];
let val = [50, 120, 150, 210, 240];
let cap = 50;
// 貪欲法
let res = fractional_knapsack(&wgt, &val, cap);
println!("ナップサック容量を超えない最大価値は {}", res);
}
@@ -0,0 +1,36 @@
/*
* File: coin_change_greedy.rs
* Created Time: 2023-07-22
* Author: night-cruise (2586447362@qq.com)
*/
/* 最大容量:貪欲法 */
fn max_capacity(ht: &[i32]) -> i32 {
// i, j を初期化し、それぞれ配列の両端に置く
let mut i = 0;
let mut j = ht.len() - 1;
// 初期の最大容量は 0
let mut res = 0;
// 2 枚の板が出会うまで貪欲選択を繰り返す
while i < j {
// 最大容量を更新する
let cap = std::cmp::min(ht[i], ht[j]) * (j - i) as i32;
res = std::cmp::max(res, cap);
// 短い方を内側へ動かす
if ht[i] < ht[j] {
i += 1;
} else {
j -= 1;
}
}
res
}
/* Driver Code */
fn main() {
let ht = [3, 8, 5, 2, 7, 7, 3, 4];
// 貪欲法
let res = max_capacity(&ht);
println!("最大容量は {}", res);
}
@@ -0,0 +1,35 @@
/*
* File: coin_change_greedy.rs
* Created Time: 2023-07-22
* Author: night-cruise (2586447362@qq.com)
*/
/* 最大切断積:貪欲法 */
fn max_product_cutting(n: i32) -> i32 {
// n <= 3 のときは、必ず 1 を切り出す
if n <= 3 {
return 1 * (n - 1);
}
// 貪欲に 3 を切り出し、a を 3 の個数、b を余りとする
let a = n / 3;
let b = n % 3;
if b == 1 {
// 余りが 1 のときは、1 * 3 を 2 * 2 に変える
3_i32.pow(a as u32 - 1) * 2 * 2
} else if b == 2 {
// 余りが 2 のときは、そのままにする
3_i32.pow(a as u32) * 2
} else {
// 余りが 0 のときは、そのままにする
3_i32.pow(a as u32)
}
}
/* Driver Code */
fn main() {
let n = 58;
// 貪欲法
let res = max_product_cutting(n);
println!("最大分割積は {}", res);
}
@@ -0,0 +1,124 @@
/**
* File: array_hash_map.rs
* Created Time: 2023-2-18
* Author: xBLACICEx (xBLACKICEx@outlook.com)
*/
/* キーと値の組 */
#[derive(Debug, Clone, PartialEq)]
pub struct Pair {
pub key: i32,
pub val: String,
}
/* 配列ベースのハッシュテーブル */
pub struct ArrayHashMap {
buckets: Vec<Option<Pair>>,
}
impl ArrayHashMap {
pub fn new() -> ArrayHashMap {
// 100 個のバケットを含む配列を初期化
Self {
buckets: vec![None; 100],
}
}
/* ハッシュ関数 */
fn hash_func(&self, key: i32) -> usize {
key as usize % 100
}
/* 検索操作 */
pub fn get(&self, key: i32) -> Option<&String> {
let index = self.hash_func(key);
self.buckets[index].as_ref().map(|pair| &pair.val)
}
/* 追加操作 */
pub fn put(&mut self, key: i32, val: &str) {
let index = self.hash_func(key);
self.buckets[index] = Some(Pair {
key,
val: val.to_string(),
});
}
/* 削除操作 */
pub fn remove(&mut self, key: i32) {
let index = self.hash_func(key);
// None に設定し、削除を表す
self.buckets[index] = None;
}
/* すべてのキーと値のペアを取得 */
pub fn entry_set(&self) -> Vec<&Pair> {
self.buckets
.iter()
.filter_map(|pair| pair.as_ref())
.collect()
}
/* すべてのキーを取得 */
pub fn key_set(&self) -> Vec<&i32> {
self.buckets
.iter()
.filter_map(|pair| pair.as_ref().map(|pair| &pair.key))
.collect()
}
/* すべての値を取得 */
pub fn value_set(&self) -> Vec<&String> {
self.buckets
.iter()
.filter_map(|pair| pair.as_ref().map(|pair| &pair.val))
.collect()
}
/* ハッシュテーブルを出力 */
pub fn print(&self) {
for pair in self.entry_set() {
println!("{} -> {}", pair.key, pair.val);
}
}
}
fn main() {
/* ハッシュテーブルを初期化 */
let mut map = ArrayHashMap::new();
/* 追加操作 */
// ハッシュテーブルにキーと値の組(key, value)を追加
map.put(12836, "シャオハー");
map.put(15937, "シャオルオ");
map.put(16750, "シャオスワン");
map.put(13276, "シャオファー");
map.put(10583, "シャオヤー");
println!("\n追加後、ハッシュテーブルは\nKey -> Value");
map.print();
/* 検索操作 */
// キー key をハッシュテーブルに渡し、値 value を取得
let name = map.get(15937).unwrap();
println!("\n学籍番号 15937 を入力すると、名前 {} が見つかりました", name);
/* 削除操作 */
// ハッシュテーブルからキーと値のペア (key, value) を削除
map.remove(10583);
println!("\n10583 を削除した後、ハッシュテーブルは\nKey -> Value");
map.print();
/* ハッシュテーブルを走査 */
println!("\nキーと値のペア Key->Value を走査");
for pair in map.entry_set() {
println!("{} -> {}", pair.key, pair.val);
}
println!("\nキー Key のみを走査");
for key in map.key_set() {
println!("{}", key);
}
println!("\n値 Value のみを走査");
for val in map.value_set() {
println!("{}", val);
}
}
@@ -0,0 +1,49 @@
/*
* File: build_in_hash.rs
* Created Time: 2023-7-6
* Author: WSL0809 (wslzzy@outlook.com)
*/
use hello_algo_rust::include::ListNode;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
/* Driver Code */
fn main() {
let num = 3;
let mut num_hasher = DefaultHasher::new();
num.hash(&mut num_hasher);
let hash_num = num_hasher.finish();
println!("整数 {} のハッシュ値は {}", num, hash_num);
let bol = true;
let mut bol_hasher = DefaultHasher::new();
bol.hash(&mut bol_hasher);
let hash_bol = bol_hasher.finish();
println!("真偽値 {} のハッシュ値は {}", bol, hash_bol);
let dec: f32 = 3.14159;
let mut dec_hasher = DefaultHasher::new();
dec.to_bits().hash(&mut dec_hasher);
let hash_dec = dec_hasher.finish();
println!("小数 {} のハッシュ値は {}", dec, hash_dec);
let str = "Hello アルゴリズム";
let mut str_hasher = DefaultHasher::new();
str.hash(&mut str_hasher);
let hash_str = str_hasher.finish();
println!("文字列 {} のハッシュ値は {}", str, hash_str);
let arr = (&12836, &"シャオハー");
let mut tup_hasher = DefaultHasher::new();
arr.hash(&mut tup_hasher);
let hash_tup = tup_hasher.finish();
println!("タプル {:?} のハッシュ値は {}", arr, hash_tup);
let node = ListNode::new(42);
let mut hasher = DefaultHasher::new();
node.borrow().val.hash(&mut hasher);
let hash = hasher.finish();
println!("ノードオブジェクト {:?} のハッシュ値は{}", node, hash);
}
+48
View File
@@ -0,0 +1,48 @@
/*
* File: hash_map.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
use std::collections::HashMap;
/* Driver Code */
pub fn main() {
// ハッシュテーブルを初期化
let mut map = HashMap::new();
// 追加操作
// ハッシュテーブルにキーと値の組 (key, value) を追加する
map.insert(12836, "シャオハー");
map.insert(15937, "シャオロー");
map.insert(16750, "シャオスワン");
map.insert(13276, "シャオファー");
map.insert(10583, "シャオヤー");
println!("\n追加後、ハッシュテーブルは\nKey -> Value");
print_util::print_hash_map(&map);
// 検索操作
// ハッシュテーブルにキー key を入力し、値 value を取得する
let name = map.get(&15937).copied().unwrap();
println!("\n学籍番号 15937 を入力すると、名前 {name} が見つかります");
// 削除操作
// ハッシュテーブルからキーと値の組 (key, value) を削除する
_ = map.remove(&10583);
println!("\n10583 を削除した後、ハッシュテーブルは\nKey -> Value");
print_util::print_hash_map(&map);
// ハッシュテーブルを走査
println!("\nキーと値のペア Key->Value を走査");
print_util::print_hash_map(&map);
println!("\nキー Key のみを走査");
for key in map.keys() {
println!("{key}");
}
println!("\n値 value のみを走査");
for value in map.values() {
println!("{value}");
}
}
@@ -0,0 +1,160 @@
/*
* File: hash_map_chaining.rs
* Created Time: 2023-07-07
* Author: WSL0809 (wslzzy@outlook.com)
*/
#[derive(Clone)]
/* キーと値の組 */
struct Pair {
key: i32,
val: String,
}
/* チェイン法ハッシュテーブル */
struct HashMapChaining {
size: usize,
capacity: usize,
load_thres: f32,
extend_ratio: usize,
buckets: Vec<Vec<Pair>>,
}
impl HashMapChaining {
/* コンストラクタ */
fn new() -> Self {
Self {
size: 0,
capacity: 4,
load_thres: 2.0 / 3.0,
extend_ratio: 2,
buckets: vec![vec![]; 4],
}
}
/* ハッシュ関数 */
fn hash_func(&self, key: i32) -> usize {
key as usize % self.capacity
}
/* 負荷率 */
fn load_factor(&self) -> f32 {
self.size as f32 / self.capacity as f32
}
/* 削除操作 */
fn remove(&mut self, key: i32) -> Option<String> {
let index = self.hash_func(key);
// バケットを走査してキーと値のペアを削除
for (i, p) in self.buckets[index].iter_mut().enumerate() {
if p.key == key {
let pair = self.buckets[index].remove(i);
self.size -= 1;
return Some(pair.val);
}
}
// key が見つからない場合は None を返す
None
}
/* ハッシュテーブルを拡張 */
fn extend(&mut self) {
// 元のハッシュテーブルを一時保存
let buckets_tmp = std::mem::take(&mut self.buckets);
// リサイズ後の新しいハッシュテーブルを初期化
self.capacity *= self.extend_ratio;
self.buckets = vec![Vec::new(); self.capacity as usize];
self.size = 0;
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
for bucket in buckets_tmp {
for pair in bucket {
self.put(pair.key, pair.val);
}
}
}
/* ハッシュテーブルを出力 */
fn print(&self) {
for bucket in &self.buckets {
let mut res = Vec::new();
for pair in bucket {
res.push(format!("{} -> {}", pair.key, pair.val));
}
println!("{:?}", res);
}
}
/* 追加操作 */
fn put(&mut self, key: i32, val: String) {
// 負荷率がしきい値を超えたら、リサイズを実行
if self.load_factor() > self.load_thres {
self.extend();
}
let index = self.hash_func(key);
// バケットを走査し、指定した key が見つかれば対応する val を更新して返す
for pair in self.buckets[index].iter_mut() {
if pair.key == key {
pair.val = val;
return;
}
}
// その key が存在しなければ、キーと値のペアを末尾に追加
let pair = Pair { key, val };
self.buckets[index].push(pair);
self.size += 1;
}
/* 検索操作 */
fn get(&self, key: i32) -> Option<&str> {
let index = self.hash_func(key);
// バケットを走査し、key が見つかれば対応する val を返す
for pair in self.buckets[index].iter() {
if pair.key == key {
return Some(&pair.val);
}
}
// key が見つからない場合は None を返す
None
}
}
/* Driver Code */
pub fn main() {
/* ハッシュテーブルを初期化 */
let mut map = HashMapChaining::new();
/* 追加操作 */
// ハッシュテーブルにキーと値のペア (key, value) を追加
map.put(12836, "シャオハー".to_string());
map.put(15937, "シャオロー".to_string());
map.put(16750, "シャオスワン".to_string());
map.put(13276, "シャオファー".to_string());
map.put(10583, "シャオヤー".to_string());
println!("\n追加後、ハッシュテーブルは\nKey -> Value");
map.print();
/* 検索操作 */
// キー key をハッシュテーブルに渡し、値 value を取得
println!(
"\n学籍番号 13276 を入力すると、名前 {} が見つかります",
match map.get(13276) {
Some(value) => value,
None => "Not a valid Key",
}
);
/* 削除操作 */
// ハッシュテーブルからキーと値のペア (key, value) を削除
map.remove(12836);
println!("\n12836 を削除すると、ハッシュテーブルは\nKey -> Value");
map.print();
}
@@ -0,0 +1,181 @@
/*
* File: hash_map_open_addressing.rs
* Created Time: 2023-07-16
* Author: WSL0809 (wslzzy@outlook.com), night-cruise (2586447362@qq.com)
*/
#![allow(non_snake_case)]
#![allow(unused)]
mod array_hash_map;
use array_hash_map::Pair;
/* オープンアドレス法ハッシュテーブル */
struct HashMapOpenAddressing {
size: usize, // キーと値のペア数
capacity: usize, // ハッシュテーブル容量
load_thres: f64, // リサイズを発動する負荷率のしきい値
extend_ratio: usize, // 拡張倍率
buckets: Vec<Option<Pair>>, // バケット配列
TOMBSTONE: Option<Pair>, // 削除済みマーク
}
impl HashMapOpenAddressing {
/* コンストラクタ */
fn new() -> Self {
Self {
size: 0,
capacity: 4,
load_thres: 2.0 / 3.0,
extend_ratio: 2,
buckets: vec![None; 4],
TOMBSTONE: Some(Pair {
key: -1,
val: "-1".to_string(),
}),
}
}
/* ハッシュ関数 */
fn hash_func(&self, key: i32) -> usize {
(key % self.capacity as i32) as usize
}
/* 負荷率 */
fn load_factor(&self) -> f64 {
self.size as f64 / self.capacity as f64
}
/* key に対応するバケットインデックスを探す */
fn find_bucket(&mut self, key: i32) -> usize {
let mut index = self.hash_func(key);
let mut first_tombstone = -1;
// 線形プロービングを行い、空バケットに達したら終了
while self.buckets[index].is_some() {
// `key` に遭遇したら、対応するバケットのインデックスを返す
if self.buckets[index].as_ref().unwrap().key == key {
// 以前に削除マークに遭遇していた場合は、キーと値のペアをそのインデックスへ移動する
if first_tombstone != -1 {
self.buckets[first_tombstone as usize] = self.buckets[index].take();
self.buckets[index] = self.TOMBSTONE.clone();
return first_tombstone as usize; // 移動後のバケットインデックスを返す
}
return index; // バケットのインデックスを返す
}
// 最初に見つかった削除マークを記録
if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE {
first_tombstone = index as i32;
}
// バケットのインデックスを計算し、末尾を越えたら先頭に戻る
index = (index + 1) % self.capacity;
}
// key が存在しない場合は追加位置のインデックスを返す
if first_tombstone == -1 {
index
} else {
first_tombstone as usize
}
}
/* 検索操作 */
fn get(&mut self, key: i32) -> Option<&str> {
// key に対応するバケットインデックスを探す
let index = self.find_bucket(key);
// キーと値の組が見つかったら、対応する val を返す
if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE {
return self.buckets[index].as_ref().map(|pair| &pair.val as &str);
}
// キーと値の組が存在しなければ null を返す
None
}
/* 追加操作 */
fn put(&mut self, key: i32, val: String) {
// 負荷率がしきい値を超えたら、リサイズを実行
if self.load_factor() > self.load_thres {
self.extend();
}
// key に対応するバケットインデックスを探す
let index = self.find_bucket(key);
// キーと値の組が見つかったら、val を上書きして返す
if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE {
self.buckets[index].as_mut().unwrap().val = val;
return;
}
// キーと値の組が存在しない場合は、その組を追加する
self.buckets[index] = Some(Pair { key, val });
self.size += 1;
}
/* 削除操作 */
fn remove(&mut self, key: i32) {
// key に対応するバケットインデックスを探す
let index = self.find_bucket(key);
// キーと値の組が見つかったら、削除マーカーで上書きする
if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE {
self.buckets[index] = self.TOMBSTONE.clone();
self.size -= 1;
}
}
/* ハッシュテーブルを拡張 */
fn extend(&mut self) {
// 元のハッシュテーブルを一時保存
let buckets_tmp = self.buckets.clone();
// リサイズ後の新しいハッシュテーブルを初期化
self.capacity *= self.extend_ratio;
self.buckets = vec![None; self.capacity];
self.size = 0;
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
for pair in buckets_tmp {
if pair.is_none() || pair == self.TOMBSTONE {
continue;
}
let pair = pair.unwrap();
self.put(pair.key, pair.val);
}
}
/* ハッシュテーブルを出力 */
fn print(&self) {
for pair in &self.buckets {
if pair.is_none() {
println!("null");
} else if pair == &self.TOMBSTONE {
println!("TOMBSTONE");
} else {
let pair = pair.as_ref().unwrap();
println!("{} -> {}", pair.key, pair.val);
}
}
}
}
/* Driver Code */
fn main() {
/* ハッシュテーブルを初期化 */
let mut hashmap = HashMapOpenAddressing::new();
/* 追加操作 */
// ハッシュテーブルにキーと値のペア (key, value) を追加
hashmap.put(12836, "シャオハー".to_string());
hashmap.put(15937, "シャオロー".to_string());
hashmap.put(16750, "シャオスワン".to_string());
hashmap.put(13276, "シャオファー".to_string());
hashmap.put(10583, "シャオヤー".to_string());
println!("\n追加後、ハッシュテーブルは\nKey -> Value");
hashmap.print();
/* 検索操作 */
// ハッシュテーブルにキー key を入力し、値 val を得る
let name = hashmap.get(13276).unwrap();
println!("\n学籍番号 13276 を入力すると、名前 {} が見つかります", name);
/* 削除操作 */
// ハッシュテーブルからキーと値の組 (key, val) を削除
hashmap.remove(16750);
println!("\n16750 を削除すると、ハッシュテーブルは\nKey -> Value");
hashmap.print();
}
@@ -0,0 +1,70 @@
/*
* File: simple_hash.rs
* Created Time: 2023-09-07
* Author: night-cruise (2586447362@qq.com)
*/
/* 加算ハッシュ */
fn add_hash(key: &str) -> i32 {
let mut hash = 0_i64;
const MODULUS: i64 = 1000000007;
for c in key.chars() {
hash = (hash + c as i64) % MODULUS;
}
hash as i32
}
/* 乗算ハッシュ */
fn mul_hash(key: &str) -> i32 {
let mut hash = 0_i64;
const MODULUS: i64 = 1000000007;
for c in key.chars() {
hash = (31 * hash + c as i64) % MODULUS;
}
hash as i32
}
/* XOR ハッシュ */
fn xor_hash(key: &str) -> i32 {
let mut hash = 0_i64;
const MODULUS: i64 = 1000000007;
for c in key.chars() {
hash ^= c as i64;
}
(hash & MODULUS) as i32
}
/* 回転ハッシュ */
fn rot_hash(key: &str) -> i32 {
let mut hash = 0_i64;
const MODULUS: i64 = 1000000007;
for c in key.chars() {
hash = ((hash << 4) ^ (hash >> 28) ^ c as i64) % MODULUS;
}
hash as i32
}
/* Driver Code */
fn main() {
let key = "Hello アルゴリズム";
let hash = add_hash(key);
println!("加算ハッシュ値は {hash}");
let hash = mul_hash(key);
println!("乗算ハッシュ値は {hash}");
let hash = xor_hash(key);
println!("XORハッシュ値は {hash}");
let hash = rot_hash(key);
println!("回転ハッシュ値は {hash}");
}
+71
View File
@@ -0,0 +1,71 @@
/*
* File: heap.rs
* Created Time: 2023-07-16
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
use std::{cmp::Reverse, collections::BinaryHeap};
fn test_push_max(heap: &mut BinaryHeap<i32>, val: i32) {
heap.push(val); // 要素をヒープに追加
println!("\n要素 {} をヒープに追加した後", val);
print_util::print_heap(heap.iter().map(|&val| val).collect());
}
fn test_pop_max(heap: &mut BinaryHeap<i32>) {
let val = heap.pop().unwrap();
println!("\nヒープ先頭要素 {} を取り出した後", val);
print_util::print_heap(heap.iter().map(|&val| val).collect());
}
/* Driver Code */
fn main() {
/* ヒープを初期化 */
// 最小ヒープを初期化
#[allow(unused_assignments)]
let mut min_heap = BinaryHeap::new();
// Rust の BinaryHeap は最大ヒープであり、最小ヒープには通常 Reverse を使う
// 最大ヒープを初期化する
let mut max_heap = BinaryHeap::new();
println!("\n以下のテストケースは最大ヒープです");
/* 要素をヒープに追加 */
test_push_max(&mut max_heap, 1);
test_push_max(&mut max_heap, 3);
test_push_max(&mut max_heap, 2);
test_push_max(&mut max_heap, 5);
test_push_max(&mut max_heap, 4);
/* ヒープ頂点の要素を取得 */
let peek = max_heap.peek().unwrap();
println!("\nヒープ先頭要素は {}", peek);
/* ヒープ頂点の要素を取り出す */
test_pop_max(&mut max_heap);
test_pop_max(&mut max_heap);
test_pop_max(&mut max_heap);
test_pop_max(&mut max_heap);
test_pop_max(&mut max_heap);
/* ヒープのサイズを取得 */
let size = max_heap.len();
println!("\nヒープ要素数は {}", size);
/* ヒープが空かどうかを判定 */
let is_empty = max_heap.is_empty();
println!("\nヒープは空か {}", is_empty);
/* リストを入力してヒープを構築 */
// 時間計算量は O(n) であり、O(nlogn) ではない
min_heap = BinaryHeap::from(
vec![1, 3, 2, 5, 4]
.into_iter()
.map(|val| Reverse(val))
.collect::<Vec<Reverse<i32>>>(),
);
println!("\nリストを入力して最小ヒープを構築した後");
print_util::print_heap(min_heap.iter().map(|&val| val.0).collect());
}
+165
View File
@@ -0,0 +1,165 @@
/*
* File: my_heap.rs
* Created Time: 2023-07-16
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
/* 最大ヒープ */
struct MaxHeap {
// 配列ではなく vector を使うことで、拡張を考慮せずに済む
max_heap: Vec<i32>,
}
impl MaxHeap {
/* コンストラクタ。入力リストに基づいてヒープを構築する */
fn new(nums: Vec<i32>) -> Self {
// リスト要素をそのままヒープに追加
let mut heap = MaxHeap { max_heap: nums };
// 葉ノード以外のすべてのノードをヒープ化
for i in (0..=Self::parent(heap.size() - 1)).rev() {
heap.sift_down(i);
}
heap
}
/* 左子ノードのインデックスを取得 */
fn left(i: usize) -> usize {
2 * i + 1
}
/* 右子ノードのインデックスを取得 */
fn right(i: usize) -> usize {
2 * i + 2
}
/* 親ノードのインデックスを取得 */
fn parent(i: usize) -> usize {
(i - 1) / 2 // 切り捨て除算
}
/* 要素を交換 */
fn swap(&mut self, i: usize, j: usize) {
self.max_heap.swap(i, j);
}
/* ヒープのサイズを取得 */
fn size(&self) -> usize {
self.max_heap.len()
}
/* ヒープが空かどうかを判定 */
fn is_empty(&self) -> bool {
self.max_heap.is_empty()
}
/* ヒープ先頭要素にアクセス */
fn peek(&self) -> Option<i32> {
self.max_heap.first().copied()
}
/* 要素をヒープに追加 */
fn push(&mut self, val: i32) {
// ノードを追加
self.max_heap.push(val);
// 下から上へヒープ化
self.sift_up(self.size() - 1);
}
/* ノード i から始めて、下から上へヒープ化 */
fn sift_up(&mut self, mut i: usize) {
loop {
// ノード i はすでにヒープの先頭ノードなので、ヒープ化を終了する
if i == 0 {
break;
}
// ノード i の親ノードを取得
let p = Self::parent(i);
// 「ノードの修復が不要」になったら、ヒープ化を終了
if self.max_heap[i] <= self.max_heap[p] {
break;
}
// 2 つのノードを交換
self.swap(i, p);
// ループで下から上へヒープ化
i = p;
}
}
/* 要素をヒープから取り出す */
fn pop(&mut self) -> i32 {
// 空判定の処理
if self.is_empty() {
panic!("index out of bounds");
}
// 根ノードと最も右の葉ノードを交換(先頭要素と末尾要素を交換)
self.swap(0, self.size() - 1);
// ノードを削除
let val = self.max_heap.pop().unwrap();
// 上から下へヒープ化
self.sift_down(0);
// ヒープ先頭要素を返す
val
}
/* ノード i から始めて、上から下へヒープ化 */
fn sift_down(&mut self, mut i: usize) {
loop {
// ノード i, l, r のうち値が最大のノードを ma とする
let (l, r, mut ma) = (Self::left(i), Self::right(i), i);
if l < self.size() && self.max_heap[l] > self.max_heap[ma] {
ma = l;
}
if r < self.size() && self.max_heap[r] > self.max_heap[ma] {
ma = r;
}
// ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
if ma == i {
break;
}
// 2 つのノードを交換
self.swap(i, ma);
// ループで上から下へヒープ化
i = ma;
}
}
/* ヒープ(二分木)を出力 */
fn print(&self) {
print_util::print_heap(self.max_heap.clone());
}
}
/* Driver Code */
fn main() {
/* 最大ヒープを初期化 */
let mut max_heap = MaxHeap::new(vec![9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]);
println!("\nリストを入力してヒープを構築した後");
max_heap.print();
/* ヒープ頂点の要素を取得 */
let peek = max_heap.peek();
if let Some(peek) = peek {
println!("\nヒープ先頭要素は {}", peek);
}
/* 要素をヒープに追加 */
let val = 7;
max_heap.push(val);
println!("\n要素 {} をヒープに追加した後", val);
max_heap.print();
/* ヒープ頂点の要素を取り出す */
let peek = max_heap.pop();
println!("\nヒープ先頭要素 {} を取り出した後", peek);
max_heap.print();
/* ヒープのサイズを取得 */
let size = max_heap.size();
println!("\nヒープ要素数は {}", size);
/* ヒープが空かどうかを判定 */
let is_empty = max_heap.is_empty();
println!("\nヒープは空か {}", is_empty);
}
+39
View File
@@ -0,0 +1,39 @@
/*
* File: top_k.rs
* Created Time: 2023-07-16
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
use std::cmp::Reverse;
use std::collections::BinaryHeap;
/* ヒープに基づいて配列中の最大の k 個の要素を探す */
fn top_k_heap(nums: Vec<i32>, k: usize) -> BinaryHeap<Reverse<i32>> {
// BinaryHeap は最大ヒープであり、Reverse で要素の順序を反転することで最小ヒープを実現する
let mut heap = BinaryHeap::<Reverse<i32>>::new();
// 配列の先頭 k 個の要素をヒープに追加
for &num in nums.iter().take(k) {
heap.push(Reverse(num));
}
// k+1 番目の要素から開始し、ヒープ長を k に保つ
for &num in nums.iter().skip(k) {
// 現在の要素がヒープ先頭より大きければ、ヒープ先頭を取り出して現在の要素を追加する
if num > heap.peek().unwrap().0 {
heap.pop();
heap.push(Reverse(num));
}
}
heap
}
/* Driver Code */
fn main() {
let nums = vec![1, 7, 6, 3, 2];
let k = 3;
let res = top_k_heap(nums, k);
println!("最大の {} 個の要素は", k);
print_util::print_heap(res.into_iter().map(|item| item.0).collect());
}
@@ -0,0 +1,65 @@
/*
* File: binary_search.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com)
*/
/* 二分探索(両閉区間) */
fn binary_search(nums: &[i32], target: i32) -> i32 {
// 両閉区間 [0, n-1] を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素を指す
let mut i = 0;
let mut j = nums.len() as i32 - 1;
// ループし、探索区間が空になったら終了する(i > j で空)
while i <= j {
let m = i + (j - i) / 2; // 中点インデックス m を計算
if nums[m as usize] < target {
// この場合、target は区間 [m+1, j] にある
i = m + 1;
} else if nums[m as usize] > target {
// この場合、target は区間 [i, m-1] にある
j = m - 1;
} else {
// 目標要素が見つかったらそのインデックスを返す
return m;
}
}
// 目標要素が見つからなければ -1 を返す
return -1;
}
/* 二分探索(左閉右開区間) */
fn binary_search_lcro(nums: &[i32], target: i32) -> i32 {
// 左閉右開区間 [0, n) を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素+1を指す
let mut i = 0;
let mut j = nums.len() as i32;
// ループし、探索区間が空になったら終了する(i = j で空)
while i < j {
let m = i + (j - i) / 2; // 中点インデックス m を計算
if nums[m as usize] < target {
// この場合、target は区間 [m+1, j) にある
i = m + 1;
} else if nums[m as usize] > target {
// この場合、target は区間 [i, m) にある
j = m;
} else {
// 目標要素が見つかったらそのインデックスを返す
return m;
}
}
// 目標要素が見つからなければ -1 を返す
return -1;
}
/* Driver Code */
pub fn main() {
let target = 6;
let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35];
// 二分探索(両閉区間)
let mut index = binary_search(&nums, target);
println!("目的の要素 6 のインデックス = {index}");
// 二分探索(左閉右開区間)
index = binary_search_lcro(&nums, target);
println!("目的の要素 6 のインデックス = {index}");
}
@@ -0,0 +1,50 @@
/*
* File: binary_search_edge.rs
* Created Time: 2023-08-30
* Author: night-cruise (2586447362@qq.com)
*/
mod binary_search_insertion;
use binary_search_insertion::binary_search_insertion;
/* 最も左の target を二分探索 */
fn binary_search_left_edge(nums: &[i32], target: i32) -> i32 {
// target の挿入位置を探すのと等価
let i = binary_search_insertion(nums, target);
// target が見つからなければ、-1 を返す
if i == nums.len() as i32 || nums[i as usize] != target {
return -1;
}
// target が見つかったら、インデックス i を返す
i
}
/* 最も右の target を二分探索 */
fn binary_search_right_edge(nums: &[i32], target: i32) -> i32 {
// 最左の target + 1 を探す問題に変換する
let i = binary_search_insertion(nums, target + 1);
// j は最も右の target を指し、i は target より大きい最初の要素を指す
let j = i - 1;
// target が見つからなければ、-1 を返す
if j == -1 || nums[j as usize] != target {
return -1;
}
// target が見つかったら、インデックス j を返す
j
}
/* Driver Code */
fn main() {
// 重複要素を含む配列
let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15];
println!("\n配列 nums = {:?}", nums);
// 二分探索で左端と右端を探す
for target in [6, 7] {
let index = binary_search_left_edge(&nums, target);
println!("最も左にある要素 {} のインデックスは {}", target, index);
let index = binary_search_right_edge(&nums, target);
println!("最も右にある要素 {} のインデックスは {}", target, index);
}
}
@@ -0,0 +1,61 @@
/*
* File: binary_search_insertion.rs
* Created Time: 2023-08-30
* Author: night-cruise (2586447362@qq.com)
*/
#![allow(unused)]
/* 二分探索で挿入位置を探す(重複要素なし) */
fn binary_search_insertion_simple(nums: &[i32], target: i32) -> i32 {
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 両閉区間 [0, n-1] を初期化
while i <= j {
let m = i + (j - i) / 2; // 中点インデックス m を計算
if nums[m as usize] < target {
i = m + 1; // target は区間 [m+1, j] にある
} else if nums[m as usize] > target {
j = m - 1; // target は区間 [i, m-1] にある
} else {
return m;
}
}
// target が見つからなければ、挿入位置 i を返す
i
}
/* 二分探索で挿入位置を探す(重複要素あり) */
pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 {
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 両閉区間 [0, n-1] を初期化
while i <= j {
let m = i + (j - i) / 2; // 中点インデックス m を計算
if nums[m as usize] < target {
i = m + 1; // target は区間 [m+1, j] にある
} else if nums[m as usize] > target {
j = m - 1; // target は区間 [i, m-1] にある
} else {
j = m - 1; // target より小さい最初の要素は区間 [i, m-1] にある
}
}
// 挿入位置 i を返す
i
}
/* Driver Code */
fn main() {
// 重複要素のない配列
let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35];
println!("\n配列 nums = {:?}", nums);
// 二分探索で挿入位置を探す
for target in [6, 9] {
let index = binary_search_insertion_simple(&nums, target);
println!("要素 {} の挿入位置のインデックスは {}", target, index);
}
// 重複要素を含む配列
let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15];
println!("\n配列 nums = {:?}", nums);
// 二分探索で挿入位置を探す
for target in [2, 6, 20] {
let index = binary_search_insertion(&nums, target);
println!("要素 {} の挿入位置のインデックスは {}", target, index);
}
}
@@ -0,0 +1,50 @@
/*
* File: hashing_search.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::ListNode;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
/* ハッシュ探索(配列) */
fn hashing_search_array<'a>(map: &'a HashMap<i32, usize>, target: i32) -> Option<&'a usize> {
// ハッシュテーブルの key: 対象要素、value: インデックス
// ハッシュテーブルにその key がなければ None を返す
map.get(&target)
}
/* ハッシュ探索(連結リスト) */
fn hashing_search_linked_list(
map: &HashMap<i32, Rc<RefCell<ListNode<i32>>>>,
target: i32,
) -> Option<&Rc<RefCell<ListNode<i32>>>> {
// ハッシュテーブルの key: 対象ノードの値、value: ノードオブジェクト
// ハッシュテーブルにその key がなければ None を返す
map.get(&target)
}
/* Driver Code */
pub fn main() {
let target = 3;
/* ハッシュ探索(配列) */
let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8];
// ハッシュテーブルを初期化
let mut map = HashMap::new();
for (i, num) in nums.iter().enumerate() {
map.insert(*num, i); // key: 要素、value: インデックス
}
let index = hashing_search_array(&map, target);
println!("対象要素 3 のインデックス = {}", index.unwrap());
/* ハッシュ探索(連結リスト) */
let head = ListNode::arr_to_linked_list(&nums);
// ハッシュテーブルを初期化する
// let mut map1 = HashMap::new();
let map1 = ListNode::linked_list_to_hashmap(head);
let node = hashing_search_linked_list(&map1, target);
println!("対象ノード値 3 に対応するノードオブジェクトは {:?}", node);
}
@@ -0,0 +1,54 @@
/*
* File: linear_search.rs
* Created Time: 2023-07-09
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::ListNode;
use std::cell::RefCell;
use std::rc::Rc;
/* 線形探索(配列) */
fn linear_search_array(nums: &[i32], target: i32) -> i32 {
// 配列を走査
for (i, num) in nums.iter().enumerate() {
// 目標要素が見つかったらそのインデックスを返す
if num == &target {
return i as i32;
}
}
// 目標要素が見つからなければ -1 を返す
return -1;
}
/* 線形探索(連結リスト) */
fn linear_search_linked_list(
head: Rc<RefCell<ListNode<i32>>>,
target: i32,
) -> Option<Rc<RefCell<ListNode<i32>>>> {
// 対象ノードが見つかったら、それを返す
if head.borrow().val == target {
return Some(head);
};
// 対象ノードが見つかったら、それを返す
if let Some(node) = &head.borrow_mut().next {
return linear_search_linked_list(node.clone(), target);
}
// 対象ノードが見つからない場合は None を返す
return None;
}
/* Driver Code */
pub fn main() {
let target = 3;
/* 配列で線形探索を行う */
let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8];
let index = linear_search_array(&nums, target);
println!("対象要素 3 のインデックス = {}", index);
/* 連結リストで線形探索を行う */
let head = ListNode::arr_to_linked_list(&nums);
let node = linear_search_linked_list(head.unwrap(), target);
println!("対象ノード値 3 に対応するノードオブジェクトは {:?}", node);
}
@@ -0,0 +1,52 @@
/*
* File: two_sum.rs
* Created Time: 2023-01-14
* Author: xBLACICEx (xBLACKICEx@outlook.com), codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
use std::collections::HashMap;
/* 方法 1:総当たり列挙 */
pub fn two_sum_brute_force(nums: &Vec<i32>, target: i32) -> Option<Vec<i32>> {
let size = nums.len();
// 2重ループのため、時間計算量は O(n^2)
for i in 0..size - 1 {
for j in i + 1..size {
if nums[i] + nums[j] == target {
return Some(vec![i as i32, j as i32]);
}
}
}
None
}
/* 方法 2:補助ハッシュテーブル */
pub fn two_sum_hash_table(nums: &Vec<i32>, target: i32) -> Option<Vec<i32>> {
// 補助ハッシュテーブルを使用し、空間計算量は O(n)
let mut dic = HashMap::new();
// 単一ループで、時間計算量は O(n)
for (i, num) in nums.iter().enumerate() {
match dic.get(&(target - num)) {
Some(v) => return Some(vec![*v as i32, i as i32]),
None => dic.insert(num, i as i32),
};
}
None
}
fn main() {
// ======= Test Case =======
let nums = vec![2, 7, 11, 15];
let target = 13;
// ====== Driver Code ======
// 方法 1
let res = two_sum_brute_force(&nums, target).unwrap();
print!("方法1 res = ");
print_util::print_array(&res);
// 方法 2
let res = two_sum_hash_table(&nums, target).unwrap();
print!("\n方法2 res = ");
print_util::print_array(&res);
}
@@ -0,0 +1,53 @@
/*
* File: bubble_sort.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
/* バブルソート */
fn bubble_sort(nums: &mut [i32]) {
// 外側のループ:未ソート区間は [0, i]
for i in (1..nums.len()).rev() {
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
for j in 0..i {
if nums[j] > nums[j + 1] {
// nums[j] と nums[j + 1] を交換
nums.swap(j, j + 1);
}
}
}
}
/* バブルソート(フラグ最適化) */
fn bubble_sort_with_flag(nums: &mut [i32]) {
// 外側のループ:未ソート区間は [0, i]
for i in (1..nums.len()).rev() {
let mut flag = false; // フラグを初期化する
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
for j in 0..i {
if nums[j] > nums[j + 1] {
// nums[j] と nums[j + 1] を交換
nums.swap(j, j + 1);
flag = true; // 交換する要素を記録
}
}
if !flag {
break; // このバブル処理で要素交換が一度もなければそのまま終了
};
}
}
/* Driver Code */
pub fn main() {
let mut nums = [4, 1, 3, 1, 5, 2];
bubble_sort(&mut nums);
print!("バブルソート完了後 nums = ");
print_util::print_array(&nums);
let mut nums1 = [4, 1, 3, 1, 5, 2];
bubble_sort_with_flag(&mut nums1);
print!("\nバブルソート完了後 nums1 = ");
print_util::print_array(&nums1);
}
@@ -0,0 +1,43 @@
/*
* File: bucket_sort.rs
* Created Time: 2023-07-09
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
/* バケットソート */
fn bucket_sort(nums: &mut [f64]) {
// k = n/2 個のバケットを初期化し、各バケットに 2 要素ずつ割り当てる想定とする
let k = nums.len() / 2;
let mut buckets = vec![vec![]; k];
// 1. 配列要素を各バケットに振り分ける
for &num in nums.iter() {
// 入力データの範囲は [0, 1) であり、num * k を用いてインデックス範囲 [0, k-1] に写像する
let i = (num * k as f64) as usize;
// num をバケット i に追加
buckets[i].push(num);
}
// 2. 各バケットをソートする
for bucket in &mut buckets {
// 組み込みのソート関数を使う。他のソートアルゴリズムに置き換えてもよい
bucket.sort_by(|a, b| a.partial_cmp(b).unwrap());
}
// 3. バケットを走査して結果を結合
let mut i = 0;
for bucket in buckets.iter() {
for &num in bucket.iter() {
nums[i] = num;
i += 1;
}
}
}
/* Driver Code */
fn main() {
// 入力データは範囲 [0, 1) の浮動小数点数とする
let mut nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37];
bucket_sort(&mut nums);
print!("バケットソート完了後 nums = ");
print_util::print_array(&nums);
}
@@ -0,0 +1,70 @@
/*
* File: counting_sort.rs
* Created Time: 2023-07-09
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
/* 計数ソート */
// 簡易実装のため、オブジェクトのソートには使えない
fn counting_sort_naive(nums: &mut [i32]) {
// 1. 配列の最大要素 m を求める
let m = *nums.iter().max().unwrap();
// 2. 各数値の出現回数を数える
// counter[num] は num の出現回数を表す
let mut counter = vec![0; m as usize + 1];
for &num in nums.iter() {
counter[num as usize] += 1;
}
// 3. counter を走査し、各要素を元の配列 nums に書き戻す
let mut i = 0;
for num in 0..m + 1 {
for _ in 0..counter[num as usize] {
nums[i] = num;
i += 1;
}
}
}
/* 計数ソート */
// 完全な実装で、オブジェクトをソートでき、かつ安定ソートである
fn counting_sort(nums: &mut [i32]) {
// 1. 配列の最大要素 m を求める
let m = *nums.iter().max().unwrap() as usize;
// 2. 各数値の出現回数を数える
// counter[num] は num の出現回数を表す
let mut counter = vec![0; m + 1];
for &num in nums.iter() {
counter[num as usize] += 1;
}
// 3. counter の累積和を求めて、「出現回数」を「末尾インデックス」に変換する
// つまり counter[num]-1 は、num が res に最後に現れるインデックス
for i in 0..m {
counter[i + 1] += counter[i];
}
// 4. nums を逆順に走査し、各要素を結果配列 res に格納する
// 結果を記録するための配列 res を初期化
let n = nums.len();
let mut res = vec![0; n];
for i in (0..n).rev() {
let num = nums[i];
res[counter[num as usize] - 1] = num; // num を対応するインデックスに配置
counter[num as usize] -= 1; // 累積和を 1 減らして、次に num を配置するインデックスを得る
}
// 結果配列 res で元の配列 nums を上書きする
nums.copy_from_slice(&res)
}
/* Driver Code */
fn main() {
let mut nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4];
counting_sort_naive(&mut nums);
print!("カウントソート(オブジェクトはソート不可)完了後 nums = ");
print_util::print_array(&nums);
let mut nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4];
counting_sort(&mut nums1);
print!("\nカウントソート完了後 nums1 = ");
print_util::print_array(&nums1);
}
@@ -0,0 +1,54 @@
/*
* File: heap_sort.rs
* Created Time: 2023-07-04
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
/* ヒープの長さは n。ノード i から下方向にヒープ化 */
fn sift_down(nums: &mut [i32], n: usize, mut i: usize) {
loop {
// ノード i, l, r のうち値が最大のノードを ma とする
let l = 2 * i + 1;
let r = 2 * i + 2;
let mut ma = i;
if l < n && nums[l] > nums[ma] {
ma = l;
}
if r < n && nums[r] > nums[ma] {
ma = r;
}
// ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
if ma == i {
break;
}
// 2 つのノードを交換
nums.swap(i, ma);
// ループで上から下へヒープ化
i = ma;
}
}
/* ヒープソート */
fn heap_sort(nums: &mut [i32]) {
// ヒープ構築:葉ノード以外のすべてのノードをヒープ化する
for i in (0..nums.len() / 2).rev() {
sift_down(nums, nums.len(), i);
}
// ヒープから最大要素を取り出し、n-1 回繰り返す
for i in (1..nums.len()).rev() {
// 根ノードと最も右の葉ノードを交換(先頭要素と末尾要素を交換)
nums.swap(0, i);
// 根ノードを起点に、上から下へヒープ化
sift_down(nums, i, 0);
}
}
/* Driver Code */
fn main() {
let mut nums = [4, 1, 3, 1, 5, 2];
heap_sort(&mut nums);
print!("ヒープソート完了後 nums = ");
print_util::print_array(&nums);
}
@@ -0,0 +1,29 @@
/*
* File: insertion_sort.rs
* Created Time: 2023-02-13
* Author: xBLACKICEx (xBLACKICEx@outlook.com)
*/
use hello_algo_rust::include::print_util;
/* 挿入ソート */
fn insertion_sort(nums: &mut [i32]) {
// 外側ループ:整列済み区間は [0, i-1]
for i in 1..nums.len() {
let (base, mut j) = (nums[i], (i - 1) as i32);
// 内側ループ: base をソート済み区間 [0, i-1] の正しい位置に挿入する
while j >= 0 && nums[j as usize] > base {
nums[(j + 1) as usize] = nums[j as usize]; // nums[j] を 1 つ右へ移動する
j -= 1;
}
nums[(j + 1) as usize] = base; // base を正しい位置に配置する
}
}
/* Driver Code */
fn main() {
let mut nums = [4, 1, 3, 1, 5, 2];
insertion_sort(&mut nums);
print!("挿入ソート完了後 nums = ");
print_util::print_array(&nums);
}
@@ -0,0 +1,66 @@
/**
* File: merge_sort.rs
* Created Time: 2023-02-14
* Author: xBLACKICEx (xBLACKICEx@outlook.com)
*/
/* 左部分配列と右部分配列をマージ */
fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) {
// 左部分配列の区間は [left, mid]、右部分配列の区間は [mid+1, right]
// マージ結果を格納する一時配列 tmp を作成
let tmp_size = right - left + 1;
let mut tmp = vec![0; tmp_size];
// 左右の部分配列の開始インデックスを初期化する
let (mut i, mut j, mut k) = (left, mid + 1, 0);
// 左右の部分配列にまだ要素がある間は比較し、小さいほうを一時配列にコピーする
while i <= mid && j <= right {
if nums[i] <= nums[j] {
tmp[k] = nums[i];
i += 1;
} else {
tmp[k] = nums[j];
j += 1;
}
k += 1;
}
// 左右の部分配列の残り要素を一時配列にコピーする
while i <= mid {
tmp[k] = nums[i];
k += 1;
i += 1;
}
while j <= right {
tmp[k] = nums[j];
k += 1;
j += 1;
}
// 一時配列 tmp の要素を元の配列 nums の対応区間にコピーする
for k in 0..tmp_size {
nums[left + k] = tmp[k];
}
}
/* マージソート */
fn merge_sort(nums: &mut [i32], left: usize, right: usize) {
// 終了条件
if left >= right {
return; // 部分配列の長さが 1 になったら再帰を終了
}
// 分割フェーズ
let mid = left + (right - left) / 2; // 中点を計算
merge_sort(nums, left, mid); // 左部分配列を再帰処理
merge_sort(nums, mid + 1, right); // 右部分配列を再帰処理
// マージフェーズ
merge(nums, left, mid, right);
}
/* Driver Code */
fn main() {
/* マージソート */
let mut nums = [7, 3, 2, 6, 0, 1, 5, 4];
let right = nums.len() - 1;
merge_sort(&mut nums, 0, right);
println!("マージソート完了後 nums = {:?}", nums);
}
+148
View File
@@ -0,0 +1,148 @@
/**
* File: quick_sort.rs
* Created Time: 2023-02-16
* Author: xBLACKICEx (xBLACKICE@outlook.com)
*/
/* クイックソート */
struct QuickSort;
impl QuickSort {
/* 番兵分割 */
fn partition(nums: &mut [i32], left: usize, right: usize) -> usize {
// nums[left] を基準値とする
let (mut i, mut j) = (left, right);
while i < j {
while i < j && nums[j] >= nums[left] {
j -= 1; // 右から左へ基準値未満の最初の要素を探す
}
while i < j && nums[i] <= nums[left] {
i += 1; // 左から右へ基準値より大きい最初の要素を探す
}
nums.swap(i, j); // この 2 つの要素を交換
}
nums.swap(i, left); // 基準値を 2 つの部分配列の境界へ交換する
i // 基準値のインデックスを返す
}
/* クイックソート */
pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) {
// 部分配列の長さが 1 なら再帰を終了する
if left >= right {
return;
}
// 番兵分割
let pivot = Self::partition(nums, left as usize, right as usize) as i32;
// 左右の部分配列を再帰処理
Self::quick_sort(left, pivot - 1, nums);
Self::quick_sort(pivot + 1, right, nums);
}
}
/* クイックソート(中央値の基準値で最適化) */
struct QuickSortMedian;
impl QuickSortMedian {
/* 3つの候補要素の中央値を選ぶ */
fn median_three(nums: &mut [i32], left: usize, mid: usize, right: usize) -> usize {
let (l, m, r) = (nums[left], nums[mid], 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 の間
}
right
}
/* 番兵による分割処理(3 点中央値) */
fn partition(nums: &mut [i32], left: usize, right: usize) -> usize {
// 3つの候補要素の中央値を選ぶ
let med = Self::median_three(nums, left, (left + right) / 2, right);
// 中央値を配列の最左端に交換する
nums.swap(left, med);
// nums[left] を基準値とする
let (mut i, mut j) = (left, right);
while i < j {
while i < j && nums[j] >= nums[left] {
j -= 1; // 右から左へ基準値未満の最初の要素を探す
}
while i < j && nums[i] <= nums[left] {
i += 1; // 左から右へ基準値より大きい最初の要素を探す
}
nums.swap(i, j); // この 2 つの要素を交換
}
nums.swap(i, left); // 基準値を 2 つの部分配列の境界へ交換する
i // 基準値のインデックスを返す
}
/* クイックソート */
pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) {
// 部分配列の長さが 1 なら再帰を終了する
if left >= right {
return;
}
// 番兵分割
let pivot = Self::partition(nums, left as usize, right as usize) as i32;
// 左右の部分配列を再帰処理
Self::quick_sort(left, pivot - 1, nums);
Self::quick_sort(pivot + 1, right, nums);
}
}
/* クイックソート(再帰深度最適化) */
struct QuickSortTailCall;
impl QuickSortTailCall {
/* 番兵分割 */
fn partition(nums: &mut [i32], left: usize, right: usize) -> usize {
// nums[left] を基準値とする
let (mut i, mut j) = (left, right);
while i < j {
while i < j && nums[j] >= nums[left] {
j -= 1; // 右から左へ基準値未満の最初の要素を探す
}
while i < j && nums[i] <= nums[left] {
i += 1; // 左から右へ基準値より大きい最初の要素を探す
}
nums.swap(i, j); // この 2 つの要素を交換
}
nums.swap(i, left); // 基準値を 2 つの部分配列の境界へ交換する
i // 基準値のインデックスを返す
}
/* クイックソート(再帰深度最適化) */
pub fn quick_sort(mut left: i32, mut right: i32, nums: &mut [i32]) {
// 部分配列の長さが 1 なら終了
while left < right {
// 番兵による分割処理
let pivot = Self::partition(nums, left as usize, right as usize) as i32;
// 2 つの部分配列のうち短いほうにクイックソートを適用する
if pivot - left < right - pivot {
Self::quick_sort(left, pivot - 1, nums); // 左部分配列を再帰的にソート
left = pivot + 1; // 未ソート区間の残りは [pivot + 1, right]
} else {
Self::quick_sort(pivot + 1, right, nums); // 右部分配列を再帰的にソート
right = pivot - 1; // 未ソート区間の残りは [left, pivot - 1]
}
}
}
}
/* Driver Code */
fn main() {
/* クイックソート */
let mut nums = [2, 4, 1, 0, 3, 5];
QuickSort::quick_sort(0, (nums.len() - 1) as i32, &mut nums);
println!("クイックソート完了後 nums = {:?}", nums);
/* クイックソート(中央値の基準値で最適化) */
let mut nums = [2, 4, 1, 0, 3, 5];
QuickSortMedian::quick_sort(0, (nums.len() - 1) as i32, &mut nums);
println!("クイックソート(中央値ピボット最適化)完了後 nums = {:?}", nums);
/* クイックソート(再帰深度最適化) */
let mut nums = [2, 4, 1, 0, 3, 5];
QuickSortTailCall::quick_sort(0, (nums.len() - 1) as i32, &mut nums);
println!("クイックソート(再帰深度最適化)完了後 nums = {:?}", nums);
}
@@ -0,0 +1,63 @@
/*
* File: radix_sort.rs
* Created Time: 2023-07-09
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
/* 要素 num の下から k 桁目を取得(exp = 10^(k-1) */
fn digit(num: i32, exp: i32) -> usize {
// ここで高コストな累乗計算を繰り返さないよう、k ではなく exp を渡す
return ((num / exp) % 10) as usize;
}
/* 計数ソート(nums の k 桁目でソート) */
fn counting_sort_digit(nums: &mut [i32], exp: i32) {
// 10 進数の各桁は 0~9 の範囲なので、長さ 10 のバケット配列が必要
let mut counter = [0; 10];
let n = nums.len();
// 0~9 の各数字の出現回数を集計する
for i in 0..n {
let d = digit(nums[i], exp); // nums[i] の第 k 位を取得し、d とする
counter[d] += 1; // 数字 d の出現回数を数える
}
// 累積和を求め、「出現回数」を「配列インデックス」に変換する
for i in 1..10 {
counter[i] += counter[i - 1];
}
// 逆順に走査し、バケット内の集計結果に従って各要素を res に格納する
let mut res = vec![0; n];
for i in (0..n).rev() {
let d = digit(nums[i], exp);
let j = counter[d] - 1; // d の配列内インデックス j を取得する
res[j] = nums[i]; // 現在の要素をインデックス j に格納する
counter[d] -= 1; // d の個数を 1 減らす
}
// 結果で元の配列 nums を上書きする
nums.copy_from_slice(&res);
}
/* 基数ソート */
fn radix_sort(nums: &mut [i32]) {
// 最大桁数の判定用に配列の最大要素を取得
let m = *nums.into_iter().max().unwrap();
// 下位桁から上位桁の順に走査する
let mut exp = 1;
while exp <= m {
counting_sort_digit(nums, exp);
exp *= 10;
}
}
/* Driver Code */
fn main() {
// 基数ソート
let mut nums = [
10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337,
63832996,
];
radix_sort(&mut nums);
print!("基数ソート完了後 nums = ");
print_util::print_array(&nums);
}
@@ -0,0 +1,35 @@
/*
* File: selection_sort.rs
* Created Time: 2023-05-30
* Author: WSL0809 (wslzzy@outlook.com)
*/
use hello_algo_rust::include::print_util;
/* 選択ソート */
fn selection_sort(nums: &mut [i32]) {
if nums.is_empty() {
return;
}
let n = nums.len();
// 外側ループ:未整列区間は [i, n-1]
for i in 0..n - 1 {
// 内側のループ:未ソート区間の最小要素を見つける
let mut k = i;
for j in i + 1..n {
if nums[j] < nums[k] {
k = j; // 最小要素のインデックスを記録
}
}
// その最小要素を未整列区間の先頭要素と交換する
nums.swap(i, k);
}
}
/* Driver Code */
pub fn main() {
let mut nums = [4, 1, 3, 1, 5, 2];
selection_sort(&mut nums);
print!("\n選択ソート完了後 nums = ");
print_util::print_array(&nums);
}
@@ -0,0 +1,160 @@
/*
* File: array_deque.rs
* Created Time: 2023-03-11
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
/* 循環配列ベースの両端キュー */
struct ArrayDeque<T> {
nums: Vec<T>, // 両端キューの要素を格納する配列
front: usize, // 先頭ポインタ。先頭要素を指す
que_size: usize, // 両端キューの長さ
}
impl<T: Copy + Default> ArrayDeque<T> {
/* コンストラクタ */
pub fn new(capacity: usize) -> Self {
Self {
nums: vec![T::default(); capacity],
front: 0,
que_size: 0,
}
}
/* 両端キューの容量を取得 */
pub fn capacity(&self) -> usize {
self.nums.len()
}
/* 両端キューの長さを取得 */
pub fn size(&self) -> usize {
self.que_size
}
/* 両端キューが空かどうかを判定 */
pub fn is_empty(&self) -> bool {
self.que_size == 0
}
/* 循環配列のインデックスを計算 */
fn index(&self, i: i32) -> usize {
// 剰余演算により配列の先頭と末尾をつなげる
// i が配列の末尾を越えたら先頭に戻る
// i が配列の先頭を越えて前に出たら末尾に戻る
((i + self.capacity() as i32) % self.capacity() as i32) as usize
}
/* キュー先頭にエンキュー */
pub fn push_first(&mut self, num: T) {
if self.que_size == self.capacity() {
println!("両端キューがいっぱいです");
return;
}
// 先頭ポインタを左に 1 つ移動する
// 剰余演算により、front が配列先頭を越えた後に末尾へ戻るようにする
self.front = self.index(self.front as i32 - 1);
// num をキュー先頭に追加
self.nums[self.front] = num;
self.que_size += 1;
}
/* キュー末尾にエンキュー */
pub fn push_last(&mut self, num: T) {
if self.que_size == self.capacity() {
println!("両端キューがいっぱいです");
return;
}
// キュー末尾ポインタを計算し、末尾インデックス + 1 を指す
let rear = self.index(self.front as i32 + self.que_size as i32);
// num をキュー末尾に追加
self.nums[rear] = num;
self.que_size += 1;
}
/* キュー先頭からデキュー */
fn pop_first(&mut self) -> T {
let num = self.peek_first();
// 先頭ポインタを 1 つ後ろへ進める
self.front = self.index(self.front as i32 + 1);
self.que_size -= 1;
num
}
/* キュー末尾からデキュー */
fn pop_last(&mut self) -> T {
let num = self.peek_last();
self.que_size -= 1;
num
}
/* キュー先頭の要素にアクセス */
fn peek_first(&self) -> T {
if self.is_empty() {
panic!("両端キューが空です")
};
self.nums[self.front]
}
/* キュー末尾の要素にアクセス */
fn peek_last(&self) -> T {
if self.is_empty() {
panic!("両端キューが空です")
};
// 末尾要素のインデックスを計算
let last = self.index(self.front as i32 + self.que_size as i32 - 1);
self.nums[last]
}
/* 出力用の配列を返す */
fn to_array(&self) -> Vec<T> {
// 有効長の範囲内のリスト要素のみを変換
let mut res = vec![T::default(); self.que_size];
let mut j = self.front;
for i in 0..self.que_size {
res[i] = self.nums[self.index(j as i32)];
j += 1;
}
res
}
}
/* Driver Code */
fn main() {
/* 両端キューを初期化 */
let mut deque = ArrayDeque::new(10);
deque.push_last(3);
deque.push_last(2);
deque.push_last(5);
print!("両端キュー deque = ");
print_util::print_array(&deque.to_array());
/* 要素にアクセス */
let peek_first = deque.peek_first();
print!("\n先頭要素 peek_first = {}", peek_first);
let peek_last = deque.peek_last();
print!("\n末尾要素 peek_last = {}", peek_last);
/* 要素をエンキュー */
deque.push_last(4);
print!("\n要素 4 を末尾に追加後 deque = ");
print_util::print_array(&deque.to_array());
deque.push_first(1);
print!("\n要素 1 を先頭に追加後 deque = ");
print_util::print_array(&deque.to_array());
/* 要素をデキュー */
let pop_last = deque.pop_last();
print!("\n末尾から取り出した要素 = {}、取り出し後 deque = ", pop_last);
print_util::print_array(&deque.to_array());
let pop_first = deque.pop_first();
print!("\n先頭から取り出した要素 = {}、取り出し後 deque = ", pop_first);
print_util::print_array(&deque.to_array());
/* 両端キューの長さを取得 */
let size = deque.size();
print!("\n両端キューの長さ size = {}", size);
/* 両端キューが空かどうかを判定 */
let is_empty = deque.is_empty();
print!("\n両端キューが空かどうか = {}", is_empty);
}
@@ -0,0 +1,125 @@
/*
* File: array_queue.rs
* Created Time: 2023-02-06
* Author: WSL0809 (wslzzy@outlook.com)
*/
/* 循環配列ベースのキュー */
struct ArrayQueue<T> {
nums: Vec<T>, // キュー要素を格納する配列
front: i32, // 先頭ポインタ。先頭要素を指す
que_size: i32, // キューの長さ
que_capacity: i32, // キューの容量
}
impl<T: Copy + Default> ArrayQueue<T> {
/* コンストラクタ */
fn new(capacity: i32) -> ArrayQueue<T> {
ArrayQueue {
nums: vec![T::default(); capacity as usize],
front: 0,
que_size: 0,
que_capacity: capacity,
}
}
/* キューの容量を取得 */
fn capacity(&self) -> i32 {
self.que_capacity
}
/* キューの長さを取得 */
fn size(&self) -> i32 {
self.que_size
}
/* キューが空かどうかを判定 */
fn is_empty(&self) -> bool {
self.que_size == 0
}
/* エンキュー */
fn push(&mut self, num: T) {
if self.que_size == self.capacity() {
println!("キューがいっぱいです");
return;
}
// 末尾ポインタを計算し、末尾インデックス + 1 を指す
// 剰余演算により、rear が配列末尾を越えた後に先頭へ戻るようにする
let rear = (self.front + self.que_size) % self.que_capacity;
// num をキュー末尾に追加
self.nums[rear as usize] = num;
self.que_size += 1;
}
/* デキュー */
fn pop(&mut self) -> T {
let num = self.peek();
// 先頭ポインタを1つ後ろへ進め、末尾を越えたら配列先頭に戻す
self.front = (self.front + 1) % self.que_capacity;
self.que_size -= 1;
num
}
/* キュー先頭の要素にアクセス */
fn peek(&self) -> T {
if self.is_empty() {
panic!("index out of bounds");
}
self.nums[self.front as usize]
}
/* 配列を返す */
fn to_vector(&self) -> Vec<T> {
let cap = self.que_capacity;
let mut j = self.front;
let mut arr = vec![T::default(); cap as usize];
for i in 0..self.que_size {
arr[i as usize] = self.nums[(j % cap) as usize];
j += 1;
}
arr
}
}
/* Driver Code */
fn main() {
/* キューを初期化 */
let capacity = 10;
let mut queue = ArrayQueue::new(capacity);
/* 要素をエンキュー */
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);
println!("キュー queue = {:?}", queue.to_vector());
/* キュー先頭の要素にアクセス */
let peek = queue.peek();
println!("先頭要素 peek = {}", peek);
/* 要素をデキュー */
let pop = queue.pop();
println!(
"取り出した要素 pop = {:?},取り出し後 queue = {:?}",
pop,
queue.to_vector()
);
/* キューの長さを取得 */
let size = queue.size();
println!("キューの長さ size = {}", size);
/* キューが空かどうかを判定 */
let is_empty = queue.is_empty();
println!("キューが空かどうか = {}", is_empty);
/* 循環配列をテストする */
for i in 0..10 {
queue.push(i);
queue.pop();
println!("{:?} 回のエンキュー + デキュー後 queue = {:?}", i, queue.to_vector());
}
}
@@ -0,0 +1,86 @@
/*
* File: array_stack.rs
* Created Time: 2023-02-05
* Author: WSL0809 (wslzzy@outlook.com), codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
/* 配列ベースのスタック */
struct ArrayStack<T> {
stack: Vec<T>,
}
impl<T> ArrayStack<T> {
/* スタックを初期化 */
fn new() -> ArrayStack<T> {
ArrayStack::<T> {
stack: Vec::<T>::new(),
}
}
/* スタックの長さを取得 */
fn size(&self) -> usize {
self.stack.len()
}
/* スタックが空かどうかを判定 */
fn is_empty(&self) -> bool {
self.size() == 0
}
/* プッシュ */
fn push(&mut self, num: T) {
self.stack.push(num);
}
/* ポップ */
fn pop(&mut self) -> Option<T> {
self.stack.pop()
}
/* スタックトップの要素にアクセス */
fn peek(&self) -> Option<&T> {
if self.is_empty() {
panic!("スタックが空です")
};
self.stack.last()
}
/* &Vec を返す */
fn to_array(&self) -> &Vec<T> {
&self.stack
}
}
/* Driver Code */
fn main() {
// スタックを初期化
let mut stack = ArrayStack::<i32>::new();
// 要素をプッシュ
stack.push(1);
stack.push(3);
stack.push(2);
stack.push(5);
stack.push(4);
print!("スタック stack = ");
print_util::print_array(stack.to_array());
// スタックトップの要素にアクセス
let peek = stack.peek().unwrap();
print!("\nスタックトップ要素 peek = {}", peek);
// 要素をポップ
let pop = stack.pop().unwrap();
print!("\n取り出した要素 pop = {pop}、取り出し後 stack = ");
print_util::print_array(stack.to_array());
// スタックの長さを取得
let size = stack.size();
print!("\nスタックの長さ size = {size}");
// 空かどうかを判定
let is_empty = stack.is_empty();
print!("\nスタックが空かどうか = {is_empty}");
}
@@ -0,0 +1,49 @@
/*
* File: deque.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICEx@outlook.com)
*/
use hello_algo_rust::include::print_util;
use std::collections::VecDeque;
/* Driver Code */
pub fn main() {
// 両端キューを初期化
let mut deque: VecDeque<i32> = VecDeque::new();
deque.push_back(3);
deque.push_back(2);
deque.push_back(5);
print!("両端キュー deque = ");
print_util::print_queue(&deque);
// 要素にアクセス
let peek_first = deque.front().unwrap();
print!("\n先頭要素 peekFirst = {peek_first}");
let peek_last = deque.back().unwrap();
print!("\n末尾要素 peekLast = {peek_last}");
/* 要素をエンキュー */
deque.push_back(4);
print!("\n要素 4 を末尾に追加後 deque = ");
print_util::print_queue(&deque);
deque.push_front(1);
print!("\n要素 1 を先頭に追加後 deque = ");
print_util::print_queue(&deque);
// 要素をデキュー
let pop_last = deque.pop_back().unwrap();
print!("\n末尾から取り出した要素 = {pop_last}、取り出し後 deque = ");
print_util::print_queue(&deque);
let pop_first = deque.pop_front().unwrap();
print!("\n先頭から取り出した要素 = {pop_first}、取り出し後 deque = ");
print_util::print_queue(&deque);
// 両端キューの長さを取得
let size = deque.len();
print!("\n両端キューの長さ size = {size}");
// 両端キューが空かどうかを判定
let is_empty = deque.is_empty();
print!("\n両端キューが空かどうか = {is_empty}");
}
@@ -0,0 +1,218 @@
/*
* File: linkedlist_deque.rs
* Created Time: 2023-03-11
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
use std::cell::RefCell;
use std::rc::Rc;
/* 双方向連結リストノード */
pub struct ListNode<T> {
pub val: T, // ノード値
pub next: Option<Rc<RefCell<ListNode<T>>>>, // 後継ノードへのポインタ
pub prev: Option<Rc<RefCell<ListNode<T>>>>, // 前駆ノードへのポインタ
}
impl<T> ListNode<T> {
pub fn new(val: T) -> Rc<RefCell<ListNode<T>>> {
Rc::new(RefCell::new(ListNode {
val,
next: None,
prev: None,
}))
}
}
/* 双方向連結リストベースの両端キュー */
#[allow(dead_code)]
pub struct LinkedListDeque<T> {
front: Option<Rc<RefCell<ListNode<T>>>>, // 先頭ノード front
rear: Option<Rc<RefCell<ListNode<T>>>>, // 末尾ノード rear
que_size: usize, // 両端キューの長さ
}
impl<T: Copy> LinkedListDeque<T> {
pub fn new() -> Self {
Self {
front: None,
rear: None,
que_size: 0,
}
}
/* 両端キューの長さを取得 */
pub fn size(&self) -> usize {
return self.que_size;
}
/* 両端キューが空かどうかを判定 */
pub fn is_empty(&self) -> bool {
return self.que_size == 0;
}
/* エンキュー操作 */
fn push(&mut self, num: T, is_front: bool) {
let node = ListNode::new(num);
// 先頭へのエンキュー操作
if is_front {
match self.front.take() {
// 連結リストが空なら、front と rear の両方を node に向ける
None => {
self.rear = Some(node.clone());
self.front = Some(node);
}
// node を連結リストの先頭に追加
Some(old_front) => {
old_front.borrow_mut().prev = Some(node.clone());
node.borrow_mut().next = Some(old_front);
self.front = Some(node); // 先頭ノードを更新する
}
}
}
// 末尾へのエンキュー操作
else {
match self.rear.take() {
// 連結リストが空なら、front と rear の両方を node に向ける
None => {
self.front = Some(node.clone());
self.rear = Some(node);
}
// node を連結リストの末尾に追加
Some(old_rear) => {
old_rear.borrow_mut().next = Some(node.clone());
node.borrow_mut().prev = Some(old_rear);
self.rear = Some(node); // 末尾ノードを更新する
}
}
}
self.que_size += 1; // キューの長さを更新
}
/* キュー先頭にエンキュー */
pub fn push_first(&mut self, num: T) {
self.push(num, true);
}
/* キュー末尾にエンキュー */
pub fn push_last(&mut self, num: T) {
self.push(num, false);
}
/* デキュー操作 */
fn pop(&mut self, is_front: bool) -> Option<T> {
// キューが空なら、そのまま `None` を返す
if self.is_empty() {
return None;
};
// キュー先頭からの取り出し
if is_front {
self.front.take().map(|old_front| {
match old_front.borrow_mut().next.take() {
Some(new_front) => {
new_front.borrow_mut().prev.take();
self.front = Some(new_front); // 先頭ノードを更新する
}
None => {
self.rear.take();
}
}
self.que_size -= 1; // キューの長さを更新
old_front.borrow().val
})
}
// キュー末尾からの取り出し
else {
self.rear.take().map(|old_rear| {
match old_rear.borrow_mut().prev.take() {
Some(new_rear) => {
new_rear.borrow_mut().next.take();
self.rear = Some(new_rear); // 末尾ノードを更新する
}
None => {
self.front.take();
}
}
self.que_size -= 1; // キューの長さを更新
old_rear.borrow().val
})
}
}
/* キュー先頭からデキュー */
pub fn pop_first(&mut self) -> Option<T> {
return self.pop(true);
}
/* キュー末尾からデキュー */
pub fn pop_last(&mut self) -> Option<T> {
return self.pop(false);
}
/* キュー先頭の要素にアクセス */
pub fn peek_first(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
self.front.as_ref()
}
/* キュー末尾の要素にアクセス */
pub fn peek_last(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
self.rear.as_ref()
}
/* 出力用の配列を返す */
pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {
let mut res: Vec<T> = Vec::new();
fn recur<T: Copy>(cur: Option<&Rc<RefCell<ListNode<T>>>>, res: &mut Vec<T>) {
if let Some(cur) = cur {
res.push(cur.borrow().val);
recur(cur.borrow().next.as_ref(), res);
}
}
recur(head, &mut res);
res
}
}
/* Driver Code */
fn main() {
/* 両端キューを初期化 */
let mut deque = LinkedListDeque::new();
deque.push_last(3);
deque.push_last(2);
deque.push_last(5);
print!("両端キュー deque = ");
print_util::print_array(&deque.to_array(deque.peek_first()));
/* 要素にアクセス */
let peek_first = deque.peek_first().unwrap().borrow().val;
print!("\n先頭要素 peek_first = {}", peek_first);
let peek_last = deque.peek_last().unwrap().borrow().val;
print!("\n末尾要素 peek_last = {}", peek_last);
/* 要素をエンキュー */
deque.push_last(4);
print!("\n要素 4 を末尾に追加後 deque = ");
print_util::print_array(&deque.to_array(deque.peek_first()));
deque.push_first(1);
print!("\n要素 1 を先頭に追加後 deque = ");
print_util::print_array(&deque.to_array(deque.peek_first()));
/* 要素をデキュー */
let pop_last = deque.pop_last().unwrap();
print!("\n末尾から取り出した要素 = {}、取り出し後 deque = ", pop_last);
print_util::print_array(&deque.to_array(deque.peek_first()));
let pop_first = deque.pop_first().unwrap();
print!("\n先頭から取り出した要素 = {}、取り出し後 deque = ", pop_first);
print_util::print_array(&deque.to_array(deque.peek_first()));
/* 両端キューの長さを取得 */
let size = deque.size();
print!("\n両端キューの長さ size = {}", size);
/* 両端キューが空かどうかを判定 */
let is_empty = deque.is_empty();
print!("\n両端キューが空かどうか = {}", is_empty);
}
@@ -0,0 +1,126 @@
/*
* File: linkedlist_queue.rs
* Created Time: 2023-03-11
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, ListNode};
use std::cell::RefCell;
use std::rc::Rc;
/* 連結リストベースのキュー */
#[allow(dead_code)]
pub struct LinkedListQueue<T> {
front: Option<Rc<RefCell<ListNode<T>>>>, // 先頭ノード front
rear: Option<Rc<RefCell<ListNode<T>>>>, // 末尾ノード rear
que_size: usize, // キューの長さ
}
impl<T: Copy> LinkedListQueue<T> {
pub fn new() -> Self {
Self {
front: None,
rear: None,
que_size: 0,
}
}
/* キューの長さを取得 */
pub fn size(&self) -> usize {
return self.que_size;
}
/* キューが空かどうかを判定 */
pub fn is_empty(&self) -> bool {
return self.que_size == 0;
}
/* エンキュー */
pub fn push(&mut self, num: T) {
// 末尾ノードの後ろに num を追加
let new_rear = ListNode::new(num);
match self.rear.take() {
// キューが空でなければ、そのノードを末尾ノードの後ろに追加
Some(old_rear) => {
old_rear.borrow_mut().next = Some(new_rear.clone());
self.rear = Some(new_rear);
}
// キューが空なら、先頭・末尾ノードをともにそのノードに設定
None => {
self.front = Some(new_rear.clone());
self.rear = Some(new_rear);
}
}
self.que_size += 1;
}
/* デキュー */
pub fn pop(&mut self) -> Option<T> {
self.front.take().map(|old_front| {
match old_front.borrow_mut().next.take() {
Some(new_front) => {
self.front = Some(new_front);
}
None => {
self.rear.take();
}
}
self.que_size -= 1;
old_front.borrow().val
})
}
/* キュー先頭の要素にアクセス */
pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
self.front.as_ref()
}
/* 連結リストを Array に変換して返す */
pub fn to_array(&self, head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {
let mut res: Vec<T> = Vec::new();
fn recur<T: Copy>(cur: Option<&Rc<RefCell<ListNode<T>>>>, res: &mut Vec<T>) {
if let Some(cur) = cur {
res.push(cur.borrow().val);
recur(cur.borrow().next.as_ref(), res);
}
}
recur(head, &mut res);
res
}
}
/* Driver Code */
fn main() {
/* キューを初期化 */
let mut queue = LinkedListQueue::new();
/* 要素をエンキュー */
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);
print!("キュー queue = ");
print_util::print_array(&queue.to_array(queue.peek()));
/* キュー先頭の要素にアクセス */
let peek = queue.peek().unwrap().borrow().val;
print!("\n先頭要素 peek = {}", peek);
/* 要素をデキュー */
let pop = queue.pop().unwrap();
print!("\n取り出した要素 pop = {}、取り出し後 queue = ", pop);
print_util::print_array(&queue.to_array(queue.peek()));
/* キューの長さを取得 */
let size = queue.size();
print!("\nキューの長さ size = {}", size);
/* キューが空かどうかを判定 */
let is_empty = queue.is_empty();
print!("\nキューが空かどうか = {}", is_empty);
}
@@ -0,0 +1,105 @@
/*
* File: linkedlist_stack.rs
* Created Time: 2023-03-11
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::{print_util, ListNode};
use std::cell::RefCell;
use std::rc::Rc;
/* 連結リストベースのスタック */
#[allow(dead_code)]
pub struct LinkedListStack<T> {
stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // 先頭ノードをスタックトップとする
stk_size: usize, // スタックの長さ
}
impl<T: Copy> LinkedListStack<T> {
pub fn new() -> Self {
Self {
stack_peek: None,
stk_size: 0,
}
}
/* スタックの長さを取得 */
pub fn size(&self) -> usize {
return self.stk_size;
}
/* スタックが空かどうかを判定 */
pub fn is_empty(&self) -> bool {
return self.size() == 0;
}
/* プッシュ */
pub fn push(&mut self, num: T) {
let node = ListNode::new(num);
node.borrow_mut().next = self.stack_peek.take();
self.stack_peek = Some(node);
self.stk_size += 1;
}
/* ポップ */
pub fn pop(&mut self) -> Option<T> {
self.stack_peek.take().map(|old_head| {
self.stack_peek = old_head.borrow_mut().next.take();
self.stk_size -= 1;
old_head.borrow().val
})
}
/* スタックトップの要素にアクセス */
pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
self.stack_peek.as_ref()
}
/* List を Array に変換して返す */
pub fn to_array(&self) -> Vec<T> {
fn _to_array<T: Sized + Copy>(head: Option<&Rc<RefCell<ListNode<T>>>>) -> Vec<T> {
if let Some(node) = head {
let mut nums = _to_array(node.borrow().next.as_ref());
nums.push(node.borrow().val);
return nums;
}
return Vec::new();
}
_to_array(self.peek())
}
}
/* Driver Code */
fn main() {
/* スタックを初期化 */
let mut stack = LinkedListStack::new();
/* 要素をプッシュ */
stack.push(1);
stack.push(3);
stack.push(2);
stack.push(5);
stack.push(4);
print!("スタック stack = ");
print_util::print_array(&stack.to_array());
/* スタックトップの要素にアクセス */
let peek = stack.peek().unwrap().borrow().val;
print!("\nスタックトップ要素 peek = {}", peek);
/* 要素をポップ */
let pop = stack.pop().unwrap();
print!("\n取り出した要素 pop = {}、取り出し後 stack = ", pop);
print_util::print_array(&stack.to_array());
/* スタックの長さを取得 */
let size = stack.size();
print!("\nスタックの長さ size = {}", size);
/* 空かどうかを判定 */
let is_empty = stack.is_empty();
print!("\nスタックが空かどうか = {}", is_empty);
}
@@ -0,0 +1,41 @@
/*
* File: queue.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICEx@outlook.com)
*/
use hello_algo_rust::include::print_util;
use std::collections::VecDeque;
/* Driver Code */
pub fn main() {
// キューを初期化
let mut queue: VecDeque<i32> = VecDeque::new();
// 要素をエンキュー
queue.push_back(1);
queue.push_back(3);
queue.push_back(2);
queue.push_back(5);
queue.push_back(4);
print!("キュー queue = ");
print_util::print_queue(&queue);
// キュー先頭の要素にアクセス
let peek = queue.front().unwrap();
println!("\n先頭要素 peek = {peek}");
// 要素をデキュー
let pop = queue.pop_front().unwrap();
print!("取り出した要素 pop = {pop}、取り出し後 queue = ");
print_util::print_queue(&queue);
// キューの長さを取得
let size = queue.len();
print!("\nキューの長さ size = {size}");
// キューが空かどうかを判定
let is_empty = queue.is_empty();
print!("\nキューが空かどうか = {is_empty}");
}
@@ -0,0 +1,40 @@
/*
* File: stack.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com)
*/
use hello_algo_rust::include::print_util;
/* Driver Code */
pub fn main() {
// スタックを初期化する
// Rust では、Vec をスタックとして使うことが推奨される
let mut stack: Vec<i32> = Vec::new();
// 要素をプッシュ
stack.push(1);
stack.push(3);
stack.push(2);
stack.push(5);
stack.push(4);
print!("スタック stack = ");
print_util::print_array(&stack);
// スタックトップの要素にアクセス
let peek = stack.last().unwrap();
print!("\nスタックトップ要素 peek = {peek}");
// 要素をポップ
let pop = stack.pop().unwrap();
print!("\n取り出した要素 pop = {pop}、取り出し後 stack = ");
print_util::print_array(&stack);
// スタックの長さを取得
let size = stack.len();
print!("\nスタックの長さ size = {size}");
// スタックが空かどうかを判定
let is_empty = stack.is_empty();
print!("\nスタックが空かどうか = {is_empty}");
}
@@ -0,0 +1,192 @@
/*
* File: array_binary_tree.rs
* Created Time: 2023-07-25
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::{print_util, tree_node};
/* 配列表現による二分木クラス */
struct ArrayBinaryTree {
tree: Vec<Option<i32>>,
}
impl ArrayBinaryTree {
/* コンストラクタ */
fn new(arr: Vec<Option<i32>>) -> Self {
Self { tree: arr }
}
/* リスト容量 */
fn size(&self) -> i32 {
self.tree.len() as i32
}
/* インデックス i のノードの値を取得 */
fn val(&self, i: i32) -> Option<i32> {
// インデックスが範囲外なら、空きを表す None を返す
if i < 0 || i >= self.size() {
None
} else {
self.tree[i as usize]
}
}
/* インデックス i のノードの左子ノードのインデックスを取得 */
fn left(&self, i: i32) -> i32 {
2 * i + 1
}
/* インデックス i のノードの右子ノードのインデックスを取得 */
fn right(&self, i: i32) -> i32 {
2 * i + 2
}
/* インデックス i のノードの親ノードのインデックスを取得 */
fn parent(&self, i: i32) -> i32 {
(i - 1) / 2
}
/* レベル順走査 */
fn level_order(&self) -> Vec<i32> {
self.tree.iter().filter_map(|&x| x).collect()
}
/* 深さ優先探索 */
fn dfs(&self, i: i32, order: &'static str, res: &mut Vec<i32>) {
if self.val(i).is_none() {
return;
}
let val = self.val(i).unwrap();
// 先行順走査
if order == "pre" {
res.push(val);
}
self.dfs(self.left(i), order, res);
// 中順走査
if order == "in" {
res.push(val);
}
self.dfs(self.right(i), order, res);
// 後順走査
if order == "post" {
res.push(val);
}
}
/* 先行順走査 */
fn pre_order(&self) -> Vec<i32> {
let mut res = vec![];
self.dfs(0, "pre", &mut res);
res
}
/* 中順走査 */
fn in_order(&self) -> Vec<i32> {
let mut res = vec![];
self.dfs(0, "in", &mut res);
res
}
/* 後順走査 */
fn post_order(&self) -> Vec<i32> {
let mut res = vec![];
self.dfs(0, "post", &mut res);
res
}
}
/* Driver Code */
fn main() {
// 二分木を初期化
// ここでは、配列から直接二分木を生成する関数を利用する
let arr = vec![
Some(1),
Some(2),
Some(3),
Some(4),
None,
Some(6),
Some(7),
Some(8),
Some(9),
None,
None,
Some(12),
None,
None,
Some(15),
];
let root = tree_node::vec_to_tree(arr.clone()).unwrap();
println!("\n二分木を初期化\n");
println!("二分木の配列表現:");
println!(
"[{}]",
arr.iter()
.map(|&val| if let Some(val) = val {
format!("{val}")
} else {
"null".to_string()
})
.collect::<Vec<String>>()
.join(", ")
);
println!("二分木の連結リスト表現:");
print_util::print_tree(&root);
// 配列表現による二分木クラス
let abt = ArrayBinaryTree::new(arr);
// ノードにアクセス
let i = 1;
let l = abt.left(i);
let r = abt.right(i);
let p = abt.parent(i);
println!(
"\n現在のノードのインデックスは {}、値は {}",
i,
if let Some(val) = abt.val(i) {
format!("{val}")
} else {
"null".to_string()
}
);
println!(
"その左子ノードのインデックスは {}、値は {}",
l,
if let Some(val) = abt.val(l) {
format!("{val}")
} else {
"null".to_string()
}
);
println!(
"その右子ノードのインデックスは {}、値は {}",
r,
if let Some(val) = abt.val(r) {
format!("{val}")
} else {
"null".to_string()
}
);
println!(
"その親ノードのインデックスは {}、値は {}",
p,
if let Some(val) = abt.val(p) {
format!("{val}")
} else {
"null".to_string()
}
);
// 木を走査
let mut res = abt.level_order();
println!("\nレベル順走査:{:?}", res);
res = abt.pre_order();
println!("前順走査:{:?}", res);
res = abt.in_order();
println!("中順走査:{:?}", res);
res = abt.post_order();
println!("後順走査は:{:?}", res);
}
+297
View File
@@ -0,0 +1,297 @@
/*
* File: avl_tree.rs
* Created Time: 2023-07-14
* Author: night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::{print_util, TreeNode};
use std::cell::RefCell;
use std::cmp::Ordering;
use std::rc::Rc;
type OptionTreeNodeRc = Option<Rc<RefCell<TreeNode>>>;
/* AVL 木 */
struct AVLTree {
root: OptionTreeNodeRc, // 根ノード
}
impl AVLTree {
/* コンストラクタ */
fn new() -> Self {
Self { root: None }
}
/* ノードの高さを取得 */
fn height(node: OptionTreeNodeRc) -> i32 {
// 空ノードの高さは -1、葉ノードの高さは 0
match node {
Some(node) => node.borrow().height,
None => -1,
}
}
/* ノードの高さを更新する */
fn update_height(node: OptionTreeNodeRc) {
if let Some(node) = node {
let left = node.borrow().left.clone();
let right = node.borrow().right.clone();
// ノードの高さは最も高い部分木の高さ + 1 に等しい
node.borrow_mut().height = std::cmp::max(Self::height(left), Self::height(right)) + 1;
}
}
/* 平衡係数を取得 */
fn balance_factor(node: OptionTreeNodeRc) -> i32 {
match node {
// 空ノードの平衡係数は 0
None => 0,
// ノードの平衡係数 = 左部分木の高さ - 右部分木の高さ
Some(node) => {
Self::height(node.borrow().left.clone()) - Self::height(node.borrow().right.clone())
}
}
}
/* 右回転 */
fn right_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc {
match node {
Some(node) => {
let child = node.borrow().left.clone().unwrap();
let grand_child = child.borrow().right.clone();
// child を支点として node を右回転させる
child.borrow_mut().right = Some(node.clone());
node.borrow_mut().left = grand_child;
// ノードの高さを更新する
Self::update_height(Some(node));
Self::update_height(Some(child.clone()));
// 回転後の部分木の根ノードを返す
Some(child)
}
None => None,
}
}
/* 左回転 */
fn left_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc {
match node {
Some(node) => {
let child = node.borrow().right.clone().unwrap();
let grand_child = child.borrow().left.clone();
// child を支点として node を左回転させる
child.borrow_mut().left = Some(node.clone());
node.borrow_mut().right = grand_child;
// ノードの高さを更新する
Self::update_height(Some(node));
Self::update_height(Some(child.clone()));
// 回転後の部分木の根ノードを返す
Some(child)
}
None => None,
}
}
/* 回転操作を行い、この部分木の平衡を回復する */
fn rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc {
// ノード node の平衡係数を取得
let balance_factor = Self::balance_factor(node.clone());
// 左に偏った木
if balance_factor > 1 {
let node = node.unwrap();
if Self::balance_factor(node.borrow().left.clone()) >= 0 {
// 右回転
Self::right_rotate(Some(node))
} else {
// 左回転してから右回転
let left = node.borrow().left.clone();
node.borrow_mut().left = Self::left_rotate(left);
Self::right_rotate(Some(node))
}
}
// 右に偏った木
else if balance_factor < -1 {
let node = node.unwrap();
if Self::balance_factor(node.borrow().right.clone()) <= 0 {
// 左回転
Self::left_rotate(Some(node))
} else {
// 右回転してから左回転
let right = node.borrow().right.clone();
node.borrow_mut().right = Self::right_rotate(right);
Self::left_rotate(Some(node))
}
} else {
// 平衡木なので回転不要、そのまま返す
node
}
}
/* ノードを挿入 */
fn insert(&mut self, val: i32) {
self.root = Self::insert_helper(self.root.clone(), val);
}
/* ノードを再帰的に挿入する(補助メソッド) */
fn insert_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc {
match node {
Some(mut node) => {
/* 1. 挿入位置を探索してノードを挿入 */
match {
let node_val = node.borrow().val;
node_val
}
.cmp(&val)
{
Ordering::Greater => {
let left = node.borrow().left.clone();
node.borrow_mut().left = Self::insert_helper(left, val);
}
Ordering::Less => {
let right = node.borrow().right.clone();
node.borrow_mut().right = Self::insert_helper(right, val);
}
Ordering::Equal => {
return Some(node); // 重複ノードは挿入せず、そのまま返す
}
}
Self::update_height(Some(node.clone())); // ノードの高さを更新する
/* 2. 回転操作を行い、部分木の平衡を回復する */
node = Self::rotate(Some(node)).unwrap();
// 部分木の根ノードを返す
Some(node)
}
None => Some(TreeNode::new(val)),
}
}
/* ノードを削除 */
fn remove(&self, val: i32) {
Self::remove_helper(self.root.clone(), val);
}
/* ノードを再帰的に削除する(補助メソッド) */
fn remove_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc {
match node {
Some(mut node) => {
/* 1. ノードを探索して削除 */
if val < node.borrow().val {
let left = node.borrow().left.clone();
node.borrow_mut().left = Self::remove_helper(left, val);
} else if val > node.borrow().val {
let right = node.borrow().right.clone();
node.borrow_mut().right = Self::remove_helper(right, val);
} else if node.borrow().left.is_none() || node.borrow().right.is_none() {
let child = if node.borrow().left.is_some() {
node.borrow().left.clone()
} else {
node.borrow().right.clone()
};
match child {
// 子ノード数 = 0 の場合、node をそのまま削除して返す
None => {
return None;
}
// 子ノード数 = 1 の場合、node をそのまま削除する
Some(child) => node = child,
}
} else {
// 子ノード数 = 2 の場合、中順走査の次のノードを削除し、そのノードで現在のノードを置き換える
let mut temp = node.borrow().right.clone().unwrap();
loop {
let temp_left = temp.borrow().left.clone();
if temp_left.is_none() {
break;
}
temp = temp_left.unwrap();
}
let right = node.borrow().right.clone();
node.borrow_mut().right = Self::remove_helper(right, temp.borrow().val);
node.borrow_mut().val = temp.borrow().val;
}
Self::update_height(Some(node.clone())); // ノードの高さを更新する
/* 2. 回転操作を行い、部分木の平衡を回復する */
node = Self::rotate(Some(node)).unwrap();
// 部分木の根ノードを返す
Some(node)
}
None => None,
}
}
/* ノードを探索 */
fn search(&self, val: i32) -> OptionTreeNodeRc {
let mut cur = self.root.clone();
// ループで探索し、葉ノードを越えたら抜ける
while let Some(current) = cur.clone() {
match current.borrow().val.cmp(&val) {
// 目標ノードは cur の右部分木にある
Ordering::Less => {
cur = current.borrow().right.clone();
}
// 目標ノードは cur の左部分木にある
Ordering::Greater => {
cur = current.borrow().left.clone();
}
// 目標ノードが見つかったらループを抜ける
Ordering::Equal => {
break;
}
}
}
// 目標ノードを返す
cur
}
}
/* Driver Code */
fn main() {
fn test_insert(tree: &mut AVLTree, val: i32) {
tree.insert(val);
println!("\nノード {} を挿入した後、AVL 木は", val);
print_util::print_tree(&tree.root.clone().unwrap());
}
fn test_remove(tree: &mut AVLTree, val: i32) {
tree.remove(val);
println!("\nノード {} を削除した後、AVL 木は", val);
print_util::print_tree(&tree.root.clone().unwrap());
}
/* 空の AVL 木を初期化する */
let mut avl_tree = AVLTree::new();
/* ノードを挿入 */
// ノード挿入後に AVL 木がどのように平衡を保つかに注目してほしい
test_insert(&mut avl_tree, 1);
test_insert(&mut avl_tree, 2);
test_insert(&mut avl_tree, 3);
test_insert(&mut avl_tree, 4);
test_insert(&mut avl_tree, 5);
test_insert(&mut avl_tree, 8);
test_insert(&mut avl_tree, 7);
test_insert(&mut avl_tree, 9);
test_insert(&mut avl_tree, 10);
test_insert(&mut avl_tree, 6);
/* 重複ノードを挿入する */
test_insert(&mut avl_tree, 7);
/* ノードを削除 */
// ノード削除後に AVL 木がどのように平衡を保つかに注目してほしい
test_remove(&mut avl_tree, 8); // 次数 0 のノードを削除する
test_remove(&mut avl_tree, 5); // 次数 1 のノードを削除する
test_remove(&mut avl_tree, 4); // 次数 2 のノードを削除する
/* ノードを検索 */
let node = avl_tree.search(7);
if let Some(node) = node {
println!(
"\n見つかったノードオブジェクトは {:?}、ノードの値 = {}",
&*node.borrow(),
node.borrow().val
);
}
}
@@ -0,0 +1,195 @@
/*
* File: binary_search_tree.rs
* Created Time: 2023-04-20
* Author: xBLACKICEx (xBLACKICE@outlook.com)、night-cruise (2586447362@qq.com)
*/
use hello_algo_rust::include::print_util;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::rc::Rc;
use hello_algo_rust::include::TreeNode;
type OptionTreeNodeRc = Option<Rc<RefCell<TreeNode>>>;
/* 二分探索木 */
pub struct BinarySearchTree {
root: OptionTreeNodeRc,
}
impl BinarySearchTree {
/* コンストラクタ */
pub fn new() -> Self {
// 空の木を初期化する
Self { root: None }
}
/* 二分木の根ノードを取得 */
pub fn get_root(&self) -> OptionTreeNodeRc {
self.root.clone()
}
/* ノードを探索 */
pub fn search(&self, num: i32) -> OptionTreeNodeRc {
let mut cur = self.root.clone();
// ループで探索し、葉ノードを越えたら抜ける
while let Some(node) = cur.clone() {
match num.cmp(&node.borrow().val) {
// 目標ノードは cur の右部分木にある
Ordering::Greater => cur = node.borrow().right.clone(),
// 目標ノードは cur の左部分木にある
Ordering::Less => cur = node.borrow().left.clone(),
// 目標ノードが見つかったらループを抜ける
Ordering::Equal => break,
}
}
// 目標ノードを返す
cur
}
/* ノードを挿入 */
pub fn insert(&mut self, num: i32) {
// 木が空なら、根ノードを初期化する
if self.root.is_none() {
self.root = Some(TreeNode::new(num));
return;
}
let mut cur = self.root.clone();
let mut pre = None;
// ループで探索し、葉ノードを越えたら抜ける
while let Some(node) = cur.clone() {
match num.cmp(&node.borrow().val) {
// 重複ノードが見つかったら、直ちに返す
Ordering::Equal => return,
// 挿入位置は cur の右部分木にある
Ordering::Greater => {
pre = cur.clone();
cur = node.borrow().right.clone();
}
// 挿入位置は cur の左部分木にある
Ordering::Less => {
pre = cur.clone();
cur = node.borrow().left.clone();
}
}
}
// ノードを挿入
let pre = pre.unwrap();
let node = Some(TreeNode::new(num));
if num > pre.borrow().val {
pre.borrow_mut().right = node;
} else {
pre.borrow_mut().left = node;
}
}
/* ノードを削除 */
pub fn remove(&mut self, num: i32) {
// 木が空なら、そのまま早期リターンする
if self.root.is_none() {
return;
}
let mut cur = self.root.clone();
let mut pre = None;
// ループで探索し、葉ノードを越えたら抜ける
while let Some(node) = cur.clone() {
match num.cmp(&node.borrow().val) {
// 削除対象のノードが見つかったら、ループを抜ける
Ordering::Equal => break,
// 削除対象ノードは cur の右部分木にある
Ordering::Greater => {
pre = cur.clone();
cur = node.borrow().right.clone();
}
// 削除対象ノードは cur の左部分木にある
Ordering::Less => {
pre = cur.clone();
cur = node.borrow().left.clone();
}
}
}
// 削除対象ノードがなければそのまま返す
if cur.is_none() {
return;
}
let cur = cur.unwrap();
let (left_child, right_child) = (cur.borrow().left.clone(), cur.borrow().right.clone());
match (left_child.clone(), right_child.clone()) {
// 子ノード数 = 0 or 1
(None, None) | (Some(_), None) | (None, Some(_)) => {
// 子ノード数 = 0 / 1 のとき、child = nullptr / その子ノード
let child = left_child.or(right_child);
let pre = pre.unwrap();
// ノード cur を削除する
if !Rc::ptr_eq(&cur, self.root.as_ref().unwrap()) {
let left = pre.borrow().left.clone();
if left.is_some() && Rc::ptr_eq(left.as_ref().unwrap(), &cur) {
pre.borrow_mut().left = child;
} else {
pre.borrow_mut().right = child;
}
} else {
// 削除ノードが根ノードなら、根ノードを再設定
self.root = child;
}
}
// 子ノード数 = 2
(Some(_), Some(_)) => {
// 中順走査における cur の次ノードを取得
let mut tmp = cur.borrow().right.clone();
while let Some(node) = tmp.clone() {
if node.borrow().left.is_some() {
tmp = node.borrow().left.clone();
} else {
break;
}
}
let tmp_val = tmp.unwrap().borrow().val;
// ノード tmp を再帰的に削除
self.remove(tmp_val);
// tmp で cur を上書きする
cur.borrow_mut().val = tmp_val;
}
}
}
}
/* Driver Code */
fn main() {
/* 二分探索木を初期化 */
let mut bst = BinarySearchTree::new();
// 注意:挿入順序が異なると異なる二分木が生成される。このシーケンスからは完全二分木を生成できる
let nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15];
for &num in &nums {
bst.insert(num);
}
println!("\n初期化した二分木は\n");
print_util::print_tree(bst.get_root().as_ref().unwrap());
/* ノードを検索 */
let node = bst.search(7);
println!(
"\n見つかったノードオブジェクトは {:?}、ノードの値 = {}",
node.clone().unwrap(),
node.clone().unwrap().borrow().val
);
/* ノードを挿入 */
bst.insert(16);
println!("\nノード 16 を挿入した後、二分木は\n");
print_util::print_tree(bst.get_root().as_ref().unwrap());
/* ノードを削除 */
bst.remove(1);
println!("\nノード 1 を削除した後、二分木は\n");
print_util::print_tree(bst.get_root().as_ref().unwrap());
bst.remove(2);
println!("\nノード 2 を削除した後、二分木は\n");
print_util::print_tree(bst.get_root().as_ref().unwrap());
bst.remove(4);
println!("\nノード 4 を削除した後、二分木は\n");
print_util::print_tree(bst.get_root().as_ref().unwrap());
}
+38
View File
@@ -0,0 +1,38 @@
/**
* File: binary_tree.rs
* Created Time: 2023-02-27
* Author: xBLACKICEx (xBLACKICE@outlook.com)
*/
use std::rc::Rc;
use hello_algo_rust::include::{print_util, TreeNode};
/* Driver Code */
fn main() {
/* 二分木を初期化 */
// ノードを初期化
let n1 = TreeNode::new(1);
let n2 = TreeNode::new(2);
let n3 = TreeNode::new(3);
let n4 = TreeNode::new(4);
let n5 = TreeNode::new(5);
// ノード間の参照(ポインタ)を構築する
n1.borrow_mut().left = Some(Rc::clone(&n2));
n1.borrow_mut().right = Some(Rc::clone(&n3));
n2.borrow_mut().left = Some(Rc::clone(&n4));
n2.borrow_mut().right = Some(Rc::clone(&n5));
println!("\n二分木を初期化\n");
print_util::print_tree(&n1);
// ノードの挿入と削除
let p = TreeNode::new(0);
// n1 -> n2 の間にノード P を挿入
p.borrow_mut().left = Some(Rc::clone(&n2));
n1.borrow_mut().left = Some(Rc::clone(&p));
println!("\nノード P を挿入した後\n");
print_util::print_tree(&n1);
// ノード P を削除
drop(p);
n1.borrow_mut().left = Some(Rc::clone(&n2));
println!("\nノード P を削除した後\n");
print_util::print_tree(&n1);
}
@@ -0,0 +1,45 @@
/*
* File: binary_tree_bfs.rs
* Created Time: 2023-04-07
* Author: xBLACKICEx (xBLACKICE@outlook.com)
*/
use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode};
use hello_algo_rust::op_vec;
use std::collections::VecDeque;
use std::{cell::RefCell, rc::Rc};
/* レベル順走査 */
fn level_order(root: &Rc<RefCell<TreeNode>>) -> Vec<i32> {
// キューを初期化し、ルートノードを追加する
let mut que = VecDeque::new();
que.push_back(root.clone());
// 走査順序を保存するためのリストを初期化する
let mut vec = Vec::new();
while let Some(node) = que.pop_front() {
// デキュー
vec.push(node.borrow().val); // ノードの値を保存する
if let Some(left) = node.borrow().left.as_ref() {
que.push_back(left.clone()); // 左子ノードをキューに追加
}
if let Some(right) = node.borrow().right.as_ref() {
que.push_back(right.clone()); // 右子ノードをキューに追加
};
}
vec
}
/* Driver Code */
fn main() {
/* 二分木を初期化 */
// ここでは、配列から直接二分木を生成する関数を利用する
let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]).unwrap();
println!("二分木を初期化\n");
print_util::print_tree(&root);
/* レベル順走査 */
let vec = level_order(&root);
print!("\nレベル順走査のノード出力シーケンス = {:?}", vec);
}
@@ -0,0 +1,87 @@
/*
* File: binary_tree_dfs.rs
* Created Time: 2023-04-06
* Author: xBLACKICEx (xBLACKICE@outlook.com)
*/
use hello_algo_rust::include::{print_util, vec_to_tree, TreeNode};
use hello_algo_rust::op_vec;
use std::cell::RefCell;
use std::rc::Rc;
/* 先行順走査 */
fn pre_order(root: Option<&Rc<RefCell<TreeNode>>>) -> Vec<i32> {
let mut result = vec![];
fn dfs(root: Option<&Rc<RefCell<TreeNode>>>, res: &mut Vec<i32>) {
if let Some(node) = root {
// 訪問順序:根ノード -> 左部分木 -> 右部分木
let node = node.borrow();
res.push(node.val);
dfs(node.left.as_ref(), res);
dfs(node.right.as_ref(), res);
}
}
dfs(root, &mut result);
result
}
/* 中順走査 */
fn in_order(root: Option<&Rc<RefCell<TreeNode>>>) -> Vec<i32> {
let mut result = vec![];
fn dfs(root: Option<&Rc<RefCell<TreeNode>>>, res: &mut Vec<i32>) {
if let Some(node) = root {
// 訪問優先順: 左部分木 -> 根ノード -> 右部分木
let node = node.borrow();
dfs(node.left.as_ref(), res);
res.push(node.val);
dfs(node.right.as_ref(), res);
}
}
dfs(root, &mut result);
result
}
/* 後順走査 */
fn post_order(root: Option<&Rc<RefCell<TreeNode>>>) -> Vec<i32> {
let mut result = vec![];
fn dfs(root: Option<&Rc<RefCell<TreeNode>>>, res: &mut Vec<i32>) {
if let Some(node) = root {
// 訪問優先順: 左部分木 -> 右部分木 -> 根ノード
let node = node.borrow();
dfs(node.left.as_ref(), res);
dfs(node.right.as_ref(), res);
res.push(node.val);
}
}
dfs(root, &mut result);
result
}
/* Driver Code */
fn main() {
/* 二分木を初期化 */
// ここでは、配列から直接二分木を生成する関数を利用する
let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]);
println!("二分木を初期化\n");
print_util::print_tree(root.as_ref().unwrap());
/* 先行順走査 */
let vec = pre_order(root.as_ref());
println!("\n前順走査のノード出力シーケンス = {:?}", vec);
/* 中順走査 */
let vec = in_order(root.as_ref());
println!("\n中順走査のノード出力シーケンス = {:?}", vec);
/* 後順走査 */
let vec = post_order(root.as_ref());
print!("\n後順走査のノード出力シーケンス = {:?}", vec);
}
+57
View File
@@ -0,0 +1,57 @@
/*
* File: list_node.rs
* Created Time: 2023-03-05
* Author: codingonion (coderonion@gmail.com), rongyi (hiarongyi@gmail.com)
*/
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug)]
pub struct ListNode<T> {
pub val: T,
pub next: Option<Rc<RefCell<ListNode<T>>>>,
}
impl<T> ListNode<T> {
pub fn new(val: T) -> Rc<RefCell<ListNode<T>>> {
Rc::new(RefCell::new(ListNode { val, next: None }))
}
/* 配列をデシリアライズして連結リストに変換する */
pub fn arr_to_linked_list(array: &[T]) -> Option<Rc<RefCell<ListNode<T>>>>
where
T: Copy + Clone,
{
let mut head = None;
// insert in reverse order
for item in array.iter().rev() {
let node = Rc::new(RefCell::new(ListNode {
val: *item,
next: head.take(),
}));
head = Some(node);
}
head
}
/* 連結リストをハッシュテーブルに変換 */
pub fn linked_list_to_hashmap(
linked_list: Option<Rc<RefCell<ListNode<T>>>>,
) -> HashMap<T, Rc<RefCell<ListNode<T>>>>
where
T: std::hash::Hash + Eq + Copy + Clone,
{
let mut hashmap = HashMap::new();
let mut node = linked_list;
while let Some(cur) = node {
let borrow = cur.borrow();
hashmap.insert(borrow.val.clone(), cur.clone());
node = borrow.next.clone();
}
hashmap
}
}
+16
View File
@@ -0,0 +1,16 @@
/*
* File: include.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICE@outlook.com)
*/
pub mod list_node;
pub mod print_util;
pub mod tree_node;
pub mod vertex;
// rexport to include
pub use list_node::*;
pub use print_util::*;
pub use tree_node::*;
pub use vertex::*;
+103
View File
@@ -0,0 +1,103 @@
/*
* File: print_util.rs
* Created Time: 2023-02-05
* Author: codingonion (coderonion@gmail.com), xBLACKICEx (xBLACKICEx@outlook.com)
*/
use std::cell::{Cell, RefCell};
use std::fmt::Display;
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use super::list_node::ListNode;
use super::tree_node::{TreeNode, vec_to_tree};
struct Trunk<'a, 'b> {
prev: Option<&'a Trunk<'a, 'b>>,
str: Cell<&'b str>,
}
/* 配列を出力する */
pub fn print_array<T: Display>(nums: &[T]) {
print!("[");
if nums.len() > 0 {
for (i, num) in nums.iter().enumerate() {
print!("{}{}", num, if i == nums.len() - 1 {"]"} else {", "} );
}
} else {
print!("]");
}
}
/* ハッシュテーブルを出力 */
pub fn print_hash_map<TKey: Display, TValue: Display>(map: &HashMap<TKey, TValue>) {
for (key, value) in map {
println!("{key} -> {value}");
}
}
/* キューを出力(双方向キュー) */
pub fn print_queue<T: Display>(queue: &VecDeque<T>) {
print!("[");
let iter = queue.iter();
for (i, data) in iter.enumerate() {
print!("{}{}", data, if i == queue.len() - 1 {"]"} else {", "} );
}
}
/* 連結リストを出力 */
pub fn print_linked_list<T: Display>(head: &Rc<RefCell<ListNode<T>>>) {
print!("{}{}", head.borrow().val, if head.borrow().next.is_none() {"\n"} else {" -> "});
if let Some(node) = &head.borrow().next {
return print_linked_list(node);
}
}
/* 二分木を出力 */
pub fn print_tree(root: &Rc<RefCell<TreeNode>>) {
_print_tree(Some(root), None, false);
}
/* 二分木を出力 */
fn _print_tree(root: Option<&Rc<RefCell<TreeNode>>>, prev: Option<&Trunk>, is_right: bool) {
if let Some(node) = root {
let mut prev_str = " ";
let trunk = Trunk { prev, str: Cell::new(prev_str) };
_print_tree(node.borrow().right.as_ref(), Some(&trunk), true);
if prev.is_none() {
trunk.str.set("———");
} else if is_right {
trunk.str.set("/———");
prev_str = " |";
} else {
trunk.str.set("\\———");
prev.as_ref().unwrap().str.set(prev_str);
}
show_trunks(Some(&trunk));
println!(" {}", node.borrow().val);
if let Some(prev) = prev {
prev.str.set(prev_str);
}
trunk.str.set(" |");
_print_tree(node.borrow().left.as_ref(), Some(&trunk), false);
}
}
fn show_trunks(trunk: Option<&Trunk>) {
if let Some(trunk) = trunk {
show_trunks(trunk.prev);
print!("{}", trunk.str.get());
}
}
/* ヒープを出力 */
pub fn print_heap(heap: Vec<i32>) {
println!("ヒープの配列表現:{:?}", heap);
println!("ヒープの木構造表現:");
if let Some(root) = vec_to_tree(heap.into_iter().map(|val| Some(val)).collect()) {
print_tree(&root);
}
}
+92
View File
@@ -0,0 +1,92 @@
/*
* File: tree_node.rs
* Created Time: 2023-02-27
* Author: xBLACKICEx (xBLACKICE@outlook.com), night-cruise (2586447362@qq.com)
*/
use std::cell::RefCell;
use std::rc::Rc;
/* 二分木ノード型 */
#[derive(Debug)]
pub struct TreeNode {
pub val: i32,
pub height: i32,
pub parent: Option<Rc<RefCell<TreeNode>>>,
pub left: Option<Rc<RefCell<TreeNode>>>,
pub right: Option<Rc<RefCell<TreeNode>>>,
}
impl TreeNode {
/* コンストラクタ */
pub fn new(val: i32) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
val,
height: 0,
parent: None,
left: None,
right: None,
}))
}
}
#[macro_export]
macro_rules! op_vec {
( $( $x:expr ),* ) => {
vec![
$(Option::from($x)),*
]
};
}
// シリアライズの符号化規則は以下を参照:
// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
// 二分木の配列表現:
// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
// 二分木の連結リスト表現:
// /——— 15
// /——— 7
// /——— 3
// | \——— 6
// | \——— 12
// ——— 1
// \——— 2
// | /——— 9
// \——— 4
// \——— 8
/* リストを二分木にデシリアライズする: 再帰 */
fn vec_to_tree_dfs(arr: &[Option<i32>], i: usize) -> Option<Rc<RefCell<TreeNode>>> {
if i >= arr.len() || arr[i].is_none() {
return None;
}
let root = TreeNode::new(arr[i].unwrap());
root.borrow_mut().left = vec_to_tree_dfs(arr, 2 * i + 1);
root.borrow_mut().right = vec_to_tree_dfs(arr, 2 * i + 2);
Some(root)
}
/* リストを二分木にデシリアライズする */
pub fn vec_to_tree(arr: Vec<Option<i32>>) -> Option<Rc<RefCell<TreeNode>>> {
vec_to_tree_dfs(&arr, 0)
}
/* 二分木をリストにシリアライズする: 再帰 */
fn tree_to_vec_dfs(root: Option<&Rc<RefCell<TreeNode>>>, i: usize, res: &mut Vec<Option<i32>>) {
if let Some(root) = root {
// i + 1 is the minimum valid size to access index i
while res.len() < i + 1 {
res.push(None);
}
res[i] = Some(root.borrow().val);
tree_to_vec_dfs(root.borrow().left.as_ref(), 2 * i + 1, res);
tree_to_vec_dfs(root.borrow().right.as_ref(), 2 * i + 2, res);
}
}
/* 二分木をリストにシリアライズする */
pub fn tree_to_vec(root: Option<Rc<RefCell<TreeNode>>>) -> Vec<Option<i32>> {
let mut res = vec![];
tree_to_vec_dfs(root.as_ref(), 0, &mut res);
res
}
+27
View File
@@ -0,0 +1,27 @@
/*
* File: vertex.rs
* Created Time: 2023-07-13
* Author: night-cruise (2586447362@qq.com)
*/
/* 頂点の型 */
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct Vertex {
pub val: i32,
}
impl From<i32> for Vertex {
fn from(value: i32) -> Self {
Self { val: value }
}
}
/* 値リスト vals を入力し、頂点リスト vets を返す */
pub fn vals_to_vets(vals: Vec<i32>) -> Vec<Vertex> {
vals.into_iter().map(|val| val.into()).collect()
}
/* 頂点リスト vets を入力し、値リスト vals を返す */
pub fn vets_to_vals(vets: Vec<Vertex>) -> Vec<i32> {
vets.into_iter().map(|vet| vet.val).collect()
}
+1
View File
@@ -0,0 +1 @@
pub mod include;