mirror of
https://github.com/krahets/hello-algo.git
synced 2024-12-26 12:46:31 +08:00
build
This commit is contained in:
parent
af3542e3c0
commit
4d2575be7a
18 changed files with 264 additions and 156 deletions
|
@ -675,6 +675,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
|||
for i := 0; i < len(nums); i++ {
|
||||
count++
|
||||
}
|
||||
count = 0
|
||||
// 直接遍历数组
|
||||
for range nums {
|
||||
count++
|
||||
|
|
|
@ -389,7 +389,7 @@ comments: true
|
|||
|
||||
```go title="linked_list.go"
|
||||
/* 在链表的结点 n0 之后插入结点 P */
|
||||
func insert(n0 *ListNode, P *ListNode) {
|
||||
func insertNode(n0 *ListNode, P *ListNode) {
|
||||
n1 := n0.Next
|
||||
n0.Next = P
|
||||
P.Next = n1
|
||||
|
@ -412,7 +412,7 @@ comments: true
|
|||
```javascript title="linked_list.js"
|
||||
/* 在链表的结点 n0 之后插入结点 P */
|
||||
function insert(n0, P) {
|
||||
let n1 = n0.next;
|
||||
const n1 = n0.next;
|
||||
n0.next = P;
|
||||
P.next = n1;
|
||||
}
|
||||
|
@ -422,8 +422,8 @@ comments: true
|
|||
if (!n0.next)
|
||||
return;
|
||||
// n0 -> P -> n1
|
||||
let P = n0.next;
|
||||
let n1 = P.next;
|
||||
const P = n0.next;
|
||||
const n1 = P.next;
|
||||
n0.next = n1;
|
||||
}
|
||||
```
|
||||
|
@ -720,7 +720,7 @@ comments: true
|
|||
|
||||
```go title="linked_list.go"
|
||||
/* 在链表中查找值为 target 的首个结点 */
|
||||
func find(head *ListNode, target int) int {
|
||||
func findNode(head *ListNode, target int) int {
|
||||
index := 0
|
||||
for head != nil {
|
||||
if head.Val == target {
|
||||
|
|
|
@ -1101,6 +1101,12 @@ comments: true
|
|||
// 更新列表容量
|
||||
l.numsCapacity = len(l.nums)
|
||||
}
|
||||
|
||||
/* 返回有效长度的列表 */
|
||||
func (l *myList) toArray() []int {
|
||||
// 仅转换有效长度范围内的列表元素
|
||||
return l.nums[:l.numsSize]
|
||||
}
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
|
|
@ -32,10 +32,16 @@ comments: true
|
|||
|
||||
既然实际测试具有很大的局限性,那么我们是否可以仅通过一些计算,就获知算法的效率水平呢?答案是肯定的,我们将此估算方法称为「复杂度分析 Complexity Analysis」或「渐近复杂度分析 Asymptotic Complexity Analysis」。
|
||||
|
||||
**复杂度分析评估随着输入数据量的增长,算法的运行时间和占用空间的增长趋势**。根据时间和空间两方面,复杂度可分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」。
|
||||
**复杂度分析评估的是算法运行效率随着输入数据量增多时的增长趋势**。这句话有些拗口,我们可以将其分为三个重点来理解:
|
||||
|
||||
- “算法运行效率”可分为“运行时间”和“占用空间”,进而可将复杂度分为「时间复杂度 Time Complexity」和「空间复杂度 Space Complexity」。
|
||||
- “随着输入数据量增多时”代表复杂度与输入数据量有关,反映算法运行效率与输入数据量之间的关系;
|
||||
- “增长趋势”表示复杂度分析不关心算法具体使用了多少时间或占用了多少空间,而是给出一种“趋势性分析”;
|
||||
|
||||
**复杂度分析克服了实际测试方法的弊端**。一是独立于测试环境,分析结果适用于所有运行平台。二是可以体现不同数据量下的算法效率,尤其是可以反映大数据量下的算法性能。
|
||||
|
||||
如果感觉对复杂度分析的概念一知半解,无需担心,后续章节会展开介绍。
|
||||
|
||||
## 2.1.3. 复杂度分析重要性
|
||||
|
||||
复杂度分析给出一把评价算法效率的“标尺”,告诉我们执行某个算法需要多少时间和空间资源,也让我们可以开展不同算法之间的效率对比。
|
||||
|
|
|
@ -1289,8 +1289,8 @@ $$
|
|||
if n <= 0 {
|
||||
return 0
|
||||
}
|
||||
// 数组 nums 长度为 n, n-1, ..., 2, 1
|
||||
nums := make([]int, n)
|
||||
fmt.Printf("递归 n = %d 中的 nums 长度 = %d \n", n, len(nums))
|
||||
return spaceQuadraticRecur(n - 1)
|
||||
}
|
||||
```
|
||||
|
|
|
@ -80,6 +80,7 @@ comments: true
|
|||
=== "Go"
|
||||
|
||||
```go title="leetcode_two_sum.go"
|
||||
/* 方法一:暴力枚举 */
|
||||
func twoSumBruteForce(nums []int, target int) []int {
|
||||
size := len(nums)
|
||||
// 两层循环,时间复杂度 O(n^2)
|
||||
|
@ -257,6 +258,7 @@ comments: true
|
|||
=== "Go"
|
||||
|
||||
```go title="leetcode_two_sum.go"
|
||||
/* 方法二:辅助哈希表 */
|
||||
func twoSumHashTable(nums []int, target int) []int {
|
||||
// 辅助哈希表,空间复杂度 O(n)
|
||||
hashTable := map[int]int{}
|
||||
|
|
|
@ -560,7 +560,7 @@ $T(n)$ 是个一次函数,说明时间增长趋势是线性的,因此易得
|
|||
2. **省略所有系数**。例如,循环 $2n$ 次、$5n + 1$ 次、……,都可以化简记为 $n$ 次,因为 $n$ 前面的系数对时间复杂度也不产生影响。
|
||||
3. **循环嵌套时使用乘法**。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套用上述 `1.` 和 `2.` 技巧。
|
||||
|
||||
根据以下示例,使用上述技巧前、后的统计结果分别为
|
||||
以下示例展示了使用上述技巧前、后的统计结果。
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
|
@ -838,7 +838,7 @@ $$
|
|||
count := 0
|
||||
size := 100000
|
||||
for i := 0; i < size; i++ {
|
||||
count ++
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
|
|
@ -684,6 +684,7 @@ $$
|
|||
bucket []*entry
|
||||
}
|
||||
|
||||
/* 初始化哈希表 */
|
||||
func newArrayHashMap() *arrayHashMap {
|
||||
// 初始化一个长度为 100 的桶(数组)
|
||||
bucket := make([]*entry, 100)
|
||||
|
@ -719,6 +720,48 @@ $$
|
|||
// 置为 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"
|
||||
|
|
|
@ -304,18 +304,6 @@ comments: true
|
|||
=== "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 {
|
||||
return 2*i + 1
|
||||
|
@ -1060,8 +1048,8 @@ comments: true
|
|||
func newMaxHeap(nums []any) *maxHeap {
|
||||
// 将列表元素原封不动添加进堆
|
||||
h := &maxHeap{data: nums}
|
||||
// 堆化除叶结点以外的其他所有结点
|
||||
for i := len(h.data) - 1; i >= 0; i-- {
|
||||
// 堆化除叶结点以外的其他所有结点
|
||||
h.siftDown(i)
|
||||
}
|
||||
return h
|
||||
|
|
|
@ -122,12 +122,12 @@ $$
|
|||
i, j := 0, len(nums)-1
|
||||
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
|
||||
for i <= j {
|
||||
m := (i + j) / 2 // 计算中点索引 m
|
||||
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j] 中
|
||||
m := (i + j) / 2 // 计算中点索引 m
|
||||
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j] 中
|
||||
i = m + 1
|
||||
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中
|
||||
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中
|
||||
j = m - 1
|
||||
} else { // 找到目标元素,返回其索引
|
||||
} else { // 找到目标元素,返回其索引
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
@ -316,12 +316,12 @@ $$
|
|||
i, j := 0, len(nums)
|
||||
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
|
||||
for i < j {
|
||||
m := (i + j) / 2 // 计算中点索引 m
|
||||
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j) 中
|
||||
m := (i + j) / 2 // 计算中点索引 m
|
||||
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j) 中
|
||||
i = m + 1
|
||||
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中
|
||||
j = m
|
||||
} else { // 找到目标元素,返回其索引
|
||||
} else { // 找到目标元素,返回其索引
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,8 +55,8 @@ comments: true
|
|||
```go title="hashing_search.go"
|
||||
/* 哈希查找(数组) */
|
||||
func hashingSearchArray(m map[int]int, target int) int {
|
||||
// 哈希表的 key: 目标元素,value: 索引
|
||||
// 若哈希表中无此 key ,返回 -1
|
||||
// 哈希表的 key: 目标元素,value: 索引
|
||||
// 若哈希表中无此 key ,返回 -1
|
||||
if index, ok := m[target]; ok {
|
||||
return index
|
||||
} else {
|
||||
|
|
|
@ -206,8 +206,8 @@ comments: true
|
|||
=== "Go"
|
||||
|
||||
```go title="linear_search.go"
|
||||
/* 线性查找(链表)*/
|
||||
func linerSearchLinkedList(node *ListNode, target int) *ListNode {
|
||||
/* 线性查找(链表) */
|
||||
func linearSearchLinkedList(node *ListNode, target int) *ListNode {
|
||||
// 遍历链表
|
||||
for node != nil {
|
||||
// 找到目标结点,返回之
|
||||
|
|
|
@ -87,10 +87,10 @@ comments: true
|
|||
j := i - 1
|
||||
// 内循环:将 base 插入到左边的正确位置
|
||||
for j >= 0 && nums[j] > base {
|
||||
nums[j+1] = nums[j] // 1. 将 nums[j] 向右移动一位
|
||||
nums[j+1] = nums[j] // 1. 将 nums[j] 向右移动一位
|
||||
j--
|
||||
}
|
||||
nums[j+1] = base // 2. 将 base 赋值到正确位置
|
||||
nums[j+1] = base // 2. 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -118,7 +118,7 @@ comments: true
|
|||
|
||||
```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] 作为基准数
|
||||
i, j := left, right
|
||||
for i < j {
|
||||
|
@ -128,7 +128,7 @@ comments: true
|
|||
for i < j && nums[i] <= nums[left] {
|
||||
i++ // 从左向右找首个大于基准数的元素
|
||||
}
|
||||
//元素交换
|
||||
// 元素交换
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
}
|
||||
// 将基准数交换至两子数组的分界线
|
||||
|
@ -332,16 +332,16 @@ comments: true
|
|||
|
||||
```go title="quick_sort.go"
|
||||
/* 快速排序 */
|
||||
func quickSort(nums []int, left, right int) {
|
||||
func (q *quickSort) quickSort(nums []int, left, right int) {
|
||||
// 子数组长度为 1 时终止递归
|
||||
if left >= right {
|
||||
return
|
||||
}
|
||||
// 哨兵划分
|
||||
pivot := partition(nums, left, right)
|
||||
pivot := q.partition(nums, left, right)
|
||||
// 递归左子数组、右子数组
|
||||
quickSort(nums, left, pivot-1)
|
||||
quickSort(nums, pivot+1, right)
|
||||
q.quickSort(nums, left, pivot-1)
|
||||
q.quickSort(nums, pivot+1, right)
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -560,23 +560,38 @@ comments: true
|
|||
|
||||
```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]) {
|
||||
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 right
|
||||
}
|
||||
|
||||
/* 哨兵划分(三数取中值)*/
|
||||
func partition(nums []int, left, right int) int {
|
||||
func (q *quickSortMedian) partition(nums []int, left, right int) int {
|
||||
// 以 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] 作为基准数
|
||||
// 下同省略...
|
||||
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"
|
||||
/* 快速排序(尾递归优化)*/
|
||||
func quickSort(nums []int, left, right int) {
|
||||
func (q *quickSortTailCall) quickSort(nums []int, left, right int) {
|
||||
// 子数组长度为 1 时终止
|
||||
for left < right {
|
||||
// 哨兵划分操作
|
||||
pivot := partition(nums, left, right)
|
||||
pivot := q.partition(nums, left, right)
|
||||
// 对两个子数组中较短的那个执行快排
|
||||
if pivot-left < right-pivot {
|
||||
quickSort(nums, left, pivot-1) // 递归排序左子数组
|
||||
q.quickSort(nums, left, pivot-1) // 递归排序左子数组
|
||||
left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right]
|
||||
} else {
|
||||
quickSort(nums, pivot+1, right) // 递归排序右子数组
|
||||
right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1]
|
||||
q.quickSort(nums, pivot+1, right) // 递归排序右子数组
|
||||
right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -494,19 +494,19 @@ comments: true
|
|||
data *list.List
|
||||
}
|
||||
|
||||
// newLinkedListQueue 初始化链表
|
||||
/* 初始化队列 */
|
||||
func newLinkedListQueue() *linkedListQueue {
|
||||
return &linkedListQueue{
|
||||
data: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// push 入队
|
||||
/* 入队 */
|
||||
func (s *linkedListQueue) push(value any) {
|
||||
s.data.PushBack(value)
|
||||
}
|
||||
|
||||
// poll 出队
|
||||
/* 出队 */
|
||||
func (s *linkedListQueue) poll() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
|
@ -516,7 +516,7 @@ comments: true
|
|||
return e.Value
|
||||
}
|
||||
|
||||
// peek 访问队首元素
|
||||
/* 访问队首元素 */
|
||||
func (s *linkedListQueue) peek() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
|
@ -525,15 +525,20 @@ comments: true
|
|||
return e.Value
|
||||
}
|
||||
|
||||
// size 获取队列的长度
|
||||
/* 获取队列的长度 */
|
||||
func (s *linkedListQueue) size() int {
|
||||
return s.data.Len()
|
||||
}
|
||||
|
||||
// isEmpty 判断队列是否为空
|
||||
/* 判断队列是否为空 */
|
||||
func (s *linkedListQueue) isEmpty() bool {
|
||||
return s.data.Len() == 0
|
||||
}
|
||||
|
||||
/* 获取 List 用于打印 */
|
||||
func (s *linkedListQueue) toList() *list.List {
|
||||
return s.data
|
||||
}
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
@ -1073,7 +1078,7 @@ comments: true
|
|||
queCapacity int // 队列容量(即最大容纳元素数量)
|
||||
}
|
||||
|
||||
// newArrayQueue 基于环形数组实现的队列
|
||||
/* 初始化队列 */
|
||||
func newArrayQueue(queCapacity int) *arrayQueue {
|
||||
return &arrayQueue{
|
||||
nums: make([]int, queCapacity),
|
||||
|
@ -1083,17 +1088,17 @@ comments: true
|
|||
}
|
||||
}
|
||||
|
||||
// size 获取队列的长度
|
||||
/* 获取队列的长度 */
|
||||
func (q *arrayQueue) size() int {
|
||||
return q.queSize
|
||||
}
|
||||
|
||||
// isEmpty 判断队列是否为空
|
||||
/* 判断队列是否为空 */
|
||||
func (q *arrayQueue) isEmpty() bool {
|
||||
return q.queSize == 0
|
||||
}
|
||||
|
||||
// push 入队
|
||||
/* 入队 */
|
||||
func (q *arrayQueue) push(num int) {
|
||||
// 当 rear == queCapacity 表示队列已满
|
||||
if q.queSize == q.queCapacity {
|
||||
|
@ -1107,7 +1112,7 @@ comments: true
|
|||
q.queSize++
|
||||
}
|
||||
|
||||
// poll 出队
|
||||
/* 出队 */
|
||||
func (q *arrayQueue) poll() any {
|
||||
num := q.peek()
|
||||
// 队首指针向后移动一位,若越过尾部则返回到数组头部
|
||||
|
@ -1116,7 +1121,7 @@ comments: true
|
|||
return num
|
||||
}
|
||||
|
||||
// peek 访问队首元素
|
||||
/* 访问队首元素 */
|
||||
func (q *arrayQueue) peek() any {
|
||||
if q.isEmpty() {
|
||||
return nil
|
||||
|
@ -1124,7 +1129,7 @@ comments: true
|
|||
return q.nums[q.front]
|
||||
}
|
||||
|
||||
// 获取 Slice 用于打印
|
||||
/* 获取 Slice 用于打印 */
|
||||
func (q *arrayQueue) toSlice() []int {
|
||||
rear := (q.front + q.queSize)
|
||||
if rear >= q.queCapacity {
|
||||
|
|
|
@ -466,19 +466,19 @@ comments: true
|
|||
data *list.List
|
||||
}
|
||||
|
||||
// newLinkedListStack 初始化链表
|
||||
/* 初始化栈 */
|
||||
func newLinkedListStack() *linkedListStack {
|
||||
return &linkedListStack{
|
||||
data: list.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// push 入栈
|
||||
/* 入栈 */
|
||||
func (s *linkedListStack) push(value int) {
|
||||
s.data.PushBack(value)
|
||||
}
|
||||
|
||||
// pop 出栈
|
||||
/* 出栈 */
|
||||
func (s *linkedListStack) pop() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
|
@ -488,7 +488,7 @@ comments: true
|
|||
return e.Value
|
||||
}
|
||||
|
||||
// peek 访问栈顶元素
|
||||
/* 访问栈顶元素 */
|
||||
func (s *linkedListStack) peek() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
|
@ -497,15 +497,20 @@ comments: true
|
|||
return e.Value
|
||||
}
|
||||
|
||||
// size 获取栈的长度
|
||||
/* 获取栈的长度 */
|
||||
func (s *linkedListStack) size() int {
|
||||
return s.data.Len()
|
||||
}
|
||||
|
||||
// isEmpty 判断栈是否为空
|
||||
/* 判断栈是否为空 */
|
||||
func (s *linkedListStack) isEmpty() bool {
|
||||
return s.data.Len() == 0
|
||||
}
|
||||
|
||||
/* 获取 List 用于打印 */
|
||||
func (s *linkedListStack) toList() *list.List {
|
||||
return s.data
|
||||
}
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
@ -918,6 +923,7 @@ comments: true
|
|||
data []int // 数据
|
||||
}
|
||||
|
||||
/* 初始化栈 */
|
||||
func newArrayStack() *arrayStack {
|
||||
return &arrayStack{
|
||||
// 设置栈的长度为 0,容量为 16
|
||||
|
@ -925,34 +931,30 @@ comments: true
|
|||
}
|
||||
}
|
||||
|
||||
// size 栈的长度
|
||||
/* 栈的长度 */
|
||||
func (s *arrayStack) size() int {
|
||||
return len(s.data)
|
||||
}
|
||||
|
||||
// isEmpty 栈是否为空
|
||||
/* 栈是否为空 */
|
||||
func (s *arrayStack) isEmpty() bool {
|
||||
return s.size() == 0
|
||||
}
|
||||
|
||||
// push 入栈
|
||||
/* 入栈 */
|
||||
func (s *arrayStack) push(v int) {
|
||||
// 切片会自动扩容
|
||||
s.data = append(s.data, v)
|
||||
}
|
||||
|
||||
// pop 出栈
|
||||
/* 出栈 */
|
||||
func (s *arrayStack) pop() any {
|
||||
// 弹出栈前,先判断是否为空
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
val := s.peek()
|
||||
s.data = s.data[:len(s.data)-1]
|
||||
return val
|
||||
}
|
||||
|
||||
// peek 获取栈顶元素
|
||||
/* 获取栈顶元素 */
|
||||
func (s *arrayStack) peek() any {
|
||||
if s.isEmpty() {
|
||||
return nil
|
||||
|
@ -960,6 +962,11 @@ comments: true
|
|||
val := s.data[len(s.data)-1]
|
||||
return val
|
||||
}
|
||||
|
||||
/* 获取 Slice 用于打印 */
|
||||
func (s *arrayStack) toSlice() []int {
|
||||
return s.data
|
||||
}
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
|
133
chapter_tree/avl_tree.md
Executable file → Normal file
133
chapter_tree/avl_tree.md
Executable file → Normal file
|
@ -207,7 +207,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
|
|||
|
||||
```go title="avl_tree.go"
|
||||
/* 获取结点高度 */
|
||||
func height(node *TreeNode) int {
|
||||
func (t *aVLTree) height(node *TreeNode) int {
|
||||
// 空结点高度为 -1 ,叶结点高度为 0
|
||||
if node != nil {
|
||||
return node.Height
|
||||
|
@ -216,9 +216,9 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
|
|||
}
|
||||
|
||||
/* 更新结点高度 */
|
||||
func updateHeight(node *TreeNode) {
|
||||
lh := height(node.Left)
|
||||
rh := height(node.Right)
|
||||
func (t *aVLTree) updateHeight(node *TreeNode) {
|
||||
lh := t.height(node.Left)
|
||||
rh := t.height(node.Right)
|
||||
// 结点高度等于最高子树高度 + 1
|
||||
if lh > rh {
|
||||
node.Height = lh + 1
|
||||
|
@ -350,13 +350,13 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
|
|||
|
||||
```go title="avl_tree.go"
|
||||
/* 获取平衡因子 */
|
||||
func balanceFactor(node *TreeNode) int {
|
||||
func (t *aVLTree) balanceFactor(node *TreeNode) int {
|
||||
// 空结点平衡因子为 0
|
||||
if node == nil {
|
||||
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"
|
||||
/* 右旋操作 */
|
||||
func rightRotate(node *TreeNode) *TreeNode {
|
||||
func (t *aVLTree) rightRotate(node *TreeNode) *TreeNode {
|
||||
child := node.Left
|
||||
grandChild := child.Right
|
||||
// 以 child 为原点,将 node 向右旋转
|
||||
child.Right = node
|
||||
node.Left = grandChild
|
||||
// 更新结点高度
|
||||
updateHeight(node)
|
||||
updateHeight(child)
|
||||
t.updateHeight(node)
|
||||
t.updateHeight(child)
|
||||
// 返回旋转后子树的根结点
|
||||
return child
|
||||
}
|
||||
|
@ -680,15 +680,15 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
|
||||
```go title="avl_tree.go"
|
||||
/* 左旋操作 */
|
||||
func leftRotate(node *TreeNode) *TreeNode {
|
||||
func (t *aVLTree) leftRotate(node *TreeNode) *TreeNode {
|
||||
child := node.Right
|
||||
grandChild := child.Left
|
||||
// 以 child 为原点,将 node 向左旋转
|
||||
child.Left = node
|
||||
node.Right = grandChild
|
||||
// 更新结点高度
|
||||
updateHeight(node)
|
||||
updateHeight(child)
|
||||
t.updateHeight(node)
|
||||
t.updateHeight(child)
|
||||
// 返回旋转后子树的根结点
|
||||
return child
|
||||
}
|
||||
|
@ -915,30 +915,30 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
|
||||
```go title="avl_tree.go"
|
||||
/* 执行旋转操作,使该子树重新恢复平衡 */
|
||||
func rotate(node *TreeNode) *TreeNode {
|
||||
func (t *aVLTree) rotate(node *TreeNode) *TreeNode {
|
||||
// 获取结点 node 的平衡因子
|
||||
// Go 推荐短变量,这里 bf 指代 balanceFactor
|
||||
bf := balanceFactor(node)
|
||||
// Go 推荐短变量,这里 bf 指代 t.balanceFactor
|
||||
bf := t.balanceFactor(node)
|
||||
// 左偏树
|
||||
if bf > 1 {
|
||||
if balanceFactor(node.Left) >= 0 {
|
||||
if t.balanceFactor(node.Left) >= 0 {
|
||||
// 右旋
|
||||
return rightRotate(node)
|
||||
return t.rightRotate(node)
|
||||
} else {
|
||||
// 先左旋后右旋
|
||||
node.Left = leftRotate(node.Left)
|
||||
return rightRotate(node)
|
||||
node.Left = t.leftRotate(node.Left)
|
||||
return t.rightRotate(node)
|
||||
}
|
||||
}
|
||||
// 右偏树
|
||||
if bf < -1 {
|
||||
if balanceFactor(node.Right) <= 0 {
|
||||
if t.balanceFactor(node.Right) <= 0 {
|
||||
// 左旋
|
||||
return leftRotate(node)
|
||||
return t.leftRotate(node)
|
||||
} else {
|
||||
// 先右旋后左旋
|
||||
node.Right = rightRotate(node.Right)
|
||||
return leftRotate(node)
|
||||
node.Right = t.rightRotate(node.Right)
|
||||
return t.leftRotate(node)
|
||||
}
|
||||
}
|
||||
// 平衡树,无需旋转,直接返回
|
||||
|
@ -1193,28 +1193,29 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
|
||||
```go title="avl_tree.go"
|
||||
/* 插入结点 */
|
||||
func (t *avlTree) insert(val int) *TreeNode {
|
||||
t.root = insertHelper(t.root, val)
|
||||
func (t *aVLTree) insert(val int) *TreeNode {
|
||||
t.root = t.insertHelper(t.root, val)
|
||||
return t.root
|
||||
}
|
||||
|
||||
/* 递归插入结点(辅助函数) */
|
||||
func insertHelper(node *TreeNode, val int) *TreeNode {
|
||||
func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode {
|
||||
if node == nil {
|
||||
return NewTreeNode(val)
|
||||
}
|
||||
/* 1. 查找插入位置,并插入结点 */
|
||||
if val < node.Val {
|
||||
node.Left = insertHelper(node.Left, val)
|
||||
node.Left = t.insertHelper(node.Left, val)
|
||||
} else if val > node.Val {
|
||||
node.Right = insertHelper(node.Right, val)
|
||||
node.Right = t.insertHelper(node.Right, val)
|
||||
} else {
|
||||
// 重复结点不插入,直接返回
|
||||
return node
|
||||
}
|
||||
// 更新结点高度
|
||||
updateHeight(node)
|
||||
t.updateHeight(node)
|
||||
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
|
||||
node = rotate(node)
|
||||
node = t.rotate(node)
|
||||
// 返回子树的根结点
|
||||
return node
|
||||
}
|
||||
|
@ -1445,6 +1446,16 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
// 返回子树的根结点
|
||||
return node;
|
||||
}
|
||||
|
||||
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
|
||||
TreeNode* getInOrderNext(TreeNode* node) {
|
||||
if (node == nullptr) return node;
|
||||
// 循环访问左子结点,直到叶结点时为最小结点,跳出
|
||||
while (node->left != nullptr) {
|
||||
node = node->left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Python"
|
||||
|
@ -1481,27 +1492,36 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
self.__update_height(node)
|
||||
# 2. 执行旋转操作,使该子树重新恢复平衡
|
||||
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 title="avl_tree.go"
|
||||
/* 删除结点 */
|
||||
func (t *avlTree) remove(val int) *TreeNode {
|
||||
root := removeHelper(t.root, val)
|
||||
func (t *aVLTree) remove(val int) *TreeNode {
|
||||
root := t.removeHelper(t.root, val)
|
||||
return root
|
||||
}
|
||||
|
||||
/* 递归删除结点(辅助函数) */
|
||||
func removeHelper(node *TreeNode, val int) *TreeNode {
|
||||
func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
/* 1. 查找结点,并删除之 */
|
||||
if val < node.Val {
|
||||
node.Left = removeHelper(node.Left, val)
|
||||
node.Left = t.removeHelper(node.Left, val)
|
||||
} else if val > node.Val {
|
||||
node.Right = removeHelper(node.Right, val)
|
||||
node.Right = t.removeHelper(node.Right, val)
|
||||
} else {
|
||||
if node.Left == nil || node.Right == nil {
|
||||
child := node.Left
|
||||
|
@ -1517,18 +1537,30 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
}
|
||||
} else {
|
||||
// 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点
|
||||
temp := getInOrderNext(node.Right)
|
||||
node.Right = removeHelper(node.Right, temp.Val)
|
||||
temp := t.getInOrderNext(node.Right)
|
||||
node.Right = t.removeHelper(node.Right, temp.Val)
|
||||
node.Val = temp.Val
|
||||
}
|
||||
}
|
||||
// 更新结点高度
|
||||
updateHeight(node)
|
||||
t.updateHeight(node)
|
||||
/* 2. 执行旋转操作,使该子树重新恢复平衡 */
|
||||
node = rotate(node)
|
||||
node = t.rotate(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"
|
||||
|
@ -1567,16 +1599,6 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
return node;
|
||||
}
|
||||
|
||||
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
|
||||
getInOrderNext(node) {
|
||||
if (node === null) return node;
|
||||
// 循环访问左子结点,直到叶结点时为最小结点,跳出
|
||||
while (node.left !== null) {
|
||||
node = node.left;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */
|
||||
getInOrderNext(node) {
|
||||
if (node === null) return node;
|
||||
|
@ -1750,6 +1772,19 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
// 返回子树的根结点
|
||||
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"
|
||||
|
|
|
@ -86,7 +86,7 @@ comments: true
|
|||
|
||||
```go title="binary_tree_bfs.go"
|
||||
/* 层序遍历 */
|
||||
func levelOrder(root *TreeNode) []int {
|
||||
func hierOrder(root *TreeNode) []int {
|
||||
// 初始化队列,加入根结点
|
||||
queue := list.New()
|
||||
queue.PushBack(root)
|
||||
|
|
Loading…
Reference in a new issue