diff --git a/DIRECTORY.md b/DIRECTORY.md index 1b8c4b0c316..cb458fd5995 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -87,6 +87,7 @@ * [Union Find](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/union_find.rs) * [Veb Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/veb_tree.rs) * Dynamic Programming + * [Catalan Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/catalan_numbers.rs) * [Coin Change](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/coin_change.rs) * [Egg Dropping](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/egg_dropping.rs) * [Fibonacci](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/fibonacci.rs) diff --git a/src/dynamic_programming/catalan_numbers.rs b/src/dynamic_programming/catalan_numbers.rs new file mode 100644 index 00000000000..28bb040b9c5 --- /dev/null +++ b/src/dynamic_programming/catalan_numbers.rs @@ -0,0 +1,111 @@ +//! Catalan Numbers using Dynamic Programming +//! +//! The Catalan numbers are a sequence of positive integers that appear in many +//! counting problems in combinatorics. Such problems include counting: +//! - The number of Dyck words of length 2n +//! - The number of well-formed expressions with n pairs of parentheses +//! (e.g., `()()` is valid but `())(` is not) +//! - The number of different ways n + 1 factors can be completely parenthesized +//! (e.g., for n = 2, C(n) = 2 and (ab)c and a(bc) are the two valid ways) +//! - The number of full binary trees with n + 1 leaves +//! +//! A Catalan number satisfies the following recurrence relation: +//! - C(0) = C(1) = 1 +//! - C(n) = sum(C(i) * C(n-i-1)), from i = 0 to n-1 +//! +//! Sources: +//! - [Brilliant.org](https://brilliant.org/wiki/catalan-numbers/) +//! - [Wikipedia](https://en.wikipedia.org/wiki/Catalan_number) + +/// Computes the Catalan number sequence from 0 through `upper_limit`. +/// +/// # Arguments +/// +/// * `upper_limit` - The upper limit for the Catalan sequence (must be ≥ 0) +/// +/// # Returns +/// +/// A vector containing Catalan numbers from C(0) to C(upper_limit) +/// +/// # Complexity +/// +/// * Time complexity: O(n²) +/// * Space complexity: O(n) +/// +/// where `n` is the `upper_limit`. +/// +/// # Examples +/// +/// ``` +/// use the_algorithms_rust::dynamic_programming::catalan_numbers; +/// +/// assert_eq!(catalan_numbers(5), vec![1, 1, 2, 5, 14, 42]); +/// assert_eq!(catalan_numbers(2), vec![1, 1, 2]); +/// assert_eq!(catalan_numbers(0), vec![1]); +/// ``` +/// +/// # Warning +/// +/// This will overflow the 64-bit unsigned integer for large values of `upper_limit`. +/// For example, C(21) and beyond will cause overflow. +pub fn catalan_numbers(upper_limit: usize) -> Vec { + let mut catalan_list = vec![0u64; upper_limit + 1]; + + // Base case: C(0) = 1 + catalan_list[0] = 1; + + // Base case: C(1) = 1 + if upper_limit > 0 { + catalan_list[1] = 1; + } + + // Recurrence relation: C(i) = sum(C(j) * C(i-j-1)), from j = 0 to i-1 + for i in 2..=upper_limit { + for j in 0..i { + catalan_list[i] += catalan_list[j] * catalan_list[i - j - 1]; + } + } + + catalan_list +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_catalan_numbers_basic() { + assert_eq!(catalan_numbers(5), vec![1, 1, 2, 5, 14, 42]); + assert_eq!(catalan_numbers(2), vec![1, 1, 2]); + assert_eq!(catalan_numbers(0), vec![1]); + } + + #[test] + fn test_catalan_numbers_single() { + assert_eq!(catalan_numbers(1), vec![1, 1]); + } + + #[test] + fn test_catalan_numbers_extended() { + let result = catalan_numbers(10); + assert_eq!(result.len(), 11); + assert_eq!(result[0], 1); + assert_eq!(result[1], 1); + assert_eq!(result[2], 2); + assert_eq!(result[3], 5); + assert_eq!(result[4], 14); + assert_eq!(result[5], 42); + assert_eq!(result[6], 132); + assert_eq!(result[7], 429); + assert_eq!(result[8], 1430); + assert_eq!(result[9], 4862); + assert_eq!(result[10], 16796); + } + + #[test] + fn test_catalan_first_few() { + // Verify the first few Catalan numbers match known values + assert_eq!(catalan_numbers(3), vec![1, 1, 2, 5]); + assert_eq!(catalan_numbers(4), vec![1, 1, 2, 5, 14]); + } +} diff --git a/src/dynamic_programming/mod.rs b/src/dynamic_programming/mod.rs index b3dc169201a..f3d14a2c33d 100644 --- a/src/dynamic_programming/mod.rs +++ b/src/dynamic_programming/mod.rs @@ -1,3 +1,4 @@ +mod catalan_numbers; mod coin_change; mod egg_dropping; mod fibonacci; @@ -21,6 +22,7 @@ mod task_assignment; mod trapped_rainwater; mod word_break; +pub use self::catalan_numbers::catalan_numbers; pub use self::coin_change::coin_change; pub use self::egg_dropping::egg_drop; pub use self::fibonacci::binary_lifting_fibonacci;