Compare commits
191 Commits
Author | SHA1 | Date |
---|---|---|
|
0918fd06f2 | |
|
83a806a602 | |
|
5c085b5592 | |
|
ee4d27dc17 | |
|
301adcf416 | |
|
f47d371035 | |
|
e058e14253 | |
|
b0c147b67c | |
|
e8dc4736a2 | |
|
6a4d62449c | |
|
ceec0eaa3a | |
|
7751ea1256 | |
|
5a4aa8c554 | |
|
db64108e5d | |
|
f42cec0f88 | |
|
09f7223067 | |
|
e79a56f540 | |
|
e4f5787685 | |
|
3ad70167db | |
|
f9d37e0d16 | |
|
1904bb8afc | |
|
e1f0903bf7 | |
|
cc0852fae8 | |
|
9997c2822c | |
|
d976116292 | |
|
8e38c61455 | |
|
a9d44c3a25 | |
|
3177d35eb2 | |
|
1db2001e9b | |
|
0a3d3940c4 | |
|
bd03b8c66e | |
|
7e904c8ff6 | |
|
82fa8cb222 | |
|
f45f847742 | |
|
8e3b795599 | |
|
59bd843953 | |
|
2fcdd499ea | |
|
18a5fec836 | |
|
e3170c372a | |
|
f5282698d5 | |
|
98cd3a8076 | |
|
35c1885b3e | |
|
34b01c12d6 | |
|
0114546a31 | |
|
16fbc4979f | |
|
9dfd0217a3 | |
|
e19276a5f1 | |
|
3a309c88af | |
|
4ff889c249 | |
|
00738f5bb4 | |
|
eec69f45af | |
|
95bd58b0d0 | |
|
2725e5a9dc | |
|
387f93b56f | |
|
3d12f84a01 | |
|
4c181c45c5 | |
|
28e43b4446 | |
|
e36453ac25 | |
|
ac0da11157 | |
|
5dc9dd879c | |
|
4db90555a7 | |
|
aa8a7f59d3 | |
|
18027ee26c | |
|
98daefa67a | |
|
1a8b4f6364 | |
|
9c78c513c2 | |
|
828405d234 | |
|
a2cec10e7f | |
|
7503a33e8c | |
|
2a955f937c | |
|
636b0738fa | |
|
a36b301464 | |
|
292b107af2 | |
|
4db5c19011 | |
|
356cc35e8a | |
|
dad0a3fd95 | |
|
e41b0a3156 | |
|
b6939da46c | |
|
abf1f115bf | |
|
6348dbe18d | |
|
ca774eefbf | |
|
2a9db6d039 | |
|
01a5f7b09f | |
|
894e3d536b | |
|
14608d4e92 | |
|
954169d618 | |
|
05e0e1d244 | |
|
fb04ff6535 | |
|
9a2c1355ec | |
|
6a74972080 | |
|
73eab4c0ec | |
|
57cf6b1ea6 | |
|
b3b10f2300 | |
|
68a61f23d5 | |
|
7d708b4fce | |
|
ef3010bd77 | |
|
0c60037e56 | |
|
5849ae4ada | |
|
dedb4aaced | |
|
40d13cb81f | |
|
c12d01a752 | |
|
2737357242 | |
|
e0d617edbb | |
|
7a345fc66b | |
|
1cc310a682 | |
|
eecf6bac3f | |
|
2cd2a94ce6 | |
|
8a6ce26f6a | |
|
6b2c38cae4 | |
|
f4baa7d9de | |
|
fc7efa28a7 | |
|
0bff488383 | |
|
f0755bdfa6 | |
|
c9041c5c5e | |
|
89a911583d | |
|
b884d385e3 | |
|
2758e06374 | |
|
ff6d42bd9b | |
|
f4a6d2127c | |
|
27d59f65a6 | |
|
4190eca41a | |
|
a7c241609f | |
|
9f0bec5f50 | |
|
427c7ac59d | |
|
017b95f003 | |
|
0774920d7f | |
|
3f4220de81 | |
|
39a6890b7e | |
|
59839c2437 | |
|
8b3e705c71 | |
|
ae2ff7a68a | |
|
a704c0d7f2 | |
|
a14be17b74 | |
|
63bcdb798a | |
|
aa818945f0 | |
|
21be3fdaf8 | |
|
0e221540a3 | |
|
9afbc9eda5 | |
|
840692acce | |
|
9e569cf520 | |
|
063a41fa7f | |
|
ebff1cce9f | |
|
7a96f6a743 | |
|
1f606d6852 | |
|
56a165bf98 | |
|
c4a7966882 | |
|
8e60d12151 | |
|
9e4017b3fb | |
|
1c0f350ad6 | |
|
b5e198db7d | |
|
0047934042 | |
|
cac10b07a1 | |
|
aebaa3ef02 | |
|
cb32c525e7 | |
|
a6be0ffdc3 | |
|
ee67d3e6a7 | |
|
d2d49551e3 | |
|
354b81cb6c | |
|
bb511e50e6 | |
|
316b0e9215 | |
|
baf30b1c92 | |
|
3eb929ca6d | |
|
3bd416600e | |
|
01a2e31203 | |
|
96597b8aaa | |
|
59d07264fc | |
|
84bb11522e | |
|
68f3a453e1 | |
|
3f857db457 | |
|
870e3e5cb2 | |
|
84b1ce2497 | |
|
a3950e1def | |
|
ca55e818a5 | |
|
75873f0a05 | |
|
cfc273783b | |
|
a88c7b9862 | |
|
71b7cc8ef6 | |
|
9e7fbf249f | |
|
69cb171dd8 | |
|
1a99dd420f | |
|
a7f4340e55 | |
|
54ced94e4f | |
|
f616dac7da | |
|
74f1a63e8c | |
|
587344da62 | |
|
499419e4ce | |
|
19488b13c4 | |
|
a587b01c4d | |
|
411a4b8a46 | |
|
be9931c8a1 | |
|
2b11639103 |
|
@ -0,0 +1,27 @@
|
|||
name: JavaScript
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
paths: ['codes/javascript/**/*.js']
|
||||
pull_request:
|
||||
branches: ['main']
|
||||
paths: ['codes/javascript/**/*.js']
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
- uses: denoland/setup-deno@v1
|
||||
with:
|
||||
deno-version: v1.x
|
||||
- name: Run JavaScript Code
|
||||
run: deno run -A codes/javascript/test_all.js
|
|
@ -14,16 +14,12 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: Swift ${{ matrix.swift }} on ${{ matrix.os }}
|
||||
name: Swift on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
swift: ["5.10", "5.9", "5.8"]
|
||||
os: ["ubuntu-22.04", "macos-14"]
|
||||
steps:
|
||||
- uses: swift-actions/setup-swift@v2
|
||||
with:
|
||||
swift-version: ${{ matrix.swift }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: swift build --package-path codes/swift
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
name: TypeScript
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
paths: ['codes/typescript/**/*.ts']
|
||||
pull_request:
|
||||
branches: ['main']
|
||||
paths: ['codes/typescript/**/*.ts']
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
- name: Install dependencies
|
||||
run: cd codes/typescript && npm install
|
||||
- name: Check TypeScript code
|
||||
run: cd codes/typescript && npm run check
|
|
@ -1,7 +1,7 @@
|
|||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Editor
|
||||
# editors
|
||||
.vscode/
|
||||
**/.idea
|
||||
|
||||
|
@ -12,6 +12,3 @@
|
|||
/build
|
||||
/site
|
||||
/utils
|
||||
|
||||
# test script
|
||||
test.sh
|
||||
|
|
32
README.md
32
README.md
|
@ -17,24 +17,24 @@
|
|||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://www.hello-algo.com/index.assets/animation.gif" width="389">
|
||||
<img src="https://www.hello-algo.com/index.assets/running_code.gif" width="389">
|
||||
<img src="https://www.hello-algo.com/index.assets/animation.gif" width="395">
|
||||
<img src="https://www.hello-algo.com/index.assets/running_code.gif" width="395">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB">
|
||||
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C">
|
||||
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02">
|
||||
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4">
|
||||
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8">
|
||||
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138">
|
||||
<img src="https://img.shields.io/badge/JavaScript-snow?logo=javascript&logoColor=E9CE30">
|
||||
<img src="https://img.shields.io/badge/TypeScript-snow?logo=typescript&logoColor=3178C6">
|
||||
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2">
|
||||
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000">
|
||||
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC">
|
||||
<img src="https://img.shields.io/badge/Kotlin-snow?logo=kotlin&logoColor=7F52FF">
|
||||
<img src="https://img.shields.io/badge/Zig-snow?logo=zig&logoColor=F7A41D">
|
||||
<img src="https://img.shields.io/badge/Python-snow?logo=python&logoColor=3776AB" alt="" />
|
||||
<img src="https://img.shields.io/badge/Java-snow?logo=coffeescript&logoColor=FC4C02" alt="" />
|
||||
<img src="https://img.shields.io/badge/C%2B%2B-snow?logo=c%2B%2B&logoColor=00599C" alt="" />
|
||||
<img src="https://img.shields.io/badge/C-snow?logo=c&logoColor=A8B9CC" alt="" />
|
||||
<img src="https://img.shields.io/badge/C%23-snow?logo=csharp&logoColor=512BD4" alt="" />
|
||||
<img src="https://img.shields.io/badge/JavaScript-snow?logo=javascript&logoColor=E9CE30" alt="" />
|
||||
<img src="https://img.shields.io/badge/Go-snow?logo=go&logoColor=00ADD8" alt="" />
|
||||
<img src="https://img.shields.io/badge/Swift-snow?logo=swift&logoColor=F05138" alt="" />
|
||||
<img src="https://img.shields.io/badge/Rust-snow?logo=rust&logoColor=000000" alt="" />
|
||||
<img src="https://img.shields.io/badge/Ruby-snow?logo=ruby&logoColor=CC342D" alt="" />
|
||||
<img src="https://img.shields.io/badge/Kotlin-snow?logo=kotlin&logoColor=7F52FF" alt="" />
|
||||
<img src="https://img.shields.io/badge/TypeScript-snow?logo=typescript&logoColor=3178C6" alt="" />
|
||||
<img src="https://img.shields.io/badge/Dart-snow?logo=dart&logoColor=0175C2" alt="" />
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
|
@ -51,7 +51,7 @@
|
|||
|
||||
- 全书采用动画图解,内容清晰易懂、学习曲线平滑,引导初学者探索数据结构与算法的知识地图。
|
||||
- 源代码可一键运行,帮助读者在练习中提升编程技能,了解算法工作原理和数据结构底层实现。
|
||||
- 鼓励读者互助学习,提问与评论通常可在两日内得到回复。
|
||||
- 提倡读者互助学习,欢迎大家在评论区提出问题与分享见解,在交流讨论中共同进步。
|
||||
|
||||
若本书对您有所帮助,请在页面右上角点个 Star :star: 支持一下,谢谢!
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ int myMin(int a, int b) {
|
|||
}
|
||||
/* 求最大值 */
|
||||
int myMax(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
/* 最大容量:贪心 */
|
||||
|
|
|
@ -32,7 +32,7 @@ HashMapOpenAddressing *newHashMapOpenAddressing() {
|
|||
hashMap->capacity = 4;
|
||||
hashMap->loadThres = 2.0 / 3.0;
|
||||
hashMap->extendRatio = 2;
|
||||
hashMap->buckets = (Pair **)malloc(sizeof(Pair *) * hashMap->capacity);
|
||||
hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *));
|
||||
hashMap->TOMBSTONE = (Pair *)malloc(sizeof(Pair));
|
||||
hashMap->TOMBSTONE->key = -1;
|
||||
hashMap->TOMBSTONE->val = "-1";
|
||||
|
@ -151,7 +151,7 @@ void extend(HashMapOpenAddressing *hashMap) {
|
|||
int oldCapacity = hashMap->capacity;
|
||||
// 初始化扩容后的新哈希表
|
||||
hashMap->capacity *= hashMap->extendRatio;
|
||||
hashMap->buckets = (Pair **)malloc(sizeof(Pair *) * hashMap->capacity);
|
||||
hashMap->buckets = (Pair **)calloc(hashMap->capacity, sizeof(Pair *));
|
||||
hashMap->size = 0;
|
||||
// 将键值对从原哈希表搬运至新哈希表
|
||||
for (int i = 0; i < oldCapacity; i++) {
|
||||
|
|
|
@ -50,7 +50,7 @@ int rotHash(char *key) {
|
|||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
char *key = "Hello dsad3241241dsa算123法";
|
||||
char *key = "Hello 算法";
|
||||
|
||||
int hash = addHash(key);
|
||||
printf("加法哈希值为 %d\n", hash);
|
||||
|
|
|
@ -37,12 +37,12 @@ HashTable *find(HashTable *h, int key) {
|
|||
}
|
||||
|
||||
/* 哈希表元素插入 */
|
||||
void insert(HashTable *h, int key, int val) {
|
||||
HashTable *t = find(h, key);
|
||||
void insert(HashTable **h, int key, int val) {
|
||||
HashTable *t = find(*h, key);
|
||||
if (t == NULL) {
|
||||
HashTable *tmp = malloc(sizeof(HashTable));
|
||||
tmp->key = key, tmp->val = val;
|
||||
HASH_ADD_INT(h, key, tmp);
|
||||
HASH_ADD_INT(*h, key, tmp);
|
||||
} else {
|
||||
t->val = val;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ int *twoSumHashTable(int *nums, int numsSize, int target, int *returnSize) {
|
|||
*returnSize = 2;
|
||||
return res;
|
||||
}
|
||||
insert(hashtable, nums[i], i);
|
||||
insert(&hashtable, nums[i], i);
|
||||
}
|
||||
*returnSize = 0;
|
||||
return NULL;
|
||||
|
@ -83,4 +83,4 @@ int main() {
|
|||
printArray(res, returnSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -65,6 +65,7 @@ void countingSort(int nums[], int size) {
|
|||
// 使用结果数组 res 覆盖原数组 nums
|
||||
memcpy(nums, res, size * sizeof(int));
|
||||
// 5. 释放内存
|
||||
free(res);
|
||||
free(counter);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ void mergeSort(int *nums, int left, int right) {
|
|||
if (left >= right)
|
||||
return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
int mid = (left + right) / 2; // 计算中点
|
||||
int mid = left + (right - left) / 2; // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
|
|
|
@ -89,9 +89,9 @@ void quickSortMedian(int nums[], int left, int right) {
|
|||
quickSortMedian(nums, pivot + 1, right);
|
||||
}
|
||||
|
||||
// 以下为尾递归优化的快速排序
|
||||
// 以下为递归深度优化的快速排序
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
void quickSortTailCall(int nums[], int left, int right) {
|
||||
// 子数组长度为 1 时终止
|
||||
while (left < right) {
|
||||
|
@ -127,10 +127,10 @@ int main() {
|
|||
printf("快速排序(中位基准数优化)完成后 nums = ");
|
||||
printArray(nums1, size);
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
int nums2[] = {2, 4, 1, 0, 3, 5};
|
||||
quickSortTailCall(nums2, 0, size - 1);
|
||||
printf("快速排序(尾递归优化)完成后 nums = ");
|
||||
printf("快速排序(递归深度优化)完成后 nums = ");
|
||||
printArray(nums1, size);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -16,6 +16,7 @@ int digit(int num, int exp) {
|
|||
void countingSortDigit(int nums[], int size, int exp) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
int *counter = (int *)malloc((sizeof(int) * 10));
|
||||
memset(counter, 0, sizeof(int) * 10); // 初始化为 0 以支持后续内存释放
|
||||
// 统计 0~9 各数字的出现次数
|
||||
for (int i = 0; i < size; i++) {
|
||||
// 获取 nums[i] 第 k 位,记为 d
|
||||
|
@ -39,13 +40,16 @@ void countingSortDigit(int nums[], int size, int exp) {
|
|||
for (int i = 0; i < size; i++) {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
// 释放内存
|
||||
free(res);
|
||||
free(counter);
|
||||
}
|
||||
|
||||
/* 基数排序 */
|
||||
void radixSort(int nums[], int size) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
int max = INT32_MIN;
|
||||
for (size_t i = 0; i < size - 1; i++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (nums[i] > max) {
|
||||
max = nums[i];
|
||||
}
|
||||
|
|
|
@ -111,16 +111,29 @@ int popLast(ArrayDeque *deque) {
|
|||
return num;
|
||||
}
|
||||
|
||||
/* 返回数组用于打印 */
|
||||
int *toArray(ArrayDeque *deque, int *queSize) {
|
||||
*queSize = deque->queSize;
|
||||
int *res = (int *)calloc(deque->queSize, sizeof(int));
|
||||
int j = deque->front;
|
||||
for (int i = 0; i < deque->queSize; i++) {
|
||||
res[i] = deque->nums[j % deque->queCapacity];
|
||||
j++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化队列 */
|
||||
int capacity = 10;
|
||||
int queSize;
|
||||
ArrayDeque *deque = newArrayDeque(capacity);
|
||||
pushLast(deque, 3);
|
||||
pushLast(deque, 2);
|
||||
pushLast(deque, 5);
|
||||
printf("双向队列 deque = ");
|
||||
printArray(deque->nums, deque->queSize);
|
||||
printArray(toArray(deque, &queSize), queSize);
|
||||
|
||||
/* 访问元素 */
|
||||
int peekFirstNum = peekFirst(deque);
|
||||
|
@ -131,18 +144,18 @@ int main() {
|
|||
/* 元素入队 */
|
||||
pushLast(deque, 4);
|
||||
printf("元素 4 队尾入队后 deque = ");
|
||||
printArray(deque->nums, deque->queSize);
|
||||
printArray(toArray(deque, &queSize), queSize);
|
||||
pushFirst(deque, 1);
|
||||
printf("元素 1 队首入队后 deque = ");
|
||||
printArray(deque->nums, deque->queSize);
|
||||
printArray(toArray(deque, &queSize), queSize);
|
||||
|
||||
/* 元素出队 */
|
||||
int popLastNum = popLast(deque);
|
||||
printf("队尾出队元素 = %d ,队尾出队后 deque= ", popLastNum);
|
||||
printArray(deque->nums, deque->queSize);
|
||||
printArray(toArray(deque, &queSize), queSize);
|
||||
int popFirstNum = popFirst(deque);
|
||||
printf("队首出队元素 = %d ,队首出队后 deque= ", popFirstNum);
|
||||
printArray(deque->nums, deque->queSize);
|
||||
printArray(toArray(deque, &queSize), queSize);
|
||||
|
||||
/* 获取队列的长度 */
|
||||
int dequeSize = size(deque);
|
||||
|
@ -156,4 +169,4 @@ int main() {
|
|||
delArrayDeque(deque);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -74,10 +74,23 @@ int pop(ArrayQueue *queue) {
|
|||
return num;
|
||||
}
|
||||
|
||||
/* 返回数组用于打印 */
|
||||
int *toArray(ArrayQueue *queue, int *queSize) {
|
||||
*queSize = queue->queSize;
|
||||
int *res = (int *)calloc(queue->queSize, sizeof(int));
|
||||
int j = queue->front;
|
||||
for (int i = 0; i < queue->queSize; i++) {
|
||||
res[i] = queue->nums[j % queue->queCapacity];
|
||||
j++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化队列 */
|
||||
int capacity = 10;
|
||||
int queSize;
|
||||
ArrayQueue *queue = newArrayQueue(capacity);
|
||||
|
||||
/* 元素入队 */
|
||||
|
@ -87,7 +100,7 @@ int main() {
|
|||
push(queue, 5);
|
||||
push(queue, 4);
|
||||
printf("队列 queue = ");
|
||||
printArray(queue->nums, queue->queSize);
|
||||
printArray(toArray(queue, &queSize), queSize);
|
||||
|
||||
/* 访问队首元素 */
|
||||
int peekNum = peek(queue);
|
||||
|
@ -96,7 +109,7 @@ int main() {
|
|||
/* 元素出队 */
|
||||
peekNum = pop(queue);
|
||||
printf("出队元素 pop = %d ,出队后 queue = ", peekNum);
|
||||
printArray(queue->nums, queue->queSize);
|
||||
printArray(toArray(queue, &queSize), queSize);
|
||||
|
||||
/* 获取队列的长度 */
|
||||
int queueSize = size(queue);
|
||||
|
@ -111,11 +124,11 @@ int main() {
|
|||
push(queue, i);
|
||||
pop(queue);
|
||||
printf("第 %d 轮入队 + 出队后 queue = ", i);
|
||||
printArray(queue->nums, queue->queSize);
|
||||
printArray(toArray(queue, &queSize), queSize);
|
||||
}
|
||||
|
||||
// 释放内存
|
||||
delArrayQueue(queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -90,11 +90,11 @@ int main() {
|
|||
|
||||
/* 获取栈的长度 */
|
||||
int size = stack->size;
|
||||
printf("栈的长度 size = %d\n", size);
|
||||
printf("栈的长度 size = %d\n", size);
|
||||
|
||||
/* 判断是否为空 */
|
||||
bool empty = isEmpty(stack);
|
||||
printf("栈是否为空 = %stack\n", empty ? "true" : "false");
|
||||
printf("栈是否为空 = %s\n", empty ? "true" : "false");
|
||||
|
||||
// 释放内存
|
||||
delArrayStack(stack);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* Author: MolDuM (moldum@163.com)、Reanon (793584285@qq.com)
|
||||
*/
|
||||
|
||||
#ifndef C_INCLUDE_H
|
||||
#define C_INCLUDE_H
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -33,4 +33,4 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif // C_INCLUDE_H
|
||||
#endif // COMMON_H
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
vector<Vertex *> graphBFS(GraphAdjList &graph, Vertex *startVet) {
|
||||
// 顶点遍历序列
|
||||
vector<Vertex *> res;
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
unordered_set<Vertex *> visited = {startVet};
|
||||
// 队列用于实现 BFS
|
||||
queue<Vertex *> que;
|
||||
|
|
|
@ -25,7 +25,7 @@ void dfs(GraphAdjList &graph, unordered_set<Vertex *> &visited, vector<Vertex *>
|
|||
vector<Vertex *> graphDFS(GraphAdjList &graph, Vertex *startVet) {
|
||||
// 顶点遍历序列
|
||||
vector<Vertex *> res;
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
unordered_set<Vertex *> visited;
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -48,7 +48,7 @@ int rotHash(string key) {
|
|||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
string key = "Hello dsad3241241dsa算123法";
|
||||
string key = "Hello 算法";
|
||||
|
||||
int hash = addHash(key);
|
||||
cout << "加法哈希值为 " << hash << endl;
|
||||
|
|
|
@ -39,7 +39,7 @@ void mergeSort(vector<int> &nums, int left, int right) {
|
|||
if (left >= right)
|
||||
return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
int mid = (left + right) / 2; // 计算中点
|
||||
int mid = left + (right - left) / 2; // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
|
|
|
@ -9,26 +9,19 @@
|
|||
/* 快速排序类 */
|
||||
class QuickSort {
|
||||
private:
|
||||
/* 元素交换 */
|
||||
static void swap(vector<int> &nums, int i, int j) {
|
||||
int tmp = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = tmp;
|
||||
}
|
||||
|
||||
/* 哨兵划分 */
|
||||
static int partition(vector<int> &nums, int left, int right) {
|
||||
// 以 nums[left] 为基准数
|
||||
int i = left, j = right;
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left])
|
||||
j--; // 从右向左找首个小于基准数的元素
|
||||
j--; // 从右向左找首个小于基准数的元素
|
||||
while (i < j && nums[i] <= nums[left])
|
||||
i++; // 从左向右找首个大于基准数的元素
|
||||
swap(nums, i, j); // 交换这两个元素
|
||||
i++; // 从左向右找首个大于基准数的元素
|
||||
swap(nums[i], nums[j]); // 交换这两个元素
|
||||
}
|
||||
swap(nums, i, left); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
swap(nums[i], nums[left]); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -48,13 +41,6 @@ class QuickSort {
|
|||
/* 快速排序类(中位基准数优化) */
|
||||
class QuickSortMedian {
|
||||
private:
|
||||
/* 元素交换 */
|
||||
static void swap(vector<int> &nums, int i, int j) {
|
||||
int tmp = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = tmp;
|
||||
}
|
||||
|
||||
/* 选取三个候选元素的中位数 */
|
||||
static int medianThree(vector<int> &nums, int left, int mid, int right) {
|
||||
int l = nums[left], m = nums[mid], r = nums[right];
|
||||
|
@ -70,18 +56,18 @@ class QuickSortMedian {
|
|||
// 选取三个候选元素的中位数
|
||||
int med = medianThree(nums, left, (left + right) / 2, right);
|
||||
// 将中位数交换至数组最左端
|
||||
swap(nums, left, med);
|
||||
swap(nums[left], nums[med]);
|
||||
// 以 nums[left] 为基准数
|
||||
int i = left, j = right;
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left])
|
||||
j--; // 从右向左找首个小于基准数的元素
|
||||
j--; // 从右向左找首个小于基准数的元素
|
||||
while (i < j && nums[i] <= nums[left])
|
||||
i++; // 从左向右找首个大于基准数的元素
|
||||
swap(nums, i, j); // 交换这两个元素
|
||||
i++; // 从左向右找首个大于基准数的元素
|
||||
swap(nums[i], nums[j]); // 交换这两个元素
|
||||
}
|
||||
swap(nums, i, left); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
swap(nums[i], nums[left]); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -98,33 +84,26 @@ class QuickSortMedian {
|
|||
}
|
||||
};
|
||||
|
||||
/* 快速排序类(尾递归优化) */
|
||||
/* 快速排序类(递归深度优化) */
|
||||
class QuickSortTailCall {
|
||||
private:
|
||||
/* 元素交换 */
|
||||
static void swap(vector<int> &nums, int i, int j) {
|
||||
int tmp = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = tmp;
|
||||
}
|
||||
|
||||
/* 哨兵划分 */
|
||||
static int partition(vector<int> &nums, int left, int right) {
|
||||
// 以 nums[left] 为基准数
|
||||
int i = left, j = right;
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left])
|
||||
j--; // 从右向左找首个小于基准数的元素
|
||||
j--; // 从右向左找首个小于基准数的元素
|
||||
while (i < j && nums[i] <= nums[left])
|
||||
i++; // 从左向右找首个大于基准数的元素
|
||||
swap(nums, i, j); // 交换这两个元素
|
||||
i++; // 从左向右找首个大于基准数的元素
|
||||
swap(nums[i], nums[j]); // 交换这两个元素
|
||||
}
|
||||
swap(nums, i, left); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
swap(nums[i], nums[left]); // 将基准数交换至两子数组的分界线
|
||||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
public:
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
static void quickSort(vector<int> &nums, int left, int right) {
|
||||
// 子数组长度为 1 时终止
|
||||
while (left < right) {
|
||||
|
@ -156,10 +135,10 @@ int main() {
|
|||
cout << "快速排序(中位基准数优化)完成后 nums = ";
|
||||
printVector(nums1);
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
vector<int> nums2 = {2, 4, 1, 0, 3, 5};
|
||||
QuickSortTailCall::quickSort(nums2, 0, nums2.size() - 1);
|
||||
cout << "快速排序(尾递归优化)完成后 nums = ";
|
||||
cout << "快速排序(递归深度优化)完成后 nums = ";
|
||||
printVector(nums2);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -115,9 +115,9 @@ int main() {
|
|||
int i = 1;
|
||||
int l = abt.left(i), r = abt.right(i), p = abt.parent(i);
|
||||
cout << "\n当前节点的索引为 " << i << ",值为 " << abt.val(i) << "\n";
|
||||
cout << "其左子节点的索引为 " << l << ",值为 " << (l != INT_MAX ? to_string(abt.val(l)) : "nullptr") << "\n";
|
||||
cout << "其右子节点的索引为 " << r << ",值为 " << (r != INT_MAX ? to_string(abt.val(r)) : "nullptr") << "\n";
|
||||
cout << "其父节点的索引为 " << p << ",值为 " << (p != INT_MAX ? to_string(abt.val(p)) : "nullptr") << "\n";
|
||||
cout << "其左子节点的索引为 " << l << ",值为 " << (abt.val(l) != INT_MAX ? to_string(abt.val(l)) : "nullptr") << "\n";
|
||||
cout << "其右子节点的索引为 " << r << ",值为 " << (abt.val(r) != INT_MAX ? to_string(abt.val(r)) : "nullptr") << "\n";
|
||||
cout << "其父节点的索引为 " << p << ",值为 " << (abt.val(p) != INT_MAX ? to_string(abt.val(p)) : "nullptr") << "\n";
|
||||
|
||||
// 遍历树
|
||||
vector<int> res = abt.levelOrder();
|
||||
|
|
|
@ -105,7 +105,7 @@ public class min_path_sum {
|
|||
|
||||
// 暴力搜索
|
||||
int res = MinPathSumDFS(grid, n - 1, m - 1);
|
||||
Console.WriteLine("从左上角到右下角的做小路径和为 " + res);
|
||||
Console.WriteLine("从左上角到右下角的最小路径和为 " + res);
|
||||
|
||||
// 记忆化搜索
|
||||
int[][] mem = new int[n][];
|
||||
|
@ -114,14 +114,14 @@ public class min_path_sum {
|
|||
Array.Fill(mem[i], -1);
|
||||
}
|
||||
res = MinPathSumDFSMem(grid, mem, n - 1, m - 1);
|
||||
Console.WriteLine("从左上角到右下角的做小路径和为 " + res);
|
||||
Console.WriteLine("从左上角到右下角的最小路径和为 " + res);
|
||||
|
||||
// 动态规划
|
||||
res = MinPathSumDP(grid);
|
||||
Console.WriteLine("从左上角到右下角的做小路径和为 " + res);
|
||||
Console.WriteLine("从左上角到右下角的最小路径和为 " + res);
|
||||
|
||||
// 空间优化后的动态规划
|
||||
res = MinPathSumDPComp(grid);
|
||||
Console.WriteLine("从左上角到右下角的做小路径和为 " + res);
|
||||
Console.WriteLine("从左上角到右下角的最小路径和为 " + res);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ public class graph_bfs {
|
|||
List<Vertex> GraphBFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 顶点遍历序列
|
||||
List<Vertex> res = [];
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
HashSet<Vertex> visited = [startVet];
|
||||
// 队列用于实现 BFS
|
||||
Queue<Vertex> que = new();
|
||||
|
|
|
@ -26,7 +26,7 @@ public class graph_dfs {
|
|||
List<Vertex> GraphDFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 顶点遍历序列
|
||||
List<Vertex> res = [];
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
HashSet<Vertex> visited = [];
|
||||
DFS(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -24,8 +24,8 @@ public class heap {
|
|||
/* 初始化堆 */
|
||||
// 初始化小顶堆
|
||||
PriorityQueue<int, int> minHeap = new();
|
||||
// 初始化大顶堆(使用 lambda 表达式修改 Comparator 即可)
|
||||
PriorityQueue<int, int> maxHeap = new(Comparer<int>.Create((x, y) => y - x));
|
||||
// 初始化大顶堆(使用 lambda 表达式修改 Comparer 即可)
|
||||
PriorityQueue<int, int> maxHeap = new(Comparer<int>.Create((x, y) => y.CompareTo(x)));
|
||||
Console.WriteLine("以下测试样例为大顶堆");
|
||||
|
||||
/* 元素入堆 */
|
||||
|
|
|
@ -39,7 +39,7 @@ public class merge_sort {
|
|||
// 终止条件
|
||||
if (left >= right) return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
int mid = (left + right) / 2; // 计算中点
|
||||
int mid = left + (right - left) / 2; // 计算中点
|
||||
MergeSort(nums, left, mid); // 递归左子数组
|
||||
MergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
|
|
|
@ -89,7 +89,7 @@ class QuickSortMedian {
|
|||
}
|
||||
}
|
||||
|
||||
/* 快速排序类(尾递归优化) */
|
||||
/* 快速排序类(递归深度优化) */
|
||||
class QuickSortTailCall {
|
||||
/* 元素交换 */
|
||||
static void Swap(int[] nums, int i, int j) {
|
||||
|
@ -111,7 +111,7 @@ class QuickSortTailCall {
|
|||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
public static void QuickSort(int[] nums, int left, int right) {
|
||||
// 子数组长度为 1 时终止
|
||||
while (left < right) {
|
||||
|
@ -142,9 +142,9 @@ public class quick_sort {
|
|||
QuickSortMedian.QuickSort(nums1, 0, nums1.Length - 1);
|
||||
Console.WriteLine("快速排序(中位基准数优化)完成后 nums1 = " + string.Join(",", nums1));
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
int[] nums2 = [2, 4, 1, 0, 3, 5];
|
||||
QuickSortTailCall.QuickSort(nums2, 0, nums2.Length - 1);
|
||||
Console.WriteLine("快速排序(尾递归优化)完成后 nums2 = " + string.Join(",", nums2));
|
||||
Console.WriteLine("快速排序(递归深度优化)完成后 nums2 = " + string.Join(",", nums2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,18 +103,18 @@ void main() {
|
|||
|
||||
// 暴力搜索
|
||||
int res = minPathSumDFS(grid, n - 1, m - 1);
|
||||
print("从左上角到右下角的做小路径和为 $res");
|
||||
print("从左上角到右下角的最小路径和为 $res");
|
||||
|
||||
// 记忆化搜索
|
||||
List<List<int>> mem = List.generate(n, (i) => List.filled(m, -1));
|
||||
res = minPathSumDFSMem(grid, mem, n - 1, m - 1);
|
||||
print("从左上角到右下角的做小路径和为 $res");
|
||||
print("从左上角到右下角的最小路径和为 $res");
|
||||
|
||||
// 动态规划
|
||||
res = minPathSumDP(grid);
|
||||
print("从左上角到右下角的做小路径和为 $res");
|
||||
print("从左上角到右下角的最小路径和为 $res");
|
||||
|
||||
// 空间优化后的动态规划
|
||||
res = minPathSumDPComp(grid);
|
||||
print("从左上角到右下角的做小路径和为 $res");
|
||||
print("从左上角到右下角的最小路径和为 $res");
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) {
|
|||
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
|
||||
// 顶点遍历序列
|
||||
List<Vertex> res = [];
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
Set<Vertex> visited = {};
|
||||
visited.add(startVet);
|
||||
// 队列用于实现 BFS
|
||||
|
|
|
@ -30,7 +30,7 @@ void dfs(
|
|||
List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 顶点遍历序列
|
||||
List<Vertex> res = [];
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
Set<Vertex> visited = {};
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -36,7 +36,7 @@ void mergeSort(List<int> nums, int left, int right) {
|
|||
// 终止条件
|
||||
if (left >= right) return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
int mid = (left + right) ~/ 2; // 计算中点
|
||||
int mid = left + (right - left) ~/ 2; // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
|
|
|
@ -86,7 +86,7 @@ class QuickSortMedian {
|
|||
}
|
||||
}
|
||||
|
||||
/* 快速排序类(尾递归优化) */
|
||||
/* 快速排序类(递归深度优化) */
|
||||
class QuickSortTailCall {
|
||||
/* 元素交换 */
|
||||
static void _swap(List<int> nums, int i, int j) {
|
||||
|
@ -108,7 +108,7 @@ class QuickSortTailCall {
|
|||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
static void quickSort(List<int> nums, int left, int right) {
|
||||
// 子数组长度为 1 时终止
|
||||
while (left < right) {
|
||||
|
@ -138,8 +138,8 @@ void main() {
|
|||
QuickSortMedian.quickSort(nums1, 0, nums1.length - 1);
|
||||
print("快速排序(中位基准数优化)完成后 nums1 = $nums1");
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
List<int> nums2 = [2, 4, 1, 0, 3, 5];
|
||||
QuickSortTailCall.quickSort(nums2, 0, nums2.length - 1);
|
||||
print("快速排序(尾递归优化)完成后 nums2 = $nums2");
|
||||
print("快速排序(递归深度优化)完成后 nums2 = $nums2");
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, d
|
|||
|
||||
}
|
||||
*res = append(*res, newState)
|
||||
return
|
||||
}
|
||||
// 遍历所有列
|
||||
for col := 0; col < n; col++ {
|
||||
|
|
|
@ -23,7 +23,7 @@ func backtrackII(state *[]int, choices *[]int, selected *[]bool, res *[][]int) {
|
|||
(*selected)[i] = true
|
||||
*state = append(*state, choice)
|
||||
// 进行下一轮选择
|
||||
backtrackI(state, choices, selected, res)
|
||||
backtrackII(state, choices, selected, res)
|
||||
// 回退:撤销选择,恢复到之前的状态
|
||||
(*selected)[i] = false
|
||||
*state = (*state)[:len(*state)-1]
|
||||
|
|
|
@ -18,7 +18,7 @@ func dfs(nums []int, target, i, j int) int {
|
|||
// 递归子问题 f(m+1, j)
|
||||
return dfs(nums, target, m+1, j)
|
||||
} else if nums[m] > target {
|
||||
// 小于则递归左半数组
|
||||
// 大于则递归左半数组
|
||||
// 递归子问题 f(i, m-1)
|
||||
return dfs(nums, target, i, m-1)
|
||||
} else {
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestMinPathSum(t *testing.T) {
|
|||
|
||||
// 暴力搜索
|
||||
res := minPathSumDFS(grid, n-1, m-1)
|
||||
fmt.Printf("从左上角到右下角的做小路径和为 %d\n", res)
|
||||
fmt.Printf("从左上角到右下角的最小路径和为 %d\n", res)
|
||||
|
||||
// 记忆化搜索
|
||||
mem := make([][]int, n)
|
||||
|
@ -31,13 +31,13 @@ func TestMinPathSum(t *testing.T) {
|
|||
}
|
||||
}
|
||||
res = minPathSumDFSMem(grid, mem, n-1, m-1)
|
||||
fmt.Printf("从左上角到右下角的做小路径和为 %d\n", res)
|
||||
fmt.Printf("从左上角到右下角的最小路径和为 %d\n", res)
|
||||
|
||||
// 动态规划
|
||||
res = minPathSumDP(grid)
|
||||
fmt.Printf("从左上角到右下角的做小路径和为 %d\n", res)
|
||||
fmt.Printf("从左上角到右下角的最小路径和为 %d\n", res)
|
||||
|
||||
// 空间优化后的动态规划
|
||||
res = minPathSumDPComp(grid)
|
||||
fmt.Printf("从左上角到右下角的做小路径和为 %d\n", res)
|
||||
fmt.Printf("从左上角到右下角的最小路径和为 %d\n", res)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
func graphBFS(g *graphAdjList, startVet Vertex) []Vertex {
|
||||
// 顶点遍历序列
|
||||
res := make([]Vertex, 0)
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
visited := make(map[Vertex]struct{})
|
||||
visited[startVet] = struct{}{}
|
||||
// 队列用于实现 BFS, 使用切片模拟队列
|
||||
|
|
|
@ -28,7 +28,7 @@ func dfs(g *graphAdjList, visited map[Vertex]struct{}, res *[]Vertex, vet Vertex
|
|||
func graphDFS(g *graphAdjList, startVet Vertex) []Vertex {
|
||||
// 顶点遍历序列
|
||||
res := make([]Vertex, 0)
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
visited := make(map[Vertex]struct{})
|
||||
dfs(g, visited, &res, startVet)
|
||||
// 返回顶点遍历序列
|
||||
|
|
|
@ -9,7 +9,7 @@ func twoSumBruteForce(nums []int, target int) []int {
|
|||
size := len(nums)
|
||||
// 两层循环,时间复杂度为 O(n^2)
|
||||
for i := 0; i < size-1; i++ {
|
||||
for j := i + 1; i < size; j++ {
|
||||
for j := i + 1; j < size; j++ {
|
||||
if nums[i]+nums[j] == target {
|
||||
return []int{i, j}
|
||||
}
|
||||
|
|
|
@ -16,5 +16,5 @@ func TestBubbleSort(t *testing.T) {
|
|||
|
||||
nums1 := []int{4, 1, 3, 1, 5, 2}
|
||||
bubbleSortWithFlag(nums1)
|
||||
fmt.Println("冒泡排序完成后 nums1 = ", nums)
|
||||
fmt.Println("冒泡排序完成后 nums1 = ", nums1)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ func mergeSort(nums []int, left, right int) {
|
|||
return
|
||||
}
|
||||
// 划分阶段
|
||||
mid := (left + right) / 2
|
||||
mid := left + (right - left) / 2
|
||||
mergeSort(nums, left, mid)
|
||||
mergeSort(nums, mid+1, right)
|
||||
// 合并阶段
|
||||
|
|
|
@ -10,7 +10,7 @@ type quickSort struct{}
|
|||
// 快速排序(中位基准数优化)
|
||||
type quickSortMedian struct{}
|
||||
|
||||
// 快速排序(尾递归优化)
|
||||
// 快速排序(递归深度优化)
|
||||
type quickSortTailCall struct{}
|
||||
|
||||
/* 哨兵划分 */
|
||||
|
@ -112,7 +112,7 @@ func (q *quickSortTailCall) partition(nums []int, left, right int) int {
|
|||
return i // 返回基准数的索引
|
||||
}
|
||||
|
||||
/* 快速排序(尾递归优化)*/
|
||||
/* 快速排序(递归深度优化)*/
|
||||
func (q *quickSortTailCall) quickSort(nums []int, left, right int) {
|
||||
// 子数组长度为 1 时终止
|
||||
for left < right {
|
||||
|
|
|
@ -25,10 +25,10 @@ func TestQuickSortMedian(t *testing.T) {
|
|||
fmt.Println("快速排序(中位基准数优化)完成后 nums = ", nums)
|
||||
}
|
||||
|
||||
// 快速排序(尾递归优化)
|
||||
// 快速排序(递归深度优化)
|
||||
func TestQuickSortTailCall(t *testing.T) {
|
||||
q := quickSortTailCall{}
|
||||
nums := []int{4, 1, 3, 1, 5, 2}
|
||||
q.quickSort(nums, 0, len(nums)-1)
|
||||
fmt.Println("快速排序(尾递归优化)完成后 nums = ", nums)
|
||||
fmt.Println("快速排序(递归深度优化)完成后 nums = ", nums)
|
||||
}
|
||||
|
|
|
@ -72,6 +72,9 @@ func (q *arrayDeque) pushLast(num int) {
|
|||
/* 队首出队 */
|
||||
func (q *arrayDeque) popFirst() any {
|
||||
num := q.peekFirst()
|
||||
if num == nil {
|
||||
return nil
|
||||
}
|
||||
// 队首指针向后移动一位
|
||||
q.front = q.index(q.front + 1)
|
||||
q.queSize--
|
||||
|
@ -81,6 +84,9 @@ func (q *arrayDeque) popFirst() any {
|
|||
/* 队尾出队 */
|
||||
func (q *arrayDeque) popLast() any {
|
||||
num := q.peekLast()
|
||||
if num == nil {
|
||||
return nil
|
||||
}
|
||||
q.queSize--
|
||||
return num
|
||||
}
|
||||
|
|
|
@ -49,6 +49,10 @@ func (q *arrayQueue) push(num int) {
|
|||
/* 出队 */
|
||||
func (q *arrayQueue) pop() any {
|
||||
num := q.peek()
|
||||
if num == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 队首指针向后移动一位,若越过尾部,则返回到数组头部
|
||||
q.front = (q.front + 1) % q.queCapacity
|
||||
q.queSize--
|
||||
|
|
|
@ -46,9 +46,13 @@ func TestQueue(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestArrayQueue(t *testing.T) {
|
||||
|
||||
// 初始化队列,使用队列的通用接口
|
||||
capacity := 10
|
||||
queue := newArrayQueue(capacity)
|
||||
if queue.pop() != nil {
|
||||
t.Errorf("want:%v,got:%v", nil, queue.pop())
|
||||
}
|
||||
|
||||
// 元素入队
|
||||
queue.push(1)
|
||||
|
|
|
@ -15,7 +15,7 @@ public class graph_bfs {
|
|||
static List<Vertex> graphBFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 顶点遍历序列
|
||||
List<Vertex> res = new ArrayList<>();
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
Set<Vertex> visited = new HashSet<>();
|
||||
visited.add(startVet);
|
||||
// 队列用于实现 BFS
|
||||
|
|
|
@ -28,7 +28,7 @@ public class graph_dfs {
|
|||
static List<Vertex> graphDFS(GraphAdjList graph, Vertex startVet) {
|
||||
// 顶点遍历序列
|
||||
List<Vertex> res = new ArrayList<>();
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
Set<Vertex> visited = new HashSet<>();
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -42,7 +42,7 @@ public class merge_sort {
|
|||
if (left >= right)
|
||||
return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
int mid = (left + right) / 2; // 计算中点
|
||||
int mid = left + (right - left) / 2; // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
|
|
|
@ -96,7 +96,7 @@ class QuickSortMedian {
|
|||
}
|
||||
}
|
||||
|
||||
/* 快速排序类(尾递归优化) */
|
||||
/* 快速排序类(递归深度优化) */
|
||||
class QuickSortTailCall {
|
||||
/* 元素交换 */
|
||||
static void swap(int[] nums, int i, int j) {
|
||||
|
@ -120,7 +120,7 @@ class QuickSortTailCall {
|
|||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
public static void quickSort(int[] nums, int left, int right) {
|
||||
// 子数组长度为 1 时终止
|
||||
while (left < right) {
|
||||
|
@ -150,9 +150,9 @@ public class quick_sort {
|
|||
QuickSortMedian.quickSort(nums1, 0, nums1.length - 1);
|
||||
System.out.println("快速排序(中位基准数优化)完成后 nums1 = " + Arrays.toString(nums1));
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
int[] nums2 = { 2, 4, 1, 0, 3, 5 };
|
||||
QuickSortTailCall.quickSort(nums2, 0, nums2.length - 1);
|
||||
System.out.println("快速排序(尾递归优化)完成后 nums2 = " + Arrays.toString(nums2));
|
||||
System.out.println("快速排序(递归深度优化)完成后 nums2 = " + Arrays.toString(nums2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ class MyList {
|
|||
remove(index) {
|
||||
if (index < 0 || index >= this.#size) throw new Error('索引越界');
|
||||
let num = this.#arr[index];
|
||||
// 将将索引 index 之后的元素都向前移动一位
|
||||
// 将索引 index 之后的元素都向前移动一位
|
||||
for (let j = index; j < this.#size - 1; j++) {
|
||||
this.#arr[j] = this.#arr[j + 1];
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ function coinChangeDP(coins, amt) {
|
|||
return dp[n][amt] !== MAX ? dp[n][amt] : -1;
|
||||
}
|
||||
|
||||
/* 零钱兑换:状态压缩后的动态规划 */
|
||||
/* 零钱兑换:空间优化后的动态规划 */
|
||||
function coinChangeDPComp(coins, amt) {
|
||||
const n = coins.length;
|
||||
const MAX = amt + 1;
|
||||
|
@ -61,6 +61,6 @@ const amt = 4;
|
|||
let res = coinChangeDP(coins, amt);
|
||||
console.log(`凑到目标金额所需的最少硬币数量为 ${res}`);
|
||||
|
||||
// 状态压缩后的动态规划
|
||||
// 空间优化后的动态规划
|
||||
res = coinChangeDPComp(coins, amt);
|
||||
console.log(`凑到目标金额所需的最少硬币数量为 ${res}`);
|
||||
|
|
|
@ -30,7 +30,7 @@ function coinChangeIIDP(coins, amt) {
|
|||
return dp[n][amt];
|
||||
}
|
||||
|
||||
/* 零钱兑换 II:状态压缩后的动态规划 */
|
||||
/* 零钱兑换 II:空间优化后的动态规划 */
|
||||
function coinChangeIIDPComp(coins, amt) {
|
||||
const n = coins.length;
|
||||
// 初始化 dp 表
|
||||
|
@ -59,6 +59,6 @@ const amt = 5;
|
|||
let res = coinChangeIIDP(coins, amt);
|
||||
console.log(`凑出目标金额的硬币组合数量为 ${res}`);
|
||||
|
||||
// 状态压缩后的动态规划
|
||||
// 空间优化后的动态规划
|
||||
res = coinChangeIIDPComp(coins, amt);
|
||||
console.log(`凑出目标金额的硬币组合数量为 ${res}`);
|
||||
|
|
|
@ -82,7 +82,7 @@ function editDistanceDP(s, t) {
|
|||
return dp[n][m];
|
||||
}
|
||||
|
||||
/* 编辑距离:状态压缩后的动态规划 */
|
||||
/* 编辑距离:空间优化后的动态规划 */
|
||||
function editDistanceDPComp(s, t) {
|
||||
const n = s.length,
|
||||
m = t.length;
|
||||
|
@ -130,6 +130,6 @@ console.log(`将 ${s} 更改为 ${t} 最少需要编辑 ${res} 步`);
|
|||
res = editDistanceDP(s, t);
|
||||
console.log(`将 ${s} 更改为 ${t} 最少需要编辑 ${res} 步`);
|
||||
|
||||
// 状态压缩后的动态规划
|
||||
// 空间优化后的动态规划
|
||||
res = editDistanceDPComp(s, t);
|
||||
console.log(`将 ${s} 更改为 ${t} 最少需要编辑 ${res} 步`);
|
||||
|
|
|
@ -69,7 +69,7 @@ function knapsackDP(wgt, val, cap) {
|
|||
return dp[n][cap];
|
||||
}
|
||||
|
||||
/* 0-1 背包:状态压缩后的动态规划 */
|
||||
/* 0-1 背包:空间优化后的动态规划 */
|
||||
function knapsackDPComp(wgt, val, cap) {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
|
@ -108,6 +108,6 @@ console.log(`不超过背包容量的最大物品价值为 ${res}`);
|
|||
res = knapsackDP(wgt, val, cap);
|
||||
console.log(`不超过背包容量的最大物品价值为 ${res}`);
|
||||
|
||||
// 状态压缩后的动态规划
|
||||
// 空间优化后的动态规划
|
||||
res = knapsackDPComp(wgt, val, cap);
|
||||
console.log(`不超过背包容量的最大物品价值为 ${res}`);
|
||||
|
|
|
@ -22,7 +22,7 @@ function minCostClimbingStairsDP(cost) {
|
|||
return dp[n];
|
||||
}
|
||||
|
||||
/* 爬楼梯最小代价:状态压缩后的动态规划 */
|
||||
/* 爬楼梯最小代价:空间优化后的动态规划 */
|
||||
function minCostClimbingStairsDPComp(cost) {
|
||||
const n = cost.length - 1;
|
||||
if (n === 1 || n === 2) {
|
||||
|
|
|
@ -69,7 +69,7 @@ function minPathSumDP(grid) {
|
|||
return dp[n - 1][m - 1];
|
||||
}
|
||||
|
||||
/* 最小路径和:状态压缩后的动态规划 */
|
||||
/* 最小路径和:空间优化后的动态规划 */
|
||||
function minPathSumDPComp(grid) {
|
||||
const n = grid.length,
|
||||
m = grid[0].length;
|
||||
|
@ -116,6 +116,6 @@ console.log(`从左上角到右下角的最小路径和为 ${res}`);
|
|||
res = minPathSumDP(grid);
|
||||
console.log(`从左上角到右下角的最小路径和为 ${res}`);
|
||||
|
||||
// 状态压缩后的动态规划
|
||||
// 空间优化后的动态规划
|
||||
res = minPathSumDPComp(grid);
|
||||
console.log(`从左上角到右下角的最小路径和为 ${res}`);
|
||||
|
|
|
@ -29,7 +29,7 @@ function unboundedKnapsackDP(wgt, val, cap) {
|
|||
return dp[n][cap];
|
||||
}
|
||||
|
||||
/* 完全背包:状态压缩后的动态规划 */
|
||||
/* 完全背包:空间优化后的动态规划 */
|
||||
function unboundedKnapsackDPComp(wgt, val, cap) {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
|
@ -58,6 +58,6 @@ const cap = 4;
|
|||
let res = unboundedKnapsackDP(wgt, val, cap);
|
||||
console.log(`不超过背包容量的最大物品价值为 ${res}`);
|
||||
|
||||
// 状态压缩后的动态规划
|
||||
// 空间优化后的动态规划
|
||||
res = unboundedKnapsackDPComp(wgt, val, cap);
|
||||
console.log(`不超过背包容量的最大物品价值为 ${res}`);
|
||||
|
|
|
@ -46,7 +46,8 @@ class GraphAdjList {
|
|||
if (
|
||||
!this.adjList.has(vet1) ||
|
||||
!this.adjList.has(vet2) ||
|
||||
vet1 === vet2
|
||||
vet1 === vet2 ||
|
||||
this.adjList.get(vet1).indexOf(vet2) === -1
|
||||
) {
|
||||
throw new Error('Illegal Argument Exception');
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ const { Vertex } = require('../modules/Vertex');
|
|||
function graphBFS(graph, startVet) {
|
||||
// 顶点遍历序列
|
||||
const res = [];
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
const visited = new Set();
|
||||
visited.add(startVet);
|
||||
// 队列用于实现 BFS
|
||||
|
|
|
@ -27,7 +27,7 @@ function dfs(graph, visited, res, vet) {
|
|||
function graphDFS(graph, startVet) {
|
||||
// 顶点遍历序列
|
||||
const res = [];
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
const visited = new Set();
|
||||
dfs(graph, visited, res, startVet);
|
||||
return res;
|
||||
|
|
|
@ -31,7 +31,7 @@ function xorHash(key) {
|
|||
for (const c of key) {
|
||||
hash ^= c.charCodeAt(0);
|
||||
}
|
||||
return hash & MODULUS;
|
||||
return hash % MODULUS;
|
||||
}
|
||||
|
||||
/* 旋转哈希 */
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
// 简单实现,无法用于排序对象
|
||||
function countingSortNaive(nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = 0;
|
||||
for (const num of nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
let m = Math.max(...nums);
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
const counter = new Array(m + 1).fill(0);
|
||||
|
@ -31,10 +28,7 @@ function countingSortNaive(nums) {
|
|||
// 完整实现,可排序对象,并且是稳定排序
|
||||
function countingSort(nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = 0;
|
||||
for (const num of nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
let m = Math.max(...nums);
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
const counter = new Array(m + 1).fill(0);
|
||||
|
|
|
@ -39,7 +39,7 @@ function mergeSort(nums, left, right) {
|
|||
// 终止条件
|
||||
if (left >= right) return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
let mid = Math.floor((left + right) / 2); // 计算中点
|
||||
let mid = Math.floor(left + (right - left) / 2); // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
|
|
|
@ -100,7 +100,7 @@ class QuickSortMedian {
|
|||
}
|
||||
}
|
||||
|
||||
/* 快速排序类(尾递归优化) */
|
||||
/* 快速排序类(递归深度优化) */
|
||||
class QuickSortTailCall {
|
||||
/* 元素交换 */
|
||||
swap(nums, i, j) {
|
||||
|
@ -123,7 +123,7 @@ class QuickSortTailCall {
|
|||
return i; // 返回基准数的索引
|
||||
}
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
quickSort(nums, left, right) {
|
||||
// 子数组长度为 1 时终止
|
||||
while (left < right) {
|
||||
|
@ -154,8 +154,8 @@ const quickSortMedian = new QuickSortMedian();
|
|||
quickSortMedian.quickSort(nums1, 0, nums1.length - 1);
|
||||
console.log('快速排序(中位基准数优化)完成后 nums =', nums1);
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
const nums2 = [2, 4, 1, 0, 3, 5];
|
||||
const quickSortTailCall = new QuickSortTailCall();
|
||||
quickSortTailCall.quickSort(nums2, 0, nums2.length - 1);
|
||||
console.log('快速排序(尾递归优化)完成后 nums =', nums2);
|
||||
console.log('快速排序(递归深度优化)完成后 nums =', nums2);
|
||||
|
|
|
@ -41,12 +41,7 @@ function countingSortDigit(nums, exp) {
|
|||
/* 基数排序 */
|
||||
function radixSort(nums) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
let m = Number.MIN_VALUE;
|
||||
for (const num of nums) {
|
||||
if (num > m) {
|
||||
m = num;
|
||||
}
|
||||
}
|
||||
let m = Math.max(... nums);
|
||||
// 按照从低位到高位的顺序遍历
|
||||
for (let exp = 1; exp <= m; exp *= 10) {
|
||||
// 对数组元素的第 k 位执行计数排序
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import { bold, brightRed } from 'jsr:@std/fmt/colors';
|
||||
import { expandGlob } from 'jsr:@std/fs';
|
||||
import { relative, resolve } from 'jsr:@std/path';
|
||||
|
||||
/**
|
||||
* @typedef {import('jsr:@std/fs').WalkEntry} WalkEntry
|
||||
* @type {WalkEntry[]}
|
||||
*/
|
||||
const entries = [];
|
||||
|
||||
for await (const entry of expandGlob(
|
||||
resolve(import.meta.dirname, './chapter_*/*.js')
|
||||
)) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
/** @type {{ status: Promise<Deno.CommandStatus>; stderr: ReadableStream<Uint8Array>; }[]} */
|
||||
const processes = [];
|
||||
|
||||
for (const file of entries) {
|
||||
const execute = new Deno.Command('node', {
|
||||
args: [relative(import.meta.dirname, file.path)],
|
||||
cwd: import.meta.dirname,
|
||||
stdin: 'piped',
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const process = execute.spawn();
|
||||
processes.push({ status: process.status, stderr: process.stderr });
|
||||
}
|
||||
|
||||
const results = await Promise.all(
|
||||
processes.map(async (item) => {
|
||||
const status = await item.status;
|
||||
return { status, stderr: item.stderr };
|
||||
})
|
||||
);
|
||||
|
||||
/** @type {ReadableStream<Uint8Array>[]} */
|
||||
const errors = [];
|
||||
|
||||
for (const result of results) {
|
||||
if (!result.status.success) {
|
||||
errors.push(result.stderr);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Tested ${entries.length} files`);
|
||||
console.log(`Found exception in ${errors.length} files`);
|
||||
|
||||
if (errors.length) {
|
||||
console.log();
|
||||
|
||||
for (const error of errors) {
|
||||
const reader = error.getReader();
|
||||
const { value } = await reader.read();
|
||||
const decoder = new TextDecoder();
|
||||
console.log(`${bold(brightRed('error'))}: ${decoder.decode(value)}`);
|
||||
}
|
||||
|
||||
throw new Error('Test failed');
|
||||
}
|
|
@ -14,7 +14,7 @@ import java.util.*
|
|||
fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> {
|
||||
// 顶点遍历序列
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
val visited = HashSet<Vertex>()
|
||||
visited.add(startVet)
|
||||
// 队列用于实现 BFS
|
||||
|
|
|
@ -31,7 +31,7 @@ fun dfs(
|
|||
fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> {
|
||||
// 顶点遍历序列
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// 哈希表,用于记录已被访问过的顶点
|
||||
// 哈希集合,用于记录已被访问过的顶点
|
||||
val visited = HashSet<Vertex?>()
|
||||
dfs(graph, visited, res, startVet)
|
||||
return res
|
||||
|
|
|
@ -19,7 +19,7 @@ fun merge(nums: IntArray, left: Int, mid: Int, right: Int) {
|
|||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++]
|
||||
else
|
||||
else
|
||||
tmp[k++] = nums[j++]
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
|
@ -40,7 +40,7 @@ fun mergeSort(nums: IntArray, left: Int, right: Int) {
|
|||
// 终止条件
|
||||
if (left >= right) return // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
val mid = (left + right) / 2 // 计算中点
|
||||
val mid = left + (right - left) / 2 // 计算中点
|
||||
mergeSort(nums, left, mid) // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right) // 递归右子数组
|
||||
// 合并阶段
|
||||
|
@ -53,4 +53,4 @@ fun main() {
|
|||
val nums = intArrayOf(7, 3, 2, 6, 0, 1, 5, 4)
|
||||
mergeSort(nums, 0, nums.size - 1)
|
||||
println("归并排序完成后 nums = ${nums.contentToString()}")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ fun quickSortMedian(nums: IntArray, left: Int, right: Int) {
|
|||
quickSort(nums, pivot + 1, right)
|
||||
}
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
fun quickSortTailCall(nums: IntArray, left: Int, right: Int) {
|
||||
// 子数组长度为 1 时终止
|
||||
var l = left
|
||||
|
@ -114,8 +114,8 @@ fun main() {
|
|||
quickSortMedian(nums1, 0, nums1.size - 1)
|
||||
println("快速排序(中位基准数优化)完成后 nums1 = ${nums1.contentToString()}")
|
||||
|
||||
/* 快速排序(尾递归优化) */
|
||||
/* 快速排序(递归深度优化) */
|
||||
val nums2 = intArrayOf(2, 4, 1, 0, 3, 5)
|
||||
quickSortTailCall(nums2, 0, nums2.size - 1)
|
||||
println("快速排序(尾递归优化)完成后 nums2 = ${nums2.contentToString()}")
|
||||
println("快速排序(递归深度优化)完成后 nums2 = ${nums2.contentToString()}")
|
||||
}
|
|
@ -97,7 +97,9 @@ def linear_log_recur(n: int) -> int:
|
|||
"""线性对数阶"""
|
||||
if n <= 1:
|
||||
return 1
|
||||
count: int = linear_log_recur(n // 2) + linear_log_recur(n // 2)
|
||||
# 一分为二,子问题的规模减小一半
|
||||
count = linear_log_recur(n // 2) + linear_log_recur(n // 2)
|
||||
# 当前子问题包含 n 个操作
|
||||
for _ in range(n):
|
||||
count += 1
|
||||
return count
|
||||
|
@ -120,32 +122,32 @@ if __name__ == "__main__":
|
|||
n = 8
|
||||
print("输入数据大小 n =", n)
|
||||
|
||||
count: int = constant(n)
|
||||
count = constant(n)
|
||||
print("常数阶的操作数量 =", count)
|
||||
|
||||
count: int = linear(n)
|
||||
count = linear(n)
|
||||
print("线性阶的操作数量 =", count)
|
||||
count: int = array_traversal([0] * n)
|
||||
count = array_traversal([0] * n)
|
||||
print("线性阶(遍历数组)的操作数量 =", count)
|
||||
|
||||
count: int = quadratic(n)
|
||||
count = quadratic(n)
|
||||
print("平方阶的操作数量 =", count)
|
||||
nums = [i for i in range(n, 0, -1)] # [n, n-1, ..., 2, 1]
|
||||
count: int = bubble_sort(nums)
|
||||
count = bubble_sort(nums)
|
||||
print("平方阶(冒泡排序)的操作数量 =", count)
|
||||
|
||||
count: int = exponential(n)
|
||||
count = exponential(n)
|
||||
print("指数阶(循环实现)的操作数量 =", count)
|
||||
count: int = exp_recur(n)
|
||||
count = exp_recur(n)
|
||||
print("指数阶(递归实现)的操作数量 =", count)
|
||||
|
||||
count: int = logarithmic(n)
|
||||
count = logarithmic(n)
|
||||
print("对数阶(循环实现)的操作数量 =", count)
|
||||
count: int = log_recur(n)
|
||||
count = log_recur(n)
|
||||
print("对数阶(递归实现)的操作数量 =", count)
|
||||
|
||||
count: int = linear_log_recur(n)
|
||||
count = linear_log_recur(n)
|
||||
print("线性对数阶(递归实现)的操作数量 =", count)
|
||||
|
||||
count: int = factorial_recur(n)
|
||||
count = factorial_recur(n)
|
||||
print("阶乘阶(递归实现)的操作数量 =", count)
|
||||
|
|
|
@ -88,17 +88,17 @@ if __name__ == "__main__":
|
|||
|
||||
# 暴力搜索
|
||||
res = min_path_sum_dfs(grid, n - 1, m - 1)
|
||||
print(f"从左上角到右下角的做小路径和为 {res}")
|
||||
print(f"从左上角到右下角的最小路径和为 {res}")
|
||||
|
||||
# 记忆化搜索
|
||||
mem = [[-1] * m for _ in range(n)]
|
||||
res = min_path_sum_dfs_mem(grid, mem, n - 1, m - 1)
|
||||
print(f"从左上角到右下角的做小路径和为 {res}")
|
||||
print(f"从左上角到右下角的最小路径和为 {res}")
|
||||
|
||||
# 动态规划
|
||||
res = min_path_sum_dp(grid)
|
||||
print(f"从左上角到右下角的做小路径和为 {res}")
|
||||
print(f"从左上角到右下角的最小路径和为 {res}")
|
||||
|
||||
# 空间优化后的动态规划
|
||||
res = min_path_sum_dp_comp(grid)
|
||||
print(f"从左上角到右下角的做小路径和为 {res}")
|
||||
print(f"从左上角到右下角的最小路径和为 {res}")
|
||||
|
|
|
@ -18,7 +18,7 @@ def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
|
|||
# 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
|
||||
# 顶点遍历序列
|
||||
res = []
|
||||
# 哈希表,用于记录已被访问过的顶点
|
||||
# 哈希集合,用于记录已被访问过的顶点
|
||||
visited = set[Vertex]([start_vet])
|
||||
# 队列用于实现 BFS
|
||||
que = deque[Vertex]([start_vet])
|
||||
|
|
|
@ -29,7 +29,7 @@ def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
|
|||
# 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
|
||||
# 顶点遍历序列
|
||||
res = []
|
||||
# 哈希表,用于记录已被访问过的顶点
|
||||
# 哈希集合,用于记录已被访问过的顶点
|
||||
visited = set[Vertex]()
|
||||
dfs(graph, visited, res, start_vet)
|
||||
return res
|
||||
|
|
|
@ -9,9 +9,7 @@ def counting_sort_naive(nums: list[int]):
|
|||
"""计数排序"""
|
||||
# 简单实现,无法用于排序对象
|
||||
# 1. 统计数组最大元素 m
|
||||
m = 0
|
||||
for num in nums:
|
||||
m = max(m, num)
|
||||
m = max(nums)
|
||||
# 2. 统计各数字的出现次数
|
||||
# counter[num] 代表 num 的出现次数
|
||||
counter = [0] * (m + 1)
|
||||
|
|
|
@ -41,7 +41,7 @@ def merge_sort(nums: list[int], left: int, right: int):
|
|||
if left >= right:
|
||||
return # 当子数组长度为 1 时终止递归
|
||||
# 划分阶段
|
||||
mid = (left + right) // 2 # 计算中点
|
||||
mid = (left + right) // 2 # 计算中点
|
||||
merge_sort(nums, left, mid) # 递归左子数组
|
||||
merge_sort(nums, mid + 1, right) # 递归右子数组
|
||||
# 合并阶段
|
||||
|
|
|
@ -79,7 +79,7 @@ class QuickSortMedian:
|
|||
|
||||
|
||||
class QuickSortTailCall:
|
||||
"""快速排序类(尾递归优化)"""
|
||||
"""快速排序类(递归深度优化)"""
|
||||
|
||||
def partition(self, nums: list[int], left: int, right: int) -> int:
|
||||
"""哨兵划分"""
|
||||
|
@ -97,7 +97,7 @@ class QuickSortTailCall:
|
|||
return i # 返回基准数的索引
|
||||
|
||||
def quick_sort(self, nums: list[int], left: int, right: int):
|
||||
"""快速排序(尾递归优化)"""
|
||||
"""快速排序(递归深度优化)"""
|
||||
# 子数组长度为 1 时终止
|
||||
while left < right:
|
||||
# 哨兵划分操作
|
||||
|
@ -123,7 +123,7 @@ if __name__ == "__main__":
|
|||
QuickSortMedian().quick_sort(nums1, 0, len(nums1) - 1)
|
||||
print("快速排序(中位基准数优化)完成后 nums =", nums1)
|
||||
|
||||
# 快速排序(尾递归优化)
|
||||
# 快速排序(递归深度优化)
|
||||
nums2 = [2, 4, 1, 0, 3, 5]
|
||||
QuickSortTailCall().quick_sort(nums2, 0, len(nums2) - 1)
|
||||
print("快速排序(尾递归优化)完成后 nums =", nums2)
|
||||
print("快速排序(递归深度优化)完成后 nums =", nums2)
|
||||
|
|
|
@ -18,7 +18,7 @@ class ArrayStack:
|
|||
|
||||
def is_empty(self) -> bool:
|
||||
"""判断栈是否为空"""
|
||||
return self._stack == []
|
||||
return self.size() == 0
|
||||
|
||||
def push(self, item: int):
|
||||
"""入栈"""
|
||||
|
|
|
@ -30,7 +30,7 @@ class LinkedListDeque:
|
|||
|
||||
def is_empty(self) -> bool:
|
||||
"""判断双向队列是否为空"""
|
||||
return self.size() == 0
|
||||
return self._size == 0
|
||||
|
||||
def push(self, num: int, is_front: bool):
|
||||
"""入队操作"""
|
||||
|
@ -69,7 +69,7 @@ class LinkedListDeque:
|
|||
val: int = self._front.val # 暂存头节点值
|
||||
# 删除头节点
|
||||
fnext: ListNode | None = self._front.next
|
||||
if fnext != None:
|
||||
if fnext is not None:
|
||||
fnext.prev = None
|
||||
self._front.next = None
|
||||
self._front = fnext # 更新头节点
|
||||
|
@ -78,7 +78,7 @@ class LinkedListDeque:
|
|||
val: int = self._rear.val # 暂存尾节点值
|
||||
# 删除尾节点
|
||||
rprev: ListNode | None = self._rear.prev
|
||||
if rprev != None:
|
||||
if rprev is not None:
|
||||
rprev.next = None
|
||||
self._rear.prev = None
|
||||
self._rear = rprev # 更新尾节点
|
||||
|
|
|
@ -26,7 +26,7 @@ class LinkedListQueue:
|
|||
|
||||
def is_empty(self) -> bool:
|
||||
"""判断队列是否为空"""
|
||||
return not self._front
|
||||
return self._size == 0
|
||||
|
||||
def push(self, num: int):
|
||||
"""入队"""
|
||||
|
|
|
@ -25,7 +25,7 @@ class LinkedListStack:
|
|||
|
||||
def is_empty(self) -> bool:
|
||||
"""判断栈是否为空"""
|
||||
return not self._peek
|
||||
return self._size == 0
|
||||
|
||||
def push(self, val: int):
|
||||
"""入栈"""
|
||||
|
|
|
@ -22,7 +22,7 @@ class ArrayBinaryTree:
|
|||
"""列表容量"""
|
||||
return len(self._tree)
|
||||
|
||||
def val(self, i: int) -> int:
|
||||
def val(self, i: int) -> int | None:
|
||||
"""获取索引为 i 节点的值"""
|
||||
# 若索引越界,则返回 None ,代表空位
|
||||
if i < 0 or i >= self.size():
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
=begin
|
||||
File: n_queens.rb
|
||||
Created Time: 2024-05-21
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 回溯算法:n 皇后 ###
|
||||
def backtrack(row, n, state, res, cols, diags1, diags2)
|
||||
# 当放置完所有行时,记录解
|
||||
if row == n
|
||||
res << state.map { |row| row.dup }
|
||||
return
|
||||
end
|
||||
|
||||
# 遍历所有列
|
||||
for col in 0...n
|
||||
# 计算该格子对应的主对角线和次对角线
|
||||
diag1 = row - col + n - 1
|
||||
diag2 = row + col
|
||||
# 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
|
||||
if !cols[col] && !diags1[diag1] && !diags2[diag2]
|
||||
# 尝试:将皇后放置在该格子
|
||||
state[row][col] = "Q"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true
|
||||
# 放置下一行
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2)
|
||||
# 回退:将该格子恢复为空位
|
||||
state[row][col] = "#"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### 求解 n 皇后 ###
|
||||
def n_queens(n)
|
||||
# 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
|
||||
state = Array.new(n) { Array.new(n, "#") }
|
||||
cols = Array.new(n, false) # 记录列是否有皇后
|
||||
diags1 = Array.new(2 * n - 1, false) # 记录主对角线上是否有皇后
|
||||
diags2 = Array.new(2 * n - 1, false) # 记录次对角线上是否有皇后
|
||||
res = []
|
||||
backtrack(0, n, state, res, cols, diags1, diags2)
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
n = 4
|
||||
res = n_queens(n)
|
||||
|
||||
puts "输入棋盘长宽为 #{n}"
|
||||
puts "皇后放置方案共有 #{res.length} 种"
|
||||
|
||||
for state in res
|
||||
puts "--------------------"
|
||||
for row in state
|
||||
p row
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
=begin
|
||||
File: permutations_i.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 回溯算法:全排列 I ###
|
||||
def backtrack(state, choices, selected, res)
|
||||
# 当状态长度等于元素数量时,记录解
|
||||
if state.length == choices.length
|
||||
res << state.dup
|
||||
return
|
||||
end
|
||||
|
||||
# 遍历所有选择
|
||||
choices.each_with_index do |choice, i|
|
||||
# 剪枝:不允许重复选择元素
|
||||
unless selected[i]
|
||||
# 尝试:做出选择,更新状态
|
||||
selected[i] = true
|
||||
state << choice
|
||||
# 进行下一轮选择
|
||||
backtrack(state, choices, selected, res)
|
||||
# 回退:撤销选择,恢复到之前的状态
|
||||
selected[i] = false
|
||||
state.pop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### 全排列 I ###
|
||||
def permutations_i(nums)
|
||||
res = []
|
||||
backtrack([], nums, Array.new(nums.length, false), res)
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
nums = [1, 2, 3]
|
||||
|
||||
res = permutations_i(nums)
|
||||
|
||||
puts "输入数组 nums = #{nums}"
|
||||
puts "所有排列 res = #{res}"
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
=begin
|
||||
File: permutations_ii.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 回溯算法:全排列 II ###
|
||||
def backtrack(state, choices, selected, res)
|
||||
# 当状态长度等于元素数量时,记录解
|
||||
if state.length == choices.length
|
||||
res << state.dup
|
||||
return
|
||||
end
|
||||
|
||||
# 遍历所有选择
|
||||
duplicated = Set.new
|
||||
choices.each_with_index do |choice, i|
|
||||
# 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
|
||||
if !selected[i] && !duplicated.include?(choice)
|
||||
# 尝试:做出选择,更新状态
|
||||
duplicated.add(choice)
|
||||
selected[i] = true
|
||||
state << choice
|
||||
# 进行下一轮选择
|
||||
backtrack(state, choices, selected, res)
|
||||
# 回退:撤销选择,恢复到之前的状态
|
||||
selected[i] = false
|
||||
state.pop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### 全排列 II ###
|
||||
def permutations_ii(nums)
|
||||
res = []
|
||||
backtrack([], nums, Array.new(nums.length, false), res)
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
nums = [1, 2, 2]
|
||||
|
||||
res = permutations_ii(nums)
|
||||
|
||||
puts "输入数组 nums = #{nums}"
|
||||
puts "所有排列 res = #{res}"
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
=begin
|
||||
File: preorder_traversal_i_compact.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
### 前序遍历:例题一 ###
|
||||
def pre_order(root)
|
||||
return unless root
|
||||
|
||||
# 记录解
|
||||
$res << root if root.val == 7
|
||||
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
root = arr_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
puts "\n初始化二叉树"
|
||||
print_tree(root)
|
||||
|
||||
# 前序遍历
|
||||
$res = []
|
||||
pre_order(root)
|
||||
|
||||
puts "\n输出所有值为 7 的节点"
|
||||
p $res.map { |node| node.val }
|
||||
end
|
|
@ -0,0 +1,41 @@
|
|||
=begin
|
||||
File: preorder_traversal_ii_compact.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
### 前序遍历:例题二 ###
|
||||
def pre_order(root)
|
||||
return unless root
|
||||
|
||||
# 尝试
|
||||
$path << root
|
||||
|
||||
# 记录解
|
||||
$res << $path.dup if root.val == 7
|
||||
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
|
||||
# 回退
|
||||
$path.pop
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
root = arr_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
puts "\n初始化二叉树"
|
||||
print_tree(root)
|
||||
|
||||
# 前序遍历
|
||||
$path, $res = [], []
|
||||
pre_order(root)
|
||||
|
||||
puts "\n输出所有根节点到节点 7 的路径"
|
||||
for path in $res
|
||||
p path.map { |node| node.val }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
=begin
|
||||
File: preorder_traversal_iii_compact.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
### 前序遍历:例题三 ###
|
||||
def pre_order(root)
|
||||
# 剪枝
|
||||
return if !root || root.val == 3
|
||||
|
||||
# 尝试
|
||||
$path.append(root)
|
||||
|
||||
# 记录解
|
||||
$res << $path.dup if root.val == 7
|
||||
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
|
||||
# 回退
|
||||
$path.pop
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
root = arr_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
puts "\n初始化二叉树"
|
||||
print_tree(root)
|
||||
|
||||
# 前序遍历
|
||||
$path, $res = [], []
|
||||
pre_order(root)
|
||||
|
||||
puts "\n输出所有根节点到节点 7 的路径,路径中不包含值为 3 的节点"
|
||||
for path in $res
|
||||
p path.map { |node| node.val }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,68 @@
|
|||
=begin
|
||||
File: preorder_traversal_iii_template.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
### 判断当前状态是否为解 ###
|
||||
def is_solution?(state)
|
||||
!state.empty? && state.last.val == 7
|
||||
end
|
||||
|
||||
### 记录解 ###
|
||||
def record_solution(state, res)
|
||||
res << state.dup
|
||||
end
|
||||
|
||||
### 判断在当前状态下,该选择是否合法 ###
|
||||
def is_valid?(state, choice)
|
||||
choice && choice.val != 3
|
||||
end
|
||||
|
||||
### 更新状态 ###
|
||||
def make_choice(state, choice)
|
||||
state << choice
|
||||
end
|
||||
|
||||
### 恢复状态 ###
|
||||
def undo_choice(state, choice)
|
||||
state.pop
|
||||
end
|
||||
|
||||
### 回溯算法:例题三 ###
|
||||
def backtrack(state, choices, res)
|
||||
# 检查是否为解
|
||||
record_solution(state, res) if is_solution?(state)
|
||||
|
||||
# 遍历所有选择
|
||||
for choice in choices
|
||||
# 剪枝:检查选择是否合法
|
||||
if is_valid?(state, choice)
|
||||
# 尝试:做出选择,更新状态
|
||||
make_choice(state, choice)
|
||||
# 进行下一轮选择
|
||||
backtrack(state, [choice.left, choice.right], res)
|
||||
# 回退:撤销选择,恢复到之前的状态
|
||||
undo_choice(state, choice)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
root = arr_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
puts "\n初始化二叉树"
|
||||
print_tree(root)
|
||||
|
||||
# 回溯算法
|
||||
res = []
|
||||
backtrack([], [root], res)
|
||||
|
||||
puts "\n输出所有根节点到节点 7 的路径,要求路径中不包含值为 3 的节点"
|
||||
for path in res
|
||||
p path.map { |node| node.val }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,47 @@
|
|||
=begin
|
||||
File: subset_sum_i.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 回溯算法:子集和 I ###
|
||||
def backtrack(state, target, choices, start, res)
|
||||
# 子集和等于 target 时,记录解
|
||||
if target.zero?
|
||||
res << state.dup
|
||||
return
|
||||
end
|
||||
# 遍历所有选择
|
||||
# 剪枝二:从 start 开始遍历,避免生成重复子集
|
||||
for i in start...choices.length
|
||||
# 剪枝一:若子集和超过 target ,则直接结束循环
|
||||
# 这是因为数组已排序,后边元素更大,子集和一定超过 target
|
||||
break if target - choices[i] < 0
|
||||
# 尝试:做出选择,更新 target, start
|
||||
state << choices[i]
|
||||
# 进行下一轮选择
|
||||
backtrack(state, target - choices[i], choices, i, res)
|
||||
# 回退:撤销选择,恢复到之前的状态
|
||||
state.pop
|
||||
end
|
||||
end
|
||||
|
||||
### 求解子集和 I ###
|
||||
def subset_sum_i(nums, target)
|
||||
state = [] # 状态(子集)
|
||||
nums.sort! # 对 nums 进行排序
|
||||
start = 0 # 遍历起始点
|
||||
res = [] # 结果列表(子集列表)
|
||||
backtrack(state, target, nums, start, res)
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
nums = [3, 4, 5]
|
||||
target = 9
|
||||
res = subset_sum_i(nums, target)
|
||||
|
||||
puts "输入数组 = #{nums}, target = #{target}"
|
||||
puts "所有和等于 #{target} 的子集 res = #{res}"
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
=begin
|
||||
File: subset_sum_i_naive.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 回溯算法:子集和 I ###
|
||||
def backtrack(state, target, total, choices, res)
|
||||
# 子集和等于 target 时,记录解
|
||||
if total == target
|
||||
res << state.dup
|
||||
return
|
||||
end
|
||||
|
||||
# 遍历所有选择
|
||||
for i in 0...choices.length
|
||||
# 剪枝:若子集和超过 target ,则跳过该选择
|
||||
next if total + choices[i] > target
|
||||
# 尝试:做出选择,更新元素和 total
|
||||
state << choices[i]
|
||||
# 进行下一轮选择
|
||||
backtrack(state, target, total + choices[i], choices, res)
|
||||
# 回退:撤销选择,恢复到之前的状态
|
||||
state.pop
|
||||
end
|
||||
end
|
||||
|
||||
### 求解子集和 I(包含重复子集)###
|
||||
def subset_sum_i_naive(nums, target)
|
||||
state = [] # 状态(子集)
|
||||
total = 0 # 子集和
|
||||
res = [] # 结果列表(子集列表)
|
||||
backtrack(state, target, total, nums, res)
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
nums = [3, 4, 5]
|
||||
target = 9
|
||||
res = subset_sum_i_naive(nums, target)
|
||||
|
||||
puts "输入数组 nums = #{nums}, target = #{target}"
|
||||
puts "所有和等于 #{target} 的子集 res = #{res}"
|
||||
puts "请注意,该方法输出的结果包含重复集合"
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
=begin
|
||||
File: subset_sum_ii.rb
|
||||
Created Time: 2024-05-22
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 回溯算法:子集和 II ###
|
||||
def backtrack(state, target, choices, start, res)
|
||||
# 子集和等于 target 时,记录解
|
||||
if target.zero?
|
||||
res << state.dup
|
||||
return
|
||||
end
|
||||
|
||||
# 遍历所有选择
|
||||
# 剪枝二:从 start 开始遍历,避免生成重复子集
|
||||
# 剪枝三:从 start 开始遍历,避免重复选择同一元素
|
||||
for i in start...choices.length
|
||||
# 剪枝一:若子集和超过 target ,则直接结束循环
|
||||
# 这是因为数组已排序,后边元素更大,子集和一定超过 target
|
||||
break if target - choices[i] < 0
|
||||
# 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过
|
||||
next if i > start && choices[i] == choices[i - 1]
|
||||
# 尝试:做出选择,更新 target, start
|
||||
state << choices[i]
|
||||
# 进行下一轮选择
|
||||
backtrack(state, target - choices[i], choices, i + 1, res)
|
||||
# 回退:撤销选择,恢复到之前的状态
|
||||
state.pop
|
||||
end
|
||||
end
|
||||
|
||||
### 求解子集和 II ###
|
||||
def subset_sum_ii(nums, target)
|
||||
state = [] # 状态(子集)
|
||||
nums.sort! # 对 nums 进行排序
|
||||
start = 0 # 遍历起始点
|
||||
res = [] # 结果列表(子集列表)
|
||||
backtrack(state, target, nums, start, res)
|
||||
res
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
nums = [4, 4, 5]
|
||||
target = 9
|
||||
res = subset_sum_ii(nums, target)
|
||||
|
||||
puts "输入数组 nums = #{nums}, target = #{target}"
|
||||
puts "所有和等于 #{target} 的子集 res = #{res}"
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
=begin
|
||||
File: binary_search_recur.rb
|
||||
Created Time: 2024-05-13
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 二分查找:问题 f(i, j) ###
|
||||
def dfs(nums, target, i, j)
|
||||
# 若区间为空,代表无目标元素,则返回 -1
|
||||
return -1 if i > j
|
||||
|
||||
# 计算中点索引 m
|
||||
m = (i + j) / 2
|
||||
|
||||
if nums[m] < target
|
||||
# 递归子问题 f(m+1, j)
|
||||
return dfs(nums, target, m + 1, j)
|
||||
elsif nums[m] > target
|
||||
# 递归子问题 f(i, m-1)
|
||||
return dfs(nums, target, i, m - 1)
|
||||
else
|
||||
# 找到目标元素,返回其索引
|
||||
return m
|
||||
end
|
||||
end
|
||||
|
||||
### 二分查找 ###
|
||||
def binary_search(nums, target)
|
||||
n = nums.length
|
||||
# 求解问题 f(0, n-1)
|
||||
dfs(nums, target, 0, n - 1)
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
target = 6
|
||||
nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
|
||||
# 二分查找(双闭区间)
|
||||
index = binary_search(nums, target)
|
||||
puts "目标元素 6 的索引 = #{index}"
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
=begin
|
||||
File: build_tree.rb
|
||||
Created Time: 2024-05-13
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
require_relative '../utils/tree_node'
|
||||
require_relative '../utils/print_util'
|
||||
|
||||
### 构建二叉树:分治 ###
|
||||
def dfs(preorder, inorder_map, i, l, r)
|
||||
# 子树区间为空时终止
|
||||
return if r - l < 0
|
||||
|
||||
# 初始化根节点
|
||||
root = TreeNode.new(preorder[i])
|
||||
# 查询 m ,从而划分左右子树
|
||||
m = inorder_map[preorder[i]]
|
||||
# 子问题:构建左子树
|
||||
root.left = dfs(preorder, inorder_map, i + 1, l, m - 1)
|
||||
# 子问题:构建右子树
|
||||
root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r)
|
||||
|
||||
# 返回根节点
|
||||
root
|
||||
end
|
||||
|
||||
### 构建二叉树 ###
|
||||
def build_tree(preorder, inorder)
|
||||
# 初始化哈希表,存储 inorder 元素到索引的映射
|
||||
inorder_map = {}
|
||||
inorder.each_with_index { |val, i| inorder_map[val] = i }
|
||||
dfs(preorder, inorder_map, 0, 0, inorder.length - 1)
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
preorder = [3, 9, 2, 1, 7]
|
||||
inorder = [9, 3, 1, 2, 7]
|
||||
puts "前序遍历 = #{preorder}"
|
||||
puts "中序遍历 = #{inorder}"
|
||||
|
||||
root = build_tree(preorder, inorder)
|
||||
puts "构建的二叉树为:"
|
||||
print_tree(root)
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
=begin
|
||||
File: hanota.rb
|
||||
Created Time: 2024-05-13
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 移动一个圆盘 ###
|
||||
def move(src, tar)
|
||||
# 从 src 顶部拿出一个圆盘
|
||||
pan = src.pop
|
||||
# 将圆盘放入 tar 顶部
|
||||
tar << pan
|
||||
end
|
||||
|
||||
### 求解汉诺塔问题 f(i) ###
|
||||
def dfs(i, src, buf, tar)
|
||||
# 若 src 只剩下一个圆盘,则直接将其移到 tar
|
||||
if i == 1
|
||||
move(src, tar)
|
||||
return
|
||||
end
|
||||
|
||||
# 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf
|
||||
dfs(i - 1, src, tar, buf)
|
||||
# 子问题 f(1) :将 src 剩余一个圆盘移到 tar
|
||||
move(src, tar)
|
||||
# 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar
|
||||
dfs(i - 1, buf, src, tar)
|
||||
end
|
||||
|
||||
### 求解汉诺塔问题 ###
|
||||
def solve_hanota(_A, _B, _C)
|
||||
n = _A.length
|
||||
# 将 A 顶部 n 个圆盘借助 B 移到 C
|
||||
dfs(n, _A, _B, _C)
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
# 列表尾部是柱子顶部
|
||||
A = [5, 4, 3, 2, 1]
|
||||
B = []
|
||||
C = []
|
||||
puts "初始状态下:"
|
||||
puts "A = #{A}"
|
||||
puts "B = #{B}"
|
||||
puts "C = #{C}"
|
||||
|
||||
solve_hanota(A, B, C)
|
||||
|
||||
puts "圆盘移动完成后:"
|
||||
puts "A = #{A}"
|
||||
puts "B = #{B}"
|
||||
puts "C = #{C}"
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
=begin
|
||||
File: climbing_stairs_backtrack.rb
|
||||
Created Time: 2024-05-29
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 回溯 ###
|
||||
def backtrack(choices, state, n, res)
|
||||
# 当爬到第 n 阶时,方案数量加 1
|
||||
res[0] += 1 if state == n
|
||||
# 遍历所有选择
|
||||
for choice in choices
|
||||
# 剪枝:不允许越过第 n 阶
|
||||
next if state + choice > n
|
||||
|
||||
# 尝试:做出选择,更新状态
|
||||
backtrack(choices, state + choice, n, res)
|
||||
end
|
||||
# 回退
|
||||
end
|
||||
|
||||
### 爬楼梯:回溯 ###
|
||||
def climbing_stairs_backtrack(n)
|
||||
choices = [1, 2] # 可选择向上爬 1 阶或 2 阶
|
||||
state = 0 # 从第 0 阶开始爬
|
||||
res = [0] # 使用 res[0] 记录方案数量
|
||||
backtrack(choices, state, n, res)
|
||||
res.first
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_backtrack(n)
|
||||
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
=begin
|
||||
File: climbing_stairs_constraint_dp.rb
|
||||
Created Time: 2024-05-29
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
### 带约束爬楼梯:动态规划 ###
|
||||
def climbing_stairs_constraint_dp(n)
|
||||
return 1 if n == 1 || n == 2
|
||||
|
||||
# 初始化 dp 表,用于存储子问题的解
|
||||
dp = Array.new(n + 1) { Array.new(3, 0) }
|
||||
# 初始状态:预设最小子问题的解
|
||||
dp[1][1], dp[1][2] = 1, 0
|
||||
dp[2][1], dp[2][2] = 0, 1
|
||||
# 状态转移:从较小子问题逐步求解较大子问题
|
||||
for i in 3...(n + 1)
|
||||
dp[i][1] = dp[i - 1][2]
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2]
|
||||
end
|
||||
|
||||
dp[n][1] + dp[n][2]
|
||||
end
|
||||
|
||||
### Driver Code ###
|
||||
if __FILE__ == $0
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_constraint_dp(n)
|
||||
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue