Skip to content

Commit dd60b5d

Browse files
authored
Newbee (#3)
1. 新手村题目 2. 调整一下macro 3. 并查集
1 parent dc42073 commit dd60b5d

File tree

22 files changed

+977
-524
lines changed

22 files changed

+977
-524
lines changed

datastructure/src/list.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
#[derive(PartialEq, Eq, Clone, Debug)]
22
pub struct ListNode {
3-
pub val: i32,
4-
pub next: Option<Box<ListNode>>
5-
}
3+
pub val: i32,
4+
pub next: Option<Box<ListNode>>,
5+
}
6+
7+
// impl TryFrom<&[i32]> for ListNode {
8+
// type Error = &'static str;
9+
10+
// fn try_from(s: &[i32]) -> Result<Self, Self::Error> {
11+
12+
// if s.is_empty() {
13+
// return Err("empty");
14+
// }
15+
16+
// let head = ListNode {
17+
// val: s.first().copied().unwrap(),
18+
// next: Self::try_from(&s[1..]).ok(),
19+
// };
20+
// Ok(head)
21+
// }
22+
23+
// }
24+

macros/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ use proc_macro::TokenStream;
88
mod tree_impl;
99
mod list_impl;
1010

11-
/// tree!(tree-define)
11+
/// tree!(tree-define) => Option<Rc<RefCell<TreeNode>>>
1212
/// tree-define: {val: int, [left: tree-define], [right: tree-define] }
1313
#[proc_macro]
1414
pub fn tree(input: TokenStream) -> TokenStream {
1515
tree_impl::tree(input.into()).into()
1616
}
1717

