pull/944/head
krahets 1 year ago
parent 00adffaca7
commit 799698e67c

@ -901,7 +901,7 @@ comments: true
}
```
假设元素两两之间互不相同,则 $n$ 个元素共有 $n!$ 种排列(阶乘);在记录结果时,需要复制长度为 $n$ 的列表,使用 $O(n)$ 时间。因此**时间复杂度为 $O(n!n)$** 。
假设元素两两之间互不相同,则 $n$ 个元素共有 $n!$ 种排列(阶乘);在记录结果时,需要复制长度为 $n$ 的列表,使用 $O(n)$ 时间。**因此时间复杂度为 $O(n!n)$** 。
最大递归深度为 $n$ ,使用 $O(n)$ 栈帧空间。`selected` 使用 $O(n)$ 空间。同一时刻最多共有 $n$ 个 `duplicated` ,使用 $O(n^2)$ 空间。**因此空间复杂度为 $O(n^2)$** 。

@ -74,13 +74,29 @@ status: new
=== "JS"
```javascript title="iteration.js"
[class]{}-[func]{forLoop}
/* for 循环 */
function forLoop(n) {
let res = 0;
// 循环求和 1, 2, ..., n-1, n
for (let i = 1; i <= n; i++) {
res += i;
}
return res;
}
```
=== "TS"
```typescript title="iteration.ts"
[class]{}-[func]{forLoop}
/* for 循环 */
function forLoop(n: number): number {
let res = 0;
// 循环求和 1, 2, ..., n-1, n
for (let i = 1; i <= n; i++) {
res += i;
}
return res;
}
```
=== "C"
@ -132,7 +148,15 @@ status: new
=== "Rust"
```rust title="iteration.rs"
[class]{}-[func]{for_loop}
/* for 循环 */
fn for_loop(n: i32) -> i32 {
let mut res = 0;
// 循环求和 1, 2, ..., n-1, n
for i in 1..=n {
res += i;
}
res
}
```
图 2-1 展示了该求和函数的流程框图。
@ -216,13 +240,33 @@ status: new
=== "JS"
```javascript title="iteration.js"
[class]{}-[func]{whileLoop}
/* while 循环 */
function whileLoop(n) {
let res = 0;
let i = 1; // 初始化条件变量
// 循环求和 1, 2, ..., n-1, n
while (i <= n) {
res += i;
i++; // 更新条件变量
}
return res;
}
```
=== "TS"
```typescript title="iteration.ts"
[class]{}-[func]{whileLoop}
/* while 循环 */
function whileLoop(n: number): number {
let res = 0;
let i = 1; // 初始化条件变量
// 循环求和 1, 2, ..., n-1, n
while (i <= n) {
res += i;
i++; // 更新条件变量
}
return res;
}
```
=== "C"
@ -278,7 +322,17 @@ status: new
=== "Rust"
```rust title="iteration.rs"
[class]{}-[func]{while_loop}
/* while 循环 */
fn while_loop(n: i32) -> i32 {
let mut res = 0;
let mut i = 1; // 初始化条件变量
// 循环求和 1, 2, ..., n-1, n
while i <= n {
res += i;
i += 1; // 更新条件变量
}
res
}
```
`while` 循环中,由于初始化和更新条件变量的步骤是独立在循环结构之外的,**因此它比 `for` 循环的自由度更高**。
@ -359,13 +413,37 @@ status: new
=== "JS"
```javascript title="iteration.js"
[class]{}-[func]{whileLoopII}
/* while 循环(两次更新) */
function whileLoopII(n) {
let res = 0;
let i = 1; // 初始化条件变量
// 循环求和 1, 4, ...
while (i <= n) {
res += i;
// 更新条件变量
i++;
i *= 2;
}
return res;
}
```
=== "TS"
```typescript title="iteration.ts"
[class]{}-[func]{whileLoopII}
/* while 循环(两次更新) */
function whileLoopII(n: number): number {
let res = 0;
let i = 1; // 初始化条件变量
// 循环求和 1, 4, ...
while (i <= n) {
res += i;
// 更新条件变量
i++;
i *= 2;
}
return res;
}
```
=== "C"
@ -425,7 +503,19 @@ status: new
=== "Rust"
```rust title="iteration.rs"
[class]{}-[func]{while_loop_ii}
/* while 循环(两次更新) */
fn while_loop_ii(n: i32) -> i32 {
let mut res = 0;
let mut i = 1; // 初始化条件变量
// 循环求和 1, 4, ...
while i <= n {
res += i;
// 更新条件变量
i += 1;
i *= 2;
}
res
}
```
总的来说,**`for` 循环的代码更加紧凑,`while` 循环更加灵活**,两者都可以实现迭代结构。选择使用哪一个应该根据特定问题的需求来决定。
@ -502,13 +592,35 @@ status: new
=== "JS"
```javascript title="iteration.js"
[class]{}-[func]{nestedForLoop}
/* 双层 for 循环 */
function nestedForLoop(n) {
let res = '';
// 循环 i = 1, 2, ..., n-1, n
for (let i = 1; i <= n; i++) {
// 循环 j = 1, 2, ..., n-1, n
for (let j = 1; j <= n; j++) {
res += `(${i}, ${j}), `;
}
}
return res;
}
```
=== "TS"
```typescript title="iteration.ts"
[class]{}-[func]{nestedForLoop}
/* 双层 for 循环 */
function nestedForLoop(n: number): string {
let res = '';
// 循环 i = 1, 2, ..., n-1, n
for (let i = 1; i <= n; i++) {
// 循环 j = 1, 2, ..., n-1, n
for (let j = 1; j <= n; j++) {
res += `(${i}, ${j}), `;
}
}
return res;
}
```
=== "C"
@ -566,7 +678,18 @@ status: new
=== "Rust"
```rust title="iteration.rs"
[class]{}-[func]{nested_for_loop}
/* 双层 for 循环 */
fn nested_for_loop(n: i32) -> String {
let mut res = vec![];
// 循环 i = 1, 2, ..., n-1, n
for i in 1..=n {
// 循环 j = 1, 2, ..., n-1, n
for j in 1..=n {
res.push(format!("({}, {}), ", i, j));
}
}
res.join("")
}
```
图 2-2 给出了该嵌套循环的流程框图。
@ -657,13 +780,29 @@ status: new
=== "JS"
```javascript title="recursion.js"
[class]{}-[func]{recur}
/* 递归 */
function recur(n) {
// 终止条件
if (n === 1) return 1;
// 递:递归调用
const res = recur(n - 1);
// 归:返回结果
return n + res;
}
```
=== "TS"
```typescript title="recursion.ts"
[class]{}-[func]{recur}
/* 递归 */
function recur(n: number): number {
// 终止条件
if (n === 1) return 1;
// 递:递归调用
const res = recur(n - 1);
// 归:返回结果
return n + res;
}
```
=== "C"
@ -716,7 +855,17 @@ status: new
=== "Rust"
```rust title="recursion.rs"
[class]{}-[func]{recur}
/* 递归 */
fn recur(n: i32) -> i32 {
// 终止条件
if n == 1 {
return 1;
}
// 递:递归调用
let res = recur(n - 1);
// 归:返回结果
n + res
}
```
图 2-3 展示了该函数的递归过程。
@ -814,13 +963,25 @@ status: new
=== "JS"
```javascript title="recursion.js"
[class]{}-[func]{tailRecur}
/* 尾递归 */
function tailRecur(n, res) {
// 终止条件
if (n === 0) return res;
// 尾递归调用
return tailRecur(n - 1, res + n);
}
```
=== "TS"
```typescript title="recursion.ts"
[class]{}-[func]{tailRecur}
/* 尾递归 */
function tailRecur(n: number, res: number): number {
// 终止条件
if (n === 0) return res;
// 尾递归调用
return tailRecur(n - 1, res + n);
}
```
=== "C"
@ -869,7 +1030,15 @@ status: new
=== "Rust"
```rust title="recursion.rs"
[class]{}-[func]{tail_recur}
/* 尾递归 */
fn tail_recur(n: i32, res: i32) -> i32 {
// 终止条件
if n == 0 {
return res;
}
// 尾递归调用
tail_recur(n - 1, res + n)
}
```
两种递归的过程对比如图 2-5 所示。
@ -961,13 +1130,29 @@ status: new
=== "JS"
```javascript title="recursion.js"
[class]{}-[func]{fib}
/* 斐波那契数列:递归 */
function fib(n) {
// 终止条件 f(1) = 0, f(2) = 1
if (n === 1 || n === 2) return n - 1;
// 递归调用 f(n) = f(n-1) + f(n-2)
const res = fib(n - 1) + fib(n - 2);
// 返回结果 f(n)
return res;
}
```
=== "TS"
```typescript title="recursion.ts"
[class]{}-[func]{fib}
/* 斐波那契数列:递归 */
function fib(n: number): number {
// 终止条件 f(1) = 0, f(2) = 1
if (n === 1 || n === 2) return n - 1;
// 递归调用 f(n) = f(n-1) + f(n-2)
const res = fib(n - 1) + fib(n - 2);
// 返回结果 f(n)
return res;
}
```
=== "C"
@ -1020,7 +1205,17 @@ status: new
=== "Rust"
```rust title="recursion.rs"
[class]{}-[func]{fib}
/* 斐波那契数列:递归 */
fn fib(n: i32) -> i32 {
// 终止条件 f(1) = 0, f(2) = 1
if n == 1 || n == 2 {
return n - 1;
}
// 递归调用 f(n) = f(n-1) + f(n-2)
let res = fib(n - 1) + fib(n - 2);
// 返回结果
res
}
```
观察以上代码,我们在函数内递归调用了两个函数,**这意味着从一个调用产生了两个调用分支**。如图 2-6 所示,这样不断递归调用下去,最终将产生一个层数为 $n$ 的「递归树 recursion tree」。

@ -177,7 +177,17 @@ status: new
=== "Rust"
```rust title="binary_search_edge.rs"
[class]{}-[func]{binary_search_left_edge}
/* 二分查找最左一个 target */
fn binary_search_left_edge(nums: &[i32], target: i32) -> i32 {
// 等价于查找 target 的插入点
let i = binary_search_insertion(nums, target);
// 未找到 target ,返回 -1
if i == nums.len() as i32 || nums[i as usize] != target {
return -1;
}
// 找到 target ,返回索引 i
i
}
```
## 10.3.2 &nbsp; 查找右边界
@ -373,7 +383,19 @@ status: new
=== "Rust"
```rust title="binary_search_edge.rs"
[class]{}-[func]{binary_search_right_edge}
/* 二分查找最右一个 target */
fn binary_search_right_edge(nums: &[i32], target: i32) -> i32 {
// 转化为查找最左一个 target + 1
let i = binary_search_insertion(nums, target + 1);
// j 指向最右一个 target i 指向首个大于 target 的元素
let j = i - 1;
// 未找到 target ,返回 -1
if j == -1 || nums[j as usize] != target {
return -1;
}
// 找到 target ,返回索引 j
j
}
```
### 2. &nbsp; 转化为查找元素

@ -237,7 +237,22 @@ status: new
=== "Rust"
```rust title="binary_search_insertion.rs"
[class]{}-[func]{binary_search_insertion}
/* 二分查找插入点(存在重复元素) */
pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 {
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1]
while i <= j {
let m = i + (j - i) / 2; // 计算中点索引 m
if nums[m as usize] < target {
i = m + 1; // target 在区间 [m+1, j] 中
} else if nums[m as usize] > target {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
i
}
```
## 10.2.2 &nbsp; 存在重复元素的情况
@ -504,7 +519,22 @@ status: new
=== "Rust"
```rust title="binary_search_insertion.rs"
[class]{}-[func]{binary_search_insertion}
/* 二分查找插入点(存在重复元素) */
pub fn binary_search_insertion(nums: &[i32], target: i32) -> i32 {
let (mut i, mut j) = (0, nums.len() as i32 - 1); // 初始化双闭区间 [0, n-1]
while i <= j {
let m = i + (j - i) / 2; // 计算中点索引 m
if nums[m as usize] < target {
i = m + 1; // target 在区间 [m+1, j] 中
} else if nums[m as usize] > target {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
i
}
```
!!! tip

@ -715,7 +715,7 @@ comments: true
pub fn insert(&mut self, num: i32) {
// 若树为空,则初始化根节点
if self.root.is_none() {
self.root = TreeNode::new(num);
self.root = Some(TreeNode::new(num));
return;
}
let mut cur = self.root.clone();
@ -1356,7 +1356,6 @@ comments: true
void remove(int num) {
// 若树为空,直接提前返回
if (_root == null) return;
TreeNode? cur = _root;
TreeNode? pre = null;
// 循环查找,越过叶节点后跳出

Loading…
Cancel
Save