mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-22 19:36:06 +00:00
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:
@@ -0,0 +1,2 @@
|
||||
target/
|
||||
Cargo.lock
|
||||
@@ -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); // append(move)の後では 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();
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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}");
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod include;
|
||||
Reference in New Issue
Block a user