This commit is contained in:
krahets 2023-02-09 04:43:12 +08:00
parent af3542e3c0
commit 4d2575be7a
18 changed files with 264 additions and 156 deletions

View file

@ -675,6 +675,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
for i := 0; i < len(nums); i++ { for i := 0; i < len(nums); i++ {
count++ count++
} }
count = 0
// 直接遍历数组 // 直接遍历数组
for range nums { for range nums {
count++ count++

View file

@ -389,7 +389,7 @@ comments: true
```go title="linked_list.go" ```go title="linked_list.go"
/* 在链表的结点 n0 之后插入结点 P */ /* 在链表的结点 n0 之后插入结点 P */
func insert(n0 *ListNode, P *ListNode) { func insertNode(n0 *ListNode, P *ListNode) {
n1 := n0.Next n1 := n0.Next
n0.Next = P n0.Next = P
P.Next = n1 P.Next = n1
@ -412,7 +412,7 @@ comments: true
```javascript title="linked_list.js" ```javascript title="linked_list.js"
/* 在链表的结点 n0 之后插入结点 P */ /* 在链表的结点 n0 之后插入结点 P */
function insert(n0, P) { function insert(n0, P) {
let n1 = n0.next; const n1 = n0.next;
n0.next = P; n0.next = P;
P.next = n1; P.next = n1;
} }
@ -422,8 +422,8 @@ comments: true
if (!n0.next) if (!n0.next)
return; return;
// n0 -> P -> n1 // n0 -> P -> n1
let P = n0.next; const P = n0.next;
let n1 = P.next; const n1 = P.next;
n0.next = n1; n0.next = n1;
} }
``` ```
@ -720,7 +720,7 @@ comments: true
```go title="linked_list.go" ```go title="linked_list.go"
/* 在链表中查找值为 target 的首个结点 */ /* 在链表中查找值为 target 的首个结点 */
func find(head *ListNode, target int) int { func findNode(head *ListNode, target int) int {
index := 0 index := 0
for head != nil { for head != nil {
if head.Val == target { if head.Val == target {

View file

@ -1101,6 +1101,12 @@ comments: true
// 更新列表容量 // 更新列表容量
l.numsCapacity = len(l.nums) l.numsCapacity = len(l.nums)
} }
/* 返回有效长度的列表 */
func (l *myList) toArray() []int {
// 仅转换有效长度范围内的列表元素
return l.nums[:l.numsSize]
}
``` ```
=== "JavaScript" === "JavaScript"

View file

@ -32,10 +32,16 @@ comments: true
既然实际测试具有很大的局限性,那么我们是否可以仅通过一些计算,就获知算法的效率水平呢?答案是肯定的,我们将此估算方法称为「复杂度分析 Complexity Analysis」或「渐近复杂度分析 Asymptotic Complexity Analysis」。 既然实际测试具有很大的局限性,那么我们是否可以仅通过一些计算,就获知算法的效率水平呢?答案是肯定的,我们将此估算方法称为「复杂度分析 Complexity Analysis」或「渐近复杂度分析 Asymptotic Complexity Analysis」。
**复杂度分析评估随着输入数据量的增长,算法的运行时间和占用空间的增长趋势**。根据时间和空间两方面,复杂度可分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」。 **复杂度分析评估的是算法运行效率随着输入数据量增多时的增长趋势**。这句话有些拗口,我们可以将其分为三个重点来理解:
- “算法运行效率”可分为“运行时间”和“占用空间”,进而可将复杂度分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」。
- “随着输入数据量增多时”代表复杂度与输入数据量有关,反映算法运行效率与输入数据量之间的关系;
- “增长趋势”表示复杂度分析不关心算法具体使用了多少时间或占用了多少空间,而是给出一种“趋势性分析”;
**复杂度分析克服了实际测试方法的弊端**。一是独立于测试环境,分析结果适用于所有运行平台。二是可以体现不同数据量下的算法效率,尤其是可以反映大数据量下的算法性能。 **复杂度分析克服了实际测试方法的弊端**。一是独立于测试环境,分析结果适用于所有运行平台。二是可以体现不同数据量下的算法效率,尤其是可以反映大数据量下的算法性能。
如果感觉对复杂度分析的概念一知半解,无需担心,后续章节会展开介绍。
## 2.1.3. 复杂度分析重要性 ## 2.1.3. 复杂度分析重要性
复杂度分析给出一把评价算法效率的“标尺”,告诉我们执行某个算法需要多少时间和空间资源,也让我们可以开展不同算法之间的效率对比。 复杂度分析给出一把评价算法效率的“标尺”,告诉我们执行某个算法需要多少时间和空间资源,也让我们可以开展不同算法之间的效率对比。

View file

@ -1289,8 +1289,8 @@ $$
if n <= 0 { if n <= 0 {
return 0 return 0
} }
// 数组 nums 长度为 n, n-1, ..., 2, 1
nums := make([]int, n) nums := make([]int, n)
fmt.Printf("递归 n = %d 中的 nums 长度 = %d \n", n, len(nums))
return spaceQuadraticRecur(n - 1) return spaceQuadraticRecur(n - 1)
} }
``` ```

View file

@ -80,6 +80,7 @@ comments: true
=== "Go" === "Go"
```go title="leetcode_two_sum.go" ```go title="leetcode_two_sum.go"
/* 方法一:暴力枚举 */
func twoSumBruteForce(nums []int, target int) []int { func twoSumBruteForce(nums []int, target int) []int {
size := len(nums) size := len(nums)
// 两层循环,时间复杂度 O(n^2) // 两层循环,时间复杂度 O(n^2)
@ -257,6 +258,7 @@ comments: true
=== "Go" === "Go"
```go title="leetcode_two_sum.go" ```go title="leetcode_two_sum.go"
/* 方法二:辅助哈希表 */
func twoSumHashTable(nums []int, target int) []int { func twoSumHashTable(nums []int, target int) []int {
// 辅助哈希表,空间复杂度 O(n) // 辅助哈希表,空间复杂度 O(n)
hashTable := map[int]int{} hashTable := map[int]int{}

View file

@ -560,7 +560,7 @@ $T(n)$ 是个一次函数,说明时间增长趋势是线性的,因此易得
2. **省略所有系数**。例如,循环 $2n$ 次、$5n + 1$ 次、……,都可以化简记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度也不产生影响。 2. **省略所有系数**。例如,循环 $2n$ 次、$5n + 1$ 次、……,都可以化简记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度也不产生影响。
3. **循环嵌套时使用乘法**。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用上述 `1.``2.` 技巧。 3. **循环嵌套时使用乘法**。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用上述 `1.``2.` 技巧。
根据以下示例,使用上述技巧前、后的统计结果分别为 以下示例展示了使用上述技巧前、后的统计结果。
$$ $$
\begin{aligned} \begin{aligned}
@ -838,7 +838,7 @@ $$
count := 0 count := 0
size := 100000 size := 100000
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
count ++ count++
} }
return count return count
} }

View file

@ -684,6 +684,7 @@ $$
bucket []*entry bucket []*entry
} }
/* 初始化哈希表 */
func newArrayHashMap() *arrayHashMap { func newArrayHashMap() *arrayHashMap {
// 初始化一个长度为 100 的桶(数组) // 初始化一个长度为 100 的桶(数组)
bucket := make([]*entry, 100) bucket := make([]*entry, 100)
@ -719,6 +720,48 @@ $$
// 置为 nil ,代表删除 // 置为 nil ,代表删除
a.bucket[index] = nil a.bucket[index] = nil
} }
/* 获取所有键对 */
func (a *arrayHashMap) entrySet() []*entry {
var pairs []*entry
for _, pair := range a.bucket {
if pair != nil {
pairs = append(pairs, pair)
}
}
return pairs
}
/* 获取所有键 */
func (a *arrayHashMap) keySet() []int {
var keys []int
for _, pair := range a.bucket {
if pair != nil {
keys = append(keys, pair.key)
}
}
return keys
}
/* 获取所有值 */
func (a *arrayHashMap) valueSet() []string {
var values []string
for _, pair := range a.bucket {
if pair != nil {
values = append(values, pair.val)
}
}
return values
}
/* 打印哈希表 */
func (a *arrayHashMap) print() {
for _, pair := range a.bucket {
if pair != nil {
fmt.Println(pair.key, "->", pair.val)
}
}
}
``` ```
=== "JavaScript" === "JavaScript"

View file

@ -304,18 +304,6 @@ comments: true
=== "Go" === "Go"
```go title="my_heap.go" ```go title="my_heap.go"
type maxHeap struct {
// 使用切片而非数组,这样无需考虑扩容问题
data []any
}
/* 构造函数,建立空堆 */
func newHeap() *maxHeap {
return &maxHeap{
data: make([]any, 0),
}
}
/* 获取左子结点索引 */ /* 获取左子结点索引 */
func (h *maxHeap) left(i int) int { func (h *maxHeap) left(i int) int {
return 2*i + 1 return 2*i + 1
@ -1060,8 +1048,8 @@ comments: true
func newMaxHeap(nums []any) *maxHeap { func newMaxHeap(nums []any) *maxHeap {
// 将列表元素原封不动添加进堆 // 将列表元素原封不动添加进堆
h := &maxHeap{data: nums} h := &maxHeap{data: nums}
// 堆化除叶结点以外的其他所有结点
for i := len(h.data) - 1; i >= 0; i-- { for i := len(h.data) - 1; i >= 0; i-- {
// 堆化除叶结点以外的其他所有结点
h.siftDown(i) h.siftDown(i)
} }
return h return h

View file

@ -122,12 +122,12 @@ $$
i, j := 0, len(nums)-1 i, j := 0, len(nums)-1
// 循环,当搜索区间为空时跳出(当 i > j 时为空) // 循环,当搜索区间为空时跳出(当 i > j 时为空)
for i <= j { for i <= j {
m := (i + j) / 2 // 计算中点索引 m m := (i + j) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j] if nums[m] < target { // 此情况说明 target 在区间 [m+1, j]
i = m + 1 i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中 } else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1 j = m - 1
} else { // 找到目标元素,返回其索引 } else { // 找到目标元素,返回其索引
return m return m
} }
} }
@ -316,12 +316,12 @@ $$
i, j := 0, len(nums) i, j := 0, len(nums)
// 循环,当搜索区间为空时跳出(当 i = j 时为空) // 循环,当搜索区间为空时跳出(当 i = j 时为空)
for i < j { for i < j {
m := (i + j) / 2 // 计算中点索引 m m := (i + j) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j) if nums[m] < target { // 此情况说明 target 在区间 [m+1, j)
i = m + 1 i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中 } else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中
j = m j = m
} else { // 找到目标元素,返回其索引 } else { // 找到目标元素,返回其索引
return m return m
} }
} }

