Rust语言实战:LeetCode算法精解
基于Rust语言算法实践
以下是基于Rust语言的LeetCode算法的分类整理和实现示例,涵盖常见数据结构与算法题型。内容以实用性为主,代码遵循Rust最佳实践。
数组与字符串
两数之和 (Two Sum)
use std::collections::HashMap;pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {let mut map = HashMap::new();for (i, &num) in nums.iter().enumerate() {if let Some(&j) = map.get(&(target - num)) {return vec![j as i32, i as i32];}map.insert(num, i);}vec![]
}
最长无重复子串 (Longest Substring Without Repeating Characters)
use std::collections::HashSet;pub fn length_of_longest_substring(s: String) -> i32 {let mut set = HashSet::new();let (mut max, mut left) = (0, 0);let chars: Vec<char> = s.chars().collect();for right in 0..chars.len() {while set.contains(&chars[right]) {set.remove(&chars[left]);left += 1;}set.insert(chars[right]);max = max.max(right - left + 1);}max as i32
}
链表
反转链表 (Reverse Linked List)
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {pub val: i32,pub next: Option<Box<ListNode>>,
}pub fn reverse_list(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {let (mut prev, mut curr) = (None, head);while let Some(mut node) = curr {curr = node.next.take();node.next = prev;prev = Some(node);}prev
}
合并两个有序链表 (Merge Two Sorted Lists)
pub fn merge_two_lists(l1: Option<Box<ListNode>>,l2: Option<Box<ListNode>>,
) -> Option<Box<ListNode>> {match (l1, l2) {(None, None) => None,(Some(node), None) | (None, Some(node)) => Some(node),(Some(mut l1_node), Some(mut l2_node)) => {if l1_node.val < l2_node.val {l1_node.next = merge_two_lists(l1_node.next, Some(l2_node));Some(l1_node)} else {l2_node.next = merge_two_lists(Some(l1_node), l2_node.next);Some(l2_node)}}}
}
树与递归
二叉树的最大深度 (Maximum Depth of Binary Tree)
use std::rc::Rc;
use std::cell::RefCell;#[derive(Debug, PartialEq, Eq)]
pub struct TreeNode {pub val: i32,pub left: Option<Rc<RefCell<TreeNode>>>,pub right: Option<Rc<RefCell<TreeNode>>>,
}pub fn max_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {match root {None => 0,Some(node) => {let node = node.borrow();1 + max_depth(node.left.clone()).max(max_depth(node.right.clone()))}}
}
验证二叉搜索树 (Validate Binary Search Tree)
pub fn is_valid_bst(root: Option<Rc<RefCell<TreeNode>>>) -> bool {fn validate(node: Option<Rc<RefCell<TreeNode>>>,min: Option<i32>,max: Option<i32>,) -> bool {match node {None => true,Some(n) => {let n = n.borrow();if min.map_or(false, |m| n.val <= m) || max.map_or(false, |m| n.val >= m) {return false;}validate(n.left.clone(), min, Some(n.val)) && validate(n.right.clone(), Some(n.val), max)}}}validate(root, None, None)
}
动态规划
爬楼梯
(Climbing Stairs)
pub fn climb_stairs(n: i32) -> i32 {if n <= 2 { return n; }let (mut a, mut b) = (1, 2);for _ in 3..=n {let c = a + b;a = b;b = c;}b
}
最长递增子序列
(Longest Increasing Subsequence)
pub fn length_of_lis(nums: Vec<i32>) -> i32 {let mut dp = vec![1; nums.len()];for i in 1..nums.len() {for j in 0..i {if nums[i] > nums[j] {dp[i] = dp[i].max(dp[j] + 1);}}}*dp.iter().max().unwrap_or(&0)
}
图论
课程表 (Course Schedule) 拓扑排序
pub fn can_finish(num_courses: i32, prerequisites: Vec<Vec<i32>>) -> bool {let mut graph = vec![vec![]; num_courses as usize];let mut in_degree = vec![0; num_courses as usize];for edge in prerequisites {graph[edge[1] as usize].push(edge[0] as usize);in_degree[edge[0] as usize] += 1;}let mut queue: std::collections::VecDeque<_> = in_degree.iter().enumerate().filter(|&(i, &d)| d == 0).map(|(i, _)| i).collect();let mut count = 0;while let Some(node) = queue.pop_front() {count += 1;for &neighbor in &graph[node] {in_degree[neighbor] -= 1;if in_degree[neighbor] == 0 {queue.push_back(neighbor);}}}count == num_courses
}
其他题型
LRU缓存 (LRU Cache)
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;struct LRUCache {capacity: usize,map: HashMap<i32, Rc<RefCell<Node>>>,head: Option<Rc<RefCell<Node>>>,tail: Option<Rc<RefCell<Node>>>,
}struct Node {key: i32,value: i32,prev: Option<Rc<RefCell<Node>>>,next: Option<Rc<RefCell<Node>>>,
}impl LRUCache {fn new(capacity: i32) -> Self {LRUCache {capacity: capacity as usize,map: HashMap::new(),head: None,tail: None,}}fn get(&mut self, key: i32) -> i32 {if let Some(node) = self.map.get(&key) {let value = node.borrow().value;self.remove_node(node.clone());self.push_front(node.clone());value} else {-1}}fn put(&mut self, key: i32, value: i32) {// 实现节点更新和容量管理}
}
以下是基于常见算法分类整理的100个Rust算法实现关键词,涵盖回溯、贪心、分治等类别。每个分类提供典型算法名称和简短描述,可用于进一步代码实现参考:
回溯算法
- 全排列:生成数组所有可能的排列组合
以下是基于Rust的10个全排列算法实现示例,涵盖递归、堆算法(Heap's Algorithm)、迭代等多种方法,并提供代码示例和简要说明。
递归回溯法
fn permute_recursive(nums: &mut Vec<i32>, start: usize, result: &mut Vec<Vec<i32>>) {if start == nums.len() {result.push(nums.clone());return;}for i in start..nums.len() {nums.swap(start, i);permute_recursive(nums, start + 1, result);nums.swap(start, i);}
}
通过交换元素固定位置并递归处理剩余部分,回溯时恢复原数组。
堆算法(Heap's Algorithm)
fn heap_permute(nums: &mut Vec<i32>, size: usize, result: &mut Vec<Vec<i32>>) {if size == 1 {result.push(nums.clone());return;}for i in 0..size {heap_permute(nums, size - 1, result);if size % 2 == 1 {nums.swap(0, size - 1);} else {nums.swap(i, size - 1);}}
}
堆算法通过交替交换元素生成排列,避免额外内存开销。
迭代法(字典序)
fn next_permutation(nums: &mut Vec<i32>) -> bool {let len = nums.len();let mut i = len - 2;while i < len && nums[i] >= nums[i + 1] {if i == 0 { return false; }i -= 1;}let mut j = len - 1;while j > i && nums[j] <= nums[i] { j -= 1; }nums.swap(i, j);nums[i + 1..].reverse();true
}fn permute_iterative(mut nums: Vec<i32>) -> Vec<Vec<i32>> {nums.sort();let mut result = vec![nums.clone()];while next_permutation(&mut nums) {result.push(nums.clone());}result
}
通过字典序生成下一个排列,直到所有排列完成。
使用permutations
库
use itertools::Itertools;fn permute_itertools(nums: Vec<i32>) -> Vec<Vec<i32>> {nums.into_iter().permutations(nums.len()).collect()
}
依赖itertools
库的permutations
方法快速实现。
基于位掩码的递归
fn permute_mask(nums: &Vec<i32>, path: &mut Vec<i32>, used: &mut Vec<bool>, result: &mut Vec<Vec<i32>>) {if path.len() == nums.len() {result.push(path.clone());return;}for i in 0..nums.len() {if !used[i] {used[i] = true;path.push(nums[i]);permute_mask(nums, path, used, result);path.pop();used[i] = false;}}
}
利用used
数组标记已选元素,避免重复选择。
基于std::collections::VecDeque
use std::collections::VecDeque;fn permute_deque(nums: Vec<i32>) -> Vec<Vec<i32>> {let mut queue = VecDeque::new();queue.push_back(vec![]);for num in nums {for _ in 0..queue.len() {let path = queue.pop_front().unwrap();for i in 0..=path.len() {let mut new_path = path.clone();new_path.insert(i, num);queue.push_back(new_path);}}}queue.into_iter().collect()
}
通过队列动态生成所有可能的插入位置。
基于生成器模式
fn permute_generator(nums: Vec<i32>) -> impl Iterator<Item = Vec<i32>> {nums.into_iter().permutations(nums.len())
}
返回迭代器,适合懒加载场景(需itertools
)。
并行化处理(Rayon)
use rayon::prelude::*;fn permute_parallel(nums: Vec<i32>) -> Vec<Vec<i32>> {nums.into_par_iter().permutations(nums.len()).collect()
}
利用rayon
库实现并行计算(需itertools
和rayon
)。
基于BTreeSet
去重
use std::collections::BTreeSet;fn permute_unique(nums: Vec<i32>) -> Vec<Vec<i32>> {let mut result = BTreeSet::new();heap_permute_unique(&mut nums.clone(), nums.len(), &mut result);result.into_iter().collect()
}fn heap_permute_unique(nums: &mut Vec<i32>, size: usize, result: &mut BTreeSet<Vec<i32>>) {if size == 1 {result.insert(nums.clone());return;}for i in 0..size {heap_permute_unique(nums, size - 1, result);if size % 2 == 1 {nums.swap(0, size - 1);} else {nums.swap(i, size - 1);}}
}
适用于含重复元素的输入,使用BTreeSet
自动去重。
基于动态规划
fn permute_dp(nums: Vec<i32>) -> Vec<Vec<i32>> {let mut dp = vec![vec![nums[0]]];for &num in &nums[1..] {dp = dp.into_iter().flat_map(|perm| {(0..=perm.len()).map(move |i| {let mut new_perm = perm.clone();new_perm.insert(i, num);new_perm})}).collect();}dp
}
逐步构建排列,适合小规模数据。
以上示例覆盖了全排列的常见场景,包括去重、并行化和库函数调用。根据需求选择适合的方法即可。
- N皇后问题:在N×N棋盘放置皇后且互不攻击
以下是基于 Rust 语言的 N 皇后问题 10 个实例,包含代码实现和解析。N 皇后问题要求在 N×N 的棋盘上放置 N 个皇后,使得它们互不攻击(即不在同一行、同一列或同一对角线上)。
基础实现(回溯法)
fn solve_n_queens(n: usize) -> Vec<Vec<String>> {let mut solutions = Vec::new();let mut board = vec![vec!['.'; n]; n];backtrack(&mut board, 0, &mut solutions);solutions
}fn backtrack(board: &mut Vec<Vec<char>>, row: usize, solutions: &mut Vec<Vec<String>>) {if row == board.len() {solutions.push(board.iter().map(|r| r.iter().collect()).collect());return;}for col in 0..board.len() {if is_valid(board, row, col) {board[row][col] = 'Q';backtrack(board, row + 1, solutions);board[row][col] = '.';}}
}fn is_valid(board: &Vec<Vec<char>>, row: usize, col: usize) -> bool {for i in 0..row {if board[i][col] == 'Q' {return false;}let left_diag = col as i32 - (row - i) as i32;if left_diag >= 0 && board[i][left_diag as usize] == 'Q' {return false;}let right_diag = col + (row - i);if right_diag < board.len() && board[i][right_diag] == 'Q' {return false;}}true
}
实例 1:4 皇后问题
let solutions = solve_n_queens(4);
assert_eq!(solutions.len(), 2);
实例 2:8 皇后问题
let solutions = solve_n_queens(8);
assert_eq!(solutions.len(), 92);
实例 3:优化版(位运算)
fn solve_n_queens_bitwise(n: usize) -> usize {fn backtrack(row: usize, cols: usize, diags1: usize, diags2: usize, n: usize) -> usize {if row == n { return 1; }let mut count = 0;let mut available = (!(cols | diags1 | diags2)) & ((1 << n) - 1);while available != 0 {let pos = available & (!available + 1);available ^= pos;count += backtrack(row + 1,cols | pos,(diags1 | pos) << 1,(diags2 | pos) >> 1,n,);}count}backtrack(0, 0, 0, 0, n)
}
实例 4:5 皇后问题(位运算版)
let count = solve_n_queens_bitwise(5);
assert_eq!(count, 10);
实例 5:并行求解(Rayon 库)
use rayon::prelude::*;fn solve_parallel(n: usize) -> usize {(0..n).into_par_iter().map(|col| {backtrack(1, 1 << col, 1 << (col + 1), 1 << (col - 1), n)}).sum()
}
实例 6:输出棋盘格式
fn print_solutions(solutions: &Vec<Vec<String>>) {for sol in solutions {for row in sol {println!("{}", row);}println!();}
}
实例 7:6皇后问题
let solutions = solve_n_queens(6);
assert_eq!(solutions.len(), 4);
实例 8:性能测试
use std::time::Instant;
let start = Instant::now();
solve_n_queens(12);
println!("Time: {:?}", start.elapsed());
实例 9:动态大小棋盘
fn dynamic_n_queens(max_n: usize) {for n in 1..=max_n {println!("N={}: {} solutions", n, solve_n_queens_bitwise(n));}
}
实例 10:约束编程(使用 rust-cp
库)
// 需添加依赖:cp_model
fn solve_with_cp(n: usize) -> cp_model::Solution {let mut model = cp_model::CpModel::new();let queens = model.new_int_vars(n, 0, n as i64 - 1);model.add_all_different(&queens);// 添加对角线约束// ...model.solve()
}
每个实例展示了不同的实现方法或应用场景,涵盖基础回溯、位运算优化、并行计算和约束编程等方向。实际使用时需根据需求选择合适的方法,较大 N 值时推荐位运算或并行优化。
数独求解器 Rust 算法实现
数独求解可以通过回溯算法或约束传播(如Dancing Links)实现。以下是基于回溯算法的 Rust 实现示例。
数据结构设计
用一个 9x9 的二维数组表示数独棋盘,空格用 0 表示:
type SudokuBoard = [[u8; 9]; 9];
检查数字有效性
验证在给定位置填入数字是否合法:
fn is_valid(board: &SudokuBoard, row: usize, col: usize, num: u8) -> bool {// 检查行for i in 0..9 {if board[row][i] == num {return false;}}// 检查列for i in 0..9 {if board[i][col] == num {return false;}}// 检查 3x3 宫格let start_row = (row / 3) * 3;let start_col = (col / 3) * 3;for i in 0..3 {for j in 0..3 {if board[start_row + i][start_col + j] == num {return false;}}}true
}
回溯求解
递归尝试填充数字,失败时回溯:
fn solve(board: &mut SudokuBoard) -> bool {for row in 0..9 {for col in 0..9 {if board[row][col] == 0 {for num in 1..=9 {if is_valid(board, row, col, num) {board[row][col] = num;if solve(board) {return true;}board[row][col] = 0; // 回溯}}return false; // 无解}}}true // 全部填完
}
优化策略
为提高效率,可以结合以下优化:
- 最小候选数优先:优先处理可选数字最少的空格
- 预处理:先填充唯一候选数的格子
- 位运算:用位掩码记录行、列、宫格的已用数字
示例优化后的检查逻辑(位运算版):
fn solve_optimized(board: &mut SudokuBoard,rows: &mut [u16; 9],cols: &mut [u16; 9],blocks: &mut [u16; 9],
) -> bool {// 实现类似但使用位掩码记录约束
}
完整调用示例
fn main() {let mut board: SudokuBoard = [[5, 3, 0, 0, 7, 0, 0, 0, 0],[6, 0, 0, 1, 9, 5, 0, 0, 0],[0, 9, 8, 0, 0, 0, 0, 6, 0],[8, 0, 0, 0, 6, 0, 0, 0, 3],[4, 0, 0, 8, 0, 3, 0, 0, 1],[7, 0, 0, 0, 2, 0, 0, 0, 6],[0, 6, 0, 0, 0, 0, 2, 8, 0],[0, 0, 0, 4, 1, 9, 0, 0, 5],[0, 0, 0, 0, 8, 0, 0, 7, 9],];if solve(&mut board) {println!("Solved: {:?}", board);} else {println!("No solution");}
}
该实现平均能在毫秒级解决标准数独问题。对于极端困难 puzzle,可进一步结合Dancing Links算法或并行计算优化。
找出数组中总和为目标的组合(Rust实现)
该问题可以通过回溯算法解决,以下是完整的Rust实现代码及解析:
fn combination_sum(candidates: Vec<i32>, target: i32) -> Vec<Vec<i32>> {let mut result = Vec::new();let mut current = Vec::new();backtrack(&candidates, target, 0, &mut current, &mut result);result
}fn backtrack(candidates: &[i32],remaining: i32,start: usize,current: &mut Vec<i32>,result: &mut Vec<Vec<i32>>,
) {if remaining < 0 {return;}if remaining == 0 {result.push(current.clone());return;}for i in start..candidates.len() {current.push(candidates[i]);backtrack(candidates, remaining - candidates[i], i, current, result);current.pop();}
}
算法解析
回溯算法是解决组合问题的经典方法,其核心思想是通过递归尝试所有可能的组合,并在满足条件时保存结果。
初始化阶段创建一个空的结果列表和当前组合列表。回溯函数处理主要逻辑:
递归终止条件分为两种情况:当剩余目标值小于0时直接返回;当剩余目标值等于0时将当前组合加入结果列表。
遍历阶段从当前索引开始,避免重复组合。每次将候选元素加入当前组合,递归调用回溯函数处理剩余目标值,最后弹出当前元素进行回溯。
使用示例
fn main() {let candidates = vec![2, 3, 6, 7];let target = 7;let result = combination_sum(candidates, target);println!("{:?}", result); // 输出 [[2, 2, 3], [7]]
}
优化建议
对于包含重复元素的数组,应先进行排序并在回溯时跳过重复元素:
fn combination_sum2(mut candidates: Vec<i32>, target: i32) -> Vec<Vec<i32>> {candidates.sort();let mut result = Vec::new();let mut current = Vec::new();backtrack_unique(&candidates, target, 0, &mut current, &mut result);result
}fn backtrack_unique(candidates: &[i32],remaining: i32,start: usize,current: &mut Vec<i32>,result: &mut Vec<Vec<i32>>,
) {if remaining < 0 {return;}if remaining == 0 {result.push(current.clone());return;}for i in start..candidates.len() {if i > start && candidates[i] == candidates[i - 1] {continue;}current.push(candidates[i]);backtrack_unique(candidates, remaining - candidates[i], i + 1, current, result);current.pop();}
}
生成数组所有可能的子集
使用递归生成子集
递归方法可以直观地生成所有子集。通过遍历数组元素,每次递归调用决定是否包含当前元素。
fn subsets(nums: &[i32]) -> Vec<Vec<i32>> {let mut result = Vec::new();generate_subsets(nums, 0, &mut vec![], &mut result);result
}fn generate_subsets(nums: &[i32], index: usize, current: &mut Vec<i32>, result: &mut Vec<Vec<i32>>) {if index == nums.len() {result.push(current.clone());return;}generate_subsets(nums, index + 1, current, result);current.push(nums[index]);generate_subsets(nums, index + 1, current, result);current.pop();
}
使用位运算生成子集
对于小型数组,位运算是一种高效的方法。每个子集对应一个二进制数,其中每一位表示是否包含对应元素。
fn subsets_bitwise(nums: &[i32]) -> Vec<Vec<i32>> {let n = nums.len();let mut result = Vec::with_capacity(1 << n);for mask in 0..(1 << n) {let mut subset = Vec::new();for i in 0..n {if mask & (1 << i) != 0 {subset.push(nums[i]);}}result.push(subset);}result
}
使用迭代方法生成子集
迭代方法逐步构建子集,每次将新元素添加到所有现有子集中。
fn subsets_iterative(nums: &[i32]) -> Vec<Vec<i32>> {let mut result = vec![vec![]];for &num in nums {let mut new_subsets = Vec::new();for subset in &result {let mut new_subset = subset.clone();new_subset.push(num);new_subsets.push(new_subset);}result.extend(new_subsets);}result
}
生成空集的子集
空集的子集仅包含空集本身。
let nums: Vec<i32> = vec![];
let result = subsets(&nums);
assert_eq!(result, vec![vec![]]);
生成单元素数组的子集
单元素数组的子集包含空集和该元素本身。
let nums = vec![1];
let result = subsets(&nums);
assert_eq!(result, vec![vec![], vec![1]]);
生成双元素数组的子集
双元素数组的子集包含空集、两个单元素子集和全集。
let nums = vec![1, 2];
let result = subsets(&nums);
assert_eq!(result, vec![vec![], vec![2], vec![1], vec![1, 2]]);
生成三元素数组的子集
三元素数组的子集包含所有可能的组合,共8个子集。
let nums = vec![1, 2, 3];
let result = subsets(&nums);
assert_eq!(result.len(), 8);
处理重复元素
如果数组包含重复元素,需要额外步骤去重。以下代码先排序再跳过重复元素。
fn subsets_with_dup(nums: &mut [i32]) -> Vec<Vec<i32>> {nums.sort();let mut result = Vec::new();generate_subsets_dup(nums, 0, &mut vec![], &mut result);result
}fn generate_subsets_dup(nums: &[i32], index: usize, current: &mut Vec<i32>, result: &mut Vec<Vec<i32>>) {result.push(current.clone());for i in index..nums.len() {if i > index && nums[i] == nums[i - 1] {continue;}current.push(nums[i]);generate_subsets_dup(nums, i + 1, current, result);current.pop();}
}
生成大型数组的子集
对于大型数组(如超过20个元素),递归方法可能导致栈溢出。迭代方法更适合此类场景。
let nums: Vec<i32> = (0..20).collect();
let result = subsets_iterative(&nums);
assert_eq!(result.len(), 1 << 20);
性能比较测试
比较三种方法在小型数组上的性能差异。
use std::time::Instant;let nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];let start = Instant::now();
let _ = subsets(&nums);
println!("Recursive: {:?}", start.elapsed());let start = Instant::now();
let _ = subsets_bitwise(&nums);
println!("Bitwise: {:?}", start.elapsed());let start = Instant::now();
let _ = subsets_iterative(&nums);
println!("Iterative: {:?}", start.elapsed());
在二维矩阵中查找单词路径
深度优先搜索(DFS)回溯法
使用DFS探索矩阵中所有可能的路径,检查是否能匹配目标单词。从每个单元格出发,向四个方向(上下左右)递归搜索,注意避免重复访问。
def exist(board, word):def dfs(i, j, k):if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k]:return Falseif k == len(word) - 1:return Truetmp, board[i][j] = board[i][j], '/'res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1)board[i][j] = tmpreturn resfor i in range(len(board)):for j in range(len(board[0])):if dfs(i, j, 0):return Truereturn False
预处理优化
针对大规模矩阵,可先统计矩阵和单词的字符频率,若单词中存在矩阵没有的字符,直接返回False。
from collections import defaultdictdef exist(board, word):freq = defaultdict(int)for row in board:for c in row:freq[c] += 1for c in word:if freq[c] == 0:return Falsefreq[c] -= 1return _original_exist(board, word) # 调用原始DFS函数
Trie树加速搜索
当需要同时搜索多个单词时,构建Trie树可减少重复路径搜索。将目标单词插入Trie,在DFS过程中同步遍历Trie节点。
class TrieNode:def __init__(self):self.children = {}self.is_word = Falsedef findWords(board, words):trie = TrieNode()for word in words:node = triefor c in word:node = node.children.setdefault(c, TrieNode())node.is_word = Trueresult = []def dfs(i, j, node, path):if node.is_word:result.append(path)node.is_word = False # 避免重复添加if 0 <= i < len(board) and 0 <= j < len(board[0]) and board[i][j] in node.children:c = board[i][j]board[i][j] = '#'for x, y in [(i+1,j), (i-1,j), (i,j+1), (i,j-1)]:dfs(x, y, node.children[c], path + c)board[i][j] = cfor i in range(len(board)):for j in range(len(board[0])):dfs(i, j, trie, "")return result
方向剪枝策略
在DFS过程中优先搜索更可能的方向。例如,若当前字符在单词末尾,优先向较少的分支方向搜索。
def exist(board, word):directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]def dfs(i, j, k):if board[i][j] != word[k]:return Falseif k == len(word) - 1:return Trueboard[i][j] = '#'for di, dj in directions:x, y = i + di, j + djif 0 <= x < len(board) and 0 <= y < len(board[0]):if dfs(x, y, k + 1):board[i][j] = word[k]return Trueboard[i][j] = word[k]return Falsefor i in range(len(board)):for j in range(len(board[0])):if dfs(i, j, 0):return Truereturn False
生成所有有效的括号组合
回溯法生成有效括号组合
使用回溯算法可以系统地生成所有有效的括号组合。回溯法的核心思想是通过递归尝试所有可能的组合,并在生成过程中剪枝无效的路径。
def generateParenthesis(n):def backtrack(current, open_count, close_count, result):if len(current) == 2 * n:result.append(current)returnif open_count < n:backtrack(current + '(', open_count + 1, close_count, result)if close_count < open_count:backtrack(current + ')', open_count, close_count + 1, result)result = []backtrack("", 0, 0, result)return result
动态规划方法
动态规划可以通过构建子问题的解来生成有效括号组合。将问题分解为更小的子问题,逐步构建最终结果。
def generateParenthesis(n):dp = [[] for _ in range(n + 1)]dp[0] = [""]for i in range(1, n + 1):for j in range(i):for left in dp[j]:for right in dp[i - 1 - j]:dp[i].append(f"({left}){right}")return dp[n]
迭代法生成括号组合
迭代法通过模拟括号的插入过程来生成所有有效组合。这种方法利用队列或栈来管理生成过程中的中间状态。
from collections import dequedef generateParenthesis(n):queue = deque()queue.append(("", 0, 0))result = []while queue:current, open_count, close_count = queue.popleft()if len(current) == 2 * n:result.append(current)continueif open_count < n:queue.append((current + '(', open_count + 1, close_count))if close_count < open_count:queue.append((current + ')', open_count, close_count + 1))return result
数学公式计算组合数量
有效的括号组合数量可以用卡特兰数(Catalan number)计算。卡特兰数的公式为:
[ C_n = \frac{1}{n+1} \binom{2n}{n} ]
其中 ( \binom{2n}{n} ) 是组合数,表示从 ( 2n ) 个位置中选择 ( n ) 个放置左括号的方式。