This commit is contained in:
krahets
2023-10-26 03:00:28 +08:00
parent f442443773
commit d71e2dd46b
6 changed files with 304 additions and 202 deletions

View File

@ -1707,12 +1707,12 @@ status: new
/* 使用迭代模拟递归 */ /* 使用迭代模拟递归 */
int forLoopRecur(int n) { int forLoopRecur(int n) {
int stack[1000]; // 借助一个大数组来模拟栈 int stack[1000]; // 借助一个大数组来模拟栈
int top = 0; int top = -1; // 栈顶索引
int res = 0; int res = 0;
// 递:递归调用 // 递:递归调用
for (int i = n; i > 0; i--) { for (int i = n; i > 0; i--) {
// 通过“入栈操作”模拟“递” // 通过“入栈操作”模拟“递”
stack[top++] = i; stack[1 + top++] = i;
} }
// 归:返回结果 // 归:返回结果
while (top >= 0) { while (top >= 0) {

View File

@ -2044,9 +2044,9 @@ comments: true
// 将顶点前移 // 将顶点前移
for (int i = index; i < graph->size - 1; i++) { for (int i = index; i < graph->size - 1; i++) {
graph->vertices[i] = graph->vertices[i + 1]; // 顶点前移 graph->vertices[i] = graph->vertices[i + 1]; // 顶点前移
graph->vertices[i]->pos--; // 所有前移的顶点索引值减1 graph->vertices[i]->pos--; // 所有前移的顶点索引值减 1
} }
graph->vertices[graph->size - 1] = 0; // 将被删除顶点的位置置 0 graph->vertices[graph->size - 1] = 0;
graph->size--; graph->size--;
// 释放内存 // 释放内存
freeVertex(vet); freeVertex(vet);

View File

@ -81,6 +81,7 @@ comments: true
```python title="top_k.py" ```python title="top_k.py"
def top_k_heap(nums: list[int], k: int) -> list[int]: def top_k_heap(nums: list[int], k: int) -> list[int]:
"""基于堆查找数组中最大的 k 个元素""" """基于堆查找数组中最大的 k 个元素"""
# 初始化小顶堆
heap = [] heap = []
# 将数组的前 k 个元素入堆 # 将数组的前 k 个元素入堆
for i in range(k): for i in range(k):
@ -99,6 +100,7 @@ comments: true
```cpp title="top_k.cpp" ```cpp title="top_k.cpp"
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
priority_queue<int, vector<int>, greater<int>> topKHeap(vector<int> &nums, int k) { priority_queue<int, vector<int>, greater<int>> topKHeap(vector<int> &nums, int k) {
// 初始化小顶堆
priority_queue<int, vector<int>, greater<int>> heap; priority_queue<int, vector<int>, greater<int>> heap;
// 将数组的前 k 个元素入堆 // 将数组的前 k 个元素入堆
for (int i = 0; i < k; i++) { for (int i = 0; i < k; i++) {
@ -121,6 +123,7 @@ comments: true
```java title="top_k.java" ```java title="top_k.java"
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
Queue<Integer> topKHeap(int[] nums, int k) { Queue<Integer> topKHeap(int[] nums, int k) {
// 初始化小顶堆
Queue<Integer> heap = new PriorityQueue<Integer>(); Queue<Integer> heap = new PriorityQueue<Integer>();
// 将数组的前 k 个元素入堆 // 将数组的前 k 个元素入堆
for (int i = 0; i < k; i++) { for (int i = 0; i < k; i++) {
@ -143,6 +146,7 @@ comments: true
```csharp title="top_k.cs" ```csharp title="top_k.cs"
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
PriorityQueue<int, int> TopKHeap(int[] nums, int k) { PriorityQueue<int, int> TopKHeap(int[] nums, int k) {
// 初始化小顶堆
PriorityQueue<int, int> heap = new(); PriorityQueue<int, int> heap = new();
// 将数组的前 k 个元素入堆 // 将数组的前 k 个元素入堆
for (int i = 0; i < k; i++) { for (int i = 0; i < k; i++) {
@ -165,6 +169,7 @@ comments: true
```go title="top_k.go" ```go title="top_k.go"
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
func topKHeap(nums []int, k int) *minHeap { func topKHeap(nums []int, k int) *minHeap {
// 初始化小顶堆
h := &minHeap{} h := &minHeap{}
heap.Init(h) heap.Init(h)
// 将数组的前 k 个元素入堆 // 将数组的前 k 个元素入堆
@ -205,31 +210,63 @@ comments: true
=== "JS" === "JS"
```javascript title="top_k.js" ```javascript title="top_k.js"
/* 元素入堆 */
function pushMinHeap(maxHeap, val) {
// 元素取反
maxHeap.push(-val);
}
/* 元素出堆 */
function popMinHeap(maxHeap) {
// 元素取反
return -maxHeap.pop();
}
/* 访问堆顶元素 */
function peekMinHeap(maxHeap) {
// 元素取反
return -maxHeap.peek();
}
/* 取出堆中元素 */
function getMinHeap(maxHeap) {
// 元素取反
return maxHeap.getMaxHeap().map((num) => -num);
}
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
function topKHeap(nums, k) { function topKHeap(nums, k) {
// 使用大顶堆 MaxHeap ,对数组 nums 取相反数 // 初始化小顶堆
const invertedNums = nums.map((num) => -num); // 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆
const maxHeap = new MaxHeap([]);
// 将数组的前 k 个元素入堆 // 将数组的前 k 个元素入堆
const heap = new MaxHeap(invertedNums.slice(0, k)); for (let i = 0; i < k; i++) {
pushMinHeap(maxHeap, nums[i]);
}
// 从第 k+1 个元素开始,保持堆的长度为 k // 从第 k+1 个元素开始,保持堆的长度为 k
for (let i = k; i < invertedNums.length; i++) { for (let i = k; i < nums.length; i++) {
// 若当前元素于堆顶元素,则将堆顶元素出堆、当前元素入堆 // 若当前元素于堆顶元素,则将堆顶元素出堆、当前元素入堆
if (invertedNums[i] < heap.peek()) { if (nums[i] > peekMinHeap(maxHeap)) {
heap.pop(); popMinHeap(maxHeap);
heap.push(invertedNums[i]); pushMinHeap(maxHeap, nums[i]);
} }
} }
// 取出堆中元素 // 返回堆中元素
const maxHeap = heap.getMaxHeap(); return getMinHeap(maxHeap);
// 对堆中元素取相反数
const invertedMaxHeap = maxHeap.map((num) => -num);
return invertedMaxHeap;
} }
``` ```
=== "TS" === "TS"
```typescript title="top_k.ts" ```typescript title="top_k.ts"
[class]{}-[func]{pushMinHeap}
[class]{}-[func]{popMinHeap}
[class]{}-[func]{peekMinHeap}
[class]{}-[func]{getMinHeap}
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
function topKHeap(nums: number[], k: number): number[] { function topKHeap(nums: number[], k: number): number[] {
// 将堆中所有元素取反,从而用大顶堆来模拟小顶堆 // 将堆中所有元素取反,从而用大顶堆来模拟小顶堆
@ -257,7 +294,7 @@ comments: true
```dart title="top_k.dart" ```dart title="top_k.dart"
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
MinHeap topKHeap(List<int> nums, int k) { MinHeap topKHeap(List<int> nums, int k) {
// 将数组的前 k 个元素入堆 // 初始化小顶堆,将数组的前 k 个元素入堆
MinHeap heap = MinHeap(nums.sublist(0, k)); MinHeap heap = MinHeap(nums.sublist(0, k));
// 从第 k+1 个元素开始,保持堆的长度为 k // 从第 k+1 个元素开始,保持堆的长度为 k
for (int i = k; i < nums.length; i++) { for (int i = k; i < nums.length; i++) {
@ -276,7 +313,7 @@ comments: true
```rust title="top_k.rs" ```rust title="top_k.rs"
/* 基于堆查找数组中最大的 k 个元素 */ /* 基于堆查找数组中最大的 k 个元素 */
fn top_k_heap(nums: Vec<i32>, k: usize) -> BinaryHeap<Reverse<i32>> { fn top_k_heap(nums: Vec<i32>, k: usize) -> BinaryHeap<Reverse<i32>> {
// Rust 的 BinaryHeap 是大顶堆,使用 Reverse 将元素大小反转 // BinaryHeap 是大顶堆,使用 Reverse 将元素取反,从而实现小顶堆
let mut heap = BinaryHeap::<Reverse<i32>>::new(); let mut heap = BinaryHeap::<Reverse<i32>>::new();
// 将数组的前 k 个元素入堆 // 将数组的前 k 个元素入堆
for &num in nums.iter().take(k) { for &num in nums.iter().take(k) {
@ -297,7 +334,67 @@ comments: true
=== "C" === "C"
```c title="top_k.c" ```c title="top_k.c"
[class]{}-[func]{topKHeap} /* 元素入堆 */
void pushMinHeap(MaxHeap *maxHeap, int val) {
// 元素取反
push(maxHeap, -val);
}
/* 元素出堆 */
int popMinHeap(MaxHeap *maxHeap) {
// 元素取反
return -pop(maxHeap);
}
/* 访问堆顶元素 */
int peekMinHeap(MaxHeap *maxHeap) {
// 元素取反
return -peek(maxHeap);
}
/* 取出堆中元素 */
int *getMinHeap(MaxHeap *maxHeap) {
// 将堆中所有元素取反并存入 res 数组
int *res = (int *)malloc(maxHeap->size * sizeof(int));
for (int i = 0; i < maxHeap->size; i++) {
res[i] = -maxHeap->data[i];
}
return res;
}
/* 取出堆中元素 */
int *getMinHeap(MaxHeap *maxHeap) {
// 将堆中所有元素取反并存入 res 数组
int *res = (int *)malloc(maxHeap->size * sizeof(int));
for (int i = 0; i < maxHeap->size; i++) {
res[i] = -maxHeap->data[i];
}
return res;
}
// 基于堆查找数组中最大的 k 个元素的函数
int *topKHeap(int *nums, int sizeNums, int k) {
// 初始化小顶堆
// 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆
int empty[0];
MaxHeap *maxHeap = newMaxHeap(empty, 0);
// 将数组的前 k 个元素入堆
for (int i = 0; i < k; i++) {
pushMinHeap(maxHeap, nums[i]);
}
// 从第 k+1 个元素开始,保持堆的长度为 k
for (int i = k; i < sizeNums; i++) {
// 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆
if (nums[i] > peekMinHeap(maxHeap)) {
popMinHeap(maxHeap);
pushMinHeap(maxHeap, nums[i]);
}
}
int *res = getMinHeap(maxHeap);
// 释放内存
freeMaxHeap(maxHeap);
return res;
}
``` ```
=== "Zig" === "Zig"

View File

@ -64,33 +64,32 @@ comments: true
```python title="merge_sort.py" ```python title="merge_sort.py"
def merge(nums: list[int], left: int, mid: int, right: int): def merge(nums: list[int], left: int, mid: int, right: int):
"""合并左子数组和右子数组""" """合并左子数组和右子数组"""
# 左子数组区间 [left, mid] # 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
# 右子数组区间 [mid + 1, right] # 创建一个临时数组 tmp ,用于存放合并后的结果
# 初始化辅助数组 tmp = [0] * (right - left + 1)
tmp = list(nums[left : right + 1]) # 初始化左子数组和右子数组的起始索引
# 左子数组的起始索引和结束索引 i, j, k = left, mid + 1, 0
left_start = 0 # 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
left_end = mid - left while i <= mid and j <= right:
# 右子数组的起始索引和结束索引 if nums[i] <= nums[j]:
right_start = mid + 1 - left tmp[k] = nums[i]
right_end = right - left
# i, j 分别指向左子数组、右子数组的首元素
i = left_start
j = right_start
# 通过覆盖原数组 nums 来合并左子数组和右子数组
for k in range(left, right + 1):
# 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if i > left_end:
nums[k] = tmp[j]
j += 1
# 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
elif j > right_end or tmp[i] <= tmp[j]:
nums[k] = tmp[i]
i += 1 i += 1
# 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else: else:
nums[k] = tmp[j] tmp[k] = nums[j]
j += 1 j += 1
k += 1
# 将左子数组和右子数组的剩余元素复制到临时数组中
while i <= mid:
tmp[k] = nums[i]
i += 1
k += 1
while j <= right:
tmp[k] = nums[j]
j += 1
k += 1
# 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for k in range(0, len(tmp)):
nums[left + k] = tmp[k]
def merge_sort(nums: list[int], left: int, right: int): def merge_sort(nums: list[int], left: int, right: int):
"""归并排序""" """归并排序"""
@ -109,28 +108,29 @@ comments: true
```cpp title="merge_sort.cpp" ```cpp title="merge_sort.cpp"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(vector<int> &nums, int left, int mid, int right) { void merge(vector<int> &nums, int left, int mid, int right) {
// 初始化辅助数组 // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
vector<int> tmp(nums.begin() + left, nums.begin() + right + 1); // 创建一个临时数组 tmp ,用于存放合并后的结果
// 左子数组的起始索引和结束索引 vector<int> tmp(right - left + 1);
int leftStart = left - left, leftEnd = mid - left; // 初始化左子数组和右子数组的起始索引
// 右子数组的起始索引和结束索引 int i = left, j = mid + 1, k = 0;
int rightStart = mid + 1 - left, rightEnd = right - left; // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
// i, j 分别指向左子数组、右子数组的首元素 while (i <= mid && j <= right) {
int i = leftStart, j = rightStart; if (nums[i] <= nums[j])
// 通过覆盖原数组 nums 来合并左子数组和右子数组 tmp[k++] = nums[i++];
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else else
nums[k] = tmp[j++]; tmp[k++] = nums[j++];
}
// 将左子数组和右子数组的剩余元素复制到临时数组中
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for (k = 0; k < tmp.size(); k++) {
nums[left + k] = tmp[k];
} }
} }
@ -152,28 +152,29 @@ comments: true
```java title="merge_sort.java" ```java title="merge_sort.java"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int[] nums, int left, int mid, int right) { void merge(int[] nums, int left, int mid, int right) {
// 初始化辅助数组 // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
int[] tmp = Arrays.copyOfRange(nums, left, right + 1); // 创建一个临时数组 tmp ,用于存放合并后的结果
// 左子数组的起始索引和结束索引 int[] tmp = new int[right - left + 1];
int leftStart = left - left, leftEnd = mid - left; // 初始化左子数组和右子数组的起始索引
// 右子数组的起始索引和结束索引 int i = left, j = mid + 1, k = 0;
int rightStart = mid + 1 - left, rightEnd = right - left; // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
// i, j 分别指向左子数组、右子数组的首元素 while (i <= mid && j <= right) {
int i = leftStart, j = rightStart; if (nums[i] <= nums[j])
// 通过覆盖原数组 nums 来合并左子数组和右子数组 tmp[k++] = nums[i++];
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else else
nums[k] = tmp[j++]; tmp[k++] = nums[j++];
}
// 将左子数组和右子数组的剩余元素复制到临时数组中
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for (k = 0; k < tmp.length; k++) {
nums[left + k] = tmp[k];
} }
} }
@ -195,28 +196,29 @@ comments: true
```csharp title="merge_sort.cs" ```csharp title="merge_sort.cs"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void Merge(int[] nums, int left, int mid, int right) { void Merge(int[] nums, int left, int mid, int right) {
// 初始化辅助数组 // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
int[] tmp = nums[left..(right + 1)]; // 创建一个临时数组 tmp ,用于存放合并后的结果
// 左子数组的起始索引和结束索引 int[] tmp = new int[right - left + 1];
int leftStart = left - left, leftEnd = mid - left; // 初始化左子数组和右子数组的起始索引
// 右子数组的起始索引和结束索引 int i = left, j = mid + 1, k = 0;
int rightStart = mid + 1 - left, rightEnd = right - left; // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
// i, j 分别指向左子数组、右子数组的首元素 while (i <= mid && j <= right) {
int i = leftStart, j = rightStart; if (nums[i] <= nums[j])
// 通过覆盖原数组 nums 来合并左子数组和右子数组 tmp[k++] = nums[i++];
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else else
nums[k] = tmp[j++]; tmp[k++] = nums[j++];
}
// 将左子数组和右子数组的剩余元素复制到临时数组中
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for (k = 0; k < tmp.Length; ++k) {
nums[left + k] = tmp[k];
} }
} }
@ -237,35 +239,37 @@ comments: true
```go title="merge_sort.go" ```go title="merge_sort.go"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
func merge(nums []int, left, mid, right int) { func merge(nums []int, left, mid, right int) {
// 初始化辅助数组 借助 copy 模块 // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
// 创建一个临时数组 tmp ,用于存放合并后的结果
tmp := make([]int, right-left+1) tmp := make([]int, right-left+1)
for i := left; i <= right; i++ { // 初始化左子数组和右子数组的起始索引
tmp[i-left] = nums[i] i, j, k := left, mid+1, 0
} // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
// 左子数组的起始索引和结束索引 for i <= mid && j <= right {
leftStart, leftEnd := left-left, mid-left if nums[i] <= nums[j] {
// 右子数组的起始索引和结束索引 tmp[k] = nums[i]
rightStart, rightEnd := mid+1-left, right-left
// i, j 分别指向左子数组、右子数组的首元素
i, j := leftStart, rightStart
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for k := left; k <= right; k++ {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if i > leftEnd {
nums[k] = tmp[j]
j++
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
} else if j > rightEnd || tmp[i] <= tmp[j] {
nums[k] = tmp[i]
i++ i++
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
} else { } else {
nums[k] = tmp[j] tmp[k] = nums[j]
j++ j++
} }
k++
}
// 将左子数组和右子数组的剩余元素复制到临时数组中
for i <= mid {
tmp[k] = nums[i]
i++
k++
}
for j <= right {
tmp[k] = nums[j]
j++
k++
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for k := 0; k < len(tmp); k++ {
nums[left+k] = tmp[k]
} }
} }
@ -341,33 +345,33 @@ comments: true
```javascript title="merge_sort.js" ```javascript title="merge_sort.js"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
function merge(nums, left, mid, right) { function merge(nums, left, mid, right) {
// 初始化辅助数组 // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
let tmp = nums.slice(left, right + 1); // 创建一个临时数组 tmp ,用于存放合并后的结果
// 左子数组的起始索引和结束索引 const tmp = new Array(right - left + 1);
let leftStart = left - left, // 初始化左子数组和右子数组的起始索引
leftEnd = mid - left; let i = left,
// 右子数组的起始索引和结束索引 j = mid + 1,
let rightStart = mid + 1 - left, k = 0;
rightEnd = right - left; // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
// i, j 分别指向左子数组、右子数组的首元素 while (i <= mid && j <= right) {
let i = leftStart, if (nums[i] <= nums[j]) {
j = rightStart; tmp[k++] = nums[i++];
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for (let k = left; k <= right; k++) {
if (i > leftEnd) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
nums[k] = tmp[j++];
} else if (j > rightEnd || tmp[i] <= tmp[j]) {
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
nums[k] = tmp[i++];
} else { } else {
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ tmp[k++] = nums[j++];
nums[k] = tmp[j++];
} }
} }
// 将左子数组和右子数组的剩余元素复制到临时数组中
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for (k = 0; k < tmp.length; k++) {
nums[left + k] = tmp[k];
}
} }
/* 归并排序 */ /* 归并排序 */
@ -387,33 +391,33 @@ comments: true
```typescript title="merge_sort.ts" ```typescript title="merge_sort.ts"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
function merge(nums: number[], left: number, mid: number, right: number): void { function merge(nums: number[], left: number, mid: number, right: number): void {
// 初始化辅助数组 // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
let tmp = nums.slice(left, right + 1); // 创建一个临时数组 tmp ,用于存放合并后的结果
// 左子数组的起始索引和结束索引 const tmp = new Array(right - left + 1);
let leftStart = left - left, // 初始化左子数组和右子数组的起始索引
leftEnd = mid - left; let i = left,
// 右子数组的起始索引和结束索引 j = mid + 1,
let rightStart = mid + 1 - left, k = 0;
rightEnd = right - left; // 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
// i, j 分别指向左子数组、右子数组的首元素 while (i <= mid && j <= right) {
let i = leftStart, if (nums[i] <= nums[j]) {
j = rightStart; tmp[k++] = nums[i++];
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for (let k = left; k <= right; k++) {
if (i > leftEnd) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
} else if (j > rightEnd || tmp[i] <= tmp[j]) {
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
} else { } else {
nums[k] = tmp[j++]; tmp[k++] = nums[j++];
} }
} }
// 将左子数组和右子数组的剩余元素复制到临时数组中
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for (k = 0; k < tmp.length; k++) {
nums[left + k] = tmp[k];
}
} }
/* 归并排序 */ /* 归并排序 */
@ -523,33 +527,34 @@ comments: true
```c title="merge_sort.c" ```c title="merge_sort.c"
/* 合并左子数组和右子数组 */ /* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int *nums, int left, int mid, int right) { void merge(int *nums, int left, int mid, int right) {
int index; // 左子数组区间 [left, mid], 右子数组区间 [mid+1, right]
// 初始化辅助数组 // 创建一个临时数组 tmp ,用于存放合并后的结果
int tmp[right + 1 - left]; int tmpSize = right - left + 1;
for (index = left; index < right + 1; index++) { int *tmp = (int *)malloc(tmpSize * sizeof(int));
tmp[index - left] = nums[index]; // 初始化左子数组和右子数组的起始索引
int i = left, j = mid + 1, k = 0;
// 当左右子数组都还有元素时,比较并将较小的元素复制到临时数组中
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) {
tmp[k++] = nums[i++];
} else {
tmp[k++] = nums[j++];
} }
// 左子数组的起始索引和结束索引
int leftStart = left - left, leftEnd = mid - left;
// 右子数组的起始索引和结束索引
int rightStart = mid + 1 - left, rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
int i = leftStart, j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else
nums[k] = tmp[j++];
} }
// 将左子数组和右子数组的剩余元素复制到临时数组中
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
for (k = 0; k < tmpSize; ++k) {
nums[left + k] = tmp[k];
}
// 释放内存
free(tmp);
} }
/* 归并排序 */ /* 归并排序 */

View File

@ -215,7 +215,7 @@ AVL 树既是二叉搜索树也是平衡二叉树,同时满足这两类二叉
``` ```
“节点高度”是指从该节点到最远叶节点的距离,即所经过的“边”的数量。需要特别注意的是,叶节点的高度为 0 ,而空节点的高度为 -1 。我们将创建两个工具函数,分别用于获取和更新节点的高度。 “节点高度”是指从该节点到最远叶节点的距离,即所经过的“边”的数量。需要特别注意的是,叶节点的高度为 0 ,而空节点的高度为 -1 。我们将创建两个工具函数,分别用于获取和更新节点的高度。
=== "Python" === "Python"

View File

@ -206,7 +206,7 @@ comments: true
- 节点的「度 degree」节点的子节点的数量。在二叉树中度的取值范围是 0、1、2 。 - 节点的「度 degree」节点的子节点的数量。在二叉树中度的取值范围是 0、1、2 。
- 二叉树的「高度 height」从根节点到最远叶节点所经过的边的数量。 - 二叉树的「高度 height」从根节点到最远叶节点所经过的边的数量。
- 节点的「深度 depth」从根节点到该节点所经过的边的数量。 - 节点的「深度 depth」从根节点到该节点所经过的边的数量。
- 节点的「高度 height」从最远叶节点到该节点所经过的边的数量。 - 节点的「高度 height」距离该节点最远叶节点到该节点所经过的边的数量。
![二叉树的常用术语](binary_tree.assets/binary_tree_terminology.png) ![二叉树的常用术语](binary_tree.assets/binary_tree_terminology.png)