Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(chapter_backtracking): Add js and ts codes for chapter 13.3 #667

Merged
merged 11 commits into from
Aug 3, 2023
Merged
2 changes: 1 addition & 1 deletion codes/javascript/chapter_array_and_linkedlist/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function traverse(nums) {
/* 在数组中查找指定元素 */
function find(nums, target) {
for (let i = 0; i < nums.length; i++) {
if (nums[i] == target) return i;
if (nums[i] === target) return i;
}
return -1;
}
Expand Down
46 changes: 46 additions & 0 deletions codes/javascript/chapter_backtracking/subset_sum_i.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* File: subset_sum_i.js
* Created Time: 2023-07-30
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 回溯算法:子集和 I */
function backtrack(state, target, choices, start, res) {
// 子集和等于 target 时,记录解
if (target === 0) {
res.push([...state]);
return;
}
// 遍历所有选择
// 剪枝二:从 start 开始遍历,避免生成重复子集
for (let i = start; i < choices.length; i++) {
// 剪枝一:若子集和超过 target ,则直接结束循环
// 这是因为数组已排序,后边元素更大,子集和一定超过 target
if (target - choices[i] < 0) {
break;
}
// 尝试:做出选择,更新 target, start
state.push(choices[i]);
// 进行下一轮选择
backtrack(state, target - choices[i], choices, i, res);
// 回退:撤销选择,恢复到之前的状态
state.pop();
}
}

/* 求解子集和 I */
function subsetSumI(nums, target) {
const state = []; // 状态(子集)
nums.sort(); // 对 nums 进行排序
const start = 0; // 遍历起始点
const res = []; // 结果列表(子集列表)
backtrack(state, target, nums, start, res);
return res;
}

/* Driver Code */
const nums = [3, 4, 5];
const target = 9;
const res = subsetSumI(nums, target);
console.log(`输入数组 nums = ${JSON.stringify(nums)}, target = ${target}`);
console.log(`所有和等于 ${target} 的子集 res = ${JSON.stringify(res)}`);
44 changes: 44 additions & 0 deletions codes/javascript/chapter_backtracking/subset_sum_i_naive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* File: subset_sum_i_naive.js
* Created Time: 2023-07-30
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 回溯算法:子集和 I */
function backtrack(state, target, total, choices, res) {
// 子集和等于 target 时,记录解
if (total === target) {
res.push([...state]);
return;
}
// 遍历所有选择
for (let i = 0; i < choices.length; i++) {
// 剪枝:若子集和超过 target ,则跳过该选择
if (total + choices[i] > target) {
continue;
}
// 尝试:做出选择,更新元素和 total
state.push(choices[i]);
// 进行下一轮选择
backtrack(state, target, total + choices[i], choices, res);
// 回退:撤销选择,恢复到之前的状态
state.pop();
}
}

/* 求解子集和 I(包含重复子集) */
function subsetSumINaive(nums, target) {
const state = []; // 状态(子集)
const total = 0; // 子集和
const res = []; // 结果列表(子集列表)
backtrack(state, target, total, nums, res);
return res;
}

/* Driver Code */
const nums = [3, 4, 5];
const target = 9;
const res = subsetSumINaive(nums, target);
console.log(`输入数组 nums = ${JSON.stringify(nums)}, target = ${target}`);
console.log(`所有和等于 ${target} 的子集 res = ${JSON.stringify(res)}`);
console.log('请注意,该方法输出的结果包含重复集合');
51 changes: 51 additions & 0 deletions codes/javascript/chapter_backtracking/subset_sum_ii.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* File: subset_sum_ii.js
* Created Time: 2023-07-30
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 回溯算法:子集和 II */
function backtrack(state, target, choices, start, res) {
// 子集和等于 target 时,记录解
if (target === 0) {
res.push([...state]);
return;
}
// 遍历所有选择
// 剪枝二:从 start 开始遍历,避免生成重复子集
// 剪枝三:从 start 开始遍历,避免重复选择同一元素
for (let i = start; i < choices.length; i++) {
// 剪枝一:若子集和超过 target ,则直接结束循环
// 这是因为数组已排序,后边元素更大,子集和一定超过 target
if (target - choices[i] < 0) {
break;
}
// 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过
if (i > start && choices[i] === choices[i - 1]) {
continue;
}
// 尝试:做出选择,更新 target, start
state.push(choices[i]);
// 进行下一轮选择
backtrack(state, target - choices[i], choices, i + 1, res);
// 回退:撤销选择,恢复到之前的状态
state.pop();
}
}

/* 求解子集和 II */
function subsetSumII(nums, target) {
const state = []; // 状态(子集)
nums.sort(); // 对 nums 进行排序
const start = 0; // 遍历起始点
const res = []; // 结果列表(子集列表)
backtrack(state, target, nums, start, res);
return res;
}

/* Driver Code */
const nums = [4, 4, 5];
const target = 9;
const res = subsetSumII(nums, target);
console.log(`输入数组 nums = ${JSON.stringify(nums)}, target = ${target}`);
console.log(`所有和等于 ${target} 的子集 res = ${JSON.stringify(res)}`);
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function exponential(n) {

/* 指数阶(递归实现) */
function expRecur(n) {
if (n == 1) return 1;
if (n === 1) return 1;
return expRecur(n - 1) + expRecur(n - 1) + 1;
}

Expand Down Expand Up @@ -109,7 +109,7 @@ function linearLogRecur(n) {

/* 阶乘阶(递归实现) */
function factorialRecur(n) {
if (n == 0) return 1;
if (n === 0) return 1;
let count = 0;
// 从 1 个分裂出 n 个
for (let i = 0; i < n; i++) {
Expand Down
39 changes: 39 additions & 0 deletions codes/javascript/chapter_divide_and_conquer/binary_search_recur.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* File: binary_search_recur.js
* Created Time: 2023-07-30
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 二分查找:问题 f(i, j) */
function dfs(nums, target, i, j) {
// 若区间为空,代表无目标元素,则返回 -1
if (i > j) {
return -1;
}
// 计算中点索引 m
const m = i + ((j - i) >> 1);
if (nums[m] < target) {
// 递归子问题 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 {
// 找到目标元素,返回其索引
return m;
}
}

/* 二分查找 */
function binarySearch(nums, target) {
const n = nums.length;
// 求解问题 f(0, n-1)
return dfs(nums, target, 0, n - 1);
}

/* Driver Code */
const target = 6;
const nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35];
// 二分查找(双闭区间)
const index = binarySearch(nums, target);
console.log(`目标元素 6 的索引 = ${index}`);
44 changes: 44 additions & 0 deletions codes/javascript/chapter_divide_and_conquer/build_tree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* File: build_tree.js
* Created Time: 2023-07-30
* Author: yuan0221 (yl1452491917@gmail.com)
*/

const { printTree } = require('../modules/PrintUtil');
const { TreeNode } = require('../modules/TreeNode');

/* 构建二叉树:分治 */
function dfs(preorder, inorder, hmap, i, l, r) {
// 子树区间为空时终止
if (r - l < 0) return null;
// 初始化根节点
const root = new TreeNode(preorder[i]);
// 查询 m ,从而划分左右子树
const m = hmap.get(preorder[i]);
// 子问题:构建左子树
root.left = dfs(preorder, inorder, hmap, i + 1, l, m - 1);
// 子问题:构建右子树
root.right = dfs(preorder, inorder, hmap, i + 1 + m - l, m + 1, r);
// 返回根节点
return root;
}

/* 构建二叉树 */
function buildTree(preorder, inorder) {
// 初始化哈希表,存储 inorder 元素到索引的映射
let hmap = new Map();
for (let i = 0; i < inorder.length; i++) {
hmap.set(inorder[i], i);
}
const root = dfs(preorder, inorder, hmap, 0, 0, inorder.length - 1);
return root;
}

/* Driver Code */
const preorder = [3, 9, 2, 1, 7];
const inorder = [9, 3, 1, 2, 7];
console.log('前序遍历 = ' + JSON.stringify(preorder));
console.log('中序遍历 = ' + JSON.stringify(inorder));
const root = buildTree(preorder, inorder);
console.log('构建的二叉树为:');
printTree(root);
52 changes: 52 additions & 0 deletions codes/javascript/chapter_divide_and_conquer/hanota.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* File: hanota.js
* Created Time: 2023-07-30
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 移动一个圆盘 */
function move(src, tar) {
// 从 src 顶部拿出一个圆盘
const pan = src.pop();
// 将圆盘放入 tar 顶部
tar.push(pan);
}

/* 求解汉诺塔:问题 f(i) */
function dfs(i, src, buf, tar) {
// 若 src 只剩下一个圆盘,则直接将其移到 tar
if (i === 1) {
move(src, tar);
return;
}
// 子问题 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);
}

/* 求解汉诺塔 */
function solveHanota(A, B, C) {
const n = A.length;
// 将 A 顶部 n 个圆盘借助 B 移到 C
dfs(n, A, B, C);
}

/* Driver Code */
// 列表尾部是柱子顶部
const A = [5, 4, 3, 2, 1];
const B = [];
const C = [];
console.log('初始状态下:');
console.log(`A = ${JSON.stringify(A)}`);
console.log(`B = ${JSON.stringify(B)}`);
console.log(`C = ${JSON.stringify(C)}`);

solveHanota(A, B, C);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
solveHanota(A, B, C);
solveHanota(A, B, C);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our code basis is Java code, addressed with others.


console.log('圆盘移动完成后:');
console.log(`A = ${JSON.stringify(A)}`);
console.log(`B = ${JSON.stringify(B)}`);
console.log(`C = ${JSON.stringify(C)}`);
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/* 搜索 */
function dfs(i) {
// 已知 dp[1] 和 dp[2] ,返回之
if (i == 1 || i == 2) return i;
if (i === 1 || i === 2) return i;
// dp[i] = dp[i-1] + dp[i-2]
const count = dfs(i - 1) + dfs(i - 2);
return count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/* 记忆化搜索 */
function dfs(i, mem) {
// 已知 dp[1] 和 dp[2] ,返回之
if (i == 1 || i == 2) return i;
if (i === 1 || i === 2) return i;
// 若存在记录 dp[i] ,则直接返回之
if (mem[i] != -1) return mem[i];
// dp[i] = dp[i-1] + dp[i-2]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

/* 爬楼梯:动态规划 */
function climbingStairsDP(n) {
if (n == 1 || n == 2) return n;
if (n === 1 || n === 2) return n;
// 初始化 dp 表,用于存储子问题的解
const dp = new Array(n + 1).fill(-1);
// 初始状态:预设最小子问题的解
Expand All @@ -21,7 +21,7 @@ function climbingStairsDP(n) {

/* 爬楼梯:状态压缩后的动态规划 */
function climbingStairsDPComp(n) {
if (n == 1 || n == 2) return n;
if (n === 1 || n === 2) return n;
let a = 1,
b = 2;
for (let i = 3; i <= n; i++) {
Expand Down
2 changes: 1 addition & 1 deletion codes/javascript/chapter_graph/graph_adjacency_matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class GraphAdjMat {
if (i < 0 || j < 0 || i >= this.size() || j >= this.size() || i === j) {
throw new RangeError('Index Out Of Bounds Exception');
}
// 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i)
// 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) === (j, i)
this.adjMat[i][j] = 1;
this.adjMat[j][i] = 1;
}
Expand Down
2 changes: 1 addition & 1 deletion codes/javascript/chapter_heap/my_heap.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class MaxHeap {
if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[ma]) ma = l;
if (r < this.size() && this.#maxHeap[r] > this.#maxHeap[ma]) ma = r;
// 若节点 i 最大或索引 l, r 越界,则无需继续堆化,跳出
if (ma == i) break;
if (ma === i) break;
// 交换两节点
this.#swap(i, ma);
// 循环向下堆化
Expand Down
2 changes: 1 addition & 1 deletion codes/javascript/chapter_searching/binary_search_edge.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function binarySearchLeftEdge(nums, target) {
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
if (i == nums.length || nums[i] != target) {
if (i === nums.length || nums[i] != target) {
return -1; // 未找到目标元素,返回 -1
}
return i;
Expand Down
4 changes: 2 additions & 2 deletions codes/javascript/chapter_stack_and_queue/array_queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ class ArrayQueue {

/* 判断队列是否为空 */
empty() {
return this.#queSize == 0;
return this.#queSize === 0;
}

/* 入队 */
push(num) {
if (this.size == this.capacity) {
if (this.size === this.capacity) {
console.log('队列已满');
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class LinkedListStack {

/* 判断栈是否为空 */
isEmpty() {
return this.size == 0;
return this.size === 0;
}

/* 入栈 */
Expand Down
Loading