View file

@ -55,8 +55,8 @@ comments: true
```go title="hashing_search.go" ```go title="hashing_search.go"
/* 哈希查找(数组) */ /* 哈希查找(数组) */
func hashingSearchArray(m map[int]int, target int) int { func hashingSearchArray(m map[int]int, target int) int {
// 哈希表的 key: 目标元素value: 索引 // 哈希表的 key: 目标元素value: 索引
// 若哈希表中无此 key ,返回 -1 // 若哈希表中无此 key ,返回 -1
if index, ok := m[target]; ok { if index, ok := m[target]; ok {
return index return index
} else { } else {

View file

@ -206,8 +206,8 @@ comments: true
=== "Go" === "Go"
```go title="linear_search.go" ```go title="linear_search.go"
/* 线性查找(链表)*/ /* 线性查找(链表) */
func linerSearchLinkedList(node *ListNode, target int) *ListNode { func linearSearchLinkedList(node *ListNode, target int) *ListNode {
// 遍历链表 // 遍历链表
for node != nil { for node != nil {
// 找到目标结点,返回之 // 找到目标结点,返回之

View file

@ -87,10 +87,10 @@ comments: true
j := i - 1 j := i - 1
// 内循环:将 base 插入到左边的正确位置 // 内循环:将 base 插入到左边的正确位置
for j >= 0 && nums[j] > base { for j >= 0 && nums[j] > base {
nums[j+1] = nums[j] // 1. 将 nums[j] 向右移动一位 nums[j+1] = nums[j] // 1. 将 nums[j] 向右移动一位
j-- j--
} }
nums[j+1] = base // 2. 将 base 赋值到正确位置 nums[j+1] = base // 2. 将 base 赋值到正确位置
} }
} }
``` ```

View file

@ -118,7 +118,7 @@ comments: true
```go title="quick_sort.go" ```go title="quick_sort.go"
/* 哨兵划分 */ /* 哨兵划分 */
func partition(nums []int, left, right int) int { func (q *quickSort) partition(nums []int, left, right int) int {
// 以 nums[left] 作为基准数 // 以 nums[left] 作为基准数
i, j := left, right i, j := left, right
for i < j { for i < j {
@ -128,7 +128,7 @@ comments: true
for i < j && nums[i] <= nums[left] { for i < j && nums[i] <= nums[left] {
i++ // 从左向右找首个大于基准数的元素 i++ // 从左向右找首个大于基准数的元素
} }
//元素交换 // 元素交换
nums[i], nums[j] = nums[j], nums[i] nums[i], nums[j] = nums[j], nums[i]
} }
// 将基准数交换至两子数组的分界线 // 将基准数交换至两子数组的分界线
@ -332,16 +332,16 @@ comments: true
```go title="quick_sort.go" ```go title="quick_sort.go"
/* 快速排序 */ /* 快速排序 */
func quickSort(nums []int, left, right int) { func (q *quickSort) quickSort(nums []int, left, right int) {
// 子数组长度为 1 时终止递归 // 子数组长度为 1 时终止递归
if left >= right { if left >= right {
return return
} }
// 哨兵划分 // 哨兵划分
pivot := partition(nums, left, right) pivot := q.partition(nums, left, right)
// 递归左子数组、右子数组 // 递归左子数组、右子数组
quickSort(nums, left, pivot-1) q.quickSort(nums, left, pivot-1)
quickSort(nums, pivot+1, right) q.quickSort(nums, pivot+1, right)
} }
``` ```
@ -560,23 +560,38 @@ comments: true
```go title="quick_sort.go" ```go title="quick_sort.go"
/* 选取三个元素的中位数 */ /* 选取三个元素的中位数 */
func medianThree(nums []int, left, mid, right int) int { func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int {
// 使用了异或操作来简化代码(!= 在这里起到异或的作用)
// 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
if (nums[left] < nums[mid]) != (nums[left] < nums[right]) { if (nums[left] < nums[mid]) != (nums[left] < nums[right]) {
return left return left
} else if (nums[mid] > nums[left]) != (nums[mid] > nums[right]) { } else if (nums[mid] < nums[left]) != (nums[mid] < nums[right]) {
return mid return mid
} }
return right return right
} }
/* 哨兵划分(三数取中值)*/ /* 哨兵划分(三数取中值)*/
func partition(nums []int, left, right int) int { func (q *quickSortMedian) partition(nums []int, left, right int) int {
// 以 nums[left] 作为基准数 // 以 nums[left] 作为基准数
med := medianThree(nums, left, (left+right)/2, right) med := q.medianThree(nums, left, (left+right)/2, right)
// 将中位数交换至数组最左端 // 将中位数交换至数组最左端
nums[left], nums[med] = nums[med], nums[left] nums[left], nums[med] = nums[med], nums[left]
// 以 nums[left] 作为基准数 // 以 nums[left] 作为基准数
// 下同省略... i, j := left, right
for i < j {
for i < j && nums[j] >= nums[left] {
j-- //从右向左找首个小于基准数的元素
}
for i < j && nums[i] <= nums[left] {
i++ //从左向右找首个大于基准数的元素
}
//元素交换
nums[i], nums[j] = nums[j], nums[i]
}
//将基准数交换至两子数组的分界线
nums[i], nums[left] = nums[left], nums[i]
return i //返回基准数的索引
} }
``` ```
@ -792,18 +807,18 @@ comments: true
```go title="quick_sort.go" ```go title="quick_sort.go"
/* 快速排序(尾递归优化)*/ /* 快速排序(尾递归优化)*/
func quickSort(nums []int, left, right int) { func (q *quickSortTailCall) quickSort(nums []int, left, right int) {
// 子数组长度为 1 时终止 // 子数组长度为 1 时终止
for left < right { for left < right {
// 哨兵划分操作 // 哨兵划分操作
pivot := partition(nums, left, right) pivot := q.partition(nums, left, right)
// 对两个子数组中较短的那个执行快排 // 对两个子数组中较短的那个执行快排
if pivot-left < right-pivot { if pivot-left < right-pivot {
quickSort(nums, left, pivot-1) // 递归排序左子数组 q.quickSort(nums, left, pivot-1) // 递归排序左子数组
left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right] left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right]
} else { } else {
quickSort(nums, pivot+1, right) // 递归排序右子数组 q.quickSort(nums, pivot+1, right) // 递归排序右子数组
right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1] right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1]
} }
} }
} }

View file

@ -494,19 +494,19 @@ comments: true
data *list.List data *list.List
} }
// newLinkedListQueue 初始化链表 /* 初始化队列 */
func newLinkedListQueue() *linkedListQueue { func newLinkedListQueue() *linkedListQueue {
return &linkedListQueue{ return &linkedListQueue{
data: list.New(), data: list.New(),
} }
} }
// push 入队 /* 入队 */
func (s *linkedListQueue) push(value any) { func (s *linkedListQueue) push(value any) {
s.data.PushBack(value) s.data.PushBack(value)
} }
// poll 出队 /* 出队 */
func (s *linkedListQueue) poll() any { func (s *linkedListQueue) poll() any {
if s.isEmpty() { if s.isEmpty() {
return nil return nil
@ -516,7 +516,7 @@ comments: true
return e.Value return e.Value
} }
// peek 访问队首元素 /* 访问队首元素 */
func (s *linkedListQueue) peek() any { func (s *linkedListQueue) peek() any {
if s.isEmpty() { if s.isEmpty() {
return nil return nil
@ -525,15 +525,20 @@ comments: true
return e.Value return e.Value
} }
// size 获取队列的长度 /* 获取队列的长度 */
func (s *linkedListQueue) size() int { func (s *linkedListQueue) size() int {
return s.data.Len() return s.data.Len()
} }
// isEmpty 判断队列是否为空 /* 判断队列是否为空 */
func (s *linkedListQueue) isEmpty() bool { func (s *linkedListQueue) isEmpty() bool {
return s.data.Len() == 0 return s.data.Len() == 0
} }
/* 获取 List 用于打印 */
func (s *linkedListQueue) toList() *list.List {
return s.data
}
``` ```
=== "JavaScript" === "JavaScript"
@ -1073,7 +1078,7 @@ comments: true
queCapacity int // 队列容量(即最大容纳元素数量) queCapacity int // 队列容量(即最大容纳元素数量)
} }
// newArrayQueue 基于环形数组实现的队列 /* 初始化队列 */
func newArrayQueue(queCapacity int) *arrayQueue { func newArrayQueue(queCapacity int) *arrayQueue {
return &arrayQueue{ return &arrayQueue{
nums: make([]int, queCapacity), nums: make([]int, queCapacity),
@ -1083,17 +1088,17 @@ comments: true
} }
} }
// size 获取队列的长度 /* 获取队列的长度 */
func (q *arrayQueue) size() int { func (q *arrayQueue) size() int {
return q.queSize return q.queSize
} }
// isEmpty 判断队列是否为空 /* 判断队列是否为空 */
func (q *arrayQueue) isEmpty() bool { func (q *arrayQueue) isEmpty() bool {
return q.queSize == 0 return q.queSize == 0
} }
// push 入队 /* 入队 */
func (q *arrayQueue) push(num int) { func (q *arrayQueue) push(num int) {
// 当 rear == queCapacity 表示队列已满 // 当 rear == queCapacity 表示队列已满
if q.queSize == q.queCapacity { if q.queSize == q.queCapacity {
@ -1107,7 +1112,7 @@ comments: true
q.queSize++ q.queSize++
} }
// poll 出队 /* 出队 */
func (q *arrayQueue) poll() any { func (q *arrayQueue) poll() any {
num := q.peek() num := q.peek()
// 队首指针向后移动一位,若越过尾部则返回到数组头部 // 队首指针向后移动一位,若越过尾部则返回到数组头部
@ -1116,7 +1121,7 @@ comments: true
return num return num
} }
// peek 访问队首元素 /* 访问队首元素 */
func (q *arrayQueue) peek() any { func (q *arrayQueue) peek() any {
if q.isEmpty() { if q.isEmpty() {
return nil return nil
@ -1124,7 +1129,7 @@ comments: true
return q.nums[q.front] return q.nums[q.front]
} }
// 获取 Slice 用于打印 /* 获取 Slice 用于打印 */
func (q *arrayQueue) toSlice() []int { func (q *arrayQueue) toSlice() []int {
rear := (q.front + q.queSize) rear := (q.front + q.queSize)
if rear >= q.queCapacity { if rear >= q.queCapacity {

View file

@ -466,19 +466,19 @@ comments: true
data *list.List data *list.List
} }
// newLinkedListStack 初始化链表 /* 初始化栈 */
func newLinkedListStack() *linkedListStack { func newLinkedListStack() *linkedListStack {
return &linkedListStack{ return &linkedListStack{
data: list.New(), data: list.New(),
} }
} }
// push 入栈 /* 入栈 */
func (s *linkedListStack) push(value int) { func (s *linkedListStack) push(value int) {
s.data.PushBack(value) s.data.PushBack(value)
} }
// pop 出栈 /* 出栈 */
func (s *linkedListStack) pop() any { func (s *linkedListStack) pop() any {
if s.isEmpty() { if s.isEmpty() {
return nil return nil
@ -488,7 +488,7 @@ comments: true
return e.Value return e.Value
} }
// peek 访问栈顶元素 /* 访问栈顶元素 */
func (s *linkedListStack) peek() any { func (s *linkedListStack) peek() any {
if s.isEmpty() { if s.isEmpty() {
return nil return nil
@ -497,15 +497,20 @@ comments: true
return e.Value return e.Value
} }
// size 获取栈的长度 /* 获取栈的长度 */
func (s *linkedListStack) size() int { func (s *linkedListStack) size() int {
return s.data.Len() return s.data.Len()
} }
// isEmpty 判断栈是否为空 /* 判断栈是否为空 */
func (s *linkedListStack) isEmpty() bool { func (s *linkedListStack) isEmpty() bool {
return s.data.Len() == 0 return s.data.Len() == 0
} }
/* 获取 List 用于打印 */
func (s *linkedListStack) toList() *list.List {
return s.data
}
``` ```
=== "JavaScript" === "JavaScript"
@ -918,6 +923,7 @@ comments: true
data []int // 数据 data []int // 数据
} }
/* 初始化栈 */
func newArrayStack() *arrayStack { func newArrayStack() *arrayStack {
return &arrayStack{ return &arrayStack{
// 设置栈的长度为 0容量为 16 // 设置栈的长度为 0容量为 16
@ -925,34 +931,30 @@ comments: true
} }
} }
// size 栈的长度 /* 栈的长度 */
func (s *arrayStack) size() int { func (s *arrayStack) size() int {
return len(s.data) return len(s.data)
} }
// isEmpty 栈是否为空 /* 栈是否为空 */
func (s *arrayStack) isEmpty() bool { func (s *arrayStack) isEmpty() bool {
return s.size() == 0 return s.size() == 0
} }
// push 入栈 /* 入栈 */
func (s *arrayStack) push(v int) { func (s *arrayStack) push(v int) {
// 切片会自动扩容 // 切片会自动扩容
s.data = append(s.data, v) s.data = append(s.data, v)
} }
// pop 出栈 /* 出栈 */
func (s *arrayStack) pop() any { func (s *arrayStack) pop() any {
// 弹出栈前,先判断是否为空
if s.isEmpty() {
return nil
}
val := s.peek() val := s.peek()
s.data = s.data[:len(s.data)-1] s.data = s.data[:len(s.data)-1]
return val return val
} }
// peek 获取栈顶元素 /* 获取栈顶元素 */
func (s *arrayStack) peek() any { func (s *arrayStack) peek() any {
if s.isEmpty() { if s.isEmpty() {
return nil return nil
@ -960,6 +962,11 @@ comments: true
val := s.data[len(s.data)-1] val := s.data[len(s.data)-1]
return val return val
} }
/* 获取 Slice 用于打印 */
func (s *arrayStack) toSlice() []int {
return s.data
}
``` ```
=== "JavaScript" === "JavaScript"

133
chapter_tree/avl_tree.md Executable file → Normal file
View file

@ -207,7 +207,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
```go title="avl_tree.go" ```go title="avl_tree.go"
/* 获取结点高度 */ /* 获取结点高度 */
func height(node *TreeNode) int { func (t *aVLTree) height(node *TreeNode) int {
// 空结点高度为 -1 ,叶结点高度为 0 // 空结点高度为 -1 ,叶结点高度为 0
if node != nil { if node != nil {
return node.Height return node.Height
@ -216,9 +216,9 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
} }
/* 更新结点高度 */ /* 更新结点高度 */
func updateHeight(node *TreeNode) { func (t *aVLTree) updateHeight(node *TreeNode) {
lh := height(node.Left) lh := t.height(node.Left)
rh := height(node.Right) rh := t.height(node.Right)
// 结点高度等于最高子树高度 + 1 // 结点高度等于最高子树高度 + 1
if lh > rh { if lh > rh {
node.Height = lh + 1 node.Height = lh + 1
@ -350,13 +350,13 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
```go title="avl_tree.go" ```go title="avl_tree.go"
/* 获取平衡因子 */ /* 获取平衡因子 */
func balanceFactor(node *TreeNode) int { func (t *aVLTree) balanceFactor(node *TreeNode) int {
// 空结点平衡因子为 0 // 空结点平衡因子为 0
if node == nil { if node == nil {
return 0 return 0
} }
// 结点平衡因子 = 左子树高度 - 右子树高度 // 结点平衡因子 = 左子树高度 - 右子树高度
return height(node.Left) - height(node.Right) return t.height(node.Left) - t.height(node.Right)
} }
``` ```
@ -512,15 +512,15 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
```go title="avl_tree.go" ```go title="avl_tree.go"
/* 右旋操作 */ /* 右旋操作 */
func rightRotate(node *TreeNode) *TreeNode { func (t *aVLTree) rightRotate(node *TreeNode) *TreeNode {
child := node.Left child := node.Left
grandChild := child.Right grandChild := child.Right
// 以 child 为原点,将 node 向右旋转 // 以 child 为原点,将 node 向右旋转
child.Right = node child.Right = node
node.Left = grandChild node.Left = grandChild
// 更新结点高度 // 更新结点高度
updateHeight(node) t.updateHeight(node)
updateHeight(child) t.updateHeight(child)
// 返回旋转后子树的根结点 // 返回旋转后子树的根结点
return child return child
} }
@ -680,15 +680,15 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
```go title="avl_tree.go" ```go title="avl_tree.go"
/* 左旋操作 */ /* 左旋操作 */
func leftRotate(node *TreeNode) *TreeNode { func (t *aVLTree) leftRotate(node *TreeNode) *TreeNode {
child := node.Right child := node.Right
grandChild := child.Left grandChild := child.Left
// 以 child 为原点,将 node 向左旋转 // 以 child 为原点,将 node 向左旋转
child.Left = node child.Left = node
node.Right = grandChild node.Right = grandChild
// 更新结点高度 // 更新结点高度
updateHeight(node) t.updateHeight(node)
updateHeight(child) t.updateHeight(child)
// 返回旋转后子树的根结点 // 返回旋转后子树的根结点
return child return child
} }
@ -915,30 +915,30 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
```go title="avl_tree.go" ```go title="avl_tree.go"
/* 执行旋转操作,使该子树重新恢复平衡 */ /* 执行旋转操作,使该子树重新恢复平衡 */
func rotate(node *TreeNode) *TreeNode { func (t *aVLTree) rotate(node *TreeNode) *TreeNode {
// 获取结点 node 的平衡因子 // 获取结点 node 的平衡因子
// Go 推荐短变量,这里 bf 指代 balanceFactor // Go 推荐短变量,这里 bf 指代 t.balanceFactor
bf := balanceFactor(node) bf := t.balanceFactor(node)
// 左偏树 // 左偏树
if bf > 1 { if bf > 1 {
if balanceFactor(node.Left) >= 0 { if t.balanceFactor(node.Left) >= 0 {
// 右旋 // 右旋
return rightRotate(node) return t.rightRotate(node)
} else { } else {
// 先左旋后右旋 // 先左旋后右旋
node.Left = leftRotate(node.Left) node.Left = t.leftRotate(node.Left)
return rightRotate(node) return t.rightRotate(node)
} }
} }
// 右偏树 // 右偏树
if bf < -1 { if bf < -1 {
if balanceFactor(node.Right) <= 0 { if t.balanceFactor(node.Right) <= 0 {
// 左旋 // 左旋
return leftRotate(node) return t.leftRotate(node)
} else { } else {
// 先右旋后左旋 // 先右旋后左旋
node.Right = rightRotate(node.Right) node.Right = t.rightRotate(node.Right)
return leftRotate(node) return t.leftRotate(node)
} }
} }
// 平衡树,无需旋转,直接返回 // 平衡树,无需旋转,直接返回
@ -1193,28 +1193,29 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
```go title="avl_tree.go" ```go title="avl_tree.go"
/* 插入结点 */ /* 插入结点 */
func (t *avlTree) insert(val int) *TreeNode { func (t *aVLTree) insert(val int) *TreeNode {
t.root = insertHelper(t.root, val) t.root = t.insertHelper(t.root, val)
return t.root return t.root
} }
/* 递归插入结点(辅助函数) */ /* 递归插入结点(辅助函数) */
func insertHelper(node *TreeNode, val int) *TreeNode { func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode {
if node == nil { if node == nil {
return NewTreeNode(val) return NewTreeNode(val)
} }
/* 1. 查找插入位置,并插入结点 */ /* 1. 查找插入位置,并插入结点 */
if val < node.Val { if val < node.Val {
node.Left = insertHelper(node.Left, val) node.Left = t.insertHelper(node.Left, val)
} else if val > node.Val { } else if val > node.Val {
node.Right = insertHelper(node.Right, val) node.Right = t.insertHelper(node.Right, val)
} else { } else {
// 重复结点不插入,直接返回 // 重复结点不插入,直接返回
return node return node
} }
// 更新结点高度 // 更新结点高度
updateHeight(node) t.updateHeight(node)
/* 2. 执行旋转操作,使该子树重新恢复平衡 */ /* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node) node = t.rotate(node)
// 返回子树的根结点 // 返回子树的根结点
return node return node
} }
@ -1445,6 +1446,16 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
// 返回子树的根结点 // 返回子树的根结点
return node; return node;
} }
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
TreeNode* getInOrderNext(TreeNode* node) {
if (node == nullptr) return node;
// 循环访问左子结点,直到叶结点时为最小结点,跳出
while (node->left != nullptr) {
node = node->left;
}
return node;
}
``` ```
=== "Python" === "Python"
@ -1481,27 +1492,36 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
self.__update_height(node) self.__update_height(node)
# 2. 执行旋转操作,使该子树重新恢复平衡 # 2. 执行旋转操作,使该子树重新恢复平衡
return self.__rotate(node) return self.__rotate(node)
""" 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """
def __get_inorder_next(self, node: Optional[TreeNode]) -> Optional[TreeNode]:
if node is None:
return None
# 循环访问左子结点,直到叶结点时为最小结点,跳出
while node.left is not None:
node = node.left
return node
``` ```
=== "Go" === "Go"
```go title="avl_tree.go" ```go title="avl_tree.go"
/* 删除结点 */ /* 删除结点 */
func (t *avlTree) remove(val int) *TreeNode { func (t *aVLTree) remove(val int) *TreeNode {
root := removeHelper(t.root, val) root := t.removeHelper(t.root, val)
return root return root
} }
/* 递归删除结点(辅助函数) */ /* 递归删除结点(辅助函数) */
func removeHelper(node *TreeNode, val int) *TreeNode { func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode {
if node == nil { if node == nil {
return nil return nil
} }
/* 1. 查找结点,并删除之 */ /* 1. 查找结点,并删除之 */
if val < node.Val { if val < node.Val {
node.Left = removeHelper(node.Left, val) node.Left = t.removeHelper(node.Left, val)
} else if val > node.Val { } else if val > node.Val {
node.Right = removeHelper(node.Right, val) node.Right = t.removeHelper(node.Right, val)
} else { } else {
if node.Left == nil || node.Right == nil { if node.Left == nil || node.Right == nil {
child := node.Left child := node.Left
@ -1517,18 +1537,30 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
} }
} else { } else {
// 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点
temp := getInOrderNext(node.Right) temp := t.getInOrderNext(node.Right)
node.Right = removeHelper(node.Right, temp.Val) node.Right = t.removeHelper(node.Right, temp.Val)
node.Val = temp.Val node.Val = temp.Val
} }
} }
// 更新结点高度 // 更新结点高度
updateHeight(node) t.updateHeight(node)
/* 2. 执行旋转操作,使该子树重新恢复平衡 */ /* 2. 执行旋转操作,使该子树重新恢复平衡 */
node = rotate(node) node = t.rotate(node)
// 返回子树的根结点 // 返回子树的根结点
return node return node
} }
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
func (t *aVLTree) getInOrderNext(node *TreeNode) *TreeNode {
if node == nil {
return node
}
// 循环访问左子结点,直到叶结点时为最小结点,跳出
for node.Left != nil {
node = node.Left
}
return node
}
``` ```
=== "JavaScript" === "JavaScript"
@ -1567,16 +1599,6 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
return node; return node;
} }
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
getInOrderNext(node) {
if (node === null) return node;
// 循环访问左子结点,直到叶结点时为最小结点,跳出
while (node.left !== null) {
node = node.left;
}
return node;
}
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
getInOrderNext(node) { getInOrderNext(node) {
if (node === null) return node; if (node === null) return node;
@ -1750,6 +1772,19 @@ AVL 树的独特之处在于「旋转 Rotation」的操作其可 **在不影
// 返回子树的根结点 // 返回子树的根结点
return node return node
} }
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
func getInOrderNext(node: TreeNode?) -> TreeNode? {
var node = node
if node == nil {
return node
}
// 循环访问左子结点,直到叶结点时为最小结点,跳出
while node?.left != nil {
node = node?.left
}
return node
}
``` ```
=== "Zig" === "Zig"

View file

@ -86,7 +86,7 @@ comments: true
```go title="binary_tree_bfs.go" ```go title="binary_tree_bfs.go"
/* 层序遍历 */ /* 层序遍历 */
func levelOrder(root *TreeNode) []int { func hierOrder(root *TreeNode) []int {
// 初始化队列,加入根结点 // 初始化队列,加入根结点
queue := list.New() queue := list.New()
queue.PushBack(root) queue.PushBack(root)