Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c4ec8c3
feat: ssml for course 2.
Milky2018 Aug 5, 2025
5657838
chore: adjust pronounciation for course 2
Milky2018 Aug 5, 2025
c83202e
chore: add spaces between Chinese and English characters.
Milky2018 Aug 8, 2025
2c6e331
Merge branch 'moonbitlang:main' into main
Milky2018 Aug 8, 2025
5058fe7
Merge branch 'main' of https://github.com/moonbitlang/moonbit-course
Milky2018 Aug 8, 2025
0613136
Merge branch 'main' of https://github.com/Milky2018/moonbit-course
Milky2018 Aug 8, 2025
c61ac28
feat: ssml and slide update for course2. Main changes: number upcast …
Milky2018 Aug 19, 2025
7d6d303
feat: ssml for course 3.
Milky2018 Aug 25, 2025
2999a64
chore: update slides for course 3.
Milky2018 Aug 25, 2025
55bbe37
fix: pronounciation `mut`; illustration for `concat`.
Milky2018 Aug 25, 2025
f260782
feat: links for moonbitlang.cn and tour.
Milky2018 Aug 25, 2025
538e1dd
feat: slides update for course4.
Milky2018 Aug 26, 2025
3e37730
feat: ssml for course4.
Milky2018 Aug 26, 2025
564b73e
Merge branch 'main' of https://github.com/moonbitlang/moonbit-course …
Milky2018 Aug 26, 2025
6c699c6
Merge branch 'main' of https://github.com/moonbitlang/moonbit-course
Milky2018 Aug 26, 2025
6f751e4
Merge branch 'course3'
Milky2018 Aug 26, 2025
309afcb
feat: ssml for course4 (wip)
Milky2018 Aug 26, 2025
d78f1e7
Merge branch 'main' into course4
Milky2018 Sep 2, 2025
0ecf573
feat: ssml update for course 4.
Milky2018 Sep 12, 2025
2039b85
feat: slides update for course 4.
Milky2018 Sep 12, 2025
6448278
feat: ssml for course 5.
Milky2018 Sep 28, 2025
eba859e
Merge branch 'moonbitlang:main' into course5
Milky2018 Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 112 additions & 88 deletions course5/lec5.mbt.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ headingDivider: 1

## 树

### Hongbo Zhang
### 月兔公开课课程组

# 数据结构:树
- 树
Expand All @@ -37,20 +37,20 @@ headingDivider: 1
- 如果没有子节点的节点可称为叶节点
- 任何节点不能是自己的后代节点:树中不能有环路
- 树的一条边指的是一对节点(u, v),其中u是v的父节点或者v是u的父节点
![](../pics/abstract-tree.drawio.png)
![](../pics/abstract-tree.drawio.png)

# 树的逻辑结构

- 这不是一颗树
![](../pics/not-a-tree.drawio.png)
![](../pics/not-a-tree.drawio.png)

# 树的逻辑结构

- 计算机中的树根节点在上,子节点在父节点下方
- 相关术语
- 节点的**深度**:根节点下到这个节点的路径的长度(边的数量)
- 节点的**深度**:从根节点向下,到这个节点的路径的长度(边的数量)
- 根节点深度为0
- 节点的**高度**:节点下到叶节点的最长路径的长度
- 节点的**高度**:从节点向下,到叶节点的最长路径的长度
- 叶节点高度为0
- 树的**高度**:根节点的高度
- 只有一个节点的树高度为0
Expand All @@ -77,8 +77,8 @@ headingDivider: 1

- 线段树:每一个节点都存储了一根线段及对应的数据,适合一维查询
- 二叉树:每个节点至多有两个分支:左子树与右子树
- KD-Tree:支持K-维度的数据(例如平面中的点、空间中的点等)的存储与查询的二叉树,每一层更换分叉判定的维度
- B-Tree:适合顺序访问,利于硬盘存储数据
- KD-Tree:支持K-维度的数据(例如平面中的点、空间中的点等)的存储与查询的二叉树,每一层更换分叉判定的维度
- R-Tree:存储空间几何结构
- ……

Expand All @@ -87,12 +87,20 @@ headingDivider: 1
- 二叉树要么是一棵空树,要么是一个节点;它最多具有两个子树:左子树与右子树
- 叶节点的两个子树都是空树
- 基于**递归枚举类型**的定义(本节课默认存储数据为整数)
```moonbit
enum IntTree {
Node(Int, IntTree, IntTree) // 存储的数据,左子树,右子树
Empty
}
```

```moonbit
enum IntTree {
Node(Int, left~ : IntTree, right~ : IntTree) // 存储的数据,左子树,右子树
Empty
}

let tree : IntTree = Node(1, left=Node(2, left=Empty, right=Empty), Empty)

match tree {
Empty => ...
Node(value, left=l, right~) => ... // 左子树和右子树被绑定到 l 和 right
}
```

# 二叉树的遍历