18-
/// list!(1,2,3,4)
18+
/// list!(1,2,3,4) => Option<Box<ListNode>>
1919
/// list!([1,2,3,4])
2020
#[proc_macro]
2121
pub fn list(input: TokenStream) -> TokenStream{

macros/src/tree_impl.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ use std::str::FromStr;
33

44

55
pub fn tree(input: TokenStream) -> TokenStream {
6-
let tree = TreeNode::from(input);
7-
TokenStream::from_str(format!("{}", tree).as_str()).unwrap()
6+
if input.is_empty(){
7+
TokenStream::from_str("None").unwrap()
8+
} else {
9+
let tree = TreeNode::from(input);
10+
TokenStream::from_str(format!("Some(::std::rc::Rc::new(::std::cell::RefCell::new({})))", tree).as_str()).unwrap()
11+
}
12+
813
}
914

1015
#[derive(Debug)]

src/array/ext.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ pub mod rng;
1414
// pub mod topological;
1515

1616
pub mod trie;
17+
18+
pub mod disjoint;

src/array/ext/disjoint.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//! # Disjoint-set data structure
2+
//!
3+
//! wiki: [Disjoint-set data structure](https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
4+
//!
5+
//! 效果: 将数据分组
6+
//!
7+
//! ## 题目
8+
//! * 简单
9+
//! * 中等
10+
//! * [547. 省份数量](find_circle_num)
11+
//!
12+
13+
struct UnionFind {
14+
count: usize, // 连通分量的个数, 总共分成了几组
15+
parent: std::cell::RefCell<Vec<usize>>, // 记录每个节点的父节点,父节点为自身的是根节点
16+
size: Vec<usize>, // 记录每个连通分量的大小
17+
}
18+
19+
impl UnionFind {
20+
pub fn new(size: usize) -> Self {
21+
Self {
22+
count: size,
23+
parent: std::cell::RefCell::new((0..size).collect()),
24+
size: vec![1; size],
25+
}
26+
}
27+
pub fn count(&self) -> usize {
28+
self.count
29+
}
30+
pub fn find(&self, p: usize) -> usize {
31+
let mut root = p;
32+
while root != self.parent.borrow()[root] {
33+
root = self.parent.borrow()[root];
34+
}
35+
let mut p = p;
36+
while p != root {
37+
let next = self.parent.borrow()[p];
38+
self.parent.borrow_mut()[p] = root;
39+
p = next;
40+
}
41+
root
42+
}
43+
pub fn is_connected(&self, p: usize, q: usize) -> bool {
44+
self.find(p) == self.find(q)
45+
}
46+
pub fn connect(&mut self, p: usize, q: usize) {
47+
let (p_root, q_root) = (self.find(p), self.find(q));
48+
if p_root == q_root {
49+
return;
50+
}
51+
if self.size[p_root] < self.size[q_root] {
52+
self.parent.borrow_mut()[p_root] = q_root;
53+
self.size[q_root] += self.size[p_root];
54+
} else {
55+
self.parent.borrow_mut()[q_root] = p_root;
56+
self.size[p_root] += self.size[q_root];
57+
}
58+
self.count -= 1;
59+
}
60+
}
61+
62+
/// [547. 省份数量](https://leetcode.cn/problems/number-of-provinces/)
63+
pub fn find_circle_num(is_connected: Vec<Vec<i32>>) -> i32 {
64+
let mut uf = UnionFind::new(is_connected.len());
65+
is_connected.into_iter().enumerate().for_each(|(i, line)| {
66+
line.into_iter().enumerate().for_each(|(j, x)| {
67+
if x == 1 {
68+
uf.connect(i, j);
69+
}
70+
})
71+
});
72+
uf.count() as i32
73+
}
74+
75+
/// [684. 冗余连接](https://leetcode.cn/problems/redundant-connection/)
76+
pub fn find_redundant_connection(edges: Vec<Vec<i32>>) -> Vec<i32> {
77+
let mut uf = UnionFind::new(edges.len()+1);
78+
let mut curr_count = uf.count();
79+
let mut last_edge = vec![];
80+
81+
for edge in edges {
82+
if let &[p, q] = edge.as_slice() {
83+
uf.connect(p as usize, q as usize);
84+
if uf.count() == curr_count {
85+
last_edge = edge.clone();
86+
}
87+
curr_count = uf.count;
88+
}
89+
}
90+
last_edge
91+
}
92+
93+
#[cfg(test)]
94+
mod test {
95+
use super::*;
96+
use crate::vec2;
97+
98+
#[test]
99+
fn test_find_redundant_connection() {
100+
struct Testcase {
101+
edges: Vec<Vec<i32>>,
102+
expect: Vec<i32>,
103+
}
104+
105+
vec![
106+
Testcase {
107+
edges: vec2![[1, 2], [1, 3], [2, 3]],
108+
expect: vec![2, 3],
109+
},
110+
Testcase {
111+
edges: vec2![[1, 2], [2, 3], [3, 4], [1, 4], [1, 5]],
112+
expect: vec![1, 4],
113+
},
114+
]
115+
.into_iter()
116+
.enumerate()
117+
.for_each(|(idx, testcase)| {
118+
let Testcase { edges, expect } = testcase;
119+
let actual = find_redundant_connection(edges);
120+
assert_eq!(expect, actual, "case {} failed", idx);
121+
});
122+
}
123+
124+
#[test]
125+
fn test_find_circle_num() {
126+
struct Testcase {
127+
is_connected: Vec<Vec<i32>>,
128+
expect: i32,
129+
}
130+
131+
vec![
132+
Testcase {
133+
is_connected: vec2![[1, 1, 0], [1, 1, 0], [0, 0, 1]],
134+
expect: 2,
135+
},
136+
Testcase {
137+
is_connected: vec2![[1, 0, 0], [0, 1, 0], [0, 0, 1]],
138+
expect: 3,
139+
},
140+
]
141+
.into_iter()
142+
.enumerate()
143+
.for_each(|(idx, testcase)| {
144+
let Testcase {
145+
is_connected,
146+
expect,
147+
} = testcase;
148+
let actual = find_circle_num(is_connected);
149+
assert_eq!(expect, actual, "case {} failed", idx);
150+
});
151+
}
152+
}

src/array/ext/math.rs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
//! 数学相关题目
1+
//! # 数学相关题目
2+
//!
3+
//! ## 题目
4+
//! * 简单
5+
//! * [1470. 重新排列数组](shuffle)
6+
//! * [1582. 二进制矩阵中的特殊位置](num_special)
7+
//! * 中等
8+
//! * [462. 最小操作次数使数组元素相等 II](min_moves2)
9+
//! * [667. 优美的排列 II](construct_array)
210
311
/// [462. 最少移动次数使数组元素相等 II](https://leetcode.cn/problems/minimum-moves-to-equal-array-elements-ii/)
412
///
@@ -126,10 +134,118 @@ pub fn construct_array(n: i32, k: i32) -> Vec<i32> {
126134
result
127135
}
128136

137+
/// [412. Fizz Buzz](https://leetcode.cn/problems/fizz-buzz/)
138+
pub fn fizz_buzz(n: i32) -> Vec<String> {
139+
(1..=n)
140+
.into_iter()
141+
.map(|num| {
142+
if num % 3 == 0 && num % 5 == 0 {
143+
"FizzBuzz".to_string()
144+
} else if num % 5 == 0 {
145+
"Buzz".to_string()
146+
} else if num % 3 == 0 {
147+
"Fizz".to_string()
148+
} else {
149+
num.to_string()
150+
}
151+
})
152+
.collect()
153+
}
154+
155+
/// [1342. 将数字变成 0 的操作次数](https://leetcode.cn/problems/number-of-steps-to-reduce-a-number-to-zero/)
156+
///
157+
/// 思路1: 模拟
158+
/// ```
159+
/// pub fn number_of_steps(num: i32) -> i32 {
160+
/// let mut num = num;
161+
/// let mut cnt = 0;
162+
/// while num > 0 {
163+
/// if num % 2 == 0 {
164+
/// num = num / 2;
165+
/// } else {
166+
/// num = num - 1;
167+
/// }
168+
/// cnt += 1;
169+
/// }
170+
/// cnt
171+
/// }
172+
/// ```
173+
///
174+
/// 思路2: 计算
175+
/// 将num用用二进制表示, 则每次减1, 实际对应为 低位的 1变0, 每次除2, 实际对应为 整体右移一位
176+
/// 也就是总的操作数为可以视为 将最高位1变为0 的步数
177+
///
178+
/// 其中右移总共 `31 - (leading_zero)` (有符号数, 第一位为符号位, 忽略)
179+
/// 减1 次数, 即为1的个数
180+
///
181+
/// 注意: 如果原本为0, 这时 `31 - (leading_zero)`可能有溢出
182+
///
183+
pub fn number_of_steps(num: i32) -> i32 {
184+
(num.count_ones() + 31u32.checked_sub(num.leading_zeros()).unwrap_or(0)) as i32
185+
}
186+
129187
#[cfg(test)]
130188
mod tests {
131189
use super::*;
132190

191+
#[test]
192+
fn test_number_of_steps() {
193+
struct TestCase {
194+
num: i32,
195+
expect: i32,
196+
}
197+
198+
vec![
199+
TestCase { num: 14, expect: 6 },
200+
TestCase { num: 8, expect: 4 },
201+
TestCase {
202+
num: 123,
203+
expect: 12,
204+
},
205+
TestCase{num: 0, expect: 0}
206+
]
207+
.into_iter()
208+
.enumerate()
209+
.for_each(|(idx, testcase)| {
210+
let TestCase { num, expect } = testcase;
211+
let actual = number_of_steps(num);
212+
assert_eq!(expect, actual, "case {} failed", idx);
213+
});
214+
}
215+
216+
#[test]
217+
fn test_fizz_buzz() {
218+
struct TestCase {
219+
n: i32,
220+
expect: Vec<&'static str>,
221+
}
222+
223+
vec![
224+
TestCase {
225+
n: 3,
226+
expect: vec!["1", "2", "Fizz"],
227+
},
228+
TestCase {
229+
n: 5,
230+
expect: vec!["1", "2", "Fizz", "4", "Buzz"],
231+
},
232+
TestCase {
233+
n: 15,
234+
expect: vec![
235+
"1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz",
236+
"13", "14", "FizzBuzz",
237+
],
238+
},
239+
]
240+
.into_iter()
241+
.enumerate()
242+
.for_each(|(idx, testcase)| {
243+
let TestCase { n, expect } = testcase;
244+
let actual = fizz_buzz(n);
245+
assert_eq!(expect, actual, "case {} failed", idx);
246+
});
247+
}
248+
133249
#[test]
134250
fn test_construct_array() {
135251
struct TestCase {

src/array/ext/quick.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
/// **快排** 主要思路是 分治, 分治可以用递归实现.
1313
/// 步骤: 1. 确认 *轴*; 2. 根据 *轴*, 将数组切分; 3. 根据轴的位置, 递归处理每个新数组
1414
/// 步骤一可变性不强, 可以选开头, 结尾, 中间, 随机, 方式很多不过没啥难度和技巧; 步骤三同理
15-
/// 步骤2, 将小于x的放在x左边, 将大于x的放x右边.
15+
///
16+
/// 步骤2, 将小于x的放在x左边, 将大于x的放x右边, 有下面两种方式
1617
///
17-
/// 方式1: 使用额外数组, 挑数, 挑出来, 然后拼接在一起
18-
/// 方式2: 快慢指针
18+
/// * 方式1: 使用额外数组, 挑数, 挑出来, 然后拼接在一起
19+
/// * 方式2: 快慢指针
20+
///
1921
/// ```rust
2022
/// fn partition(nums: &mut [i32], left: usize, right: usize, pivot: usize) -> usize {
2123
/// nums.swap(pivot, right);
@@ -51,7 +53,7 @@
5153
/// return r;
5254
/// }
5355
/// ```
54-
/// 不过思路和方式2的快慢指针是一样的, 只是用加了预处理, 将左右边界上明显不符合交换的元素, 做了跳过
56+
/// 不过思路和方式2的快慢指针是一样的, 只是加了预处理, 将左右边界上明显不符合交换的元素, 做了跳过
5557
///
5658
pub fn sort_array(nums: Vec<i32>) -> Vec<i32> {
5759
fn partition(nums: &mut [i32], left: usize, right: usize, pivot: usize) -> usize {

src/array/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
//! 线性表相关的都会涉及
33
//! 比如数组(连续存储), 简单链表, 字符串(连续存储)
44
5+
/// vec2![[1,2,3],[3,2,1]] => vec![vec![1,2,3], vec![3,2,1]]
6+
57
/// 一些周边题目
68
pub mod ext;
9+
/// 暂时不归类的题目
10+
pub mod no_class;
711
/// 姑且能所做一个系列的题目
812
pub mod ser;
9-
/// 暂时不归类的题目
10-
pub mod no_class;

0 commit comments

Comments
 (0)