mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-23 00:37:13 +00:00
Translate all code to English (#1836)
* Review the EN heading format. * Fix pythontutor headings. * Fix pythontutor headings. * bug fixes * Fix headings in **/summary.md * Revisit the CN-to-EN translation for Python code using Claude-4.5 * Revisit the CN-to-EN translation for Java code using Claude-4.5 * Revisit the CN-to-EN translation for Cpp code using Claude-4.5. * Fix the dictionary. * Fix cpp code translation for the multipart strings. * Translate Go code to English. * Update workflows to test EN code. * Add EN translation for C. * Add EN translation for CSharp. * Add EN translation for Swift. * Trigger the CI check. * Revert. * Update en/hash_map.md * Add the EN version of Dart code. * Add the EN version of Kotlin code. * Add missing code files. * Add the EN version of JavaScript code. * Add the EN version of TypeScript code. * Fix the workflows. * Add the EN version of Ruby code. * Add the EN version of Rust code. * Update the CI check for the English version code. * Update Python CI check. * Fix cmakelists for en/C code. * Fix Ruby comments
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;
|
||||
|
||||
/* Random access to element */
|
||||
fn random_access(nums: &[i32]) -> i32 {
|
||||
// Randomly select a number in interval [0, nums.len())
|
||||
let random_index = rand::thread_rng().gen_range(0..nums.len());
|
||||
// Retrieve and return the random element
|
||||
let random_num = nums[random_index];
|
||||
random_num
|
||||
}
|
||||
|
||||
/* Extend array length */
|
||||
fn extend(nums: &[i32], enlarge: usize) -> Vec<i32> {
|
||||
// Initialize an array with extended length
|
||||
let mut res: Vec<i32> = vec![0; nums.len() + enlarge];
|
||||
// Copy all elements from original array to new
|
||||
res[0..nums.len()].copy_from_slice(nums);
|
||||
|
||||
// Return the extended new array
|
||||
res
|
||||
}
|
||||
|
||||
/* Insert element num at index index in the array */
|
||||
fn insert(nums: &mut [i32], num: i32, index: usize) {
|
||||
// Move all elements at and after index index backward by one position
|
||||
for i in (index + 1..nums.len()).rev() {
|
||||
nums[i] = nums[i - 1];
|
||||
}
|
||||
// Assign num to the element at index index
|
||||
nums[index] = num;
|
||||
}
|
||||
|
||||
/* Remove the element at index index */
|
||||
fn remove(nums: &mut [i32], index: usize) {
|
||||
// Move all elements after index index forward by one position
|
||||
for i in index..nums.len() - 1 {
|
||||
nums[i] = nums[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse array */
|
||||
fn traverse(nums: &[i32]) {
|
||||
let mut _count = 0;
|
||||
// Traverse array by index
|
||||
for i in 0..nums.len() {
|
||||
_count += nums[i];
|
||||
}
|
||||
// Direct traversal of array elements
|
||||
_count = 0;
|
||||
for &num in nums {
|
||||
_count += num;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the specified element in the array */
|
||||
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() {
|
||||
/* Initialize array */
|
||||
let arr: [i32; 5] = [0; 5];
|
||||
print!("Array arr = ");
|
||||
print_util::print_array(&arr);
|
||||
// In Rust, specifying length ([i32; 5]) is an array, without length (&[i32]) is a slice
|
||||
// Since Rust arrays are designed to have compile-time determined length, only constants can specify length
|
||||
// Vector is the type Rust generally uses as a dynamic array
|
||||
// To facilitate implementing the extend() method, the following treats vector as array
|
||||
let nums: Vec<i32> = vec![1, 3, 2, 5, 4];
|
||||
print!("\nArray nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Insert element
|
||||
let random_num = random_access(&nums);
|
||||
println!("\nGet random element {} from nums", random_num);
|
||||
|
||||
// Traverse array
|
||||
let mut nums: Vec<i32> = extend(&nums, 3);
|
||||
print!("Extend array length to 8, resulting in nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Insert element
|
||||
insert(&mut nums, 6, 3);
|
||||
print!("\nInsert number 6 at index 3, get nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Remove element
|
||||
remove(&mut nums, 2);
|
||||
print!("\nDelete element at index 2, get nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Traverse array
|
||||
traverse(&nums);
|
||||
|
||||
// Find element
|
||||
let index = find(&nums, 3).unwrap();
|
||||
println!("\nFind element 3 in nums, index = {}", 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;
|
||||
|
||||
/* Insert node P after node n0 in the linked list */
|
||||
#[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);
|
||||
}
|
||||
|
||||
/* Remove the first node after node n0 in the linked list */
|
||||
#[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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Access the node at index index in the linked list */
|
||||
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)
|
||||
}
|
||||
|
||||
/* Find the first node with value target in the linked list */
|
||||
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() {
|
||||
/* Initialize linked list */
|
||||
// Initialize each node
|
||||
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);
|
||||
// Build references between nodes
|
||||
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!("Initialized linked list is ");
|
||||
print_util::print_linked_list(&n0);
|
||||
|
||||
/* Insert node */
|
||||
insert(&n0, ListNode::new(0));
|
||||
print!("After inserting node, linked list is ");
|
||||
print_util::print_linked_list(&n0);
|
||||
|
||||
/* Remove node */
|
||||
remove(&n0);
|
||||
print!("After deleting node, linked list is ");
|
||||
print_util::print_linked_list(&n0);
|
||||
|
||||
/* Access node */
|
||||
let node = access(n0.clone(), 3);
|
||||
println!("Value of node at index 3 in linked list = {}", node.unwrap().borrow().val);
|
||||
|
||||
/* Search node */
|
||||
let index = find(n0.clone(), 2);
|
||||
println!("Index of node with value 2 in linked list = {}", 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() {
|
||||
// Initialize list
|
||||
let mut nums: Vec<i32> = vec![1, 3, 2, 5, 4];
|
||||
print!("List nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Update element
|
||||
let num = nums[1];
|
||||
println!("\nAccess element at index 1, get num = {num}");
|
||||
|
||||
// Add elements at the end
|
||||
nums[1] = 0;
|
||||
print!("Update element at index 1 to 0, resulting in nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Remove element
|
||||
nums.clear();
|
||||
print!("\nAfter clearing list, nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Direct traversal of list elements
|
||||
nums.push(1);
|
||||
nums.push(3);
|
||||
nums.push(2);
|
||||
nums.push(5);
|
||||
nums.push(4);
|
||||
print!("\nAfter adding elements, nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Sort list
|
||||
nums.insert(3, 6);
|
||||
print!("\nInsert number 6 at index 3, get nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Remove element
|
||||
nums.remove(3);
|
||||
print!("\nDelete element at index 3, get nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Traverse list by index
|
||||
let mut _count = 0;
|
||||
for i in 0..nums.len() {
|
||||
_count += nums[i];
|
||||
}
|
||||
// Directly traverse list elements
|
||||
_count = 0;
|
||||
for x in &nums {
|
||||
_count += x;
|
||||
}
|
||||
|
||||
// Concatenate two lists
|
||||
let mut nums1 = vec![6, 8, 7, 10, 9];
|
||||
nums.append(&mut nums1); // After append (move), nums1 is empty!
|
||||
|
||||
// nums.extend(&nums1); // extend (borrow) allows nums1 to continue being used
|
||||
print!("\nAfter concatenating list nums1 to nums, get nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
// Sort list
|
||||
nums.sort();
|
||||
print!("\nAfter sorting list, 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;
|
||||
|
||||
/* List class */
|
||||
#[allow(dead_code)]
|
||||
struct MyList {
|
||||
arr: Vec<i32>, // Array (stores list elements)
|
||||
capacity: usize, // List capacity
|
||||
size: usize, // List length (current number of elements)
|
||||
extend_ratio: usize, // Multiple by which the list capacity is extended each time
|
||||
}
|
||||
|
||||
#[allow(unused, unused_comparisons)]
|
||||
impl MyList {
|
||||
/* Constructor */
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
let mut vec = vec![0; capacity];
|
||||
Self {
|
||||
arr: vec,
|
||||
capacity,
|
||||
size: 0,
|
||||
extend_ratio: 2,
|
||||
}
|
||||
}
|
||||
|
||||
/* Get list length (current number of elements) */
|
||||
pub fn size(&self) -> usize {
|
||||
return self.size;
|
||||
}
|
||||
|
||||
/* Get list capacity */
|
||||
pub fn capacity(&self) -> usize {
|
||||
return self.capacity;
|
||||
}
|
||||
|
||||
/* Update element */
|
||||
pub fn get(&self, index: usize) -> i32 {
|
||||
// If the index is out of bounds, throw an exception, as below
|
||||
if index >= self.size {
|
||||
panic!("Index out of bounds")
|
||||
};
|
||||
return self.arr[index];
|
||||
}
|
||||
|
||||
/* Add elements at the end */
|
||||
pub fn set(&mut self, index: usize, num: i32) {
|
||||
if index >= self.size {
|
||||
panic!("Index out of bounds")
|
||||
};
|
||||
self.arr[index] = num;
|
||||
}
|
||||
|
||||
/* Direct traversal of list elements */
|
||||
pub fn add(&mut self, num: i32) {
|
||||
// When the number of elements exceeds capacity, trigger the extension mechanism
|
||||
if self.size == self.capacity() {
|
||||
self.extend_capacity();
|
||||
}
|
||||
self.arr[self.size] = num;
|
||||
// Update the number of elements
|
||||
self.size += 1;
|
||||
}
|
||||
|
||||
/* Sort list */
|
||||
pub fn insert(&mut self, index: usize, num: i32) {
|
||||
if index >= self.size() {
|
||||
panic!("Index out of bounds")
|
||||
};
|
||||
// When the number of elements exceeds capacity, trigger the extension mechanism
|
||||
if self.size == self.capacity() {
|
||||
self.extend_capacity();
|
||||
}
|
||||
// Move all elements after index index forward by one position
|
||||
for j in (index..self.size).rev() {
|
||||
self.arr[j + 1] = self.arr[j];
|
||||
}
|
||||
self.arr[index] = num;
|
||||
// Update the number of elements
|
||||
self.size += 1;
|
||||
}
|
||||
|
||||
/* Remove element */
|
||||
pub fn remove(&mut self, index: usize) -> i32 {
|
||||
if index >= self.size() {
|
||||
panic!("Index out of bounds")
|
||||
};
|
||||
let num = self.arr[index];
|
||||
// Create a new array with length _extend_ratio times the original array, and copy the original array to the new array
|
||||
for j in index..self.size - 1 {
|
||||
self.arr[j] = self.arr[j + 1];
|
||||
}
|
||||
// Update the number of elements
|
||||
self.size -= 1;
|
||||
// Return the removed element
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn extend_capacity(&mut self) {
|
||||
// Create new array with length extend_ratio times original, copy original array to new array
|
||||
let new_capacity = self.capacity * self.extend_ratio;
|
||||
self.arr.resize(new_capacity, 0);
|
||||
// Add elements at the end
|
||||
self.capacity = new_capacity;
|
||||
}
|
||||
|
||||
/* Convert list to array */
|
||||
pub fn to_array(&self) -> Vec<i32> {
|
||||
// Elements enqueue
|
||||
let mut arr = Vec::new();
|
||||
for i in 0..self.size {
|
||||
arr.push(self.get(i));
|
||||
}
|
||||
arr
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Initialize list */
|
||||
let mut nums = MyList::new(10);
|
||||
/* Direct traversal of list elements */
|
||||
nums.add(1);
|
||||
nums.add(3);
|
||||
nums.add(2);
|
||||
nums.add(5);
|
||||
nums.add(4);
|
||||
print!("List nums = ");
|
||||
print_util::print_array(&nums.to_array());
|
||||
print!(", capacity = {}, length = {}", nums.capacity(), nums.size());
|
||||
|
||||
/* Sort list */
|
||||
nums.insert(3, 6);
|
||||
print!("\nInsert number 6 at index 3, get nums = ");
|
||||
print_util::print_array(&nums.to_array());
|
||||
|
||||
/* Remove element */
|
||||
nums.remove(3);
|
||||
print!("\nDelete element at index 3, get nums = ");
|
||||
print_util::print_array(&nums.to_array());
|
||||
|
||||
/* Update element */
|
||||
let num = nums.get(1);
|
||||
println!("\nAccess element at index 1, get num = {num}");
|
||||
|
||||
/* Add elements at the end */
|
||||
nums.set(1, 0);
|
||||
print!("Update element at index 1 to 0, resulting in nums = ");
|
||||
print_util::print_array(&nums.to_array());
|
||||
|
||||
/* Test capacity expansion mechanism */
|
||||
for i in 0..10 {
|
||||
// At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism
|
||||
nums.add(i);
|
||||
}
|
||||
print!("\nAfter expanding list, nums = ");
|
||||
print_util::print_array(&nums.to_array());
|
||||
print!(", capacity = {}, length = {}", nums.capacity(), nums.size());
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* File: n_queens.rs
|
||||
* Created Time: 2023-07-15
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Backtracking algorithm: N queens */
|
||||
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],
|
||||
) {
|
||||
// When all rows are placed, record the solution
|
||||
if row == n {
|
||||
res.push(state.clone());
|
||||
return;
|
||||
}
|
||||
// Traverse all columns
|
||||
for col in 0..n {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
let diag1 = row + n - 1 - col;
|
||||
let diag2 = row + col;
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if !cols[col] && !diags1[diag1] && !diags2[diag2] {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q".into();
|
||||
(cols[col], diags1[diag1], diags2[diag2]) = (true, true, true);
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#".into();
|
||||
(cols[col], diags1[diag1], diags2[diag2]) = (false, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve N queens */
|
||||
fn n_queens(n: usize) -> Vec<Vec<Vec<String>>> {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
let mut state: Vec<Vec<String>> = vec![vec!["#".to_string(); n]; n];
|
||||
let mut cols = vec![false; n]; // Record whether there is a queen in the column
|
||||
let mut diags1 = vec![false; 2 * n - 1]; // Record whether there is a queen on the main diagonal
|
||||
let mut diags2 = vec![false; 2 * n - 1]; // Record whether there is a queen on the anti-diagonal
|
||||
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!("Input board size is {n}");
|
||||
println!("Total queen placement solutions: {}", 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)
|
||||
*/
|
||||
|
||||
/* Backtracking algorithm: Permutations I */
|
||||
fn backtrack(mut state: Vec<i32>, choices: &[i32], selected: &mut [bool], res: &mut Vec<Vec<i32>>) {
|
||||
// When the state length equals the number of elements, record the solution
|
||||
if state.len() == choices.len() {
|
||||
res.push(state);
|
||||
return;
|
||||
}
|
||||
// Traverse all choices
|
||||
for i in 0..choices.len() {
|
||||
let choice = choices[i];
|
||||
// Pruning: do not allow repeated selection of elements
|
||||
if !selected[i] {
|
||||
// Attempt: make choice, update state
|
||||
selected[i] = true;
|
||||
state.push(choice);
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state.clone(), choices, selected, res);
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
selected[i] = false;
|
||||
state.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Permutations I */
|
||||
fn permutations_i(nums: &mut [i32]) -> Vec<Vec<i32>> {
|
||||
let mut res = Vec::new(); // State (subset)
|
||||
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!("Input array nums = {:?}", &nums);
|
||||
println!("All permutations res = {:?}", &res);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* File: permutations_ii.rs
|
||||
* Created Time: 2023-07-15
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
/* Backtracking algorithm: Permutations II */
|
||||
fn backtrack(mut state: Vec<i32>, choices: &[i32], selected: &mut [bool], res: &mut Vec<Vec<i32>>) {
|
||||
// When the state length equals the number of elements, record the solution
|
||||
if state.len() == choices.len() {
|
||||
res.push(state);
|
||||
return;
|
||||
}
|
||||
// Traverse all choices
|
||||
let mut duplicated = HashSet::<i32>::new();
|
||||
for i in 0..choices.len() {
|
||||
let choice = choices[i];
|
||||
// Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements
|
||||
if !selected[i] && !duplicated.contains(&choice) {
|
||||
// Attempt: make choice, update state
|
||||
duplicated.insert(choice); // Record the selected element value
|
||||
selected[i] = true;
|
||||
state.push(choice);
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state.clone(), choices, selected, res);
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
selected[i] = false;
|
||||
state.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Permutations 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!("Input array nums = {:?}", &nums);
|
||||
println!("All permutations 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};
|
||||
|
||||
/* Preorder traversal: Example 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 {
|
||||
// Record solution
|
||||
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!("Initialize binary tree");
|
||||
print_util::print_tree(root.as_ref().unwrap());
|
||||
|
||||
// Preorder traversal
|
||||
let mut res = Vec::new();
|
||||
pre_order(&mut res, root.as_ref());
|
||||
|
||||
println!("\nOutput all nodes with value 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};
|
||||
|
||||
/* Preorder traversal: Example 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 {
|
||||
// Attempt
|
||||
path.push(node.clone());
|
||||
if node.borrow().val == 7 {
|
||||
// Record solution
|
||||
res.push(path.clone());
|
||||
}
|
||||
pre_order(res, path, node.borrow().left.as_ref());
|
||||
pre_order(res, path, node.borrow().right.as_ref());
|
||||
// Backtrack
|
||||
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!("Initialize binary tree");
|
||||
print_util::print_tree(root.as_ref().unwrap());
|
||||
|
||||
// Preorder traversal
|
||||
let mut path = Vec::new();
|
||||
let mut res = Vec::new();
|
||||
pre_order(&mut res, &mut path, root.as_ref());
|
||||
|
||||
println!("\nOutput all paths from root node to node 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};
|
||||
|
||||
/* Preorder traversal: Example 3 */
|
||||
fn pre_order(
|
||||
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
|
||||
path: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
root: Option<&Rc<RefCell<TreeNode>>>,
|
||||
) {
|
||||
// Pruning
|
||||
if root.is_none() || root.as_ref().unwrap().borrow().val == 3 {
|
||||
return;
|
||||
}
|
||||
if let Some(node) = root {
|
||||
// Attempt
|
||||
path.push(node.clone());
|
||||
if node.borrow().val == 7 {
|
||||
// Record solution
|
||||
res.push(path.clone());
|
||||
}
|
||||
pre_order(res, path, node.borrow().left.as_ref());
|
||||
pre_order(res, path, node.borrow().right.as_ref());
|
||||
// Backtrack
|
||||
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!("Initialize binary tree");
|
||||
print_util::print_tree(root.as_ref().unwrap());
|
||||
|
||||
// Preorder traversal
|
||||
let mut path = Vec::new();
|
||||
let mut res = Vec::new();
|
||||
pre_order(&mut res, &mut path, root.as_ref());
|
||||
|
||||
println!("\nOutput all paths from root node to node 7, paths do not include nodes with value 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};
|
||||
|
||||
/* Check if the current state is a solution */
|
||||
fn is_solution(state: &mut Vec<Rc<RefCell<TreeNode>>>) -> bool {
|
||||
return !state.is_empty() && state.last().unwrap().borrow().val == 7;
|
||||
}
|
||||
|
||||
/* Record solution */
|
||||
fn record_solution(
|
||||
state: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
|
||||
) {
|
||||
res.push(state.clone());
|
||||
}
|
||||
|
||||
/* Check if the choice is valid under the current state */
|
||||
fn is_valid(_: &mut Vec<Rc<RefCell<TreeNode>>>, choice: Option<&Rc<RefCell<TreeNode>>>) -> bool {
|
||||
return choice.is_some() && choice.unwrap().borrow().val != 3;
|
||||
}
|
||||
|
||||
/* Update state */
|
||||
fn make_choice(state: &mut Vec<Rc<RefCell<TreeNode>>>, choice: Rc<RefCell<TreeNode>>) {
|
||||
state.push(choice);
|
||||
}
|
||||
|
||||
/* Restore state */
|
||||
fn undo_choice(state: &mut Vec<Rc<RefCell<TreeNode>>>, _: Rc<RefCell<TreeNode>>) {
|
||||
state.pop();
|
||||
}
|
||||
|
||||
/* Backtracking algorithm: Example 3 */
|
||||
fn backtrack(
|
||||
state: &mut Vec<Rc<RefCell<TreeNode>>>,
|
||||
choices: &Vec<Option<&Rc<RefCell<TreeNode>>>>,
|
||||
res: &mut Vec<Vec<Rc<RefCell<TreeNode>>>>,
|
||||
) {
|
||||
// Check if it is a solution
|
||||
if is_solution(state) {
|
||||
// Record solution
|
||||
record_solution(state, res);
|
||||
}
|
||||
// Traverse all choices
|
||||
for &choice in choices.iter() {
|
||||
// Pruning: check if the choice is valid
|
||||
if is_valid(state, choice) {
|
||||
// Attempt: make choice, update state
|
||||
make_choice(state, choice.unwrap().clone());
|
||||
// Proceed to the next round of selection
|
||||
backtrack(
|
||||
state,
|
||||
&vec![
|
||||
choice.unwrap().borrow().left.as_ref(),
|
||||
choice.unwrap().borrow().right.as_ref(),
|
||||
],
|
||||
res,
|
||||
);
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
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!("Initialize binary tree");
|
||||
print_util::print_tree(root.as_ref().unwrap());
|
||||
|
||||
// Backtracking algorithm
|
||||
let mut res = Vec::new();
|
||||
backtrack(&mut Vec::new(), &mut vec![root.as_ref()], &mut res);
|
||||
|
||||
println!("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 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)
|
||||
*/
|
||||
|
||||
/* Backtracking algorithm: Subset sum I */
|
||||
fn backtrack(
|
||||
state: &mut Vec<i32>,
|
||||
target: i32,
|
||||
choices: &[i32],
|
||||
start: usize,
|
||||
res: &mut Vec<Vec<i32>>,
|
||||
) {
|
||||
// When the subset sum equals target, record the solution
|
||||
if target == 0 {
|
||||
res.push(state.clone());
|
||||
return;
|
||||
}
|
||||
// Traverse all choices
|
||||
// Pruning 2: start traversing from start to avoid generating duplicate subsets
|
||||
for i in start..choices.len() {
|
||||
// Pruning 1: if the subset sum exceeds target, end the loop directly
|
||||
// This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target
|
||||
if target - choices[i] < 0 {
|
||||
break;
|
||||
}
|
||||
// Attempt: make choice, update target, start
|
||||
state.push(choices[i]);
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, target - choices[i], choices, i, res);
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
state.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve subset sum I */
|
||||
fn subset_sum_i(nums: &mut [i32], target: i32) -> Vec<Vec<i32>> {
|
||||
let mut state = Vec::new(); // State (subset)
|
||||
nums.sort(); // Sort nums
|
||||
let start = 0; // Start point for traversal
|
||||
let mut res = Vec::new(); // Result list (subset list)
|
||||
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!("Input array nums = {:?}, target = {}", &nums, target);
|
||||
println!("All subsets with sum equal to {} res = {:?}", target, &res);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* File: subset_sum_i_naive.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Backtracking algorithm: Subset sum I */
|
||||
fn backtrack(
|
||||
state: &mut Vec<i32>,
|
||||
target: i32,
|
||||
total: i32,
|
||||
choices: &[i32],
|
||||
res: &mut Vec<Vec<i32>>,
|
||||
) {
|
||||
// When the subset sum equals target, record the solution
|
||||
if total == target {
|
||||
res.push(state.clone());
|
||||
return;
|
||||
}
|
||||
// Traverse all choices
|
||||
for i in 0..choices.len() {
|
||||
// Pruning: if the subset sum exceeds target, skip this choice
|
||||
if total + choices[i] > target {
|
||||
continue;
|
||||
}
|
||||
// Attempt: make choice, update element sum total
|
||||
state.push(choices[i]);
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, target, total + choices[i], choices, res);
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
state.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve subset sum I (including duplicate subsets) */
|
||||
fn subset_sum_i_naive(nums: &[i32], target: i32) -> Vec<Vec<i32>> {
|
||||
let mut state = Vec::new(); // State (subset)
|
||||
let total = 0; // Subset sum
|
||||
let mut res = Vec::new(); // Result list (subset list)
|
||||
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!("Input array nums = {:?}, target = {}", &nums, target);
|
||||
println!("All subsets with sum equal to {} res = {:?}", target, &res);
|
||||
println!("Please note that this method outputs results containing duplicate sets");
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* File: subset_sum_ii.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Backtracking algorithm: Subset sum II */
|
||||
fn backtrack(
|
||||
state: &mut Vec<i32>,
|
||||
target: i32,
|
||||
choices: &[i32],
|
||||
start: usize,
|
||||
res: &mut Vec<Vec<i32>>,
|
||||
) {
|
||||
// When the subset sum equals target, record the solution
|
||||
if target == 0 {
|
||||
res.push(state.clone());
|
||||
return;
|
||||
}
|
||||
// Traverse all choices
|
||||
// Pruning 2: start traversing from start to avoid generating duplicate subsets
|
||||
// Pruning 3: start traversing from start to avoid repeatedly selecting the same element
|
||||
for i in start..choices.len() {
|
||||
// Pruning 1: if the subset sum exceeds target, end the loop directly
|
||||
// This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target
|
||||
if target - choices[i] < 0 {
|
||||
break;
|
||||
}
|
||||
// Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly
|
||||
if i > start && choices[i] == choices[i - 1] {
|
||||
continue;
|
||||
}
|
||||
// Attempt: make choice, update target, start
|
||||
state.push(choices[i]);
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, target - choices[i], choices, i + 1, res);
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
state.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve subset sum II */
|
||||
fn subset_sum_ii(nums: &mut [i32], target: i32) -> Vec<Vec<i32>> {
|
||||
let mut state = Vec::new(); // State (subset)
|
||||
nums.sort(); // Sort nums
|
||||
let start = 0; // Start point for traversal
|
||||
let mut res = Vec::new(); // Result list (subset list)
|
||||
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!("Input array nums = {:?}, target = {}", &nums, target);
|
||||
println!("All subsets with sum equal to {} res = {:?}", target, &res);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* File: iteration.rs
|
||||
* Created Time: 2023-09-02
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
|
||||
/* for loop */
|
||||
fn for_loop(n: i32) -> i32 {
|
||||
let mut res = 0;
|
||||
// Sum 1, 2, ..., n-1, n
|
||||
for i in 1..=n {
|
||||
res += i;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/* while loop */
|
||||
fn while_loop(n: i32) -> i32 {
|
||||
let mut res = 0;
|
||||
let mut i = 1; // Initialize condition variable
|
||||
|
||||
// Sum 1, 2, ..., n-1, n
|
||||
while i <= n {
|
||||
res += i;
|
||||
i += 1; // Update condition variable
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/* while loop (two updates) */
|
||||
fn while_loop_ii(n: i32) -> i32 {
|
||||
let mut res = 0;
|
||||
let mut i = 1; // Initialize condition variable
|
||||
|
||||
// Sum 1, 4, 10, ...
|
||||
while i <= n {
|
||||
res += i;
|
||||
// Update condition variable
|
||||
i += 1;
|
||||
i *= 2;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/* Nested for loop */
|
||||
fn nested_for_loop(n: i32) -> String {
|
||||
let mut res = vec![];
|
||||
// Loop i = 1, 2, ..., n-1, n
|
||||
for i in 1..=n {
|
||||
// Loop 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 loop sum result res = {res}");
|
||||
|
||||
res = while_loop(n);
|
||||
println!("\nWhile loop sum result res = {res}");
|
||||
|
||||
res = while_loop_ii(n);
|
||||
println!("\nWhile loop (two updates) sum result res = {}", res);
|
||||
|
||||
let res = nested_for_loop(n);
|
||||
println!("\nNested for loop traversal result {res}");
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* File: recursion.rs
|
||||
* Created Time: 2023-09-02
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
|
||||
/* Recursion */
|
||||
fn recur(n: i32) -> i32 {
|
||||
// Termination condition
|
||||
if n == 1 {
|
||||
return 1;
|
||||
}
|
||||
// Recurse: recursive call
|
||||
let res = recur(n - 1);
|
||||
// Return: return result
|
||||
n + res
|
||||
}
|
||||
|
||||
/* Simulate recursion using iteration */
|
||||
fn for_loop_recur(n: i32) -> i32 {
|
||||
// Use an explicit stack to simulate the system call stack
|
||||
let mut stack = Vec::new();
|
||||
let mut res = 0;
|
||||
// Recurse: recursive call
|
||||
for i in (1..=n).rev() {
|
||||
// Simulate "recurse" with "push"
|
||||
stack.push(i);
|
||||
}
|
||||
// Return: return result
|
||||
while !stack.is_empty() {
|
||||
// Simulate "return" with "pop"
|
||||
res += stack.pop().unwrap();
|
||||
}
|
||||
// res = 1+2+3+...+n
|
||||
res
|
||||
}
|
||||
|
||||
/* Tail recursion */
|
||||
fn tail_recur(n: i32, res: i32) -> i32 {
|
||||
// Termination condition
|
||||
if n == 0 {
|
||||
return res;
|
||||
}
|
||||
// Tail recursive call
|
||||
tail_recur(n - 1, res + n)
|
||||
}
|
||||
|
||||
/* Fibonacci sequence: recursion */
|
||||
fn fib(n: i32) -> i32 {
|
||||
// Termination condition f(1) = 0, f(2) = 1
|
||||
if n == 1 || n == 2 {
|
||||
return n - 1;
|
||||
}
|
||||
// Recursive call f(n) = f(n-1) + f(n-2)
|
||||
let res = fib(n - 1) + fib(n - 2);
|
||||
// Return result
|
||||
res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
let n = 5;
|
||||
let mut res;
|
||||
|
||||
res = recur(n);
|
||||
println!("\nRecursion sum result res = {res}");
|
||||
|
||||
res = for_loop_recur(n);
|
||||
println!("\nUsing iteration to simulate recursion sum result res = {res}");
|
||||
|
||||
res = tail_recur(n, 0);
|
||||
println!("\nTail recursion sum result res = {res}");
|
||||
|
||||
res = fib(n);
|
||||
println!("\nThe {n}th Fibonacci number is {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;
|
||||
|
||||
/* Function */
|
||||
fn function() -> i32 {
|
||||
// Perform some operations
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Constant order */
|
||||
#[allow(unused)]
|
||||
fn constant(n: i32) {
|
||||
// Constants, variables, objects occupy O(1) space
|
||||
const A: i32 = 0;
|
||||
let b = 0;
|
||||
let nums = vec![0; 10000];
|
||||
let node = ListNode::new(0);
|
||||
// Variables in the loop occupy O(1) space
|
||||
for i in 0..n {
|
||||
let c = 0;
|
||||
}
|
||||
// Functions in the loop occupy O(1) space
|
||||
for i in 0..n {
|
||||
function();
|
||||
}
|
||||
}
|
||||
|
||||
/* Linear order */
|
||||
#[allow(unused)]
|
||||
fn linear(n: i32) {
|
||||
// Array of length n uses O(n) space
|
||||
let mut nums = vec![0; n as usize];
|
||||
// A list of length n occupies O(n) space
|
||||
let mut nodes = Vec::new();
|
||||
for i in 0..n {
|
||||
nodes.push(ListNode::new(i))
|
||||
}
|
||||
// A hash table of length n occupies O(n) space
|
||||
let mut map = HashMap::new();
|
||||
for i in 0..n {
|
||||
map.insert(i, i.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/* Linear order (recursive implementation) */
|
||||
fn linear_recur(n: i32) {
|
||||
println!("Recursion n = {}", n);
|
||||
if n == 1 {
|
||||
return;
|
||||
};
|
||||
linear_recur(n - 1);
|
||||
}
|
||||
|
||||
/* Exponential order */
|
||||
#[allow(unused)]
|
||||
fn quadratic(n: i32) {
|
||||
// Matrix uses O(n^2) space
|
||||
let num_matrix = vec![vec![0; n as usize]; n as usize];
|
||||
// 2D list uses O(n^2) space
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Quadratic order (recursive implementation) */
|
||||
fn quadratic_recur(n: i32) -> i32 {
|
||||
if n <= 0 {
|
||||
return 0;
|
||||
};
|
||||
// Array nums has length n, n-1, ..., 2, 1
|
||||
let nums = vec![0; n as usize];
|
||||
println!("In recursion n = {}, nums length = {}", n, nums.len());
|
||||
return quadratic_recur(n - 1);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
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 order
|
||||
constant(n);
|
||||
// Linear order
|
||||
linear(n);
|
||||
linear_recur(n);
|
||||
// Exponential order
|
||||
quadratic(n);
|
||||
quadratic_recur(n);
|
||||
// Exponential order
|
||||
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)
|
||||
*/
|
||||
|
||||
/* Constant order */
|
||||
fn constant(n: i32) -> i32 {
|
||||
_ = n;
|
||||
let mut count = 0;
|
||||
let size = 100_000;
|
||||
for _ in 0..size {
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Linear order */
|
||||
fn linear(n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
for _ in 0..n {
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Linear order (traversing array) */
|
||||
fn array_traversal(nums: &[i32]) -> i32 {
|
||||
let mut count = 0;
|
||||
// Number of iterations is proportional to the array length
|
||||
for _ in nums {
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Exponential order */
|
||||
fn quadratic(n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
// Number of iterations is quadratically related to the data size n
|
||||
for _ in 0..n {
|
||||
for _ in 0..n {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Quadratic order (bubble sort) */
|
||||
fn bubble_sort(nums: &mut [i32]) -> i32 {
|
||||
let mut count = 0; // Counter
|
||||
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
let tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
count += 3; // Element swap includes 3 unit operations
|
||||
}
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Exponential order (loop implementation) */
|
||||
fn exponential(n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
let mut base = 1;
|
||||
// Cells divide into two every round, forming sequence 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
|
||||
}
|
||||
|
||||
/* Exponential order (recursive implementation) */
|
||||
fn exp_recur(n: i32) -> i32 {
|
||||
if n == 1 {
|
||||
return 1;
|
||||
}
|
||||
exp_recur(n - 1) + exp_recur(n - 1) + 1
|
||||
}
|
||||
|
||||
/* Logarithmic order (loop implementation) */
|
||||
fn logarithmic(mut n: i32) -> i32 {
|
||||
let mut count = 0;
|
||||
while n > 1 {
|
||||
n = n / 2;
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Logarithmic order (recursive implementation) */
|
||||
fn log_recur(n: i32) -> i32 {
|
||||
if n <= 1 {
|
||||
return 0;
|
||||
}
|
||||
log_recur(n / 2) + 1
|
||||
}
|
||||
|
||||
/* Linearithmic order */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Factorial order (recursive implementation) */
|
||||
fn factorial_recur(n: i32) -> i32 {
|
||||
if n == 0 {
|
||||
return 1;
|
||||
}
|
||||
let mut count = 0;
|
||||
// Split from 1 into n
|
||||
for _ in 0..n {
|
||||
count += factorial_recur(n - 1);
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// You can modify n to run and observe the trend of the number of operations for various complexities
|
||||
let n: i32 = 8;
|
||||
println!("Input data size n = {}", n);
|
||||
|
||||
let mut count = constant(n);
|
||||
println!("Constant-time operations count = {}", count);
|
||||
|
||||
count = linear(n);
|
||||
println!("Linear-time operations count = {}", count);
|
||||
count = array_traversal(&vec![0; n as usize]);
|
||||
println!("Linear-time (array traversal) operations count = {}", count);
|
||||
|
||||
count = quadratic(n);
|
||||
println!("Quadratic-time operations count = {}", count);
|
||||
let mut nums = (1..=n).rev().collect::<Vec<_>>(); // [n,n-1,...,2,1]
|
||||
count = bubble_sort(&mut nums);
|
||||
println!("Quadratic-time (bubble sort) operations count = {}", count);
|
||||
|
||||
count = exponential(n);
|
||||
println!("Exponential-time (iterative) operations count = {}", count);
|
||||
count = exp_recur(n);
|
||||
println!("Exponential-time (recursive) operations count = {}", count);
|
||||
|
||||
count = logarithmic(n);
|
||||
println!("Logarithmic-time (iterative) operations count = {}", count);
|
||||
count = log_recur(n);
|
||||
println!("Logarithmic-time (recursive) operations count = {}", count);
|
||||
|
||||
count = linear_log_recur(n);
|
||||
println!("Linearithmic-time (recursive) operations count = {}", count);
|
||||
|
||||
count = factorial_recur(n);
|
||||
println!("Factorial-time (recursive) operations count = {}", 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;
|
||||
|
||||
/* Generate an array with elements { 1, 2, ..., n }, order shuffled */
|
||||
fn random_numbers(n: i32) -> Vec<i32> {
|
||||
// Generate array nums = { 1, 2, 3, ..., n }
|
||||
let mut nums = (1..=n).collect::<Vec<i32>>();
|
||||
// Randomly shuffle array elements
|
||||
nums.shuffle(&mut thread_rng());
|
||||
nums
|
||||
}
|
||||
|
||||
/* Find the index of number 1 in array nums */
|
||||
fn find_one(nums: &[i32]) -> Option<usize> {
|
||||
for i in 0..nums.len() {
|
||||
// When element 1 is at the head of the array, best time complexity O(1) is achieved
|
||||
// When element 1 is at the tail of the array, worst time complexity O(n) is achieved
|
||||
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!("\nArray [ 1, 2, ..., n ] after shuffling = ");
|
||||
print_util::print_array(&nums);
|
||||
println!("\nIndex of number 1 is {}", index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* File: binary_search_recur.rs
|
||||
* Created Time: 2023-07-15
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Binary search: problem f(i, j) */
|
||||
fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 {
|
||||
// If the interval is empty, it means there is no target element, return -1
|
||||
if i > j {
|
||||
return -1;
|
||||
}
|
||||
let m: i32 = i + (j - i) / 2;
|
||||
if nums[m as usize] < target {
|
||||
// Recursion subproblem f(m+1, j)
|
||||
return dfs(nums, target, m + 1, j);
|
||||
} else if nums[m as usize] > target {
|
||||
// Recursion subproblem f(i, m-1)
|
||||
return dfs(nums, target, i, m - 1);
|
||||
} else {
|
||||
// Found the target element, return its index
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
/* Binary search */
|
||||
fn binary_search(nums: &[i32], target: i32) -> i32 {
|
||||
let n = nums.len() as i32;
|
||||
// Solve the problem 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];
|
||||
|
||||
// Binary search (closed interval on both sides)
|
||||
let index = binary_search(&nums, target);
|
||||
println!("Index of target element 6 is {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};
|
||||
|
||||
/* Build binary tree: divide and conquer */
|
||||
fn dfs(
|
||||
preorder: &[i32],
|
||||
inorder_map: &HashMap<i32, i32>,
|
||||
i: i32,
|
||||
l: i32,
|
||||
r: i32,
|
||||
) -> Option<Rc<RefCell<TreeNode>>> {
|
||||
// Terminate when the subtree interval is empty
|
||||
if r - l < 0 {
|
||||
return None;
|
||||
}
|
||||
// Initialize the root node
|
||||
let root = TreeNode::new(preorder[i as usize]);
|
||||
// Query m to divide the left and right subtrees
|
||||
let m = inorder_map.get(&preorder[i as usize]).unwrap();
|
||||
// Subproblem: build the left subtree
|
||||
root.borrow_mut().left = dfs(preorder, inorder_map, i + 1, l, m - 1);
|
||||
// Subproblem: build the right subtree
|
||||
root.borrow_mut().right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r);
|
||||
// Return the root node
|
||||
Some(root)
|
||||
}
|
||||
|
||||
/* Build binary tree */
|
||||
fn build_tree(preorder: &[i32], inorder: &[i32]) -> Option<Rc<RefCell<TreeNode>>> {
|
||||
// Initialize hash map, storing the mapping from inorder elements to indices
|
||||
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!("In-order traversal = {:?}", preorder);
|
||||
println!("Pre-order traversal = {:?}", inorder);
|
||||
|
||||
let root = build_tree(&preorder, &inorder);
|
||||
println!("The constructed binary tree is:");
|
||||
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)]
|
||||
|
||||
/* Move a disk */
|
||||
fn move_pan(src: &mut Vec<i32>, tar: &mut Vec<i32>) {
|
||||
// Take out a disk from the top of src
|
||||
let pan = src.pop().unwrap();
|
||||
// Place the disk on top of tar
|
||||
tar.push(pan);
|
||||
}
|
||||
|
||||
/* Solve the Tower of Hanoi problem f(i) */
|
||||
fn dfs(i: i32, src: &mut Vec<i32>, buf: &mut Vec<i32>, tar: &mut Vec<i32>) {
|
||||
// If there is only one disk left in src, move it directly to tar
|
||||
if i == 1 {
|
||||
move_pan(src, tar);
|
||||
return;
|
||||
}
|
||||
// Subproblem f(i-1): move the top i-1 disks from src to buf using tar
|
||||
dfs(i - 1, src, tar, buf);
|
||||
// Subproblem f(1): move the remaining disk from src to tar
|
||||
move_pan(src, tar);
|
||||
// Subproblem f(i-1): move the top i-1 disks from buf to tar using src
|
||||
dfs(i - 1, buf, src, tar);
|
||||
}
|
||||
|
||||
/* Solve the Tower of Hanoi problem */
|
||||
fn solve_hanota(A: &mut Vec<i32>, B: &mut Vec<i32>, C: &mut Vec<i32>) {
|
||||
let n = A.len() as i32;
|
||||
// Move the top n disks from A to C using B
|
||||
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!("In initial state:");
|
||||
println!("A = {:?}", A);
|
||||
println!("B = {:?}", B);
|
||||
println!("C = {:?}", C);
|
||||
|
||||
solve_hanota(&mut A, &mut B, &mut C);
|
||||
|
||||
println!("After disk movement is complete:");
|
||||
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)
|
||||
*/
|
||||
|
||||
/* Backtracking */
|
||||
fn backtrack(choices: &[i32], state: i32, n: i32, res: &mut [i32]) {
|
||||
// When climbing to the n-th stair, add 1 to the solution count
|
||||
if state == n {
|
||||
res[0] = res[0] + 1;
|
||||
}
|
||||
// Traverse all choices
|
||||
for &choice in choices {
|
||||
// Pruning: not allowed to go beyond the n-th stair
|
||||
if state + choice > n {
|
||||
continue;
|
||||
}
|
||||
// Attempt: make choice, update state
|
||||
backtrack(choices, state + choice, n, res);
|
||||
// Backtrack
|
||||
}
|
||||
}
|
||||
|
||||
/* Climbing stairs: Backtracking */
|
||||
fn climbing_stairs_backtrack(n: usize) -> i32 {
|
||||
let choices = vec![1, 2]; // Can choose to climb up 1 or 2 stairs
|
||||
let state = 0; // Start climbing from the 0-th stair
|
||||
let mut res = Vec::new();
|
||||
res.push(0); // Use res[0] to record the solution count
|
||||
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!("Climbing {n} stairs has {res} solutions");
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* File: climbing_stairs_constraint_dp.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Climbing stairs with constraint: Dynamic programming */
|
||||
fn climbing_stairs_constraint_dp(n: usize) -> i32 {
|
||||
if n == 1 || n == 2 {
|
||||
return 1;
|
||||
};
|
||||
// Initialize dp table, used to store solutions to subproblems
|
||||
let mut dp = vec![vec![-1; 3]; n + 1];
|
||||
// Initial state: preset the solution to the smallest subproblem
|
||||
dp[1][1] = 1;
|
||||
dp[1][2] = 0;
|
||||
dp[2][1] = 0;
|
||||
dp[2][2] = 1;
|
||||
// State transition: gradually solve larger subproblems from smaller ones
|
||||
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!("Climbing {n} stairs has {res} solutions");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* File: climbing_stairs_dfs.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Search */
|
||||
fn dfs(i: usize) -> i32 {
|
||||
// Known dp[1] and dp[2], return them
|
||||
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
|
||||
}
|
||||
|
||||
/* Climbing stairs: Search */
|
||||
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!("Climbing {n} stairs has {res} solutions");
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* File: climbing_stairs_dfs_mem.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Memoization search */
|
||||
fn dfs(i: usize, mem: &mut [i32]) -> i32 {
|
||||
// Known dp[1] and dp[2], return them
|
||||
if i == 1 || i == 2 {
|
||||
return i as i32;
|
||||
}
|
||||
// If record dp[i] exists, return it directly
|
||||
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);
|
||||
// Record dp[i]
|
||||
mem[i] = count;
|
||||
count
|
||||
}
|
||||
|
||||
/* Climbing stairs: Memoization search */
|
||||
fn climbing_stairs_dfs_mem(n: usize) -> i32 {
|
||||
// mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record
|
||||
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!("Climbing {n} stairs has {res} solutions");
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* File: climbing_stairs_dp.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Climbing stairs: Dynamic programming */
|
||||
fn climbing_stairs_dp(n: usize) -> i32 {
|
||||
// Known dp[1] and dp[2], return them
|
||||
if n == 1 || n == 2 {
|
||||
return n as i32;
|
||||
}
|
||||
// Initialize dp table, used to store solutions to subproblems
|
||||
let mut dp = vec![-1; n + 1];
|
||||
// Initial state: preset the solution to the smallest subproblem
|
||||
dp[1] = 1;
|
||||
dp[2] = 2;
|
||||
// State transition: gradually solve larger subproblems from smaller ones
|
||||
for i in 3..=n {
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
}
|
||||
dp[n]
|
||||
}
|
||||
|
||||
/* Climbing stairs: Space-optimized dynamic programming */
|
||||
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!("Climbing {n} stairs has {res} solutions");
|
||||
|
||||
let res = climbing_stairs_dp_comp(n);
|
||||
println!("Climbing {n} stairs has {res} solutions");
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* File: coin_change.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Coin change: Dynamic programming */
|
||||
fn coin_change_dp(coins: &[i32], amt: usize) -> i32 {
|
||||
let n = coins.len();
|
||||
let max = amt + 1;
|
||||
// Initialize dp table
|
||||
let mut dp = vec![vec![0; amt + 1]; n + 1];
|
||||
// State transition: first row and first column
|
||||
for a in 1..=amt {
|
||||
dp[0][a] = max;
|
||||
}
|
||||
// State transition: rest of the rows and columns
|
||||
for i in 1..=n {
|
||||
for a in 1..=amt {
|
||||
if coins[i - 1] > a as i32 {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// The smaller value between not selecting and selecting coin 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
|
||||
}
|
||||
}
|
||||
|
||||
/* Coin change: Space-optimized dynamic programming */
|
||||
fn coin_change_dp_comp(coins: &[i32], amt: usize) -> i32 {
|
||||
let n = coins.len();
|
||||
let max = amt + 1;
|
||||
// Initialize dp table
|
||||
let mut dp = vec![0; amt + 1];
|
||||
dp.fill(max);
|
||||
dp[0] = 0;
|
||||
// State transition
|
||||
for i in 1..=n {
|
||||
for a in 1..=amt {
|
||||
if coins[i - 1] > a as i32 {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// The smaller value between not selecting and selecting coin 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;
|
||||
|
||||
// Dynamic programming
|
||||
let res = coin_change_dp(&coins, amt);
|
||||
println!("Minimum coins needed to make target amount is {res}");
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
let res = coin_change_dp_comp(&coins, amt);
|
||||
println!("Minimum coins needed to make target amount is {res}");
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* File: coin_change_ii.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Coin change II: Dynamic programming */
|
||||
fn coin_change_ii_dp(coins: &[i32], amt: usize) -> i32 {
|
||||
let n = coins.len();
|
||||
// Initialize dp table
|
||||
let mut dp = vec![vec![0; amt + 1]; n + 1];
|
||||
// Initialize first column
|
||||
for i in 0..=n {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
// State transition
|
||||
for i in 1..=n {
|
||||
for a in 1..=amt {
|
||||
if coins[i - 1] > a as i32 {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// Sum of the two options: not selecting and selecting coin i
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1] as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
dp[n][amt]
|
||||
}
|
||||
|
||||
/* Coin change II: Space-optimized dynamic programming */
|
||||
fn coin_change_ii_dp_comp(coins: &[i32], amt: usize) -> i32 {
|
||||
let n = coins.len();
|
||||
// Initialize dp table
|
||||
let mut dp = vec![0; amt + 1];
|
||||
dp[0] = 1;
|
||||
// State transition
|
||||
for i in 1..=n {
|
||||
for a in 1..=amt {
|
||||
if coins[i - 1] > a as i32 {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// Sum of the two options: not selecting and selecting coin 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;
|
||||
|
||||
// Dynamic programming
|
||||
let res = coin_change_ii_dp(&coins, amt);
|
||||
println!("Number of coin combinations to make target amount is {res}");
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
let res = coin_change_ii_dp_comp(&coins, amt);
|
||||
println!("Number of coin combinations to make target amount is {res}");
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* File: edit_distance.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Edit distance: Brute-force search */
|
||||
fn edit_distance_dfs(s: &str, t: &str, i: usize, j: usize) -> i32 {
|
||||
// If both s and t are empty, return 0
|
||||
if i == 0 && j == 0 {
|
||||
return 0;
|
||||
}
|
||||
// If s is empty, return length of t
|
||||
if i == 0 {
|
||||
return j as i32;
|
||||
}
|
||||
// If t is empty, return length of s
|
||||
if j == 0 {
|
||||
return i as i32;
|
||||
}
|
||||
// If two characters are equal, skip both characters
|
||||
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
|
||||
return edit_distance_dfs(s, t, i - 1, j - 1);
|
||||
}
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 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);
|
||||
// Return minimum edit steps
|
||||
std::cmp::min(std::cmp::min(insert, delete), replace) + 1
|
||||
}
|
||||
|
||||
/* Edit distance: Memoization search */
|
||||
fn edit_distance_dfs_mem(s: &str, t: &str, mem: &mut Vec<Vec<i32>>, i: usize, j: usize) -> i32 {
|
||||
// If both s and t are empty, return 0
|
||||
if i == 0 && j == 0 {
|
||||
return 0;
|
||||
}
|
||||
// If s is empty, return length of t
|
||||
if i == 0 {
|
||||
return j as i32;
|
||||
}
|
||||
// If t is empty, return length of s
|
||||
if j == 0 {
|
||||
return i as i32;
|
||||
}
|
||||
// If there's a record, return it directly
|
||||
if mem[i][j] != -1 {
|
||||
return mem[i][j];
|
||||
}
|
||||
// If two characters are equal, skip both characters
|
||||
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
|
||||
return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1);
|
||||
}
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 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);
|
||||
// Record and return minimum edit steps
|
||||
mem[i][j] = std::cmp::min(std::cmp::min(insert, delete), replace) + 1;
|
||||
mem[i][j]
|
||||
}
|
||||
|
||||
/* Edit distance: Dynamic programming */
|
||||
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];
|
||||
// State transition: first row and first column
|
||||
for i in 1..=n {
|
||||
dp[i][0] = i as i32;
|
||||
}
|
||||
for j in 1..m {
|
||||
dp[0][j] = j as i32;
|
||||
}
|
||||
// State transition: rest of the rows and columns
|
||||
for i in 1..=n {
|
||||
for j in 1..=m {
|
||||
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
|
||||
// If two characters are equal, skip both characters
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 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]
|
||||
}
|
||||
|
||||
/* Edit distance: Space-optimized dynamic programming */
|
||||
fn edit_distance_dp_comp(s: &str, t: &str) -> i32 {
|
||||
let (n, m) = (s.len(), t.len());
|
||||
let mut dp = vec![0; m + 1];
|
||||
// State transition: first row
|
||||
for j in 1..m {
|
||||
dp[j] = j as i32;
|
||||
}
|
||||
// State transition: rest of the rows
|
||||
for i in 1..=n {
|
||||
// State transition: first column
|
||||
let mut leftup = dp[0]; // Temporarily store dp[i-1, j-1]
|
||||
dp[0] = i as i32;
|
||||
// State transition: rest of the columns
|
||||
for j in 1..=m {
|
||||
let temp = dp[j];
|
||||
if s.chars().nth(i - 1) == t.chars().nth(j - 1) {
|
||||
// If two characters are equal, skip both characters
|
||||
dp[j] = leftup;
|
||||
} else {
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 1
|
||||
dp[j] = std::cmp::min(std::cmp::min(dp[j - 1], dp[j]), leftup) + 1;
|
||||
}
|
||||
leftup = temp; // Update for next round's 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());
|
||||
|
||||
// Brute-force search
|
||||
let res = edit_distance_dfs(s, t, n, m);
|
||||
println!("Changing {s} to {t} requires minimum {res} edits");
|
||||
|
||||
// Memoization search
|
||||
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!("Changing {s} to {t} requires minimum {res} edits");
|
||||
|
||||
// Dynamic programming
|
||||
let res = edit_distance_dp(s, t);
|
||||
println!("Changing {s} to {t} requires minimum {res} edits");
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
let res = edit_distance_dp_comp(s, t);
|
||||
println!("Changing {s} to {t} requires minimum {res} edits");
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* File: knapsack.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* 0-1 knapsack: Brute-force search */
|
||||
fn knapsack_dfs(wgt: &[i32], val: &[i32], i: usize, c: usize) -> i32 {
|
||||
// If all items have been selected or knapsack has no remaining capacity, return value 0
|
||||
if i == 0 || c == 0 {
|
||||
return 0;
|
||||
}
|
||||
// If exceeds knapsack capacity, can only choose not to put it in
|
||||
if wgt[i - 1] > c as i32 {
|
||||
return knapsack_dfs(wgt, val, i - 1, c);
|
||||
}
|
||||
// Calculate the maximum value of not putting in and putting in item 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];
|
||||
// Return the larger value of the two options
|
||||
std::cmp::max(no, yes)
|
||||
}
|
||||
|
||||
/* 0-1 knapsack: Memoization search */
|
||||
fn knapsack_dfs_mem(wgt: &[i32], val: &[i32], mem: &mut Vec<Vec<i32>>, i: usize, c: usize) -> i32 {
|
||||
// If all items have been selected or knapsack has no remaining capacity, return value 0
|
||||
if i == 0 || c == 0 {
|
||||
return 0;
|
||||
}
|
||||
// If there's a record, return it directly
|
||||
if mem[i][c] != -1 {
|
||||
return mem[i][c];
|
||||
}
|
||||
// If exceeds knapsack capacity, can only choose not to put it in
|
||||
if wgt[i - 1] > c as i32 {
|
||||
return knapsack_dfs_mem(wgt, val, mem, i - 1, c);
|
||||
}
|
||||
// Calculate the maximum value of not putting in and putting in item 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];
|
||||
// Record and return the larger value of the two options
|
||||
mem[i][c] = std::cmp::max(no, yes);
|
||||
mem[i][c]
|
||||
}
|
||||
|
||||
/* 0-1 knapsack: Dynamic programming */
|
||||
fn knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
|
||||
let n = wgt.len();
|
||||
// Initialize dp table
|
||||
let mut dp = vec![vec![0; cap + 1]; n + 1];
|
||||
// State transition
|
||||
for i in 1..=n {
|
||||
for c in 1..=cap {
|
||||
if wgt[i - 1] > c as i32 {
|
||||
// If exceeds knapsack capacity, don't select item i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// The larger value between not selecting and selecting item 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 knapsack: Space-optimized dynamic programming */
|
||||
fn knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
|
||||
let n = wgt.len();
|
||||
// Initialize dp table
|
||||
let mut dp = vec![0; cap + 1];
|
||||
// State transition
|
||||
for i in 1..=n {
|
||||
// Traverse in reverse order
|
||||
for c in (1..=cap).rev() {
|
||||
if wgt[i - 1] <= c as i32 {
|
||||
// The larger value between not selecting and selecting item 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();
|
||||
|
||||
// Brute-force search
|
||||
let res = knapsack_dfs(&wgt, &val, n, cap);
|
||||
println!("Maximum item value not exceeding knapsack capacity is {res}");
|
||||
|
||||
// Memoization search
|
||||
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!("Maximum item value not exceeding knapsack capacity is {res}");
|
||||
|
||||
// Dynamic programming
|
||||
let res = knapsack_dp(&wgt, &val, cap);
|
||||
println!("Maximum item value not exceeding knapsack capacity is {res}");
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
let res = knapsack_dp_comp(&wgt, &val, cap);
|
||||
println!("Maximum item value not exceeding knapsack capacity is {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;
|
||||
|
||||
/* Minimum cost climbing stairs: Dynamic programming */
|
||||
fn min_cost_climbing_stairs_dp(cost: &[i32]) -> i32 {
|
||||
let n = cost.len() - 1;
|
||||
if n == 1 || n == 2 {
|
||||
return cost[n];
|
||||
}
|
||||
// Initialize dp table, used to store solutions to subproblems
|
||||
let mut dp = vec![-1; n + 1];
|
||||
// Initial state: preset the solution to the smallest subproblem
|
||||
dp[1] = cost[1];
|
||||
dp[2] = cost[2];
|
||||
// State transition: gradually solve larger subproblems from smaller ones
|
||||
for i in 3..=n {
|
||||
dp[i] = cmp::min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
}
|
||||
dp[n]
|
||||
}
|
||||
|
||||
/* Minimum cost climbing stairs: Space-optimized dynamic programming */
|
||||
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!("Input stair cost list is {:?}", &cost);
|
||||
|
||||
let res = min_cost_climbing_stairs_dp(&cost);
|
||||
println!("Minimum cost to climb stairs is {res}");
|
||||
|
||||
let res = min_cost_climbing_stairs_dp_comp(&cost);
|
||||
println!("Minimum cost to climb stairs is {res}");
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* File: min_path_sum.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Minimum path sum: Brute-force search */
|
||||
fn min_path_sum_dfs(grid: &Vec<Vec<i32>>, i: i32, j: i32) -> i32 {
|
||||
// If it's the top-left cell, terminate the search
|
||||
if i == 0 && j == 0 {
|
||||
return grid[0][0];
|
||||
}
|
||||
// If row or column index is out of bounds, return +∞ cost
|
||||
if i < 0 || j < 0 {
|
||||
return i32::MAX;
|
||||
}
|
||||
// Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1)
|
||||
let up = min_path_sum_dfs(grid, i - 1, j);
|
||||
let left = min_path_sum_dfs(grid, i, j - 1);
|
||||
// Return the minimum path cost from top-left to (i, j)
|
||||
std::cmp::min(left, up) + grid[i as usize][j as usize]
|
||||
}
|
||||
|
||||
/* Minimum path sum: Memoization search */
|
||||
fn min_path_sum_dfs_mem(grid: &Vec<Vec<i32>>, mem: &mut Vec<Vec<i32>>, i: i32, j: i32) -> i32 {
|
||||
// If it's the top-left cell, terminate the search
|
||||
if i == 0 && j == 0 {
|
||||
return grid[0][0];
|
||||
}
|
||||
// If row or column index is out of bounds, return +∞ cost
|
||||
if i < 0 || j < 0 {
|
||||
return i32::MAX;
|
||||
}
|
||||
// If there's a record, return it directly
|
||||
if mem[i as usize][j as usize] != -1 {
|
||||
return mem[i as usize][j as usize];
|
||||
}
|
||||
// Minimum path cost for left and upper cells
|
||||
let up = min_path_sum_dfs_mem(grid, mem, i - 1, j);
|
||||
let left = min_path_sum_dfs_mem(grid, mem, i, j - 1);
|
||||
// Record and return the minimum path cost from top-left to (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]
|
||||
}
|
||||
|
||||
/* Minimum path sum: Dynamic programming */
|
||||
fn min_path_sum_dp(grid: &Vec<Vec<i32>>) -> i32 {
|
||||
let (n, m) = (grid.len(), grid[0].len());
|
||||
// Initialize dp table
|
||||
let mut dp = vec![vec![0; m]; n];
|
||||
dp[0][0] = grid[0][0];
|
||||
// State transition: first row
|
||||
for j in 1..m {
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j];
|
||||
}
|
||||
// State transition: first column
|
||||
for i in 1..n {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0];
|
||||
}
|
||||
// State transition: rest of the rows and columns
|
||||
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]
|
||||
}
|
||||
|
||||
/* Minimum path sum: Space-optimized dynamic programming */
|
||||
fn min_path_sum_dp_comp(grid: &Vec<Vec<i32>>) -> i32 {
|
||||
let (n, m) = (grid.len(), grid[0].len());
|
||||
// Initialize dp table
|
||||
let mut dp = vec![0; m];
|
||||
// State transition: first row
|
||||
dp[0] = grid[0][0];
|
||||
for j in 1..m {
|
||||
dp[j] = dp[j - 1] + grid[0][j];
|
||||
}
|
||||
// State transition: rest of the rows
|
||||
for i in 1..n {
|
||||
// State transition: first column
|
||||
dp[0] = dp[0] + grid[i][0];
|
||||
// State transition: rest of the columns
|
||||
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());
|
||||
|
||||
// Brute-force search
|
||||
let res = min_path_sum_dfs(&grid, n as i32 - 1, m as i32 - 1);
|
||||
println!("Minimum path sum from top-left to bottom-right is {res}");
|
||||
|
||||
// Memoization search
|
||||
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!("Minimum path sum from top-left to bottom-right is {res}");
|
||||
|
||||
// Dynamic programming
|
||||
let res = min_path_sum_dp(&grid);
|
||||
println!("Minimum path sum from top-left to bottom-right is {res}");
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
let res = min_path_sum_dp_comp(&grid);
|
||||
println!("Minimum path sum from top-left to bottom-right is {res}");
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* File: unbounded_knapsack.rs
|
||||
* Created Time: 2023-07-09
|
||||
* Author: codingonion (coderonion@gmail.com)
|
||||
*/
|
||||
|
||||
/* Unbounded knapsack: Dynamic programming */
|
||||
fn unbounded_knapsack_dp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
|
||||
let n = wgt.len();
|
||||
// Initialize dp table
|
||||
let mut dp = vec![vec![0; cap + 1]; n + 1];
|
||||
// State transition
|
||||
for i in 1..=n {
|
||||
for c in 1..=cap {
|
||||
if wgt[i - 1] > c as i32 {
|
||||
// If exceeds knapsack capacity, don't select item i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// The larger value between not selecting and selecting item 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];
|
||||
}
|
||||
|
||||
/* Unbounded knapsack: Space-optimized dynamic programming */
|
||||
fn unbounded_knapsack_dp_comp(wgt: &[i32], val: &[i32], cap: usize) -> i32 {
|
||||
let n = wgt.len();
|
||||
// Initialize dp table
|
||||
let mut dp = vec![0; cap + 1];
|
||||
// State transition
|
||||
for i in 1..=n {
|
||||
for c in 1..=cap {
|
||||
if wgt[i - 1] > c as i32 {
|
||||
// If exceeds knapsack capacity, don't select item i
|
||||
dp[c] = dp[c];
|
||||
} else {
|
||||
// The larger value between not selecting and selecting item 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;
|
||||
|
||||
// Dynamic programming
|
||||
let res = unbounded_knapsack_dp(&wgt, &val, cap);
|
||||
println!("Maximum item value not exceeding knapsack capacity is {res}");
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
let res = unbounded_knapsack_dp_comp(&wgt, &val, cap);
|
||||
println!("Maximum item value not exceeding knapsack capacity is {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;
|
||||
|
||||
/* Undirected graph type based on adjacency list */
|
||||
pub struct GraphAdjList {
|
||||
// Adjacency list, key: vertex, value: all adjacent vertices of that vertex
|
||||
pub adj_list: HashMap<Vertex, Vec<Vertex>>, // maybe HashSet<Vertex> for value part is better?
|
||||
}
|
||||
|
||||
impl GraphAdjList {
|
||||
/* Constructor */
|
||||
pub fn new(edges: Vec<[Vertex; 2]>) -> Self {
|
||||
let mut graph = GraphAdjList {
|
||||
adj_list: HashMap::new(),
|
||||
};
|
||||
// Add all vertices and edges
|
||||
for edge in edges {
|
||||
graph.add_vertex(edge[0]);
|
||||
graph.add_vertex(edge[1]);
|
||||
graph.add_edge(edge[0], edge[1]);
|
||||
}
|
||||
|
||||
graph
|
||||
}
|
||||
|
||||
/* Get the number of vertices */
|
||||
#[allow(unused)]
|
||||
pub fn size(&self) -> usize {
|
||||
self.adj_list.len()
|
||||
}
|
||||
|
||||
/* Add edge */
|
||||
pub fn add_edge(&mut self, vet1: Vertex, vet2: Vertex) {
|
||||
if vet1 == vet2 {
|
||||
panic!("value error");
|
||||
}
|
||||
// Add edge vet1 - vet2
|
||||
self.adj_list.entry(vet1).or_default().push(vet2);
|
||||
self.adj_list.entry(vet2).or_default().push(vet1);
|
||||
}
|
||||
|
||||
/* Remove edge */
|
||||
#[allow(unused)]
|
||||
pub fn remove_edge(&mut self, vet1: Vertex, vet2: Vertex) {
|
||||
if vet1 == vet2 {
|
||||
panic!("value error");
|
||||
}
|
||||
// Remove edge 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));
|
||||
}
|
||||
|
||||
/* Add vertex */
|
||||
pub fn add_vertex(&mut self, vet: Vertex) {
|
||||
if self.adj_list.contains_key(&vet) {
|
||||
return;
|
||||
}
|
||||
// Add a new linked list in the adjacency list
|
||||
self.adj_list.insert(vet, vec![]);
|
||||
}
|
||||
|
||||
/* Remove vertex */
|
||||
#[allow(unused)]
|
||||
pub fn remove_vertex(&mut self, vet: Vertex) {
|
||||
// Remove the linked list corresponding to vertex vet in the adjacency list
|
||||
self.adj_list.remove(&vet);
|
||||
// Traverse the linked lists of other vertices and remove all edges containing vet
|
||||
for list in self.adj_list.values_mut() {
|
||||
list.retain(|&v| v != vet);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print adjacency list */
|
||||
pub fn print(&self) {
|
||||
println!("Adjacency list =");
|
||||
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() {
|
||||
/* Add edge */
|
||||
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!("\nAfter initialization, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Add edge */
|
||||
// Vertices 1, 3 are v[0], v[1]
|
||||
graph.add_edge(v[0], v[2]);
|
||||
println!("\nAfter adding edge 1-2, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Remove edge */
|
||||
// Vertex 3 is v[1]
|
||||
graph.remove_edge(v[0], v[1]);
|
||||
println!("\nAfter removing edge 1-3, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Add vertex */
|
||||
let v5 = Vertex { val: 6 };
|
||||
graph.add_vertex(v5);
|
||||
println!("\nAfter adding vertex 6, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Remove vertex */
|
||||
// Vertex 3 is v[1]
|
||||
graph.remove_vertex(v[1]);
|
||||
println!("\nAfter removing vertex 3, graph is");
|
||||
graph.print();
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* File: graph_adjacency_matrix.rs
|
||||
* Created Time: 2023-07-12
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
|
||||
/* Undirected graph type based on adjacency matrix */
|
||||
pub struct GraphAdjMat {
|
||||
// Vertex list, where the element represents the "vertex value" and the index represents the "vertex index"
|
||||
pub vertices: Vec<i32>,
|
||||
// Adjacency matrix, where the row and column indices correspond to the "vertex index"
|
||||
pub adj_mat: Vec<Vec<i32>>,
|
||||
}
|
||||
|
||||
impl GraphAdjMat {
|
||||
/* Constructor */
|
||||
pub fn new(vertices: Vec<i32>, edges: Vec<[usize; 2]>) -> Self {
|
||||
let mut graph = GraphAdjMat {
|
||||
vertices: vec![],
|
||||
adj_mat: vec![],
|
||||
};
|
||||
// Add vertex
|
||||
for val in vertices {
|
||||
graph.add_vertex(val);
|
||||
}
|
||||
// Add edge
|
||||
// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices
|
||||
for edge in edges {
|
||||
graph.add_edge(edge[0], edge[1])
|
||||
}
|
||||
|
||||
graph
|
||||
}
|
||||
|
||||
/* Get the number of vertices */
|
||||
pub fn size(&self) -> usize {
|
||||
self.vertices.len()
|
||||
}
|
||||
|
||||
/* Add vertex */
|
||||
pub fn add_vertex(&mut self, val: i32) {
|
||||
let n = self.size();
|
||||
// Add the value of the new vertex to the vertex list
|
||||
self.vertices.push(val);
|
||||
// Add a row to the adjacency matrix
|
||||
self.adj_mat.push(vec![0; n]);
|
||||
// Add a column to the adjacency matrix
|
||||
for row in self.adj_mat.iter_mut() {
|
||||
row.push(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove vertex */
|
||||
pub fn remove_vertex(&mut self, index: usize) {
|
||||
if index >= self.size() {
|
||||
panic!("index error")
|
||||
}
|
||||
// Remove the vertex at index from the vertex list
|
||||
self.vertices.remove(index);
|
||||
// Remove the row at index from the adjacency matrix
|
||||
self.adj_mat.remove(index);
|
||||
// Remove the column at index from the adjacency matrix
|
||||
for row in self.adj_mat.iter_mut() {
|
||||
row.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add edge */
|
||||
pub fn add_edge(&mut self, i: usize, j: usize) {
|
||||
// Parameters i, j correspond to the vertices element indices
|
||||
// Handle index out of bounds and equality
|
||||
if i >= self.size() || j >= self.size() || i == j {
|
||||
panic!("index error")
|
||||
}
|
||||
// In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i)
|
||||
self.adj_mat[i][j] = 1;
|
||||
self.adj_mat[j][i] = 1;
|
||||
}
|
||||
|
||||
/* Remove edge */
|
||||
// Parameters i, j correspond to the vertices element indices
|
||||
pub fn remove_edge(&mut self, i: usize, j: usize) {
|
||||
// Parameters i, j correspond to the vertices element indices
|
||||
// Handle index out of bounds and equality
|
||||
if i >= self.size() || j >= self.size() || i == j {
|
||||
panic!("index error")
|
||||
}
|
||||
self.adj_mat[i][j] = 0;
|
||||
self.adj_mat[j][i] = 0;
|
||||
}
|
||||
|
||||
/* Print adjacency matrix */
|
||||
pub fn print(&self) {
|
||||
println!("Vertex list = {:?}", self.vertices);
|
||||
println!("Adjacency matrix =");
|
||||
println!("[");
|
||||
for row in &self.adj_mat {
|
||||
println!(" {:?},", row);
|
||||
}
|
||||
println!("]")
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Add edge */
|
||||
// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices
|
||||
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!("\nAfter initialization, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Add edge */
|
||||
// Add vertex
|
||||
graph.add_edge(0, 2);
|
||||
println!("\nAfter adding edge 1-2, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Remove edge */
|
||||
// Vertices 1, 3 have indices 0, 1 respectively
|
||||
graph.remove_edge(0, 1);
|
||||
println!("\nAfter removing edge 1-3, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Add vertex */
|
||||
graph.add_vertex(6);
|
||||
println!("\nAfter adding vertex 6, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Remove vertex */
|
||||
// Vertex 3 has index 1
|
||||
graph.remove_vertex(1);
|
||||
println!("\nAfter removing vertex 3, graph is");
|
||||
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};
|
||||
|
||||
/* Breadth-first traversal */
|
||||
// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex
|
||||
fn graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
|
||||
// Vertex traversal sequence
|
||||
let mut res = vec![];
|
||||
// Hash set for recording vertices that have been visited
|
||||
let mut visited = HashSet::new();
|
||||
visited.insert(start_vet);
|
||||
// Queue used to implement BFS
|
||||
let mut que = VecDeque::new();
|
||||
que.push_back(start_vet);
|
||||
// Starting from vertex vet, loop until all vertices are visited
|
||||
while let Some(vet) = que.pop_front() {
|
||||
res.push(vet); // Record visited vertex
|
||||
|
||||
// Traverse all adjacent vertices of this vertex
|
||||
if let Some(adj_vets) = graph.adj_list.get(&vet) {
|
||||
for &adj_vet in adj_vets {
|
||||
if visited.contains(&adj_vet) {
|
||||
continue; // Skip vertices that have been visited
|
||||
}
|
||||
que.push_back(adj_vet); // Only enqueue unvisited vertices
|
||||
visited.insert(adj_vet); // Mark this vertex as visited
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return vertex traversal sequence
|
||||
res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Add edge */
|
||||
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!("\nAfter initialization, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Breadth-first traversal */
|
||||
let res = graph_bfs(graph, v[0]);
|
||||
println!("\nBreadth-first traversal (BFS) vertex sequence is");
|
||||
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;
|
||||
|
||||
/* Depth-first traversal helper function */
|
||||
fn dfs(graph: &GraphAdjList, visited: &mut HashSet<Vertex>, res: &mut Vec<Vertex>, vet: Vertex) {
|
||||
res.push(vet); // Record visited vertex
|
||||
visited.insert(vet); // Mark this vertex as visited
|
||||
// Traverse all adjacent vertices of this vertex
|
||||
if let Some(adj_vets) = graph.adj_list.get(&vet) {
|
||||
for &adj_vet in adj_vets {
|
||||
if visited.contains(&adj_vet) {
|
||||
continue; // Skip vertices that have been visited
|
||||
}
|
||||
// Recursively visit adjacent vertices
|
||||
dfs(graph, visited, res, adj_vet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Depth-first traversal */
|
||||
// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex
|
||||
fn graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> Vec<Vertex> {
|
||||
// Vertex traversal sequence
|
||||
let mut res = vec![];
|
||||
// Hash set for recording vertices that have been visited
|
||||
let mut visited = HashSet::new();
|
||||
dfs(&graph, &mut visited, &mut res, start_vet);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Add edge */
|
||||
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!("\nAfter initialization, graph is");
|
||||
graph.print();
|
||||
|
||||
/* Depth-first traversal */
|
||||
let res = graph_dfs(graph, v[0]);
|
||||
println!("\nDepth-first traversal (DFS) vertex sequence is");
|
||||
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)
|
||||
*/
|
||||
|
||||
/* Coin change: Greedy algorithm */
|
||||
fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 {
|
||||
// Assume coins list is sorted
|
||||
let mut i = coins.len() - 1;
|
||||
let mut count = 0;
|
||||
// Loop to make greedy choices until no remaining amount
|
||||
while amt > 0 {
|
||||
// Find the coin that is less than and closest to the remaining amount
|
||||
while i > 0 && coins[i] > amt {
|
||||
i -= 1;
|
||||
}
|
||||
// Choose coins[i]
|
||||
amt -= coins[i];
|
||||
count += 1;
|
||||
}
|
||||
// If no feasible solution is found, return -1
|
||||
if amt == 0 {
|
||||
count
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// Greedy algorithm: Can guarantee finding the global optimal solution
|
||||
let coins = [1, 5, 10, 20, 50, 100];
|
||||
let amt = 186;
|
||||
let res = coin_change_greedy(&coins, amt);
|
||||
println!("\ncoins = {:?}, amt = {}", coins, amt);
|
||||
println!("Minimum coins needed to make {} is {}", amt, res);
|
||||
|
||||
// Greedy algorithm: Cannot guarantee finding the global optimal solution
|
||||
let coins = [1, 20, 50];
|
||||
let amt = 60;
|
||||
let res = coin_change_greedy(&coins, amt);
|
||||
println!("\ncoins = {:?}, amt = {}", coins, amt);
|
||||
println!("Minimum coins needed to make {} is {}", amt, res);
|
||||
println!("Actually the minimum number needed is 3, i.e., 20 + 20 + 20");
|
||||
|
||||
// Greedy algorithm: Cannot guarantee finding the global optimal solution
|
||||
let coins = [1, 49, 50];
|
||||
let amt = 98;
|
||||
let res = coin_change_greedy(&coins, amt);
|
||||
println!("\ncoins = {:?}, amt = {}", coins, amt);
|
||||
println!("Minimum coins needed to make {} is {}", amt, res);
|
||||
println!("Actually the minimum number needed is 2, i.e., 49 + 49");
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* File: coin_change_greedy.rs
|
||||
* Created Time: 2023-07-22
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
|
||||
/* Item */
|
||||
struct Item {
|
||||
w: i32, // Item weight
|
||||
v: i32, // Item value
|
||||
}
|
||||
|
||||
impl Item {
|
||||
fn new(w: i32, v: i32) -> Self {
|
||||
Self { w, v }
|
||||
}
|
||||
}
|
||||
|
||||
/* Fractional knapsack: Greedy algorithm */
|
||||
fn fractional_knapsack(wgt: &[i32], val: &[i32], mut cap: i32) -> f64 {
|
||||
// Create item list with two attributes: weight, value
|
||||
let mut items = wgt
|
||||
.iter()
|
||||
.zip(val.iter())
|
||||
.map(|(&w, &v)| Item::new(w, v))
|
||||
.collect::<Vec<Item>>();
|
||||
// Sort by unit value item.v / item.w from high to low
|
||||
items.sort_by(|a, b| {
|
||||
(b.v as f64 / b.w as f64)
|
||||
.partial_cmp(&(a.v as f64 / a.w as f64))
|
||||
.unwrap()
|
||||
});
|
||||
// Loop for greedy selection
|
||||
let mut res = 0.0;
|
||||
for item in &items {
|
||||
if item.w <= cap {
|
||||
// If remaining capacity is sufficient, put the entire current item into the knapsack
|
||||
res += item.v as f64;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// If remaining capacity is insufficient, put part of the current item into the knapsack
|
||||
res += item.v as f64 / item.w as f64 * cap as f64;
|
||||
// No remaining capacity, so break out of the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
let wgt = [10, 20, 30, 40, 50];
|
||||
let val = [50, 120, 150, 210, 240];
|
||||
let cap = 50;
|
||||
|
||||
// Greedy algorithm
|
||||
let res = fractional_knapsack(&wgt, &val, cap);
|
||||
println!("Maximum item value not exceeding knapsack capacity is {}", res);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* File: coin_change_greedy.rs
|
||||
* Created Time: 2023-07-22
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
|
||||
/* Max capacity: Greedy algorithm */
|
||||
fn max_capacity(ht: &[i32]) -> i32 {
|
||||
// Initialize i, j to be at both ends of the array
|
||||
let mut i = 0;
|
||||
let mut j = ht.len() - 1;
|
||||
// Initial max capacity is 0
|
||||
let mut res = 0;
|
||||
// Loop for greedy selection until the two boards meet
|
||||
while i < j {
|
||||
// Update max capacity
|
||||
let cap = std::cmp::min(ht[i], ht[j]) * (j - i) as i32;
|
||||
res = std::cmp::max(res, cap);
|
||||
// Move the shorter board inward
|
||||
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];
|
||||
|
||||
// Greedy algorithm
|
||||
let res = max_capacity(&ht);
|
||||
println!("Maximum capacity is {}", res);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* File: coin_change_greedy.rs
|
||||
* Created Time: 2023-07-22
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
|
||||
/* Max product cutting: Greedy algorithm */
|
||||
fn max_product_cutting(n: i32) -> i32 {
|
||||
// When n <= 3, must cut out a 1
|
||||
if n <= 3 {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Greedily cut out 3, a is the number of 3s, b is the remainder
|
||||
let a = n / 3;
|
||||
let b = n % 3;
|
||||
if b == 1 {
|
||||
// When the remainder is 1, convert a pair of 1 * 3 to 2 * 2
|
||||
3_i32.pow(a as u32 - 1) * 2 * 2
|
||||
} else if b == 2 {
|
||||
// When the remainder is 2, do nothing
|
||||
3_i32.pow(a as u32) * 2
|
||||
} else {
|
||||
// When the remainder is 0, do nothing
|
||||
3_i32.pow(a as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
let n = 58;
|
||||
|
||||
// Greedy algorithm
|
||||
let res = max_product_cutting(n);
|
||||
println!("Maximum cutting product is {}", res);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* File: array_hash_map.rs
|
||||
* Created Time: 2023-2-18
|
||||
* Author: xBLACICEx (xBLACKICEx@outlook.com)
|
||||
*/
|
||||
|
||||
/* Key-value pair */
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Pair {
|
||||
pub key: i32,
|
||||
pub val: String,
|
||||
}
|
||||
/* Hash table based on array implementation */
|
||||
pub struct ArrayHashMap {
|
||||
buckets: Vec<Option<Pair>>,
|
||||
}
|
||||
|
||||
impl ArrayHashMap {
|
||||
pub fn new() -> ArrayHashMap {
|
||||
// Initialize array with 100 buckets
|
||||
Self {
|
||||
buckets: vec![None; 100],
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash function */
|
||||
fn hash_func(&self, key: i32) -> usize {
|
||||
key as usize % 100
|
||||
}
|
||||
|
||||
/* Query operation */
|
||||
pub fn get(&self, key: i32) -> Option<&String> {
|
||||
let index = self.hash_func(key);
|
||||
self.buckets[index].as_ref().map(|pair| &pair.val)
|
||||
}
|
||||
|
||||
/* Add operation */
|
||||
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(),
|
||||
});
|
||||
}
|
||||
|
||||
/* Remove operation */
|
||||
pub fn remove(&mut self, key: i32) {
|
||||
let index = self.hash_func(key);
|
||||
// Set to None to represent removal
|
||||
self.buckets[index] = None;
|
||||
}
|
||||
|
||||
/* Get all key-value pairs */
|
||||
pub fn entry_set(&self) -> Vec<&Pair> {
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* Get all keys */
|
||||
pub fn key_set(&self) -> Vec<&i32> {
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref().map(|pair| &pair.key))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* Get all values */
|
||||
pub fn value_set(&self) -> Vec<&String> {
|
||||
self.buckets
|
||||
.iter()
|
||||
.filter_map(|pair| pair.as_ref().map(|pair| &pair.val))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/* Print hash table */
|
||||
pub fn print(&self) {
|
||||
for pair in self.entry_set() {
|
||||
println!("{} -> {}", pair.key, pair.val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
/* Initialize hash table */
|
||||
let mut map = ArrayHashMap::new();
|
||||
/* Add operation */
|
||||
// Add key-value pair (key, value) to hash table
|
||||
map.put(12836, "Xiao Ha");
|
||||
map.put(15937, "Xiao Luo");
|
||||
map.put(16750, "Xiao Suan");
|
||||
map.put(13276, "Xiao Fa");
|
||||
map.put(10583, "Xiao Ya");
|
||||
println!("\nAfter adding is complete, hash table is\nKey -> Value");
|
||||
map.print();
|
||||
|
||||
/* Query operation */
|
||||
// Input key into hash table to get value
|
||||
let name = map.get(15937).unwrap();
|
||||
println!("\nInput student ID 15937, found name {}", name);
|
||||
|
||||
/* Remove operation */
|
||||
// Remove key-value pair (key, value) from hash table
|
||||
map.remove(10583);
|
||||
println!("\nAfter removing 10583, hash table is\nKey -> Value");
|
||||
map.print();
|
||||
|
||||
/* Traverse hash table */
|
||||
println!("\nTraverse key-value pairs Key->Value");
|
||||
for pair in map.entry_set() {
|
||||
println!("{} -> {}", pair.key, pair.val);
|
||||
}
|
||||
|
||||
println!("\nTraverse keys only Key");
|
||||
for key in map.key_set() {
|
||||
println!("{}", key);
|
||||
}
|
||||
|
||||
println!("\nTraverse values only 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!("Hash value of integer {} is {}", num, hash_num);
|
||||
|
||||
let bol = true;
|
||||
let mut bol_hasher = DefaultHasher::new();
|
||||
bol.hash(&mut bol_hasher);
|
||||
let hash_bol = bol_hasher.finish();
|
||||
println!("Hash value of boolean {} is {}", 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!("Hash value of decimal {} is {}", dec, hash_dec);
|
||||
|
||||
let str = "Hello Algo";
|
||||
let mut str_hasher = DefaultHasher::new();
|
||||
str.hash(&mut str_hasher);
|
||||
let hash_str = str_hasher.finish();
|
||||
println!("Hash value of string {} is {}", str, hash_str);
|
||||
|
||||
let arr = (&12836, &"Xiao Ha");
|
||||
let mut tup_hasher = DefaultHasher::new();
|
||||
arr.hash(&mut tup_hasher);
|
||||
let hash_tup = tup_hasher.finish();
|
||||
println!("Hash value of tuple {:?} is {}", arr, hash_tup);
|
||||
|
||||
let node = ListNode::new(42);
|
||||
let mut hasher = DefaultHasher::new();
|
||||
node.borrow().val.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
println!("Hash value of node object {:?} is {}", 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() {
|
||||
// Initialize hash table
|
||||
let mut map = HashMap::new();
|
||||
|
||||
// Add operation
|
||||
// Add key-value pair (key, value) to the hash table
|
||||
map.insert(12836, "Xiao Ha");
|
||||
map.insert(15937, "Xiao Luo");
|
||||
map.insert(16750, "Xiao Suan");
|
||||
map.insert(13276, "Xiao Fa");
|
||||
map.insert(10583, "Xiao Ya");
|
||||
println!("\nAfter adding is complete, hash table is\nKey -> Value");
|
||||
print_util::print_hash_map(&map);
|
||||
|
||||
// Query operation
|
||||
// Input key into hash table to get value
|
||||
let name = map.get(&15937).copied().unwrap();
|
||||
println!("\nInput student ID 15937, found name {name}");
|
||||
|
||||
// Remove operation
|
||||
// Remove key-value pair (key, value) from hash table
|
||||
_ = map.remove(&10583);
|
||||
println!("\nAfter removing 10583, hash table is\nKey -> Value");
|
||||
print_util::print_hash_map(&map);
|
||||
|
||||
// Traverse hash table
|
||||
println!("\nTraverse key-value pairs Key->Value");
|
||||
print_util::print_hash_map(&map);
|
||||
println!("\nTraverse keys only Key");
|
||||
for key in map.keys() {
|
||||
println!("{key}");
|
||||
}
|
||||
println!("\nTraverse values separately");
|
||||
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)]
|
||||
/* Key-value pair */
|
||||
struct Pair {
|
||||
key: i32,
|
||||
val: String,
|
||||
}
|
||||
|
||||
/* Hash table with separate chaining */
|
||||
struct HashMapChaining {
|
||||
size: usize,
|
||||
capacity: usize,
|
||||
load_thres: f32,
|
||||
extend_ratio: usize,
|
||||
buckets: Vec<Vec<Pair>>,
|
||||
}
|
||||
|
||||
impl HashMapChaining {
|
||||
/* Constructor */
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
size: 0,
|
||||
capacity: 4,
|
||||
load_thres: 2.0 / 3.0,
|
||||
extend_ratio: 2,
|
||||
buckets: vec![vec![]; 4],
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash function */
|
||||
fn hash_func(&self, key: i32) -> usize {
|
||||
key as usize % self.capacity
|
||||
}
|
||||
|
||||
/* Load factor */
|
||||
fn load_factor(&self) -> f32 {
|
||||
self.size as f32 / self.capacity as f32
|
||||
}
|
||||
|
||||
/* Remove operation */
|
||||
fn remove(&mut self, key: i32) -> Option<String> {
|
||||
let index = self.hash_func(key);
|
||||
|
||||
// Traverse bucket and remove key-value pair from it
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// If key is not found, return None
|
||||
None
|
||||
}
|
||||
|
||||
/* Expand hash table */
|
||||
fn extend(&mut self) {
|
||||
// Temporarily store the original hash table
|
||||
let buckets_tmp = std::mem::take(&mut self.buckets);
|
||||
|
||||
// Initialize expanded new hash table
|
||||
self.capacity *= self.extend_ratio;
|
||||
self.buckets = vec![Vec::new(); self.capacity as usize];
|
||||
self.size = 0;
|
||||
|
||||
// Move key-value pairs from original hash table to new hash table
|
||||
for bucket in buckets_tmp {
|
||||
for pair in bucket {
|
||||
self.put(pair.key, pair.val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print hash table */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add operation */
|
||||
fn put(&mut self, key: i32, val: String) {
|
||||
// When load factor exceeds threshold, perform expansion
|
||||
if self.load_factor() > self.load_thres {
|
||||
self.extend();
|
||||
}
|
||||
|
||||
let index = self.hash_func(key);
|
||||
|
||||
// Traverse bucket, if specified key is encountered, update corresponding val and return
|
||||
for pair in self.buckets[index].iter_mut() {
|
||||
if pair.key == key {
|
||||
pair.val = val;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If key does not exist, append key-value pair to the end
|
||||
let pair = Pair { key, val };
|
||||
self.buckets[index].push(pair);
|
||||
self.size += 1;
|
||||
}
|
||||
|
||||
/* Query operation */
|
||||
fn get(&self, key: i32) -> Option<&str> {
|
||||
let index = self.hash_func(key);
|
||||
|
||||
// Traverse bucket, if key is found, return corresponding val
|
||||
for pair in self.buckets[index].iter() {
|
||||
if pair.key == key {
|
||||
return Some(&pair.val);
|
||||
}
|
||||
}
|
||||
|
||||
// If key is not found, return None
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn main() {
|
||||
/* Initialize hash table */
|
||||
let mut map = HashMapChaining::new();
|
||||
|
||||
/* Add operation */
|
||||
// Add key-value pair (key, value) to the hash table
|
||||
map.put(12836, "Xiao Ha".to_string());
|
||||
map.put(15937, "Xiao Luo".to_string());
|
||||
map.put(16750, "Xiao Suan".to_string());
|
||||
map.put(13276, "Xiao Fa".to_string());
|
||||
map.put(10583, "Xiao Ya".to_string());
|
||||
println!("\nAfter adding is complete, hash table is\nKey -> Value");
|
||||
map.print();
|
||||
|
||||
/* Query operation */
|
||||
// Input key into hash table to get value
|
||||
println!(
|
||||
"\nInput student ID 13276, found name {}",
|
||||
match map.get(13276) {
|
||||
Some(value) => value,
|
||||
None => "Not a valid Key",
|
||||
}
|
||||
);
|
||||
|
||||
/* Remove operation */
|
||||
// Remove key-value pair (key, value) from hash table
|
||||
map.remove(12836);
|
||||
println!("\nAfter removing 12836, hash table is\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;
|
||||
|
||||
/* Hash table with open addressing */
|
||||
struct HashMapOpenAddressing {
|
||||
size: usize, // Number of key-value pairs
|
||||
capacity: usize, // Hash table capacity
|
||||
load_thres: f64, // Load factor threshold for triggering expansion
|
||||
extend_ratio: usize, // Expansion multiplier
|
||||
buckets: Vec<Option<Pair>>, // Bucket array
|
||||
TOMBSTONE: Option<Pair>, // Removal marker
|
||||
}
|
||||
|
||||
impl HashMapOpenAddressing {
|
||||
/* Constructor */
|
||||
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(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash function */
|
||||
fn hash_func(&self, key: i32) -> usize {
|
||||
(key % self.capacity as i32) as usize
|
||||
}
|
||||
|
||||
/* Load factor */
|
||||
fn load_factor(&self) -> f64 {
|
||||
self.size as f64 / self.capacity as f64
|
||||
}
|
||||
|
||||
/* Search for bucket index corresponding to key */
|
||||
fn find_bucket(&mut self, key: i32) -> usize {
|
||||
let mut index = self.hash_func(key);
|
||||
let mut first_tombstone = -1;
|
||||
// Linear probing, break when encountering an empty bucket
|
||||
while self.buckets[index].is_some() {
|
||||
// If key is found, return corresponding bucket index
|
||||
if self.buckets[index].as_ref().unwrap().key == key {
|
||||
// If deletion marker was encountered before, move key-value pair to that index
|
||||
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 the moved bucket index
|
||||
}
|
||||
return index; // Return bucket index
|
||||
}
|
||||
// Record the first removal marker encountered
|
||||
if first_tombstone == -1 && self.buckets[index] == self.TOMBSTONE {
|
||||
first_tombstone = index as i32;
|
||||
}
|
||||
// Calculate bucket index, wrap around to the head if past the tail
|
||||
index = (index + 1) % self.capacity;
|
||||
}
|
||||
// If key does not exist, return the index for insertion
|
||||
if first_tombstone == -1 {
|
||||
index
|
||||
} else {
|
||||
first_tombstone as usize
|
||||
}
|
||||
}
|
||||
|
||||
/* Query operation */
|
||||
fn get(&mut self, key: i32) -> Option<&str> {
|
||||
// Search for bucket index corresponding to key
|
||||
let index = self.find_bucket(key);
|
||||
// If key-value pair is found, return corresponding val
|
||||
if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE {
|
||||
return self.buckets[index].as_ref().map(|pair| &pair.val as &str);
|
||||
}
|
||||
// If key-value pair does not exist, return null
|
||||
None
|
||||
}
|
||||
|
||||
/* Add operation */
|
||||
fn put(&mut self, key: i32, val: String) {
|
||||
// When load factor exceeds threshold, perform expansion
|
||||
if self.load_factor() > self.load_thres {
|
||||
self.extend();
|
||||
}
|
||||
// Search for bucket index corresponding to key
|
||||
let index = self.find_bucket(key);
|
||||
// If key-value pair is found, overwrite val and return
|
||||
if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE {
|
||||
self.buckets[index].as_mut().unwrap().val = val;
|
||||
return;
|
||||
}
|
||||
// If key-value pair does not exist, add the key-value pair
|
||||
self.buckets[index] = Some(Pair { key, val });
|
||||
self.size += 1;
|
||||
}
|
||||
|
||||
/* Remove operation */
|
||||
fn remove(&mut self, key: i32) {
|
||||
// Search for bucket index corresponding to key
|
||||
let index = self.find_bucket(key);
|
||||
// If key-value pair is found, overwrite it with removal marker
|
||||
if self.buckets[index].is_some() && self.buckets[index] != self.TOMBSTONE {
|
||||
self.buckets[index] = self.TOMBSTONE.clone();
|
||||
self.size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand hash table */
|
||||
fn extend(&mut self) {
|
||||
// Temporarily store the original hash table
|
||||
let buckets_tmp = self.buckets.clone();
|
||||
// Initialize expanded new hash table
|
||||
self.capacity *= self.extend_ratio;
|
||||
self.buckets = vec![None; self.capacity];
|
||||
self.size = 0;
|
||||
|
||||
// Move key-value pairs from original hash table to new hash table
|
||||
for pair in buckets_tmp {
|
||||
if pair.is_none() || pair == self.TOMBSTONE {
|
||||
continue;
|
||||
}
|
||||
let pair = pair.unwrap();
|
||||
|
||||
self.put(pair.key, pair.val);
|
||||
}
|
||||
}
|
||||
/* Print hash table */
|
||||
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() {
|
||||
/* Initialize hash table */
|
||||
let mut hashmap = HashMapOpenAddressing::new();
|
||||
|
||||
/* Add operation */
|
||||
// Add key-value pair (key, value) to the hash table
|
||||
hashmap.put(12836, "Xiao Ha".to_string());
|
||||
hashmap.put(15937, "Xiao Luo".to_string());
|
||||
hashmap.put(16750, "Xiao Suan".to_string());
|
||||
hashmap.put(13276, "Xiao Fa".to_string());
|
||||
hashmap.put(10583, "Xiao Ya".to_string());
|
||||
|
||||
println!("\nAfter adding is complete, hash table is\nKey -> Value");
|
||||
hashmap.print();
|
||||
|
||||
/* Query operation */
|
||||
// Input key into hash table to get value val
|
||||
let name = hashmap.get(13276).unwrap();
|
||||
println!("\nInput student ID 13276, found name {}", name);
|
||||
|
||||
/* Remove operation */
|
||||
// Remove key-value pair (key, val) from hash table
|
||||
hashmap.remove(16750);
|
||||
println!("\nAfter removing 16750, hash table is\nKey -> Value");
|
||||
hashmap.print();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* File: simple_hash.rs
|
||||
* Created Time: 2023-09-07
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
|
||||
/* Additive hash */
|
||||
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
|
||||
}
|
||||
|
||||
/* Multiplicative hash */
|
||||
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 hash */
|
||||
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
|
||||
}
|
||||
|
||||
/* Rotational hash */
|
||||
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 Algo";
|
||||
|
||||
let hash = add_hash(key);
|
||||
println!("Additive hash value is {hash}");
|
||||
|
||||
let hash = mul_hash(key);
|
||||
println!("Multiplicative hash value is {hash}");
|
||||
|
||||
let hash = xor_hash(key);
|
||||
println!("XOR hash value is {hash}");
|
||||
|
||||
let hash = rot_hash(key);
|
||||
println!("Rotational hash value is {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); // Element enters heap
|
||||
println!("\nAfter element {} pushes to heap", 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!("\nAfter heap top element {} pops from heap", val);
|
||||
print_util::print_heap(heap.iter().map(|&val| val).collect());
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Initialize heap */
|
||||
// Python's heapq module implements min heap by default
|
||||
#[allow(unused_assignments)]
|
||||
let mut min_heap = BinaryHeap::new();
|
||||
// Rust's BinaryHeap is a max heap, min heap typically wraps elements with Reverse
|
||||
// Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap
|
||||
let mut max_heap = BinaryHeap::new();
|
||||
|
||||
println!("\nThe following test cases are for max heap");
|
||||
|
||||
/* Element enters heap */
|
||||
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);
|
||||
|
||||
/* Check if heap is empty */
|
||||
let peek = max_heap.peek().unwrap();
|
||||
println!("\nHeap top element is {}", peek);
|
||||
|
||||
/* Time complexity is O(n), not O(nlogn) */
|
||||
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);
|
||||
|
||||
/* Get heap size */
|
||||
let size = max_heap.len();
|
||||
println!("\nHeap size is {}", size);
|
||||
|
||||
/* Check if heap is empty */
|
||||
let is_empty = max_heap.is_empty();
|
||||
println!("\nIs heap empty {}", is_empty);
|
||||
|
||||
/* Input list and build heap */
|
||||
// Time complexity is O(n), not O(nlogn)
|
||||
min_heap = BinaryHeap::from(
|
||||
vec![1, 3, 2, 5, 4]
|
||||
.into_iter()
|
||||
.map(|val| Reverse(val))
|
||||
.collect::<Vec<Reverse<i32>>>(),
|
||||
);
|
||||
println!("\nAfter inputting list and building min heap");
|
||||
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;
|
||||
|
||||
/* Max heap */
|
||||
struct MaxHeap {
|
||||
// Use vector instead of array to avoid capacity concerns
|
||||
max_heap: Vec<i32>,
|
||||
}
|
||||
|
||||
impl MaxHeap {
|
||||
/* Constructor, build heap based on input list */
|
||||
fn new(nums: Vec<i32>) -> Self {
|
||||
// Add list elements to heap as is
|
||||
let mut heap = MaxHeap { max_heap: nums };
|
||||
// Heapify all nodes except leaf nodes
|
||||
for i in (0..=Self::parent(heap.size() - 1)).rev() {
|
||||
heap.sift_down(i);
|
||||
}
|
||||
heap
|
||||
}
|
||||
|
||||
/* Get index of left child node */
|
||||
fn left(i: usize) -> usize {
|
||||
2 * i + 1
|
||||
}
|
||||
|
||||
/* Get index of right child node */
|
||||
fn right(i: usize) -> usize {
|
||||
2 * i + 2
|
||||
}
|
||||
|
||||
/* Get index of parent node */
|
||||
fn parent(i: usize) -> usize {
|
||||
(i - 1) / 2 // Floor division
|
||||
}
|
||||
|
||||
/* Swap elements */
|
||||
fn swap(&mut self, i: usize, j: usize) {
|
||||
self.max_heap.swap(i, j);
|
||||
}
|
||||
|
||||
/* Get heap size */
|
||||
fn size(&self) -> usize {
|
||||
self.max_heap.len()
|
||||
}
|
||||
|
||||
/* Check if heap is empty */
|
||||
fn is_empty(&self) -> bool {
|
||||
self.max_heap.is_empty()
|
||||
}
|
||||
|
||||
/* Access top element */
|
||||
fn peek(&self) -> Option<i32> {
|
||||
self.max_heap.first().copied()
|
||||
}
|
||||
|
||||
/* Element enters heap */
|
||||
fn push(&mut self, val: i32) {
|
||||
// Add node
|
||||
self.max_heap.push(val);
|
||||
// Heapify from bottom to top
|
||||
self.sift_up(self.size() - 1);
|
||||
}
|
||||
|
||||
/* Starting from node i, heapify from bottom to top */
|
||||
fn sift_up(&mut self, mut i: usize) {
|
||||
loop {
|
||||
// Node i is already the heap root, end heapification
|
||||
if i == 0 {
|
||||
break;
|
||||
}
|
||||
// Get parent node of node i
|
||||
let p = Self::parent(i);
|
||||
// When "node needs no repair", end heapification
|
||||
if self.max_heap[i] <= self.max_heap[p] {
|
||||
break;
|
||||
}
|
||||
// Swap two nodes
|
||||
self.swap(i, p);
|
||||
// Loop upward heapify
|
||||
i = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Element exits heap */
|
||||
fn pop(&mut self) -> i32 {
|
||||
// Handle empty case
|
||||
if self.is_empty() {
|
||||
panic!("index out of bounds");
|
||||
}
|
||||
// Delete node
|
||||
self.swap(0, self.size() - 1);
|
||||
// Remove node
|
||||
let val = self.max_heap.pop().unwrap();
|
||||
// Return top element
|
||||
self.sift_down(0);
|
||||
// Return heap top element
|
||||
val
|
||||
}
|
||||
|
||||
/* Starting from node i, heapify from top to bottom */
|
||||
fn sift_down(&mut self, mut i: usize) {
|
||||
loop {
|
||||
// If node i is largest or indices l, r are out of bounds, no need to continue heapify, break
|
||||
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;
|
||||
}
|
||||
// Swap two nodes
|
||||
if ma == i {
|
||||
break;
|
||||
}
|
||||
// Swap two nodes
|
||||
self.swap(i, ma);
|
||||
// Loop downwards heapification
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn print(&self) {
|
||||
print_util::print_heap(self.max_heap.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */
|
||||
let mut max_heap = MaxHeap::new(vec![9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]);
|
||||
println!("\nAfter inputting list and building heap");
|
||||
max_heap.print();
|
||||
|
||||
/* Check if heap is empty */
|
||||
let peek = max_heap.peek();
|
||||
if let Some(peek) = peek {
|
||||
println!("\nHeap top element is {}", peek);
|
||||
}
|
||||
|
||||
/* Element enters heap */
|
||||
let val = 7;
|
||||
max_heap.push(val);
|
||||
println!("\nAfter element {} pushes to heap", val);
|
||||
max_heap.print();
|
||||
|
||||
/* Time complexity is O(n), not O(nlogn) */
|
||||
let peek = max_heap.pop();
|
||||
println!("\nAfter heap top element {} pops from heap", peek);
|
||||
max_heap.print();
|
||||
|
||||
/* Get heap size */
|
||||
let size = max_heap.size();
|
||||
println!("\nHeap size is {}", size);
|
||||
|
||||
/* Check if heap is empty */
|
||||
let is_empty = max_heap.is_empty();
|
||||
println!("\nIs heap empty {}", 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;
|
||||
|
||||
/* Find the largest k elements in array based on heap */
|
||||
fn top_k_heap(nums: Vec<i32>, k: usize) -> BinaryHeap<Reverse<i32>> {
|
||||
// BinaryHeap is a max heap, use Reverse to negate elements to implement min heap
|
||||
let mut heap = BinaryHeap::<Reverse<i32>>::new();
|
||||
// Enter the first k elements of array into heap
|
||||
for &num in nums.iter().take(k) {
|
||||
heap.push(Reverse(num));
|
||||
}
|
||||
// Starting from the (k+1)th element, maintain heap length as k
|
||||
for &num in nums.iter().skip(k) {
|
||||
// If current element is greater than top element, top element exits heap, current element enters heap
|
||||
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!("The largest {} elements are", 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)
|
||||
*/
|
||||
|
||||
/* Binary search (closed interval on both sides) */
|
||||
fn binary_search(nums: &[i32], target: i32) -> i32 {
|
||||
// Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array
|
||||
let mut i = 0;
|
||||
let mut j = nums.len() as i32 - 1;
|
||||
// Loop, exit when the search interval is empty (empty when i > j)
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2; // Calculate the midpoint index m
|
||||
if nums[m as usize] < target {
|
||||
// This means target is in the interval [m+1, j]
|
||||
i = m + 1;
|
||||
} else if nums[m as usize] > target {
|
||||
// This means target is in the interval [i, m-1]
|
||||
j = m - 1;
|
||||
} else {
|
||||
// Found the target element, return its index
|
||||
return m;
|
||||
}
|
||||
}
|
||||
// Target element not found, return -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Binary search (left-closed right-open interval) */
|
||||
fn binary_search_lcro(nums: &[i32], target: i32) -> i32 {
|
||||
// Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1
|
||||
let mut i = 0;
|
||||
let mut j = nums.len() as i32;
|
||||
// Loop, exit when the search interval is empty (empty when i = j)
|
||||
while i < j {
|
||||
let m = i + (j - i) / 2; // Calculate the midpoint index m
|
||||
if nums[m as usize] < target {
|
||||
// This means target is in the interval [m+1, j)
|
||||
i = m + 1;
|
||||
} else if nums[m as usize] > target {
|
||||
// This means target is in the interval [i, m)
|
||||
j = m;
|
||||
} else {
|
||||
// Found the target element, return its index
|
||||
return m;
|
||||
}
|
||||
}
|
||||
// Target element not found, return -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn main() {
|
||||
let target = 6;
|
||||
let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35];
|
||||
|
||||
// Binary search (closed interval on both sides)
|
||||
let mut index = binary_search(&nums, target);
|
||||
println!("Index of target element 6 is {index}");
|
||||
|
||||
// Binary search (left-closed right-open interval)
|
||||
index = binary_search_lcro(&nums, target);
|
||||
println!("Index of target element 6 is {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;
|
||||
|
||||
/* Binary search for the leftmost target */
|
||||
fn binary_search_left_edge(nums: &[i32], target: i32) -> i32 {
|
||||
// Equivalent to finding the insertion point of target
|
||||
let i = binary_search_insertion(nums, target);
|
||||
// Target not found, return -1
|
||||
if i == nums.len() as i32 || nums[i as usize] != target {
|
||||
return -1;
|
||||
}
|
||||
// Found target, return index i
|
||||
i
|
||||
}
|
||||
|
||||
/* Binary search for the rightmost target */
|
||||
fn binary_search_right_edge(nums: &[i32], target: i32) -> i32 {
|
||||
// Convert to finding the leftmost target + 1
|
||||
let i = binary_search_insertion(nums, target + 1);
|
||||
// j points to the rightmost target, i points to the first element greater than target
|
||||
let j = i - 1;
|
||||
// Target not found, return -1
|
||||
if j == -1 || nums[j as usize] != target {
|
||||
return -1;
|
||||
}
|
||||
// Found target, return index j
|
||||
j
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// Array with duplicate elements
|
||||
let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15];
|
||||
println!("\nArray nums = {:?}", nums);
|
||||
|
||||
// Binary search left and right boundaries
|
||||
for target in [6, 7] {
|
||||
let index = binary_search_left_edge(&nums, target);
|
||||
println!("Leftmost element {} index is {}", target, index);
|
||||
let index = binary_search_right_edge(&nums, target);
|
||||
println!("Rightmost element {} index is {}", target, index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* File: binary_search_insertion.rs
|
||||
* Created Time: 2023-08-30
|
||||
* Author: night-cruise (2586447362@qq.com)
|
||||
*/
|
||||
#![allow(unused)]
|
||||
|
||||
/* Binary search for insertion point (no duplicate elements) */
|
||||
fn binary_search_insertion_simple(nums: &[i32], target: i32) -> i32 {
|
||||
let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize closed interval [0, n-1]
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2; // Calculate the midpoint index m
|
||||
if nums[m as usize] < target {
|
||||
i = m + 1; // target is in the interval [m+1, j]
|
||||
} else if nums[m as usize] > target {
|
||||
j = m - 1; // target is in the interval [i, m-1]
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
// Target not found, return insertion point i
|
||||
i
|
||||
}
|
||||
|
||||
/* Binary search for insertion point (with duplicate elements) */
|
||||
pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 {
|
||||
let (mut i, mut j) = (0, nums.len() as i32 - 1); // Initialize closed interval [0, n-1]
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2; // Calculate the midpoint index m
|
||||
if nums[m as usize] < target {
|
||||
i = m + 1; // target is in the interval [m+1, j]
|
||||
} else if nums[m as usize] > target {
|
||||
j = m - 1; // target is in the interval [i, m-1]
|
||||
} else {
|
||||
j = m - 1; // The first element less than target is in the interval [i, m-1]
|
||||
}
|
||||
}
|
||||
// Return insertion point i
|
||||
i
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// Array without duplicate elements
|
||||
let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35];
|
||||
println!("\nArray nums = {:?}", nums);
|
||||
// Binary search for insertion point
|
||||
for target in [6, 9] {
|
||||
let index = binary_search_insertion_simple(&nums, target);
|
||||
println!("Insertion point index for element {} is {}", target, index);
|
||||
}
|
||||
|
||||
// Array with duplicate elements
|
||||
let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15];
|
||||
println!("\nArray nums = {:?}", nums);
|
||||
// Binary search for insertion point
|
||||
for target in [2, 6, 20] {
|
||||
let index = binary_search_insertion(&nums, target);
|
||||
println!("Insertion point index for element {} is {}", 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;
|
||||
|
||||
/* Hash search (array) */
|
||||
fn hashing_search_array<'a>(map: &'a HashMap<i32, usize>, target: i32) -> Option<&'a usize> {
|
||||
// Hash table's key: target element, value: index
|
||||
// If this key does not exist in the hash table, return None
|
||||
map.get(&target)
|
||||
}
|
||||
|
||||
/* Hash search (linked list) */
|
||||
fn hashing_search_linked_list(
|
||||
map: &HashMap<i32, Rc<RefCell<ListNode<i32>>>>,
|
||||
target: i32,
|
||||
) -> Option<&Rc<RefCell<ListNode<i32>>>> {
|
||||
// Hash table key: target node value, value: node object
|
||||
// If this key does not exist in the hash table, return None
|
||||
map.get(&target)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn main() {
|
||||
let target = 3;
|
||||
|
||||
/* Hash search (array) */
|
||||
let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8];
|
||||
// Initialize hash table
|
||||
let mut map = HashMap::new();
|
||||
for (i, num) in nums.iter().enumerate() {
|
||||
map.insert(*num, i); // key: element, value: index
|
||||
}
|
||||
let index = hashing_search_array(&map, target);
|
||||
println!("Index of target element 3 = {}", index.unwrap());
|
||||
|
||||
/* Hash search (linked list) */
|
||||
let head = ListNode::arr_to_linked_list(&nums);
|
||||
// Initialize hash table
|
||||
// let mut map1 = HashMap::new();
|
||||
let map1 = ListNode::linked_list_to_hashmap(head);
|
||||
let node = hashing_search_linked_list(&map1, target);
|
||||
println!("Node object corresponding to target node value 3 is {:?}", 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;
|
||||
|
||||
/* Linear search (array) */
|
||||
fn linear_search_array(nums: &[i32], target: i32) -> i32 {
|
||||
// Traverse array
|
||||
for (i, num) in nums.iter().enumerate() {
|
||||
// Found the target element, return its index
|
||||
if num == &target {
|
||||
return i as i32;
|
||||
}
|
||||
}
|
||||
// Target element not found, return -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Linear search (linked list) */
|
||||
fn linear_search_linked_list(
|
||||
head: Rc<RefCell<ListNode<i32>>>,
|
||||
target: i32,
|
||||
) -> Option<Rc<RefCell<ListNode<i32>>>> {
|
||||
// Found the target node, return it
|
||||
if head.borrow().val == target {
|
||||
return Some(head);
|
||||
};
|
||||
// Found the target node, return it
|
||||
if let Some(node) = &head.borrow_mut().next {
|
||||
return linear_search_linked_list(node.clone(), target);
|
||||
}
|
||||
// Target node not found, return None
|
||||
return None;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn main() {
|
||||
let target = 3;
|
||||
|
||||
/* Perform linear search in array */
|
||||
let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8];
|
||||
let index = linear_search_array(&nums, target);
|
||||
println!("Index of target element 3 = {}", index);
|
||||
|
||||
/* Perform linear search in linked list */
|
||||
let head = ListNode::arr_to_linked_list(&nums);
|
||||
let node = linear_search_linked_list(head.unwrap(), target);
|
||||
println!("Node object corresponding to target node value 3 is {:?}", 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;
|
||||
|
||||
/* Method 1: Brute force enumeration */
|
||||
pub fn two_sum_brute_force(nums: &Vec<i32>, target: i32) -> Option<Vec<i32>> {
|
||||
let size = nums.len();
|
||||
// Two nested loops, time complexity is 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
|
||||
}
|
||||
|
||||
/* Method 2: Auxiliary hash table */
|
||||
pub fn two_sum_hash_table(nums: &Vec<i32>, target: i32) -> Option<Vec<i32>> {
|
||||
// Auxiliary hash table, space complexity is O(n)
|
||||
let mut dic = HashMap::new();
|
||||
// Single loop, time complexity is 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 ======
|
||||
// Method 1
|
||||
let res = two_sum_brute_force(&nums, target).unwrap();
|
||||
print!("Method 1 res = ");
|
||||
print_util::print_array(&res);
|
||||
// Method 2
|
||||
let res = two_sum_hash_table(&nums, target).unwrap();
|
||||
print!("\nMethod 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;
|
||||
|
||||
/* Bubble sort */
|
||||
fn bubble_sort(nums: &mut [i32]) {
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
nums.swap(j, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Bubble sort (flag optimization) */
|
||||
fn bubble_sort_with_flag(nums: &mut [i32]) {
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
let mut flag = false; // Initialize flag
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
nums.swap(j, j + 1);
|
||||
flag = true; // Record element swap
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
break; // No elements were swapped in this round of "bubbling", exit directly
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn main() {
|
||||
let mut nums = [4, 1, 3, 1, 5, 2];
|
||||
bubble_sort(&mut nums);
|
||||
print!("After bubble sort completes, nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
let mut nums1 = [4, 1, 3, 1, 5, 2];
|
||||
bubble_sort_with_flag(&mut nums1);
|
||||
print!("\nAfter bubble sort, 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;
|
||||
|
||||
/* Bucket sort */
|
||||
fn bucket_sort(nums: &mut [f64]) {
|
||||
// Initialize k = n/2 buckets, expected to allocate 2 elements per bucket
|
||||
let k = nums.len() / 2;
|
||||
let mut buckets = vec![vec![]; k];
|
||||
// 1. Distribute array elements into various buckets
|
||||
for &num in nums.iter() {
|
||||
// Input data range is [0, 1), use num * k to map to index range [0, k-1]
|
||||
let i = (num * k as f64) as usize;
|
||||
// Add num to bucket i
|
||||
buckets[i].push(num);
|
||||
}
|
||||
// 2. Sort each bucket
|
||||
for bucket in &mut buckets {
|
||||
// Use built-in sorting function, can also replace with other sorting algorithms
|
||||
bucket.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
}
|
||||
// 3. Traverse buckets to merge results
|
||||
let mut i = 0;
|
||||
for bucket in buckets.iter() {
|
||||
for &num in bucket.iter() {
|
||||
nums[i] = num;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// Assume input data is floating point, interval [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!("After bucket sort completes, 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;
|
||||
|
||||
/* Counting sort */
|
||||
// Simple implementation, cannot be used for sorting objects
|
||||
fn counting_sort_naive(nums: &mut [i32]) {
|
||||
// 1. Count the maximum element m in the array
|
||||
let m = *nums.iter().max().unwrap();
|
||||
// 2. Count the occurrence of each number
|
||||
// counter[num] represents the occurrence of num
|
||||
let mut counter = vec![0; m as usize + 1];
|
||||
for &num in nums.iter() {
|
||||
counter[num as usize] += 1;
|
||||
}
|
||||
// 3. Traverse counter, filling each element back into the original array nums
|
||||
let mut i = 0;
|
||||
for num in 0..m + 1 {
|
||||
for _ in 0..counter[num as usize] {
|
||||
nums[i] = num;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Counting sort */
|
||||
// Complete implementation, can sort objects and is a stable sort
|
||||
fn counting_sort(nums: &mut [i32]) {
|
||||
// 1. Count the maximum element m in the array
|
||||
let m = *nums.iter().max().unwrap() as usize;
|
||||
// 2. Count the occurrence of each number
|
||||
// counter[num] represents the occurrence of num
|
||||
let mut counter = vec![0; m + 1];
|
||||
for &num in nums.iter() {
|
||||
counter[num as usize] += 1;
|
||||
}
|
||||
// 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index"
|
||||
// counter[num]-1 is the last index where num appears in res
|
||||
for i in 0..m {
|
||||
counter[i + 1] += counter[i];
|
||||
}
|
||||
// 4. Traverse nums in reverse order, placing each element into the result array res
|
||||
// Initialize the array res to record results
|
||||
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; // Place num at the corresponding index
|
||||
counter[num as usize] -= 1; // Decrement the prefix sum by 1, getting the next index to place num
|
||||
}
|
||||
// Use result array res to overwrite the original array 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!("After counting sort (cannot sort objects) completes, nums = ");
|
||||
print_util::print_array(&nums);
|
||||
|
||||
let mut nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4];
|
||||
counting_sort(&mut nums1);
|
||||
print!("\nAfter counting sort, 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;
|
||||
|
||||
/* Heap length is n, start heapifying node i, from top to bottom */
|
||||
fn sift_down(nums: &mut [i32], n: usize, mut i: usize) {
|
||||
loop {
|
||||
// If node i is largest or indices l, r are out of bounds, no need to continue heapify, break
|
||||
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;
|
||||
}
|
||||
// Swap two nodes
|
||||
if ma == i {
|
||||
break;
|
||||
}
|
||||
// Swap two nodes
|
||||
nums.swap(i, ma);
|
||||
// Loop downwards heapification
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
|
||||
/* Heap sort */
|
||||
fn heap_sort(nums: &mut [i32]) {
|
||||
// Build heap operation: heapify all nodes except leaves
|
||||
for i in (0..nums.len() / 2).rev() {
|
||||
sift_down(nums, nums.len(), i);
|
||||
}
|
||||
// Extract the largest element from the heap and repeat for n-1 rounds
|
||||
for i in (1..nums.len()).rev() {
|
||||
// Delete node
|
||||
nums.swap(0, i);
|
||||
// Start heapifying the root node, from top to bottom
|
||||
sift_down(nums, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
let mut nums = [4, 1, 3, 1, 5, 2];
|
||||
heap_sort(&mut nums);
|
||||
print!("After heap sort completes, 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;
|
||||
|
||||
/* Insertion sort */
|
||||
fn insertion_sort(nums: &mut [i32]) {
|
||||
// Outer loop: sorted interval is [0, i-1]
|
||||
for i in 1..nums.len() {
|
||||
let (base, mut j) = (nums[i], (i - 1) as i32);
|
||||
// Inner loop: insert base into the correct position within the sorted interval [0, i-1]
|
||||
while j >= 0 && nums[j as usize] > base {
|
||||
nums[(j + 1) as usize] = nums[j as usize]; // Move nums[j] to the right by one position
|
||||
j -= 1;
|
||||
}
|
||||
nums[(j + 1) as usize] = base; // Assign base to the correct position
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
let mut nums = [4, 1, 3, 1, 5, 2];
|
||||
insertion_sort(&mut nums);
|
||||
print!("After insertion sort completes, nums = ");
|
||||
print_util::print_array(&nums);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* File: merge_sort.rs
|
||||
* Created Time: 2023-02-14
|
||||
* Author: xBLACKICEx (xBLACKICEx@outlook.com)
|
||||
*/
|
||||
|
||||
/* Merge left subarray and right subarray */
|
||||
fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) {
|
||||
// Left subarray interval is [left, mid], right subarray interval is [mid+1, right]
|
||||
// Create a temporary array tmp to store the merged results
|
||||
let tmp_size = right - left + 1;
|
||||
let mut tmp = vec![0; tmp_size];
|
||||
// Initialize the start indices of the left and right subarrays
|
||||
let (mut i, mut j, mut k) = (left, mid + 1, 0);
|
||||
// While both subarrays still have elements, compare and copy the smaller element into the temporary array
|
||||
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;
|
||||
}
|
||||
// Copy the remaining elements of the left and right subarrays into the temporary array
|
||||
while i <= mid {
|
||||
tmp[k] = nums[i];
|
||||
k += 1;
|
||||
i += 1;
|
||||
}
|
||||
while j <= right {
|
||||
tmp[k] = nums[j];
|
||||
k += 1;
|
||||
j += 1;
|
||||
}
|
||||
// Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval
|
||||
for k in 0..tmp_size {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge sort */
|
||||
fn merge_sort(nums: &mut [i32], left: usize, right: usize) {
|
||||
// Termination condition
|
||||
if left >= right {
|
||||
return; // Terminate recursion when subarray length is 1
|
||||
}
|
||||
|
||||
// Divide and conquer stage
|
||||
let mid = left + (right - left) / 2; // Calculate midpoint
|
||||
merge_sort(nums, left, mid); // Recursively process the left subarray
|
||||
merge_sort(nums, mid + 1, right); // Recursively process the right subarray
|
||||
|
||||
// Merge stage
|
||||
merge(nums, left, mid, right);
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Merge sort */
|
||||
let mut nums = [7, 3, 2, 6, 0, 1, 5, 4];
|
||||
let right = nums.len() - 1;
|
||||
merge_sort(&mut nums, 0, right);
|
||||
println!("After merge sort, nums = {:?}", nums);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* File: quick_sort.rs
|
||||
* Created Time: 2023-02-16
|
||||
* Author: xBLACKICEx (xBLACKICE@outlook.com)
|
||||
*/
|
||||
|
||||
/* Quick sort */
|
||||
struct QuickSort;
|
||||
|
||||
impl QuickSort {
|
||||
/* Sentinel partition */
|
||||
fn partition(nums: &mut [i32], left: usize, right: usize) -> usize {
|
||||
// Use nums[left] as the pivot
|
||||
let (mut i, mut j) = (left, right);
|
||||
while i < j {
|
||||
while i < j && nums[j] >= nums[left] {
|
||||
j -= 1; // Search from right to left for the first element smaller than the pivot
|
||||
}
|
||||
while i < j && nums[i] <= nums[left] {
|
||||
i += 1; // Search from left to right for the first element greater than the pivot
|
||||
}
|
||||
nums.swap(i, j); // Swap these two elements
|
||||
}
|
||||
nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays
|
||||
i // Return the index of the pivot
|
||||
}
|
||||
|
||||
/* Quick sort */
|
||||
pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) {
|
||||
// Terminate recursion when subarray length is 1
|
||||
if left >= right {
|
||||
return;
|
||||
}
|
||||
// Sentinel partition
|
||||
let pivot = Self::partition(nums, left as usize, right as usize) as i32;
|
||||
// Recursively process the left subarray and right subarray
|
||||
Self::quick_sort(left, pivot - 1, nums);
|
||||
Self::quick_sort(pivot + 1, right, nums);
|
||||
}
|
||||
}
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
struct QuickSortMedian;
|
||||
|
||||
impl QuickSortMedian {
|
||||
/* Select the median of three candidate elements */
|
||||
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 is between l and r
|
||||
}
|
||||
if (m <= l && l <= r) || (r <= l && l <= m) {
|
||||
return left; // l is between m and r
|
||||
}
|
||||
right
|
||||
}
|
||||
|
||||
/* Sentinel partition (median of three) */
|
||||
fn partition(nums: &mut [i32], left: usize, right: usize) -> usize {
|
||||
// Select the median of three candidate elements
|
||||
let med = Self::median_three(nums, left, (left + right) / 2, right);
|
||||
// Swap the median to the array's leftmost position
|
||||
nums.swap(left, med);
|
||||
// Use nums[left] as the pivot
|
||||
let (mut i, mut j) = (left, right);
|
||||
while i < j {
|
||||
while i < j && nums[j] >= nums[left] {
|
||||
j -= 1; // Search from right to left for the first element smaller than the pivot
|
||||
}
|
||||
while i < j && nums[i] <= nums[left] {
|
||||
i += 1; // Search from left to right for the first element greater than the pivot
|
||||
}
|
||||
nums.swap(i, j); // Swap these two elements
|
||||
}
|
||||
nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays
|
||||
i // Return the index of the pivot
|
||||
}
|
||||
|
||||
/* Quick sort */
|
||||
pub fn quick_sort(left: i32, right: i32, nums: &mut [i32]) {
|
||||
// Terminate recursion when subarray length is 1
|
||||
if left >= right {
|
||||
return;
|
||||
}
|
||||
// Sentinel partition
|
||||
let pivot = Self::partition(nums, left as usize, right as usize) as i32;
|
||||
// Recursively process the left subarray and right subarray
|
||||
Self::quick_sort(left, pivot - 1, nums);
|
||||
Self::quick_sort(pivot + 1, right, nums);
|
||||
}
|
||||
}
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
struct QuickSortTailCall;
|
||||
|
||||
impl QuickSortTailCall {
|
||||
/* Sentinel partition */
|
||||
fn partition(nums: &mut [i32], left: usize, right: usize) -> usize {
|
||||
// Use nums[left] as the pivot
|
||||
let (mut i, mut j) = (left, right);
|
||||
while i < j {
|
||||
while i < j && nums[j] >= nums[left] {
|
||||
j -= 1; // Search from right to left for the first element smaller than the pivot
|
||||
}
|
||||
while i < j && nums[i] <= nums[left] {
|
||||
i += 1; // Search from left to right for the first element greater than the pivot
|
||||
}
|
||||
nums.swap(i, j); // Swap these two elements
|
||||
}
|
||||
nums.swap(i, left); // Swap the pivot to the boundary between the two subarrays
|
||||
i // Return the index of the pivot
|
||||
}
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
pub fn quick_sort(mut left: i32, mut right: i32, nums: &mut [i32]) {
|
||||
// Terminate when subarray length is 1
|
||||
while left < right {
|
||||
// Sentinel partition operation
|
||||
let pivot = Self::partition(nums, left as usize, right as usize) as i32;
|
||||
// Perform quick sort on the shorter of the two subarrays
|
||||
if pivot - left < right - pivot {
|
||||
Self::quick_sort(left, pivot - 1, nums); // Recursively sort the left subarray
|
||||
left = pivot + 1; // Remaining unsorted interval is [pivot + 1, right]
|
||||
} else {
|
||||
Self::quick_sort(pivot + 1, right, nums); // Recursively sort the right subarray
|
||||
right = pivot - 1; // Remaining unsorted interval is [left, pivot - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Quick sort */
|
||||
let mut nums = [2, 4, 1, 0, 3, 5];
|
||||
QuickSort::quick_sort(0, (nums.len() - 1) as i32, &mut nums);
|
||||
println!("After quick sort, nums = {:?}", nums);
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
let mut nums = [2, 4, 1, 0, 3, 5];
|
||||
QuickSortMedian::quick_sort(0, (nums.len() - 1) as i32, &mut nums);
|
||||
println!("After quick sort (median pivot optimization), nums = {:?}", nums);
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
let mut nums = [2, 4, 1, 0, 3, 5];
|
||||
QuickSortTailCall::quick_sort(0, (nums.len() - 1) as i32, &mut nums);
|
||||
println!("After quick sort (recursion depth optimization), 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;
|
||||
|
||||
/* Get the k-th digit of element num, where exp = 10^(k-1) */
|
||||
fn digit(num: i32, exp: i32) -> usize {
|
||||
// Passing exp instead of k can avoid repeated expensive exponentiation here
|
||||
return ((num / exp) % 10) as usize;
|
||||
}
|
||||
|
||||
/* Counting sort (based on nums k-th digit) */
|
||||
fn counting_sort_digit(nums: &mut [i32], exp: i32) {
|
||||
// Decimal digit range is 0~9, therefore need a bucket array of length 10
|
||||
let mut counter = [0; 10];
|
||||
let n = nums.len();
|
||||
// Count the occurrence of digits 0~9
|
||||
for i in 0..n {
|
||||
let d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d
|
||||
counter[d] += 1; // Count the occurrence of digit d
|
||||
}
|
||||
// Calculate prefix sum, converting "occurrence count" into "array index"
|
||||
for i in 1..10 {
|
||||
counter[i] += counter[i - 1];
|
||||
}
|
||||
// Traverse in reverse, based on bucket statistics, place each element into res
|
||||
let mut res = vec![0; n];
|
||||
for i in (0..n).rev() {
|
||||
let d = digit(nums[i], exp);
|
||||
let j = counter[d] - 1; // Get the index j for d in the array
|
||||
res[j] = nums[i]; // Place the current element at index j
|
||||
counter[d] -= 1; // Decrease the count of d by 1
|
||||
}
|
||||
// Use result to overwrite the original array nums
|
||||
nums.copy_from_slice(&res);
|
||||
}
|
||||
|
||||
/* Radix sort */
|
||||
fn radix_sort(nums: &mut [i32]) {
|
||||
// Get the maximum element of the array, used to determine the maximum number of digits
|
||||
let m = *nums.into_iter().max().unwrap();
|
||||
// Traverse from the lowest to the highest digit
|
||||
let mut exp = 1;
|
||||
while exp <= m {
|
||||
counting_sort_digit(nums, exp);
|
||||
exp *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// Radix sort
|
||||
let mut nums = [
|
||||
10546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337,
|
||||
63832996,
|
||||
];
|
||||
radix_sort(&mut nums);
|
||||
print!("After radix sort completes, 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;
|
||||
|
||||
/* Selection sort */
|
||||
fn selection_sort(nums: &mut [i32]) {
|
||||
if nums.is_empty() {
|
||||
return;
|
||||
}
|
||||
let n = nums.len();
|
||||
// Outer loop: unsorted interval is [i, n-1]
|
||||
for i in 0..n - 1 {
|
||||
// Inner loop: find the smallest element within the unsorted interval
|
||||
let mut k = i;
|
||||
for j in i + 1..n {
|
||||
if nums[j] < nums[k] {
|
||||
k = j; // Record the index of the smallest element
|
||||
}
|
||||
}
|
||||
// Swap the smallest element with the first element of the unsorted interval
|
||||
nums.swap(i, k);
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn main() {
|
||||
let mut nums = [4, 1, 3, 1, 5, 2];
|
||||
selection_sort(&mut nums);
|
||||
print!("\nAfter selection sort, 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;
|
||||
/* Double-ended queue based on circular array implementation */
|
||||
struct ArrayDeque<T> {
|
||||
nums: Vec<T>, // Array for storing double-ended queue elements
|
||||
front: usize, // Front pointer, points to the front of the queue element
|
||||
que_size: usize, // Double-ended queue length
|
||||
}
|
||||
|
||||
impl<T: Copy + Default> ArrayDeque<T> {
|
||||
/* Constructor */
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
nums: vec![T::default(); capacity],
|
||||
front: 0,
|
||||
que_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the capacity of the double-ended queue */
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.nums.len()
|
||||
}
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
pub fn size(&self) -> usize {
|
||||
self.que_size
|
||||
}
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.que_size == 0
|
||||
}
|
||||
|
||||
/* Calculate circular array index */
|
||||
fn index(&self, i: i32) -> usize {
|
||||
// Use modulo operation to wrap the array head and tail together
|
||||
// When i passes the tail of the array, return to the head
|
||||
// When i passes the head of the array, return to the tail
|
||||
((i + self.capacity() as i32) % self.capacity() as i32) as usize
|
||||
}
|
||||
|
||||
/* Front of the queue enqueue */
|
||||
pub fn push_first(&mut self, num: T) {
|
||||
if self.que_size == self.capacity() {
|
||||
println!("Double-ended queue is full");
|
||||
return;
|
||||
}
|
||||
// Use modulo operation to wrap front around to the tail after passing the head of the array
|
||||
// Add num to the front of the queue
|
||||
self.front = self.index(self.front as i32 - 1);
|
||||
// Add num to front of queue
|
||||
self.nums[self.front] = num;
|
||||
self.que_size += 1;
|
||||
}
|
||||
|
||||
/* Rear of the queue enqueue */
|
||||
pub fn push_last(&mut self, num: T) {
|
||||
if self.que_size == self.capacity() {
|
||||
println!("Double-ended queue is full");
|
||||
return;
|
||||
}
|
||||
// Use modulo operation to wrap rear around to the head after passing the tail of the array
|
||||
let rear = self.index(self.front as i32 + self.que_size as i32);
|
||||
// Front pointer moves one position backward
|
||||
self.nums[rear] = num;
|
||||
self.que_size += 1;
|
||||
}
|
||||
|
||||
/* Rear of the queue dequeue */
|
||||
fn pop_first(&mut self) -> T {
|
||||
let num = self.peek_first();
|
||||
// Move front pointer backward by one position
|
||||
self.front = self.index(self.front as i32 + 1);
|
||||
self.que_size -= 1;
|
||||
num
|
||||
}
|
||||
|
||||
/* Access rear of the queue element */
|
||||
fn pop_last(&mut self) -> T {
|
||||
let num = self.peek_last();
|
||||
self.que_size -= 1;
|
||||
num
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fn peek_first(&self) -> T {
|
||||
if self.is_empty() {
|
||||
panic!("Deque is empty")
|
||||
};
|
||||
self.nums[self.front]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn peek_last(&self) -> T {
|
||||
if self.is_empty() {
|
||||
panic!("Deque is empty")
|
||||
};
|
||||
// Initialize double-ended queue
|
||||
let last = self.index(self.front as i32 + self.que_size as i32 - 1);
|
||||
self.nums[last]
|
||||
}
|
||||
|
||||
/* Return array for printing */
|
||||
fn to_array(&self) -> Vec<T> {
|
||||
// Elements enqueue
|
||||
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() {
|
||||
/* Get the length of the double-ended queue */
|
||||
let mut deque = ArrayDeque::new(10);
|
||||
deque.push_last(3);
|
||||
deque.push_last(2);
|
||||
deque.push_last(5);
|
||||
print!("Double-ended queue deque = ");
|
||||
print_util::print_array(&deque.to_array());
|
||||
|
||||
/* Update element */
|
||||
let peek_first = deque.peek_first();
|
||||
print!("\nFront element peek_first = {}", peek_first);
|
||||
let peek_last = deque.peek_last();
|
||||
print!("\nRear element peek_last = {}", peek_last);
|
||||
|
||||
/* Elements enqueue */
|
||||
deque.push_last(4);
|
||||
print!("\nAfter element 4 enqueues at rear, deque = ");
|
||||
print_util::print_array(&deque.to_array());
|
||||
deque.push_first(1);
|
||||
print!("\nAfter element 1 enqueues at front, deque = ");
|
||||
print_util::print_array(&deque.to_array());
|
||||
|
||||
/* Element dequeue */
|
||||
let pop_last = deque.pop_last();
|
||||
print!("\nDequeue rear element = {}, after dequeue deque = ", pop_last);
|
||||
print_util::print_array(&deque.to_array());
|
||||
let pop_first = deque.pop_first();
|
||||
print!("\nDequeue front element = {}, after dequeue deque = ", pop_first);
|
||||
print_util::print_array(&deque.to_array());
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
let size = deque.size();
|
||||
print!("\nDeque length size = {}", size);
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
let is_empty = deque.is_empty();
|
||||
print!("\nIs deque empty = {}", is_empty);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* File: array_queue.rs
|
||||
* Created Time: 2023-02-06
|
||||
* Author: WSL0809 (wslzzy@outlook.com)
|
||||
*/
|
||||
|
||||
/* Queue based on circular array implementation */
|
||||
struct ArrayQueue<T> {
|
||||
nums: Vec<T>, // Array for storing queue elements
|
||||
front: i32, // Front pointer, points to the front of the queue element
|
||||
que_size: i32, // Queue length
|
||||
que_capacity: i32, // Queue capacity
|
||||
}
|
||||
|
||||
impl<T: Copy + Default> ArrayQueue<T> {
|
||||
/* Constructor */
|
||||
fn new(capacity: i32) -> ArrayQueue<T> {
|
||||
ArrayQueue {
|
||||
nums: vec![T::default(); capacity as usize],
|
||||
front: 0,
|
||||
que_size: 0,
|
||||
que_capacity: capacity,
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the capacity of the queue */
|
||||
fn capacity(&self) -> i32 {
|
||||
self.que_capacity
|
||||
}
|
||||
|
||||
/* Get the length of the queue */
|
||||
fn size(&self) -> i32 {
|
||||
self.que_size
|
||||
}
|
||||
|
||||
/* Check if the queue is empty */
|
||||
fn is_empty(&self) -> bool {
|
||||
self.que_size == 0
|
||||
}
|
||||
|
||||
/* Enqueue */
|
||||
fn push(&mut self, num: T) {
|
||||
if self.que_size == self.capacity() {
|
||||
println!("Queue is full");
|
||||
return;
|
||||
}
|
||||
// Use modulo operation to wrap rear around to the head after passing the tail of the array
|
||||
// Add num to the rear of the queue
|
||||
let rear = (self.front + self.que_size) % self.que_capacity;
|
||||
// Front pointer moves one position backward
|
||||
self.nums[rear as usize] = num;
|
||||
self.que_size += 1;
|
||||
}
|
||||
|
||||
/* Dequeue */
|
||||
fn pop(&mut self) -> T {
|
||||
let num = self.peek();
|
||||
// Move front pointer backward by one position, if it passes the tail, return to array head
|
||||
self.front = (self.front + 1) % self.que_capacity;
|
||||
self.que_size -= 1;
|
||||
num
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fn peek(&self) -> T {
|
||||
if self.is_empty() {
|
||||
panic!("index out of bounds");
|
||||
}
|
||||
self.nums[self.front as usize]
|
||||
}
|
||||
|
||||
/* Return array */
|
||||
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() {
|
||||
/* Access front of the queue element */
|
||||
let capacity = 10;
|
||||
let mut queue = ArrayQueue::new(capacity);
|
||||
|
||||
/* Elements enqueue */
|
||||
queue.push(1);
|
||||
queue.push(3);
|
||||
queue.push(2);
|
||||
queue.push(5);
|
||||
queue.push(4);
|
||||
println!("Queue queue = {:?}", queue.to_vector());
|
||||
|
||||
/* Return list for printing */
|
||||
let peek = queue.peek();
|
||||
println!("Front element peek = {}", peek);
|
||||
|
||||
/* Element dequeue */
|
||||
let pop = queue.pop();
|
||||
println!(
|
||||
"Dequeue element pop = {:?}, after dequeue queue = {:?}",
|
||||
pop,
|
||||
queue.to_vector()
|
||||
);
|
||||
|
||||
/* Get the length of the queue */
|
||||
let size = queue.size();
|
||||
println!("Queue length size = {}", size);
|
||||
|
||||
/* Check if the queue is empty */
|
||||
let is_empty = queue.is_empty();
|
||||
println!("Is queue empty = {}", is_empty);
|
||||
|
||||
/* Test circular array */
|
||||
for i in 0..10 {
|
||||
queue.push(i);
|
||||
queue.pop();
|
||||
println!("After round {:?} of enqueue + dequeue, 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;
|
||||
|
||||
/* Stack based on array implementation */
|
||||
struct ArrayStack<T> {
|
||||
stack: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> ArrayStack<T> {
|
||||
/* Access top of the stack element */
|
||||
fn new() -> ArrayStack<T> {
|
||||
ArrayStack::<T> {
|
||||
stack: Vec::<T>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the length of the stack */
|
||||
fn size(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
/* Check if the stack is empty */
|
||||
fn is_empty(&self) -> bool {
|
||||
self.size() == 0
|
||||
}
|
||||
|
||||
/* Push */
|
||||
fn push(&mut self, num: T) {
|
||||
self.stack.push(num);
|
||||
}
|
||||
|
||||
/* Pop */
|
||||
fn pop(&mut self) -> Option<T> {
|
||||
self.stack.pop()
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fn peek(&self) -> Option<&T> {
|
||||
if self.is_empty() {
|
||||
panic!("Stack is empty")
|
||||
};
|
||||
self.stack.last()
|
||||
}
|
||||
|
||||
/* Return &Vec */
|
||||
fn to_array(&self) -> &Vec<T> {
|
||||
&self.stack
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// Access top of the stack element
|
||||
let mut stack = ArrayStack::<i32>::new();
|
||||
|
||||
// Elements push onto stack
|
||||
stack.push(1);
|
||||
stack.push(3);
|
||||
stack.push(2);
|
||||
stack.push(5);
|
||||
stack.push(4);
|
||||
print!("Stack stack = ");
|
||||
print_util::print_array(stack.to_array());
|
||||
|
||||
// Return list for printing
|
||||
let peek = stack.peek().unwrap();
|
||||
print!("\nTop element peek = {}", peek);
|
||||
|
||||
// Element pop from stack
|
||||
let pop = stack.pop().unwrap();
|
||||
print!("\nPop element pop = {pop}, after pop stack = ");
|
||||
print_util::print_array(stack.to_array());
|
||||
|
||||
// Get the length of the stack
|
||||
let size = stack.size();
|
||||
print!("\nStack length size = {size}");
|
||||
|
||||
// Check if empty
|
||||
let is_empty = stack.is_empty();
|
||||
print!("\nIs stack empty = {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() {
|
||||
// Get the length of the double-ended queue
|
||||
let mut deque: VecDeque<i32> = VecDeque::new();
|
||||
deque.push_back(3);
|
||||
deque.push_back(2);
|
||||
deque.push_back(5);
|
||||
print!("Double-ended queue deque = ");
|
||||
print_util::print_queue(&deque);
|
||||
|
||||
// Update element
|
||||
let peek_first = deque.front().unwrap();
|
||||
print!("\nFront element peekFirst = {peek_first}");
|
||||
let peek_last = deque.back().unwrap();
|
||||
print!("\nRear element peekLast = {peek_last}");
|
||||
|
||||
/* Elements enqueue */
|
||||
deque.push_back(4);
|
||||
print!("\nAfter element 4 enqueues at rear, deque = ");
|
||||
print_util::print_queue(&deque);
|
||||
deque.push_front(1);
|
||||
print!("\nAfter element 1 enqueues at front, deque = ");
|
||||
print_util::print_queue(&deque);
|
||||
|
||||
// Element dequeue
|
||||
let pop_last = deque.pop_back().unwrap();
|
||||
print!("\nDequeue rear element = {pop_last}, after dequeue deque = ");
|
||||
print_util::print_queue(&deque);
|
||||
let pop_first = deque.pop_front().unwrap();
|
||||
print!("\nDequeue front element = {pop_first}, after dequeue deque = ");
|
||||
print_util::print_queue(&deque);
|
||||
|
||||
// Get the length of the double-ended queue
|
||||
let size = deque.len();
|
||||
print!("\nDeque length size = {size}");
|
||||
|
||||
// Check if the double-ended queue is empty
|
||||
let is_empty = deque.is_empty();
|
||||
print!("\nIs deque empty = {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;
|
||||
|
||||
/* Doubly linked list node */
|
||||
pub struct ListNode<T> {
|
||||
pub val: T, // Node value
|
||||
pub next: Option<Rc<RefCell<ListNode<T>>>>, // Successor node pointer
|
||||
pub prev: Option<Rc<RefCell<ListNode<T>>>>, // Predecessor node pointer
|
||||
}
|
||||
|
||||
impl<T> ListNode<T> {
|
||||
pub fn new(val: T) -> Rc<RefCell<ListNode<T>>> {
|
||||
Rc::new(RefCell::new(ListNode {
|
||||
val,
|
||||
next: None,
|
||||
prev: None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/* Double-ended queue based on doubly linked list implementation */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListDeque<T> {
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // Head node front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // Tail node rear
|
||||
que_size: usize, // Length of the double-ended queue
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListDeque<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
front: None,
|
||||
rear: None,
|
||||
que_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
pub fn size(&self) -> usize {
|
||||
return self.que_size;
|
||||
}
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
pub fn is_empty(&self) -> bool {
|
||||
return self.que_size == 0;
|
||||
}
|
||||
|
||||
/* Enqueue operation */
|
||||
fn push(&mut self, num: T, is_front: bool) {
|
||||
let node = ListNode::new(num);
|
||||
// Front of the queue enqueue operation
|
||||
if is_front {
|
||||
match self.front.take() {
|
||||
// If the linked list is empty, make both front and rear point to node
|
||||
None => {
|
||||
self.rear = Some(node.clone());
|
||||
self.front = Some(node);
|
||||
}
|
||||
// Add node to the head of the linked list
|
||||
Some(old_front) => {
|
||||
old_front.borrow_mut().prev = Some(node.clone());
|
||||
node.borrow_mut().next = Some(old_front);
|
||||
self.front = Some(node); // Update head node
|
||||
}
|
||||
}
|
||||
}
|
||||
// Rear of the queue enqueue operation
|
||||
else {
|
||||
match self.rear.take() {
|
||||
// If the linked list is empty, make both front and rear point to node
|
||||
None => {
|
||||
self.front = Some(node.clone());
|
||||
self.rear = Some(node);
|
||||
}
|
||||
// Add node to the tail of the linked list
|
||||
Some(old_rear) => {
|
||||
old_rear.borrow_mut().next = Some(node.clone());
|
||||
node.borrow_mut().prev = Some(old_rear);
|
||||
self.rear = Some(node); // Update tail node
|
||||
}
|
||||
}
|
||||
}
|
||||
self.que_size += 1; // Update queue length
|
||||
}
|
||||
|
||||
/* Front of the queue enqueue */
|
||||
pub fn push_first(&mut self, num: T) {
|
||||
self.push(num, true);
|
||||
}
|
||||
|
||||
/* Rear of the queue enqueue */
|
||||
pub fn push_last(&mut self, num: T) {
|
||||
self.push(num, false);
|
||||
}
|
||||
|
||||
/* Dequeue operation */
|
||||
fn pop(&mut self, is_front: bool) -> Option<T> {
|
||||
// If queue is empty, return None directly
|
||||
if self.is_empty() {
|
||||
return None;
|
||||
};
|
||||
// Temporarily store head node value
|
||||
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); // Update head node
|
||||
}
|
||||
None => {
|
||||
self.rear.take();
|
||||
}
|
||||
}
|
||||
self.que_size -= 1; // Update queue length
|
||||
old_front.borrow().val
|
||||
})
|
||||
}
|
||||
// Temporarily store tail node value
|
||||
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); // Update tail node
|
||||
}
|
||||
None => {
|
||||
self.front.take();
|
||||
}
|
||||
}
|
||||
self.que_size -= 1; // Update queue length
|
||||
old_rear.borrow().val
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/* Rear of the queue dequeue */
|
||||
pub fn pop_first(&mut self) -> Option<T> {
|
||||
return self.pop(true);
|
||||
}
|
||||
|
||||
/* Access rear of the queue element */
|
||||
pub fn pop_last(&mut self) -> Option<T> {
|
||||
return self.pop(false);
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
pub fn peek_first(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
|
||||
self.front.as_ref()
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
pub fn peek_last(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
|
||||
self.rear.as_ref()
|
||||
}
|
||||
|
||||
/* Return array for printing */
|
||||
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() {
|
||||
/* Get the length of the double-ended queue */
|
||||
let mut deque = LinkedListDeque::new();
|
||||
deque.push_last(3);
|
||||
deque.push_last(2);
|
||||
deque.push_last(5);
|
||||
print!("Double-ended queue deque = ");
|
||||
print_util::print_array(&deque.to_array(deque.peek_first()));
|
||||
|
||||
/* Update element */
|
||||
let peek_first = deque.peek_first().unwrap().borrow().val;
|
||||
print!("\nFront element peek_first = {}", peek_first);
|
||||
let peek_last = deque.peek_last().unwrap().borrow().val;
|
||||
print!("\nRear element peek_last = {}", peek_last);
|
||||
|
||||
/* Elements enqueue */
|
||||
deque.push_last(4);
|
||||
print!("\nAfter element 4 enqueues at rear, deque = ");
|
||||
print_util::print_array(&deque.to_array(deque.peek_first()));
|
||||
deque.push_first(1);
|
||||
print!("\nAfter element 1 enqueues at front, deque = ");
|
||||
print_util::print_array(&deque.to_array(deque.peek_first()));
|
||||
|
||||
/* Element dequeue */
|
||||
let pop_last = deque.pop_last().unwrap();
|
||||
print!("\nDequeue rear element = {}, after dequeue deque = ", pop_last);
|
||||
print_util::print_array(&deque.to_array(deque.peek_first()));
|
||||
let pop_first = deque.pop_first().unwrap();
|
||||
print!("\nDequeue front element = {}, after dequeue deque = ", pop_first);
|
||||
print_util::print_array(&deque.to_array(deque.peek_first()));
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
let size = deque.size();
|
||||
print!("\nDeque length size = {}", size);
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
let is_empty = deque.is_empty();
|
||||
print!("\nIs deque empty = {}", 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;
|
||||
|
||||
/* Queue based on linked list implementation */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListQueue<T> {
|
||||
front: Option<Rc<RefCell<ListNode<T>>>>, // Head node front
|
||||
rear: Option<Rc<RefCell<ListNode<T>>>>, // Tail node rear
|
||||
que_size: usize, // Queue length
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListQueue<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
front: None,
|
||||
rear: None,
|
||||
que_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the length of the queue */
|
||||
pub fn size(&self) -> usize {
|
||||
return self.que_size;
|
||||
}
|
||||
|
||||
/* Check if the queue is empty */
|
||||
pub fn is_empty(&self) -> bool {
|
||||
return self.que_size == 0;
|
||||
}
|
||||
|
||||
/* Enqueue */
|
||||
pub fn push(&mut self, num: T) {
|
||||
// Add num after the tail node
|
||||
let new_rear = ListNode::new(num);
|
||||
match self.rear.take() {
|
||||
// If the queue is not empty, add the node after the tail node
|
||||
Some(old_rear) => {
|
||||
old_rear.borrow_mut().next = Some(new_rear.clone());
|
||||
self.rear = Some(new_rear);
|
||||
}
|
||||
// If the queue is empty, make both front and rear point to the node
|
||||
None => {
|
||||
self.front = Some(new_rear.clone());
|
||||
self.rear = Some(new_rear);
|
||||
}
|
||||
}
|
||||
self.que_size += 1;
|
||||
}
|
||||
|
||||
/* Dequeue */
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
|
||||
self.front.as_ref()
|
||||
}
|
||||
|
||||
/* Convert linked list to Array and return */
|
||||
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() {
|
||||
/* Access front of the queue element */
|
||||
let mut queue = LinkedListQueue::new();
|
||||
|
||||
/* Elements enqueue */
|
||||
queue.push(1);
|
||||
queue.push(3);
|
||||
queue.push(2);
|
||||
queue.push(5);
|
||||
queue.push(4);
|
||||
print!("Queue queue = ");
|
||||
print_util::print_array(&queue.to_array(queue.peek()));
|
||||
|
||||
/* Return list for printing */
|
||||
let peek = queue.peek().unwrap().borrow().val;
|
||||
print!("\nFront element peek = {}", peek);
|
||||
|
||||
/* Element dequeue */
|
||||
let pop = queue.pop().unwrap();
|
||||
print!("\nDequeue element pop = {}, after dequeue queue = ", pop);
|
||||
print_util::print_array(&queue.to_array(queue.peek()));
|
||||
|
||||
/* Get the length of the queue */
|
||||
let size = queue.size();
|
||||
print!("\nQueue length size = {}", size);
|
||||
|
||||
/* Check if the queue is empty */
|
||||
let is_empty = queue.is_empty();
|
||||
print!("\nIs queue empty = {}", 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;
|
||||
|
||||
/* Stack based on linked list implementation */
|
||||
#[allow(dead_code)]
|
||||
pub struct LinkedListStack<T> {
|
||||
stack_peek: Option<Rc<RefCell<ListNode<T>>>>, // Use head node as stack top
|
||||
stk_size: usize, // Stack length
|
||||
}
|
||||
|
||||
impl<T: Copy> LinkedListStack<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stack_peek: None,
|
||||
stk_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the length of the stack */
|
||||
pub fn size(&self) -> usize {
|
||||
return self.stk_size;
|
||||
}
|
||||
|
||||
/* Check if the stack is empty */
|
||||
pub fn is_empty(&self) -> bool {
|
||||
return self.size() == 0;
|
||||
}
|
||||
|
||||
/* Push */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Pop */
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
pub fn peek(&self) -> Option<&Rc<RefCell<ListNode<T>>>> {
|
||||
self.stack_peek.as_ref()
|
||||
}
|
||||
|
||||
/* Convert List to Array and return */
|
||||
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() {
|
||||
/* Access top of the stack element */
|
||||
let mut stack = LinkedListStack::new();
|
||||
|
||||
/* Elements push onto stack */
|
||||
stack.push(1);
|
||||
stack.push(3);
|
||||
stack.push(2);
|
||||
stack.push(5);
|
||||
stack.push(4);
|
||||
print!("Stack stack = ");
|
||||
print_util::print_array(&stack.to_array());
|
||||
|
||||
/* Return list for printing */
|
||||
let peek = stack.peek().unwrap().borrow().val;
|
||||
print!("\nTop element peek = {}", peek);
|
||||
|
||||
/* Element pop from stack */
|
||||
let pop = stack.pop().unwrap();
|
||||
print!("\nPop element pop = {}, after pop stack = ", pop);
|
||||
print_util::print_array(&stack.to_array());
|
||||
|
||||
/* Get the length of the stack */
|
||||
let size = stack.size();
|
||||
print!("\nStack length size = {}", size);
|
||||
|
||||
/* Check if empty */
|
||||
let is_empty = stack.is_empty();
|
||||
print!("\nIs stack empty = {}", 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() {
|
||||
// Access front of the queue element
|
||||
let mut queue: VecDeque<i32> = VecDeque::new();
|
||||
|
||||
// Elements enqueue
|
||||
queue.push_back(1);
|
||||
queue.push_back(3);
|
||||
queue.push_back(2);
|
||||
queue.push_back(5);
|
||||
queue.push_back(4);
|
||||
print!("Queue queue = ");
|
||||
print_util::print_queue(&queue);
|
||||
|
||||
// Return list for printing
|
||||
let peek = queue.front().unwrap();
|
||||
println!("\nFront element peek = {peek}");
|
||||
|
||||
// Element dequeue
|
||||
let pop = queue.pop_front().unwrap();
|
||||
print!("Dequeue element pop = {pop}, after dequeue queue = ");
|
||||
print_util::print_queue(&queue);
|
||||
|
||||
// Get the length of the queue
|
||||
let size = queue.len();
|
||||
print!("\nQueue length size = {size}");
|
||||
|
||||
// Check if the queue is empty
|
||||
let is_empty = queue.is_empty();
|
||||
print!("\nIs queue empty = {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() {
|
||||
// Access top of the stack element
|
||||
// In Rust, it's recommended to use Vec as a stack
|
||||
let mut stack: Vec<i32> = Vec::new();
|
||||
|
||||
// Elements push onto stack
|
||||
stack.push(1);
|
||||
stack.push(3);
|
||||
stack.push(2);
|
||||
stack.push(5);
|
||||
stack.push(4);
|
||||
print!("Stack stack = ");
|
||||
print_util::print_array(&stack);
|
||||
|
||||
// Return list for printing
|
||||
let peek = stack.last().unwrap();
|
||||
print!("\nTop element peek = {peek}");
|
||||
|
||||
// Element pop from stack
|
||||
let pop = stack.pop().unwrap();
|
||||
print!("\nPop element pop = {pop}, after pop stack = ");
|
||||
print_util::print_array(&stack);
|
||||
|
||||
// Get the length of the stack
|
||||
let size = stack.len();
|
||||
print!("\nStack length size = {size}");
|
||||
|
||||
// Check if the stack is empty
|
||||
let is_empty = stack.is_empty();
|
||||
print!("\nIs stack empty = {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};
|
||||
|
||||
/* Binary tree class represented by array */
|
||||
struct ArrayBinaryTree {
|
||||
tree: Vec<Option<i32>>,
|
||||
}
|
||||
|
||||
impl ArrayBinaryTree {
|
||||
/* Constructor */
|
||||
fn new(arr: Vec<Option<i32>>) -> Self {
|
||||
Self { tree: arr }
|
||||
}
|
||||
|
||||
/* List capacity */
|
||||
fn size(&self) -> i32 {
|
||||
self.tree.len() as i32
|
||||
}
|
||||
|
||||
/* Get value of node at index i */
|
||||
fn val(&self, i: i32) -> Option<i32> {
|
||||
// If index is out of bounds, return None, representing empty position
|
||||
if i < 0 || i >= self.size() {
|
||||
None
|
||||
} else {
|
||||
self.tree[i as usize]
|
||||
}
|
||||
}
|
||||
|
||||
/* Get index of left child node of node at index i */
|
||||
fn left(&self, i: i32) -> i32 {
|
||||
2 * i + 1
|
||||
}
|
||||
|
||||
/* Get index of right child node of node at index i */
|
||||
fn right(&self, i: i32) -> i32 {
|
||||
2 * i + 2
|
||||
}
|
||||
|
||||
/* Get index of parent node of node at index i */
|
||||
fn parent(&self, i: i32) -> i32 {
|
||||
(i - 1) / 2
|
||||
}
|
||||
|
||||
/* Level-order traversal */
|
||||
fn level_order(&self) -> Vec<i32> {
|
||||
self.tree.iter().filter_map(|&x| x).collect()
|
||||
}
|
||||
|
||||
/* Depth-first traversal */
|
||||
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();
|
||||
// Preorder traversal
|
||||
if order == "pre" {
|
||||
res.push(val);
|
||||
}
|
||||
self.dfs(self.left(i), order, res);
|
||||
// Inorder traversal
|
||||
if order == "in" {
|
||||
res.push(val);
|
||||
}
|
||||
self.dfs(self.right(i), order, res);
|
||||
// Postorder traversal
|
||||
if order == "post" {
|
||||
res.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Preorder traversal */
|
||||
fn pre_order(&self) -> Vec<i32> {
|
||||
let mut res = vec![];
|
||||
self.dfs(0, "pre", &mut res);
|
||||
res
|
||||
}
|
||||
|
||||
/* Inorder traversal */
|
||||
fn in_order(&self) -> Vec<i32> {
|
||||
let mut res = vec![];
|
||||
self.dfs(0, "in", &mut res);
|
||||
res
|
||||
}
|
||||
|
||||
/* Postorder traversal */
|
||||
fn post_order(&self) -> Vec<i32> {
|
||||
let mut res = vec![];
|
||||
self.dfs(0, "post", &mut res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
// Initialize binary tree
|
||||
// Here we use a function to generate a binary tree directly from an array
|
||||
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!("\nInitialize binary tree\n");
|
||||
println!("Array representation of binary tree:");
|
||||
println!(
|
||||
"[{}]",
|
||||
arr.iter()
|
||||
.map(|&val| if let Some(val) = val {
|
||||
format!("{val}")
|
||||
} else {
|
||||
"null".to_string()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
);
|
||||
println!("Linked list representation of binary tree:");
|
||||
print_util::print_tree(&root);
|
||||
|
||||
// Binary tree class represented by array
|
||||
let abt = ArrayBinaryTree::new(arr);
|
||||
|
||||
// Access node
|
||||
let i = 1;
|
||||
let l = abt.left(i);
|
||||
let r = abt.right(i);
|
||||
let p = abt.parent(i);
|
||||
println!(
|
||||
"\nCurrent node index is {}, value is {}",
|
||||
i,
|
||||
if let Some(val) = abt.val(i) {
|
||||
format!("{val}")
|
||||
} else {
|
||||
"null".to_string()
|
||||
}
|
||||
);
|
||||
println!(
|
||||
"Left child index is {}, value is {}",
|
||||
l,
|
||||
if let Some(val) = abt.val(l) {
|
||||
format!("{val}")
|
||||
} else {
|
||||
"null".to_string()
|
||||
}
|
||||
);
|
||||
println!(
|
||||
"Right child index is {}, value is {}",
|
||||
r,
|
||||
if let Some(val) = abt.val(r) {
|
||||
format!("{val}")
|
||||
} else {
|
||||
"null".to_string()
|
||||
}
|
||||
);
|
||||
println!(
|
||||
"Parent node index is {}, value is {}",
|
||||
p,
|
||||
if let Some(val) = abt.val(p) {
|
||||
format!("{val}")
|
||||
} else {
|
||||
"null".to_string()
|
||||
}
|
||||
);
|
||||
|
||||
// Traverse tree
|
||||
let mut res = abt.level_order();
|
||||
println!("\nLevel-order traversal is: {:?}", res);
|
||||
res = abt.pre_order();
|
||||
println!("Pre-order traversal is: {:?}", res);
|
||||
res = abt.in_order();
|
||||
println!("In-order traversal is: {:?}", res);
|
||||
res = abt.post_order();
|
||||
println!("Post-order traversal is: {:?}", 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 tree */
|
||||
struct AVLTree {
|
||||
root: OptionTreeNodeRc, // Root node
|
||||
}
|
||||
|
||||
impl AVLTree {
|
||||
/* Constructor */
|
||||
fn new() -> Self {
|
||||
Self { root: None }
|
||||
}
|
||||
|
||||
/* Get node height */
|
||||
fn height(node: OptionTreeNodeRc) -> i32 {
|
||||
// Empty node height is -1, leaf node height is 0
|
||||
match node {
|
||||
Some(node) => node.borrow().height,
|
||||
None => -1,
|
||||
}
|
||||
}
|
||||
|
||||
/* Update node height */
|
||||
fn update_height(node: OptionTreeNodeRc) {
|
||||
if let Some(node) = node {
|
||||
let left = node.borrow().left.clone();
|
||||
let right = node.borrow().right.clone();
|
||||
// Node height equals the height of the tallest subtree + 1
|
||||
node.borrow_mut().height = std::cmp::max(Self::height(left), Self::height(right)) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get balance factor */
|
||||
fn balance_factor(node: OptionTreeNodeRc) -> i32 {
|
||||
match node {
|
||||
// Empty node balance factor is 0
|
||||
None => 0,
|
||||
// Node balance factor = left subtree height - right subtree height
|
||||
Some(node) => {
|
||||
Self::height(node.borrow().left.clone()) - Self::height(node.borrow().right.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Right rotation operation */
|
||||
fn right_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc {
|
||||
match node {
|
||||
Some(node) => {
|
||||
let child = node.borrow().left.clone().unwrap();
|
||||
let grand_child = child.borrow().right.clone();
|
||||
// Using child as pivot, rotate node to the right
|
||||
child.borrow_mut().right = Some(node.clone());
|
||||
node.borrow_mut().left = grand_child;
|
||||
// Update node height
|
||||
Self::update_height(Some(node));
|
||||
Self::update_height(Some(child.clone()));
|
||||
// Return root node of subtree after rotation
|
||||
Some(child)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/* Left rotation operation */
|
||||
fn left_rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc {
|
||||
match node {
|
||||
Some(node) => {
|
||||
let child = node.borrow().right.clone().unwrap();
|
||||
let grand_child = child.borrow().left.clone();
|
||||
// Using child as pivot, rotate node to the left
|
||||
child.borrow_mut().left = Some(node.clone());
|
||||
node.borrow_mut().right = grand_child;
|
||||
// Update node height
|
||||
Self::update_height(Some(node));
|
||||
Self::update_height(Some(child.clone()));
|
||||
// Return root node of subtree after rotation
|
||||
Some(child)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform rotation operation to restore balance to this subtree */
|
||||
fn rotate(node: OptionTreeNodeRc) -> OptionTreeNodeRc {
|
||||
// Get balance factor of node
|
||||
let balance_factor = Self::balance_factor(node.clone());
|
||||
// Left-leaning tree
|
||||
if balance_factor > 1 {
|
||||
let node = node.unwrap();
|
||||
if Self::balance_factor(node.borrow().left.clone()) >= 0 {
|
||||
// Right rotation
|
||||
Self::right_rotate(Some(node))
|
||||
} else {
|
||||
// First left rotation then right rotation
|
||||
let left = node.borrow().left.clone();
|
||||
node.borrow_mut().left = Self::left_rotate(left);
|
||||
Self::right_rotate(Some(node))
|
||||
}
|
||||
}
|
||||
// Right-leaning tree
|
||||
else if balance_factor < -1 {
|
||||
let node = node.unwrap();
|
||||
if Self::balance_factor(node.borrow().right.clone()) <= 0 {
|
||||
// Left rotation
|
||||
Self::left_rotate(Some(node))
|
||||
} else {
|
||||
// First right rotation then left rotation
|
||||
let right = node.borrow().right.clone();
|
||||
node.borrow_mut().right = Self::right_rotate(right);
|
||||
Self::left_rotate(Some(node))
|
||||
}
|
||||
} else {
|
||||
// Balanced tree, no rotation needed, return directly
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert node */
|
||||
fn insert(&mut self, val: i32) {
|
||||
self.root = Self::insert_helper(self.root.clone(), val);
|
||||
}
|
||||
|
||||
/* Recursively insert node (helper method) */
|
||||
fn insert_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc {
|
||||
match node {
|
||||
Some(mut node) => {
|
||||
/* 1. Find insertion position and insert node */
|
||||
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); // Duplicate node not inserted, return directly
|
||||
}
|
||||
}
|
||||
Self::update_height(Some(node.clone())); // Update node height
|
||||
|
||||
/* 2. Perform rotation operation to restore balance to this subtree */
|
||||
node = Self::rotate(Some(node)).unwrap();
|
||||
// Return root node of subtree
|
||||
Some(node)
|
||||
}
|
||||
None => Some(TreeNode::new(val)),
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove node */
|
||||
fn remove(&self, val: i32) {
|
||||
Self::remove_helper(self.root.clone(), val);
|
||||
}
|
||||
|
||||
/* Recursively delete node (helper method) */
|
||||
fn remove_helper(node: OptionTreeNodeRc, val: i32) -> OptionTreeNodeRc {
|
||||
match node {
|
||||
Some(mut node) => {
|
||||
/* 1. Find node and delete */
|
||||
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 {
|
||||
// Number of child nodes = 0, delete node directly and return
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
// Number of child nodes = 1, delete node directly
|
||||
Some(child) => node = child,
|
||||
}
|
||||
} else {
|
||||
// Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it
|
||||
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())); // Update node height
|
||||
|
||||
/* 2. Perform rotation operation to restore balance to this subtree */
|
||||
node = Self::rotate(Some(node)).unwrap();
|
||||
// Return root node of subtree
|
||||
Some(node)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/* Search node */
|
||||
fn search(&self, val: i32) -> OptionTreeNodeRc {
|
||||
let mut cur = self.root.clone();
|
||||
// Loop search, exit after passing leaf node
|
||||
while let Some(current) = cur.clone() {
|
||||
match current.borrow().val.cmp(&val) {
|
||||
// Target node is in cur's right subtree
|
||||
Ordering::Less => {
|
||||
cur = current.borrow().right.clone();
|
||||
}
|
||||
// Target node is in cur's left subtree
|
||||
Ordering::Greater => {
|
||||
cur = current.borrow().left.clone();
|
||||
}
|
||||
// Found target node, exit loop
|
||||
Ordering::Equal => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return target node
|
||||
cur
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
fn test_insert(tree: &mut AVLTree, val: i32) {
|
||||
tree.insert(val);
|
||||
println!("\nAfter inserting node {}, AVL tree is", val);
|
||||
print_util::print_tree(&tree.root.clone().unwrap());
|
||||
}
|
||||
|
||||
fn test_remove(tree: &mut AVLTree, val: i32) {
|
||||
tree.remove(val);
|
||||
println!("\nAfter deleting node {}, AVL tree is", val);
|
||||
print_util::print_tree(&tree.root.clone().unwrap());
|
||||
}
|
||||
|
||||
/* Please pay attention to how the AVL tree maintains balance after inserting nodes */
|
||||
let mut avl_tree = AVLTree::new();
|
||||
|
||||
/* Insert node */
|
||||
// Delete nodes
|
||||
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);
|
||||
|
||||
/* Please pay attention to how the AVL tree maintains balance after deleting nodes */
|
||||
test_insert(&mut avl_tree, 7);
|
||||
|
||||
/* Remove node */
|
||||
// Delete node with degree 1
|
||||
test_remove(&mut avl_tree, 8); // Delete node with degree 2
|
||||
test_remove(&mut avl_tree, 5); // Remove node with degree 1
|
||||
test_remove(&mut avl_tree, 4); // Remove node with degree 2
|
||||
|
||||
/* Search node */
|
||||
let node = avl_tree.search(7);
|
||||
if let Some(node) = node {
|
||||
println!(
|
||||
"\nFound node object is {:?}, node value = {}",
|
||||
&*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>>>;
|
||||
|
||||
/* Binary search tree */
|
||||
pub struct BinarySearchTree {
|
||||
root: OptionTreeNodeRc,
|
||||
}
|
||||
|
||||
impl BinarySearchTree {
|
||||
/* Constructor */
|
||||
pub fn new() -> Self {
|
||||
// Initialize empty tree
|
||||
Self { root: None }
|
||||
}
|
||||
|
||||
/* Get binary tree root node */
|
||||
pub fn get_root(&self) -> OptionTreeNodeRc {
|
||||
self.root.clone()
|
||||
}
|
||||
|
||||
/* Search node */
|
||||
pub fn search(&self, num: i32) -> OptionTreeNodeRc {
|
||||
let mut cur = self.root.clone();
|
||||
// Loop search, exit after passing leaf node
|
||||
while let Some(node) = cur.clone() {
|
||||
match num.cmp(&node.borrow().val) {
|
||||
// Target node is in cur's right subtree
|
||||
Ordering::Greater => cur = node.borrow().right.clone(),
|
||||
// Target node is in cur's left subtree
|
||||
Ordering::Less => cur = node.borrow().left.clone(),
|
||||
// Found target node, exit loop
|
||||
Ordering::Equal => break,
|
||||
}
|
||||
}
|
||||
|
||||
// Return target node
|
||||
cur
|
||||
}
|
||||
|
||||
/* Insert node */
|
||||
pub fn insert(&mut self, num: i32) {
|
||||
// If tree is empty, initialize root node
|
||||
if self.root.is_none() {
|
||||
self.root = Some(TreeNode::new(num));
|
||||
return;
|
||||
}
|
||||
let mut cur = self.root.clone();
|
||||
let mut pre = None;
|
||||
// Loop search, exit after passing leaf node
|
||||
while let Some(node) = cur.clone() {
|
||||
match num.cmp(&node.borrow().val) {
|
||||
// Found duplicate node, return directly
|
||||
Ordering::Equal => return,
|
||||
// Insertion position is in cur's right subtree
|
||||
Ordering::Greater => {
|
||||
pre = cur.clone();
|
||||
cur = node.borrow().right.clone();
|
||||
}
|
||||
// Insertion position is in cur's left subtree
|
||||
Ordering::Less => {
|
||||
pre = cur.clone();
|
||||
cur = node.borrow().left.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Insert node
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove node */
|
||||
pub fn remove(&mut self, num: i32) {
|
||||
// If tree is empty, return directly
|
||||
if self.root.is_none() {
|
||||
return;
|
||||
}
|
||||
let mut cur = self.root.clone();
|
||||
let mut pre = None;
|
||||
// Loop search, exit after passing leaf node
|
||||
while let Some(node) = cur.clone() {
|
||||
match num.cmp(&node.borrow().val) {
|
||||
// Found node to delete, exit loop
|
||||
Ordering::Equal => break,
|
||||
// Node to delete is in cur's right subtree
|
||||
Ordering::Greater => {
|
||||
pre = cur.clone();
|
||||
cur = node.borrow().right.clone();
|
||||
}
|
||||
// Node to delete is in cur's left subtree
|
||||
Ordering::Less => {
|
||||
pre = cur.clone();
|
||||
cur = node.borrow().left.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no node to delete, return directly
|
||||
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()) {
|
||||
// Number of child nodes = 0 or 1
|
||||
(None, None) | (Some(_), None) | (None, Some(_)) => {
|
||||
// When number of child nodes = 0 / 1, child = nullptr / that child node
|
||||
let child = left_child.or(right_child);
|
||||
let pre = pre.unwrap();
|
||||
// Delete node 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 {
|
||||
// If deleted node is root node, reassign root node
|
||||
self.root = child;
|
||||
}
|
||||
}
|
||||
// Number of child nodes = 2
|
||||
(Some(_), Some(_)) => {
|
||||
// Get next node of cur in inorder traversal
|
||||
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;
|
||||
// Recursively delete node tmp
|
||||
self.remove(tmp_val);
|
||||
// Replace cur with tmp
|
||||
cur.borrow_mut().val = tmp_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Initialize binary search tree */
|
||||
let mut bst = BinarySearchTree::new();
|
||||
// Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree
|
||||
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!("\nInitialized binary tree is\n");
|
||||
print_util::print_tree(bst.get_root().as_ref().unwrap());
|
||||
|
||||
/* Search node */
|
||||
let node = bst.search(7);
|
||||
println!(
|
||||
"\nFound node object is {:?}, node value = {}",
|
||||
node.clone().unwrap(),
|
||||
node.clone().unwrap().borrow().val
|
||||
);
|
||||
|
||||
/* Insert node */
|
||||
bst.insert(16);
|
||||
println!("\nAfter inserting node 16, binary tree is\n");
|
||||
print_util::print_tree(bst.get_root().as_ref().unwrap());
|
||||
|
||||
/* Remove node */
|
||||
bst.remove(1);
|
||||
println!("\nAfter removing node 1, binary tree is\n");
|
||||
print_util::print_tree(bst.get_root().as_ref().unwrap());
|
||||
bst.remove(2);
|
||||
println!("\nAfter removing node 2, binary tree is\n");
|
||||
print_util::print_tree(bst.get_root().as_ref().unwrap());
|
||||
bst.remove(4);
|
||||
println!("\nAfter removing node 4, binary tree is\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() {
|
||||
/* Initialize binary tree */
|
||||
// Initialize nodes
|
||||
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);
|
||||
// Build references (pointers) between nodes
|
||||
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!("\nInitialize binary tree\n");
|
||||
print_util::print_tree(&n1);
|
||||
|
||||
// Insert node and delete node
|
||||
let p = TreeNode::new(0);
|
||||
// Delete node
|
||||
p.borrow_mut().left = Some(Rc::clone(&n2));
|
||||
n1.borrow_mut().left = Some(Rc::clone(&p));
|
||||
println!("\nAfter inserting node P\n");
|
||||
print_util::print_tree(&n1);
|
||||
// Remove node P
|
||||
drop(p);
|
||||
n1.borrow_mut().left = Some(Rc::clone(&n2));
|
||||
println!("\nAfter removing node 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};
|
||||
|
||||
/* Level-order traversal */
|
||||
fn level_order(root: &Rc<RefCell<TreeNode>>) -> Vec<i32> {
|
||||
// Initialize queue, add root node
|
||||
let mut que = VecDeque::new();
|
||||
que.push_back(root.clone());
|
||||
// Initialize a list to save the traversal sequence
|
||||
let mut vec = Vec::new();
|
||||
|
||||
while let Some(node) = que.pop_front() {
|
||||
// Dequeue
|
||||
vec.push(node.borrow().val); // Save node value
|
||||
if let Some(left) = node.borrow().left.as_ref() {
|
||||
que.push_back(left.clone()); // Left child node enqueue
|
||||
}
|
||||
if let Some(right) = node.borrow().right.as_ref() {
|
||||
que.push_back(right.clone()); // Right child node enqueue
|
||||
};
|
||||
}
|
||||
vec
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fn main() {
|
||||
/* Initialize binary tree */
|
||||
// Here we use a function to generate a binary tree directly from an array
|
||||
let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]).unwrap();
|
||||
println!("Initialize binary tree\n");
|
||||
print_util::print_tree(&root);
|
||||
|
||||
/* Level-order traversal */
|
||||
let vec = level_order(&root);
|
||||
print!("\nLevel-order traversal node sequence = {:?}", 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;
|
||||
|
||||
/* Preorder traversal */
|
||||
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 {
|
||||
// Visit priority: root node -> left subtree -> right subtree
|
||||
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
|
||||
}
|
||||
|
||||
/* Inorder traversal */
|
||||
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 {
|
||||
// Visit priority: left subtree -> root node -> right subtree
|
||||
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
|
||||
}
|
||||
|
||||
/* Postorder traversal */
|
||||
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 {
|
||||
// Visit priority: left subtree -> right subtree -> root node
|
||||
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() {
|
||||
/* Initialize binary tree */
|
||||
// Here we use a function to generate a binary tree directly from an array
|
||||
let root = vec_to_tree(op_vec![1, 2, 3, 4, 5, 6, 7]);
|
||||
println!("Initialize binary tree\n");
|
||||
print_util::print_tree(root.as_ref().unwrap());
|
||||
|
||||
/* Preorder traversal */
|
||||
let vec = pre_order(root.as_ref());
|
||||
println!("\nPre-order traversal node sequence = {:?}", vec);
|
||||
|
||||
/* Inorder traversal */
|
||||
let vec = in_order(root.as_ref());
|
||||
println!("\nIn-order traversal node sequence = {:?}", vec);
|
||||
|
||||
/* Postorder traversal */
|
||||
let vec = post_order(root.as_ref());
|
||||
print!("\nPost-order traversal node sequence = {:?}", 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 }))
|
||||
}
|
||||
|
||||
/* Deserialize array to linked list */
|
||||
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
|
||||
}
|
||||
|
||||
/* Convert linked list to hash table */
|
||||
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>,
|
||||
}
|
||||
|
||||
/* Print array */
|
||||
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!("]");
|
||||
}
|
||||
}
|
||||
|
||||
/* Print hash table */
|
||||
pub fn print_hash_map<TKey: Display, TValue: Display>(map: &HashMap<TKey, TValue>) {
|
||||
for (key, value) in map {
|
||||
println!("{key} -> {value}");
|
||||
}
|
||||
}
|
||||
|
||||
/* Print queue (deque) */
|
||||
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 {", "} );
|
||||
}
|
||||
}
|
||||
|
||||
/* Print linked list */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print binary tree */
|
||||
pub fn print_tree(root: &Rc<RefCell<TreeNode>>) {
|
||||
_print_tree(Some(root), None, false);
|
||||
}
|
||||
|
||||
/* Print binary tree */
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
/* Print heap */
|
||||
pub fn print_heap(heap: Vec<i32>) {
|
||||
println!("Array representation of heap: {:?}", heap);
|
||||
println!("Heap tree representation:");
|
||||
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;
|
||||
|
||||
/* Binary tree node type */
|
||||
#[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 {
|
||||
/* Constructor */
|
||||
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)),*
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// For the serialization encoding rules, please refer to:
|
||||
// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
|
||||
// Array representation of binary tree:
|
||||
// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
|
||||
// Linked list representation of binary tree:
|
||||
// /——— 15
|
||||
// /——— 7
|
||||
// /——— 3
|
||||
// | \——— 6
|
||||
// | \——— 12
|
||||
// ——— 1
|
||||
// \——— 2
|
||||
// | /——— 9
|
||||
// \——— 4
|
||||
// \——— 8
|
||||
|
||||
/* Deserialize a list into a binary tree: recursion */
|
||||
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)
|
||||
}
|
||||
|
||||
/* Deserialize a list into a binary tree */
|
||||
pub fn vec_to_tree(arr: Vec<Option<i32>>) -> Option<Rc<RefCell<TreeNode>>> {
|
||||
vec_to_tree_dfs(&arr, 0)
|
||||
}
|
||||
|
||||
/* Serialize a binary tree into a list: recursion */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Serialize a binary tree into a list */
|
||||
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)
|
||||
*/
|
||||
|
||||
/* Vertex type */
|
||||
#[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 }
|
||||
}
|
||||
}
|
||||
|
||||
/* Input value list vals, return vertex list vets */
|
||||
pub fn vals_to_vets(vals: Vec<i32>) -> Vec<Vertex> {
|
||||
vals.into_iter().map(|val| val.into()).collect()
|
||||
}
|
||||
|
||||
/* Input vertex list vets, return value list 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