diff --git a/src/array/no_class.rs b/src/array/no_class.rs index 6ad28f6..e9caf78 100644 --- a/src/array/no_class.rs +++ b/src/array/no_class.rs @@ -206,11 +206,50 @@ pub fn solve_sudoku(_board: &mut Vec>) { todo!() } +/// [1793. 好子数组的最大分数](https://leetcode.cn/problems/maximum-score-of-a-good-subarray/?envType=daily-question&envId=2024-03-19) +/// +/// 思路: +/// 1. 在不改变最小值的前提下, 延展最多的区间 +/// 2. 然后对比这些区间 +pub fn maximum_score(nums: Vec, k: i32) -> i32 { + unimplemented!("暂时不知道怎么解") +} + #[cfg(test)] mod tests { use super::*; use crate::vec2; + #[test] + fn test_maximum_score(){ + struct Testcase{ + nums: Vec, + k: i32, + expect: i32, + } + vec![ + Testcase{ + nums: vec![1, 4, 3, 7, 4, 5], + k: 3, + expect: 15, + }, + Testcase{ + nums: vec![5, 5, 4, 5, 4, 1, 1, 1], + k: 0, + expect: 20, + }, + Testcase{ + nums: vec![8182,1273,9847,6230,52,1467,6062,726,4852,4507,2460,2041,500,1025,5524], + k: 8, + expect: 9014 + } + ].into_iter().enumerate().for_each(|(idx, testcase)|{ + let Testcase{nums, k, expect} = testcase; + let actual = maximum_score(nums, k); + assert_eq!(expect, actual, "case {} failed", idx); + }); + } + #[test] #[ignore = "暂时不知道怎么解"] fn test_solve_sudoku() { diff --git a/src/array/ser/binary_search.rs b/src/array/ser/binary_search.rs index d6dce18..8861c26 100644 --- a/src/array/ser/binary_search.rs +++ b/src/array/ser/binary_search.rs @@ -563,7 +563,7 @@ where /// 要求序列满足 `[from, x]` cmp 返回 true, `[x+1, end]` 返回 false /// 即一开始必须时 true /// -/// 返回的 `I` 不保证 `cmp(I) == true` +/// 返回的 `I` 不保证 `cmp(I) == false` /// 这个函数保证的是 `(I, end]` cmp不会返回true, `(I, end]` 可能是空 /// /// 做了防溢出, `from`和`end`可以是0, 即传入索引 @@ -623,10 +623,100 @@ pub fn min_capability(nums: Vec, k: i32) -> i32 { min } + +/// [2529. 正整数和负整数的最大计数](https://leetcode.cn/problems/maximum-count-of-positive-integer-and-negative-integer) +/// +/// 注意: 0 既不是正整数也不是负整数 +/// +/// 思路: 利用单调性, 找到第一个正整数和最后一个负整数, 然后计算各自的长度 +pub fn maximum_count(nums: Vec) -> i32 { + let first_positive = { + let tmp = first_occur(0, nums.len()-1, |i| nums[i] > 0); + if tmp == 0{ + if nums[0] > 0{ + 0i32 + } else { + 1i32 + } + } else if tmp == nums.len(){ + nums.len() as i32 + } else { + tmp as i32 + } + + }; + let last_negative = { + let tmp = last_occur(0, nums.len()-1, |i| nums[i] < 0); + if tmp == 0 { + if nums[0] < 0{ + 1i32 + } else { + 0i32 + } + } else if tmp == nums.len(){ + nums.len() as i32 + } else { + tmp as i32 + 1 + } + }; + + let pos_len = nums.len() as i32 - first_positive; + let neg_len = last_negative; + + pos_len.max(neg_len) +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn test_maximum_count(){ + struct TestCase{ + name: &'static str, + nums: Vec, + expect: i32, + } + + vec![ + TestCase{ + name: "basic", + nums: vec![-5, -4, -3, -2, -1, 1, 2, 3, 4, 5], + expect: 5, + }, + TestCase{ + name: "basic 2", + nums: vec![-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5], + expect: 5, + }, + TestCase{ + name: "basic 3", + nums: vec![1, 2, 3, 4, 5], + expect: 5, + }, + TestCase{ + name: "basic 4", + nums: vec![-5, -4, -3, -2, -1], + expect: 5, + }, + TestCase{ + name: "basic 5", + nums: vec![0, 1, 2, 3, 4, 5], + expect: 5, + }, + TestCase{ + name: "basic 6", + nums: vec![-5, -4, -3, -2, -1, 0], + expect: 5, + }, + ] + .iter() + .for_each(|TestCase{name, nums, expect}|{ + let actual = maximum_count(nums.to_vec()); + assert_eq!(*expect, actual, "{} failed", name); + }); + } + #[test] fn test_min_capability() { struct TestCase { diff --git a/src/dp/no_class.rs b/src/dp/no_class.rs index 0415342..672d824 100644 --- a/src/dp/no_class.rs +++ b/src/dp/no_class.rs @@ -338,13 +338,16 @@ pub fn num_factored_binary_trees(mut arr: Vec) -> i32 { result as i32 } - - #[cfg(test)] mod test { use super::*; use crate::vec2; + #[test] + fn test_combination_sum4(){ + + } + #[test] fn test_num_factored_binary_trees() { struct TestCase { diff --git a/src/dp/ser.rs b/src/dp/ser.rs index 9a643cd..2924b63 100644 --- a/src/dp/ser.rs +++ b/src/dp/ser.rs @@ -10,4 +10,5 @@ pub mod stair; pub mod stock; pub mod tree; pub mod path; -pub mod rob; \ No newline at end of file +pub mod rob; +pub mod combination; \ No newline at end of file diff --git a/src/dp/ser/combination.rs b/src/dp/ser/combination.rs new file mode 100644 index 0000000..7fdc9f3 --- /dev/null +++ b/src/dp/ser/combination.rs @@ -0,0 +1,66 @@ +//! 组合问题 + +/// [377. 组合总和 Ⅳ](https://leetcode.cn/problems/combination-sum-iv) +/// +/// 思路: +/// 整体问题可以变成f(n) = f(n-nums[0]) + f(n-nums[1]) + ... + f(n-nums[i]) +/// 但是这种朴素的递归操作, 会因为重复计算, 使整体的计算规模扩大 +/// +/// 可以和 [70. 爬楼梯](https://leetcode-cn.com/problems/climbing-stairs/) 一样, 用dp来解决 +/// +/// 起点一定是0, 从0开始, 递推到target +pub fn combination_sum4(nums: Vec, target: i32) -> i32 { + let mut dp = vec![0; target as usize + 1]; + dp[0] = 1; + for i in 1..=target { + for &num in nums.iter() { + if i >= num { + dp[i as usize] += dp[(i - num) as usize]; + } + } + } + dp[target as usize] +} + +#[cfg(test)] +mod test { + use super::*; + use crate::vec2; + + #[test] + fn test_combination_sum4() { + struct TestCase { + nums: Vec, + target: i32, + expected: i32, + } + + vec![ + TestCase { + nums: vec![1, 2, 3], + target: 4, + expected: 7, + }, + TestCase { + nums: vec![9], + target: 3, + expected: 0, + }, + ] + .into_iter() + .enumerate() + .for_each( + |( + idx, + TestCase { + nums, + target, + expected, + }, + )| { + let actual = combination_sum4(nums, target); + assert_eq!(expected, actual, "case {} failed", idx); + }, + ); + } +} diff --git a/src/tree/dfs.rs b/src/tree/dfs.rs index ffdc302..0f4d0f5 100644 --- a/src/tree/dfs.rs +++ b/src/tree/dfs.rs @@ -98,7 +98,6 @@ mod tests { use std::vec; use super::*; - use datastructure::TreeNode; #[test] fn test_distance_k() { diff --git a/src/tree/mod.rs b/src/tree/mod.rs index bedfab0..b1f7f8e 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -9,7 +9,7 @@ pub mod dfs; mod tests { #[test] fn test_macro() { - let t = macros::tree!({val: 1, left: {2, right: {3}}}); + let t = macros::tree!(tree!{val: 16, left: {val: 8, left: {val: 1, right: {val: 2, right: {val: 7}}}, right: {val:12, left: {val: 9}}}, right: {val: 18, right: {val: 20}}}); dbg!(t); } } \ No newline at end of file diff --git a/src/tree/traversal/in_order.rs b/src/tree/traversal/in_order.rs index d8ffaeb..c28943f 100644 --- a/src/tree/traversal/in_order.rs +++ b/src/tree/traversal/in_order.rs @@ -5,6 +5,7 @@ //! * 中等 //! * [98. 验证二叉搜索树](is_valid_bst) //! * [98. 验证二叉搜索树](is_valid_bst_2) +//! * [2476. 二叉搜索树最近节点查询](closest_nodes) //! * 困难 use datastructure::TreeNode; @@ -64,14 +65,16 @@ pub fn is_valid_bst_2(root: Option>>) -> bool { let (l, lmin, lmax, lnone) = inorder(inner.borrow().left.clone()); if !l { return (false, 0, 0, false); - } else if !lnone && v <= lmax { + } + if !lnone && v <= lmax { return (false, 0, 0, false); } let (r, rmin, rmax, rnone) = inorder(inner.borrow().right.clone()); if !r { return (false, 0, 0, false); - } else if !rnone && v >= rmin { + } + if !rnone && v >= rmin { return (false, 0, 0, false); } @@ -82,11 +85,211 @@ pub fn is_valid_bst_2(root: Option>>) -> bool { flag } +/// [2476. 二叉搜索树最近节点查询](https://leetcode.cn/problems/closest-nodes-queries-in-a-binary-search-tree/) +/// +/// ## 思路2: 利用二茬搜索树的性质, 递归. 实际不可行, 因为题目没有保证树的平衡性, 会超时 +/// ## 思路1: 展开成有序数组, 二分查找 +pub fn closest_nodes(root: Option>>, queries: Vec) -> Vec> { + fn inorder(store: &mut Vec, node: Option>>) { + if node.is_none() { + return; + } + let inner = node.unwrap().clone(); + + inorder(store, inner.borrow().left.clone()); + store.push(inner.borrow().val); + inorder(store, inner.borrow().right.clone()); + } + let mut store = vec![]; + inorder(&mut store, root); + + let mut result = vec![]; + for q in queries { + match store.binary_search(&q) { + Ok(_) => { + result.push(vec![q, q]); + continue; + } + Err(idx) => { + if idx == 0 { + result.push(vec![-1, store[0]]); + } else if idx == store.len() { + result.push(vec![store[store.len() - 1], -1]); + } else { + result.push(vec![store[idx - 1], store[idx]]); + } + } + } + } + result +} + +/// [235. 二叉搜索树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/description/) +/// +/// ## 思路 +/// 由于是二叉搜索树, 因此只需要找到一个能将两个值左右分割的点即可, 不用存储父节点的栈 +pub fn lowest_common_ancestor( + root: Option>>, + p: Option>>, + q: Option>>, +) -> Option>> { + let p = p.unwrap().borrow().val; + let q = q.unwrap().borrow().val; + + let mut node = root; + while let Some(inner) = node { + let v = inner.borrow().val; + if p < v && q < v { + node = inner.borrow().left.clone(); + } else if p > v && q > v { + node = inner.borrow().right.clone(); + } else { + return Some(inner); + } + } + None +} + +/// [938. 二叉搜索树的范围和](https://leetcode.cn/problems/range-sum-of-bst/) +pub fn range_sum_bst(root: Option>>, low: i32, high: i32) -> i32 { + fn inorder(node: Option>>, store: &mut Vec) { + if node.is_none() { + return; + } + let inner = node.unwrap().clone(); + inorder(inner.borrow().left.clone(), store); + store.push(inner.borrow().val); + inorder(inner.borrow().right.clone(), store); + } + let mut store = vec![]; + inorder(root, &mut store); + + let left = match store.binary_search(&low) { + Ok(idx) => idx, + Err(idx) => idx, + }; + let right = match store.binary_search(&high) { + Ok(idx) => idx+1, + Err(idx) => idx, + }; + //println!("left: {}, right: {}, store {:?}", left, right, &store[left..right]); + + store[left..right].iter().sum() +} + #[cfg(test)] mod tests { + use crate::vec2; + use super::*; use macros::tree; + #[test] + fn test_range_sum_bst() { + struct Testcase { + tree: Option>>, + low: i32, + high: i32, + expect: i32, + } + + vec![ + Testcase { + tree: tree!({10, left: {5, left: {3}, right: {7}}, right: {15, right: {18}}}), + low: 7, + high: 15, + expect: 32, + }, + Testcase { + tree: tree!{val: 10, left: {val: 5, left: {val: 3, left: {val: 1}}, right: {val: 7, left: {val:6}}}, right: {val: 15, left: {val: 13}, right: {val: 18}}}, + low: 6, + high: 10, + expect: 23, + }, + ] + .into_iter() + .enumerate() + .for_each(|(idx, testcase)| { + let Testcase { + tree, + low, + high, + expect, + } = testcase; + let acutal = range_sum_bst(tree, low, high); + assert_eq!(expect, acutal, "case {} failed", idx); + }); + } + + #[test] + fn test_lowest_common_ancestor() { + struct Testcase { + tree: Option>>, + p: Option>>, + q: Option>>, + expect: Option>>, + } + + vec![ + Testcase { + tree: tree!({6, left: {2, left: {0}, right: {4, left: {3}, right: {5}}}, right: {8, left: {7}, right: {9}}}), + p: tree!({2}), + q: tree!({8}), + expect: tree!({6}), + }, + Testcase { + tree: tree!({6, left: {2, left: {0}, right: {4, left: {3}, right: {5}}}, right: {8, left: {7}, right: {9}}}), + p: tree!({2}), + q: tree!({4}), + expect: tree!({2}), + }, + ] + .into_iter() + .enumerate() + .for_each(|(idx, testcase)| { + let Testcase { + tree, + p, + q, + expect, + } = testcase; + let acutal = lowest_common_ancestor(tree, p, q); + let actual_val = acutal.map(|x| x.borrow().val).unwrap(); + let expect_val = expect.map(|x| x.borrow().val).unwrap(); + assert_eq!(expect_val, actual_val, "case {} failed", idx); + }); + } + + #[test] + fn test_closest_nodes() { + struct TestCase { + tree: Option>>, + queries: Vec, + expected: Vec>, + } + + vec![ + TestCase{ + tree: tree!{val:6, left:{val:2, left: {val:1}, right: {val:4}}, right: {val:13, left: {val:9}, right: {val: 15, left: {val: 14}}}}, + queries: vec![2, 5, 16], + expected: vec![vec![2, 2], vec![4, 6], vec![15, -1]], + }, + TestCase{ + tree: tree!{val: 4, right: {val: 9}}, + queries: vec![3], + expected: vec![vec![-1, 4]], + }, + TestCase{ + tree: tree!{val: 16, left: {val: 8, left: {val: 1, right: {val: 2, right: {val: 7}}}, right: {val:12, left: {val: 9}}}, right: {val: 18, right: {val: 20}}}, + queries: vec![8,14,285508,6], + expected: vec2![[8,8],[12,16],[20,-1],[2,7]], + } + ].into_iter().enumerate().for_each(|(idx, TestCase{tree, queries, expected})|{ + let result = closest_nodes(tree, queries); + assert_eq!(result, expected, "index: {}", idx); + }) + } + #[test] fn test_is_valid_bst_2() { struct Testcase { @@ -100,9 +303,8 @@ mod tests { expect: true, }, Testcase { - tree: - tree!({5, left: {1}, right:{4, left: {3}, right:{6} }}), - + tree: tree!({5, left: {1}, right:{4, left: {3}, right:{6} }}), + expect: false, }, Testcase { @@ -132,9 +334,8 @@ mod tests { expect: true, }, Testcase { - tree: - tree!({5, left: {1}, right:{4, left: {3}, right:{6} }}), - + tree: tree!({5, left: {1}, right:{4, left: {3}, right:{6} }}), + expect: false, }, ] @@ -151,7 +352,7 @@ mod tests { fn test_inorder_traversal() { struct Testcase { tree: Option>>, - expect:Vec, + expect: Vec, } vec![ @@ -171,7 +372,7 @@ mod tests { .into_iter() .enumerate() .for_each(|(idx, testcase)| { - let Testcase {tree, expect } = testcase; + let Testcase { tree, expect } = testcase; let acutal = inorder_traversal(tree); assert_eq!(expect, acutal, "case {} failed", idx); }); diff --git a/src/tree/traversal/level_order.rs b/src/tree/traversal/level_order.rs new file mode 100644 index 0000000..c04e049 --- /dev/null +++ b/src/tree/traversal/level_order.rs @@ -0,0 +1,76 @@ +//! 层序遍历 +use datastructure::TreeNode; +use std::cell::RefCell; +use std::rc::Rc; + +/// [2583. 二叉树中的第 K 大层和](https://leetcode.cn/problems/kth-largest-sum-in-a-binary-tree/) +pub fn kth_largest_level_sum(root: Option>>, k: i32) -> i64 { + use std::collections::{BinaryHeap, VecDeque}; + + let mut heap = BinaryHeap::new(); + let mut travel_queue = VecDeque::new(); + + travel_queue.push_back(root); + + // let mut lvl = 0; + while !travel_queue.is_empty() { + let mut lvl_sum = 0i64; + + for _ in 0..travel_queue.len() { + if let Some(node) = travel_queue.pop_front().unwrap() { + let node = node.borrow(); + lvl_sum += node.val as i64; + if let Some(left) = &node.left { + travel_queue.push_back(Some(left.clone())); + } + if let Some(right) = &node.right { + travel_queue.push_back(Some(right.clone())); + } + } + } + // println!("lvl: {}, lvl_sum: {}", lvl, lvl_sum); + heap.push(lvl_sum); + // lvl += 1; + } + // dbg!("heap", &heap); + if heap.len() < k as usize { + -1 + } else { + heap.into_sorted_vec() + .into_iter() + .rev() + .nth(k as usize - 1) + .unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use macros::tree; + + #[test] + fn test_kth_largest_level_sum() { + struct TestCase { + tree: Option>>, + k: i32, + expected: i64, + } + vec![ + TestCase{ + tree: tree!{val:5, left: {val: 8, left: {val: 2, left: {val: 4}, right: {val: 6}}, right: {val: 1}}, right: {val: 9, left: {val: 3}, right: {val: 7}}}, + k: 2, + expected: 13, + }, + TestCase{ + tree: tree!{val: 1, left: {val: 2, left: {val:3}}}, + k: 1, + expected: 3 + } + + ].into_iter().enumerate().for_each(|(idx, TestCase{tree, k, expected})|{ + let actual = kth_largest_level_sum(tree.clone(), k); + assert_eq!(actual, expected, "Test case {} failed", idx+1); + }); + } +} diff --git a/src/tree/traversal/mod.rs b/src/tree/traversal/mod.rs index 2bc3832..34c6c13 100644 --- a/src/tree/traversal/mod.rs +++ b/src/tree/traversal/mod.rs @@ -3,4 +3,5 @@ pub mod in_order; pub mod post_order; pub mod pre_order; -pub mod no_class; \ No newline at end of file +pub mod no_class; +pub mod level_order; \ No newline at end of file diff --git a/src/tree/traversal/no_class.rs b/src/tree/traversal/no_class.rs index 9dcadbb..74cec02 100644 --- a/src/tree/traversal/no_class.rs +++ b/src/tree/traversal/no_class.rs @@ -1,3 +1,5 @@ +//! 多种遍历方式混合的 + use datastructure::TreeNode; use std::cell::RefCell; use std::rc::Rc; @@ -18,19 +20,101 @@ pub fn build_tree(preorder: Vec, inorder: Vec) -> Option, postorder: Vec) -> Option>> { + fn build(inorder: &[i32], postorder: &[i32]) -> Option>> { + if postorder.is_empty() { + return None; + } + + let root_val = postorder.last().unwrap(); + let mut root = TreeNode::new(*root_val); + + // 题目保证值不同, 因此可以直接遍历寻找 + let root_idx = inorder.iter().position(|&x| x == *root_val).unwrap(); + let (inorder_left, inorder_right) = inorder.split_at(root_idx); + + root.left = build(inorder_left, &postorder[..inorder_left.len()]); + root.right = build( + &inorder_right[1..], + &postorder[inorder_left.len()..postorder.len() - 1], + ); + Some(Rc::new(RefCell::new(root))) + } + + build(&inorder, &postorder) +} + +/// [889. 根据前序和后序遍历构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-postorder-traversal/) +/// +/// ## 思路 +/// 1. 没有中序时, 结果可以有多种 +/// 2. 通过前序找到根节点, 和左子树的根, 通过后序找到右子树的根 +/// - 如果两个根相同, 说明只有左子树 +/// - 否则, 递归构建左右子树 +/// 3. 通过两个根节点的位置做分割 +/// 3. 递归结束条件是前序为空 +pub fn construct_from_pre_post( + preorder: Vec, + postorder: Vec, +) -> Option>> { + fn build(preorder: &[i32], postorder: &[i32]) -> Option>> { + if preorder.is_empty() { + return None; + } + let root_val = preorder[0]; + let mut root = TreeNode::new(root_val); + + if preorder.len() == 1 { + return Some(Rc::new(RefCell::new(root))); + } + if preorder.len() == 2 { + let left_val = preorder[1]; + let left = TreeNode::new(left_val); + root.left = Some(Rc::new(RefCell::new(left))); + return Some(Rc::new(RefCell::new(root))); + } + + let left_val = preorder[1]; + let left_idx_in_postorder = postorder.iter().position(|&x| x == left_val).unwrap(); + + let right_val = postorder[postorder.len() - 2]; + // check + if right_val != left_val { + let right_idx_in_preorder = preorder.iter().position(|&x| x == right_val).unwrap(); + + root.left = build( + &preorder[1..right_idx_in_preorder], + &postorder[..left_idx_in_postorder + 1], + ); + root.right = build( + &preorder[right_idx_in_preorder..], + &postorder[left_idx_in_postorder + 1..postorder.len() - 1], + ); + } else { + root.left = build(&preorder[1..], &postorder[..postorder.len() - 1]); + } + + Some(Rc::new(RefCell::new(root))) + } + build(&preorder, &postorder) +} + #[cfg(test)] mod tests { use macros::tree; @@ -47,4 +131,31 @@ mod tests { tree!({3, left: {9}, right:{20, left: {15}, right:{7} }}) ); } + + #[test] + fn test_build_tree2() { + let inorder = vec![9, 3, 15, 20, 7]; + let postorder = vec![9, 15, 7, 20, 3]; + let root = build_tree2(inorder, postorder); + assert_eq!( + root, + tree!({3, left: {9}, right:{20, left: {15}, right:{7} }}) + ); + } + + #[test] + fn test_build_tree3() { + let preorder = vec![1, 2, 4, 5, 3, 6, 7]; + let postorder = vec![4, 5, 2, 6, 7, 3, 1]; + let root = construct_from_pre_post(preorder, postorder); + assert_eq!( + root, + tree!({1, left: {2, left: {4}, right:{5}}, right:{3, left: {6}, right:{7} }}) + ); + + let preorder = vec![4, 2, 1, 3]; + let postorder = vec![3, 1, 2, 4]; + let root = construct_from_pre_post(preorder, postorder); + assert_eq!(root, tree!({4, left: {2, left: {1, left: {3}}}})); + } }