Expand All @@ -109,32 +117,32 @@ enum IntTree {
- 后序遍历:先访问左子树,再访问右子树,再访问根节点
- `[3, 5, 4, 1, 2, 0]`
- 广度优先遍历:`[0, 1, 2, 3, 4, 5]`
![height:6em](../pics/traversal.drawio.png)
![height:6em](../pics/traversal.drawio.png)

# 深度优先遍历:查找为例

- 我们想要在树的节点中查找是否有特定的值
- 结构化递归
- 先对基本情形进行处理:空树
- 再对递归情形进行处理,并递归
```moonbit
fn dfs_search(target: Int, tree: IntTree) -> Bool {
match tree { // 判断当前访问的树
Empty => false // 若为空树,则已抵达最深处
Node(value, left, right) => // 否则,再对子树轮流遍历
value == target || dfs_search(target, left) || dfs_search(target, right)
}
}
```
```moonbit
fn dfs_search(target : Int, tree : IntTree) -> Bool {
match tree { // 判断当前访问的树
Empty => false // 若为空树,则已抵达最深处
Node(value, left~, right~) => // 否则,再对子树轮流遍历
value == target || dfs_search(target, left) || dfs_search(target, right)
}
}
```
- 前序、中序、后序遍历只是改变顺序

# 逻辑值的短路运算

- 短路运算:当发现当前求解值可以被确定,则中断计算并返回结果
- `let x = true || { abort("程序中止") }`:因为`true || 任何值`均为真,因此不会计算`||`右侧表达式
- `let y = false && { abort("程序中止") }`:因为`false && 任何值`均为假,因此不会计算`&&`右侧表达式
- `let x = true || { abort("程序中止") }`:因为 `true || 任何值` 均为真,因此不会计算 `||` 右侧表达式
- `let y = false && { abort("程序中止") }`:因为 `false && 任何值` 均为假,因此不会计算 `&&` 右侧表达式
- 树的遍历
- `value == target || dfs_search(target, left) || dfs_search(target, right)`在找到后即会中止遍历
- `value == target || dfs_search(target, left) || dfs_search(target, right)` 在找到后即会中止遍历

# 广度优先遍历

Expand All @@ -148,42 +156,45 @@ fn dfs_search(target: Int, tree: IntTree) -> Bool {
- 就像生活中排队一样,先进入队伍的人最先获得服务
- 对于数据的插入和删除遵循先进先出(First In First Out, FIFO)的原则
- 队尾插入数据,队头删除数据
![](../pics/queue.drawio.png)

![](../pics/queue.drawio.png)

# 数据结构:队列
- 我们在此使用的队列由以下接口定义:
```moonbit
fn[T] empty() -> Queue[T] { ... } // 创建空队列
fn[T] enqueue(q: Queue[T], x: T) -> Queue[T] { ... } // 向队尾添加元素
// 尝试取出一个元素,并返回剩余队列;若为空则为本身
fn[T] pop(q: Queue[T]) -> (Option[T], Queue[T]) { ... }
```
```moonbit
fn[T] empty() -> Queue[T] { ... } // 创建空队列
fn[T] enqueue(q : Queue[T], x : T) -> Queue[T] { ... } // 向队尾添加元素
// 尝试取出一个元素,并返回剩余队列;若为空则为本身
fn[T] pop(q : Queue[T]) -> (Option[T], Queue[T]) { ... }
```
- 例如
```moonbit
let q = enqueue(enqueue(empty(), 1), 2)
let (head, tail) = pop(q)
assert_eq(head, Some(1))
assert_eq(tail, enqueue(empty(), 2))
```
```moonbit
test {
let q = enqueue(enqueue(empty(), 1), 2)
let (head, tail) = pop(q)
assert_eq(head, Some(1))
assert_eq(tail, enqueue(empty(), 2))
}
```

# 广度优先遍历:查找为例

- 我们想要在树的节点中查找是否有特定的值
```moonbit
fn bfs_search(target: Int, queue: Queue[IntTree]) -> Bool {
match pop(queue) {
(None, _) => false // 若队列为空,结束搜索
(Some(head), tail) => match head { // 若队列非空,对于取出的树进行操作
Empty => bfs_search(target, tail) // 若树为空树,则对剩余队列进行操作
Node(value, left, right) =>
if value == target { true } else {
// 否则,操作根节点并将子树加入队列
bfs_search(target, enqueue(enqueue(tail, left), right))
}
```moonbit
fn bfs_search(target : Int, queue : Queue[IntTree]) -> Bool {
match pop(queue) {
(None, _) => false // 若队列为空,结束搜索
(Some(head), tail) => match head { // 若队列非空,对于取出的树进行操作
Empty => bfs_search(target, tail) // 若树为空树,则对剩余队列进行操作
Node(value, left~, right~) =>
if value == target { true } else {
// 否则,操作根节点并将子树加入队列
bfs_search(target, enqueue(enqueue(tail, left), right))
}
}
}
}
}
```
```

# 数据结构:二叉搜索树

Expand All @@ -206,17 +217,17 @@ fn bfs_search(target: Int, queue: Queue[IntTree]) -> Bool {
- 对于一棵树
- 如果为空,则替换为插入值构成的子树
- 如果为非空,则与当前值进行比较,选择适当的子树替换为插入值后的子树
```moonbit
fn insert(tree: IntTree, value: Int) -> IntTree {
match tree {
Empty => Node(value, Empty, Empty) // 若为空,则构建新树
Node(v, left, right) => // 若非空,则基于更新后的子树构建新的树
if value == v { tree } else
if value < v { Node(v, insert(left, value), right) } else
{ Node(v, left, insert(right, value)) }
```moonbit
fn insert(tree : IntTree, value : Int) -> IntTree {
match tree {
Empty => Node(value, left=Empty, right=Empty) // 若为空,则构建新树
Node(v, left~, right~) => // 若非空,则基于更新后的子树构建新的树
if value == v { tree } else
if value < v { Node(v, left=insert(left, value), right~) } else
{ Node(v, left~, right=insert(right, value)) }
}
}
}
```
```

# 二叉搜索树的删除

Expand All @@ -238,19 +249,19 @@ fn insert(tree: IntTree, value: Int) -> IntTree {
我们使用辅助函数`fn remove_largest(tree: IntTree) -> (IntTree, Int)`来找到并删除子树中的最大值。我们一路向右找到没有右子树的节点为止
```moonbit
match tree {
Node(v, left, Empty) => (left, v)
Node(v, left, right) => {
let (newRight, value) = remove_largest(right)
(Node(v, left, newRight), value)
Node(v, left~, Empty) => (left, v)
Node(v, left~, right~) => {
let (new_right, value) = remove_largest(right)
(Node(v, left~, right=new_right), value)
} }
```

我们定义删除操作`fn remove(tree: IntTree, value: Int) -> IntTree`
我们定义删除操作`fn remove(tree : IntTree, value : Int) -> IntTree`
```moonbit
match tree { ...
Node(root, left, right) => if root == value {
let (newLeft, newRoot) => remove_largest(left)
Node(newRoot, newLeft, right)
Node(root, left~, right~) => if root == value {
let (new_left, new_root) => remove_largest(left)
Node(new_root, left=new_left, right~)
} else ... }
```

Expand All @@ -273,10 +284,11 @@ match tree { ...
```moonbit
enum AVLTree {
Empty
Node(Int, AVLTree, AVLTree, Int) // 当前节点值、左子树、右子树、树高度
// 当前节点值、左子树、右子树、树高度
Node(Int, left~ : AVLTree, right~ : AVLTree, height~ : Int)
}
fn create(value: Int, left: AVLTree, right: AVLTree) -> AVLTree { ... }
fn height(tree: AVLTree) -> Int { ... }
fn create(value : Int, left : AVLTree, right : AVLTree) -> AVLTree { ... }
fn height(tree : AVLTree) -> Int { ... }
```

# 二叉平衡树 AVL Tree
Expand All @@ -288,18 +300,27 @@ fn height(tree: AVLTree) -> Int { ... }
我们对一棵树进行再平衡操作

```moonbit
fn balance(left: AVLTree, z: Int, right: AVLTree) -> AVLTree {
if height(left) > height(right) + 1 {
match left {
Node(y, left_l, left_r, _) =>
if height(left_l) >= height(left_r) {
create(left_l, y, create(lr, z, right)) // x在y z同侧
} else { match left_r {
Node(x, left_right_l, left_right_r, _) => // x在y z中间
create(create(left_l, y, left_right_l), x, create(left_right_r, z, right))
} }
fn balance(tree : AVLTree) -> AVLTree {
match tree {
Empty => Empty
Node(z, left~, right~, ..) if height(left) > height(right) + 1 => {
match left {
Node(y, left=left_l, right=left_r, ..) =>
if height(left_l) >= height(left_r) {
create(y, left_l, create(z, left_r, right)) // x在y z同侧
} else {
match left_r {
Node(x, left=left_right_l, right=left_right_r, ..) => // x在y z中间
create(x,
create(y, left_l, left_right_l),
create(z, left_right_r, right),
)
}
}
}
}
} else { ... }
...
}
}
```

Expand All @@ -308,12 +329,15 @@ fn balance(left: AVLTree, z: Int, right: AVLTree) -> AVLTree {
我们在添加后对生成的树进行再平衡

```moonbit
fn add(tree: AVLTree, value: Int) -> AVLTree {
fn add(tree : AVLTree, value : Int) -> AVLTree {
match tree {
Node(v, left, right, _) as t => {
if value < v { balance(add(left, value), v, right) } else { ... }
}
Empty => ...
Node(v, left~, right~, _) =>
if value < v {
balance(create(v, add(left, value), right))
} else {
balance(create(v, left, add(right, value)))
}
Empty => create(value, Empty, Empty)
}
}
```
Expand Down
Binary file modified course5/lec5.pdf
Binary file not shown.
Loading