Skip to content

Commit

Permalink
Merge branch 'master' into ref/ds/heap
Browse files Browse the repository at this point in the history
  • Loading branch information
sozelfist committed Sep 10, 2024
2 parents 703ab21 + 77da9fe commit 97edb42
Show file tree
Hide file tree
Showing 8 changed files with 570 additions and 211 deletions.
149 changes: 105 additions & 44 deletions src/backtracking/all_combination_of_size_k.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,123 @@
/*
In this problem, we want to determine all possible combinations of k
numbers out of 1 ... n. We use backtracking to solve this problem.
Time complexity: O(C(n,k)) which is O(n choose k) = O((n!/(k! * (n - k)!)))
generate_all_combinations(n=4, k=2) => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
*/
pub fn generate_all_combinations(n: i32, k: i32) -> Vec<Vec<i32>> {
let mut result = vec![];
create_all_state(1, n, k, &mut vec![], &mut result);

result
//! This module provides a function to generate all possible combinations
//! of `k` numbers out of `0...n-1` using a backtracking algorithm.

/// Custom error type for combination generation.
#[derive(Debug, PartialEq)]
pub enum CombinationError {
KGreaterThanN,
InvalidZeroRange,
}

/// Generates all possible combinations of `k` numbers out of `0...n-1`.
///
/// # Arguments
///
/// * `n` - The upper limit of the range (`0` to `n-1`).
/// * `k` - The number of elements in each combination.
///
/// # Returns
///
/// A `Result` containing a vector with all possible combinations of `k` numbers out of `0...n-1`,
/// or a `CombinationError` if the input is invalid.
pub fn generate_all_combinations(n: usize, k: usize) -> Result<Vec<Vec<usize>>, CombinationError> {
if n == 0 && k > 0 {
return Err(CombinationError::InvalidZeroRange);
}

if k > n {
return Err(CombinationError::KGreaterThanN);
}

let mut combinations = vec![];
let mut current = vec![0; k];
backtrack(0, n, k, 0, &mut current, &mut combinations);
Ok(combinations)
}

fn create_all_state(
increment: i32,
total_number: i32,
level: i32,
current_list: &mut Vec<i32>,
total_list: &mut Vec<Vec<i32>>,
/// Helper function to generate combinations recursively.
///
/// # Arguments
///
/// * `start` - The current number to start the combination with.
/// * `n` - The upper limit of the range (`0` to `n-1`).
/// * `k` - The number of elements left to complete the combination.
/// * `index` - The current index being filled in the combination.
/// * `current` - A mutable reference to the current combination being constructed.
/// * `combinations` - A mutable reference to the vector holding all combinations.
fn backtrack(
start: usize,
n: usize,
k: usize,
index: usize,
current: &mut Vec<usize>,
combinations: &mut Vec<Vec<usize>>,
) {
if level == 0 {
total_list.push(current_list.clone());
if index == k {
combinations.push(current.clone());
return;
}

for i in increment..(total_number - level + 2) {
current_list.push(i);
create_all_state(i + 1, total_number, level - 1, current_list, total_list);
current_list.pop();
for num in start..=(n - k + index) {
current[index] = num;
backtrack(num + 1, n, k, index + 1, current, combinations);
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_output() {
let expected_res = vec![
macro_rules! combination_tests {
($($name:ident: $test_case:expr,)*) => {
$(
#[test]
fn $name() {
let (n, k, expected) = $test_case;
assert_eq!(generate_all_combinations(n, k), expected);
}
)*
}
}

combination_tests! {
test_generate_4_2: (4, 2, Ok(vec![
vec![0, 1],
vec![0, 2],
vec![0, 3],
vec![1, 2],
vec![1, 3],
vec![1, 4],
vec![2, 3],
vec![2, 4],
vec![3, 4],
];

let res = generate_all_combinations(4, 2);

assert_eq!(expected_res, res);
}

#[test]
fn test_empty() {
let expected_res: Vec<Vec<i32>> = vec![vec![]];

let res = generate_all_combinations(0, 0);

assert_eq!(expected_res, res);
])),
test_generate_4_3: (4, 3, Ok(vec![
vec![0, 1, 2],
vec![0, 1, 3],
vec![0, 2, 3],
vec![1, 2, 3],
])),
test_generate_5_3: (5, 3, Ok(vec![
vec![0, 1, 2],
vec![0, 1, 3],
vec![0, 1, 4],
vec![0, 2, 3],
vec![0, 2, 4],
vec![0, 3, 4],
vec![1, 2, 3],
vec![1, 2, 4],
vec![1, 3, 4],
vec![2, 3, 4],
])),
test_generate_5_1: (5, 1, Ok(vec![
vec![0],
vec![1],
vec![2],
vec![3],
vec![4],
])),
test_empty: (0, 0, Ok(vec![vec![]])),
test_generate_n_eq_k: (3, 3, Ok(vec![
vec![0, 1, 2],
])),
test_generate_k_greater_than_n: (3, 4, Err(CombinationError::KGreaterThanN)),
test_zero_range_with_nonzero_k: (0, 1, Err(CombinationError::InvalidZeroRange)),
}
}
162 changes: 120 additions & 42 deletions src/backtracking/permutations.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,141 @@
/*
The permutations problem involves finding all possible permutations
of a given collection of distinct integers. For instance, given [1, 2, 3],
the goal is to generate permutations like
[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], and [3, 2, 1].
This implementation uses a backtracking algorithm to generate all possible permutations.
*/

pub fn permute(nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut result = Vec::new(); // Vector to store the resulting permutations
let mut current_permutation = Vec::new(); // Vector to store the current permutation being constructed
let mut used = vec![false; nums.len()]; // A boolean array to keep track of used elements

backtrack(&nums, &mut current_permutation, &mut used, &mut result); // Call the backtracking function

result // Return the list of permutations
//! This module provides a function to generate all possible distinct permutations
//! of a given collection of integers using a backtracking algorithm.

/// Generates all possible distinct permutations of a given vector of integers.
///
/// # Arguments
///
/// * `nums` - A vector of integers. The input vector is sorted before generating
/// permutations to handle duplicates effectively.
///
/// # Returns
///
/// A vector containing all possible distinct permutations of the input vector.
pub fn permute(mut nums: Vec<isize>) -> Vec<Vec<isize>> {
let mut permutations = Vec::new();
let mut current = Vec::new();
let mut used = vec![false; nums.len()];

nums.sort();
generate(&nums, &mut current, &mut used, &mut permutations);

permutations
}

fn backtrack(
nums: &Vec<i32>,
current_permutation: &mut Vec<i32>,
/// Helper function for the `permute` function to generate distinct permutations recursively.
///
/// # Arguments
///
/// * `nums` - A reference to the sorted slice of integers.
/// * `current` - A mutable reference to the vector holding the current permutation.
/// * `used` - A mutable reference to a vector tracking which elements are used.
/// * `permutations` - A mutable reference to the vector holding all generated distinct permutations.
fn generate(
nums: &[isize],
current: &mut Vec<isize>,
used: &mut Vec<bool>,
result: &mut Vec<Vec<i32>>,
permutations: &mut Vec<Vec<isize>>,
) {
if current_permutation.len() == nums.len() {
// If the current permutation is of the same length as the input,
// it is a complete permutation, so add it to the result.
result.push(current_permutation.clone());
if current.len() == nums.len() {
permutations.push(current.clone());
return;
}

for i in 0..nums.len() {
if used[i] {
continue; // Skip used elements
for idx in 0..nums.len() {
if used[idx] {
continue;
}

if idx > 0 && nums[idx] == nums[idx - 1] && !used[idx - 1] {
continue;
}

current_permutation.push(nums[i]); // Add the current element to the permutation
used[i] = true; // Mark the element as used
current.push(nums[idx]);
used[idx] = true;

backtrack(nums, current_permutation, used, result); // Recursively generate the next permutation
generate(nums, current, used, permutations);

current_permutation.pop(); // Backtrack by removing the last element
used[i] = false; // Mark the element as unused for the next iteration
current.pop();
used[idx] = false;
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_permute() {
// Test case: Generate permutations for [1, 2, 3]
let permutations = permute(vec![1, 2, 3]);

assert_eq!(permutations.len(), 6); // There should be 6 permutations
macro_rules! permute_tests {
($($name:ident: $test_case:expr,)*) => {
$(
#[test]
fn $name() {
let (input, expected) = $test_case;
assert_eq!(permute(input), expected);
}
)*
}
}

// Verification for some of the permutations
assert!(permutations.contains(&vec![1, 2, 3]));
assert!(permutations.contains(&vec![1, 3, 2]));
assert!(permutations.contains(&vec![2, 1, 3]));
permute_tests! {
test_permute_basic: (vec![1, 2, 3], vec![
vec![1, 2, 3],
vec![1, 3, 2],
vec![2, 1, 3],
vec![2, 3, 1],
vec![3, 1, 2],
vec![3, 2, 1],
]),
test_permute_empty: (Vec::<isize>::new(), vec![vec![]]),
test_permute_single: (vec![1], vec![vec![1]]),
test_permute_duplicates: (vec![1, 1, 2], vec![
vec![1, 1, 2],
vec![1, 2, 1],
vec![2, 1, 1],
]),
test_permute_all_duplicates: (vec![1, 1, 1, 1], vec![
vec![1, 1, 1, 1],
]),
test_permute_negative: (vec![-1, -2, -3], vec![
vec![-3, -2, -1],
vec![-3, -1, -2],
vec![-2, -3, -1],
vec![-2, -1, -3],
vec![-1, -3, -2],
vec![-1, -2, -3],
]),
test_permute_mixed: (vec![-1, 0, 1], vec![
vec![-1, 0, 1],
vec![-1, 1, 0],
vec![0, -1, 1],
vec![0, 1, -1],
vec![1, -1, 0],
vec![1, 0, -1],
]),
test_permute_larger: (vec![1, 2, 3, 4], vec![
vec![1, 2, 3, 4],
vec![1, 2, 4, 3],
vec![1, 3, 2, 4],
vec![1, 3, 4, 2],
vec![1, 4, 2, 3],
vec![1, 4, 3, 2],
vec![2, 1, 3, 4],
vec![2, 1, 4, 3],
vec![2, 3, 1, 4],
vec![2, 3, 4, 1],
vec![2, 4, 1, 3],
vec![2, 4, 3, 1],
vec![3, 1, 2, 4],
vec![3, 1, 4, 2],
vec![3, 2, 1, 4],
vec![3, 2, 4, 1],
vec![3, 4, 1, 2],
vec![3, 4, 2, 1],
vec![4, 1, 2, 3],
vec![4, 1, 3, 2],
vec![4, 2, 1, 3],
vec![4, 2, 3, 1],
vec![4, 3, 1, 2],
vec![4, 3, 2, 1],
]),
}
}
Loading

0 comments on commit 97edb42

Please sign in to comment.