From dfe00729165338350d042f63056eb66101ad54c8 Mon Sep 17 00:00:00 2001 From: LouisFonda Date: Tue, 25 Mar 2025 16:49:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9D=A2=E8=AF=95=E5=B8=B8=E8=A7=81?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E9=A2=9826,27,80,88,168,189?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recursion/n皇后.js | 103 ++++++++++++++++ .../numbers-and-strings/169多数元素.js | 115 ++++++++++++++++++ .../numbers-and-strings/189轮转数组.js | 54 ++++++++ .../26删除有序数组中的重复项.js | 30 +++++ .../numbers-and-strings/27移除元素.js | 41 +++++++ .../80删除有序数组中重复项2.js | 46 +++++++ .../numbers-and-strings/88合并两个有序数组.js | 38 ++++++ 7 files changed, 427 insertions(+) create mode 100644 recursion/n皇后.js create mode 100644 top-interview-leetcode150/numbers-and-strings/169多数元素.js create mode 100644 top-interview-leetcode150/numbers-and-strings/189轮转数组.js create mode 100644 top-interview-leetcode150/numbers-and-strings/26删除有序数组中的重复项.js create mode 100644 top-interview-leetcode150/numbers-and-strings/27移除元素.js create mode 100644 top-interview-leetcode150/numbers-and-strings/80删除有序数组中重复项2.js create mode 100644 top-interview-leetcode150/numbers-and-strings/88合并两个有序数组.js diff --git a/recursion/n皇后.js b/recursion/n皇后.js new file mode 100644 index 0000000..4d8983a --- /dev/null +++ b/recursion/n皇后.js @@ -0,0 +1,103 @@ +/** + * 解决N皇后问题 + * @param {number} n - 棋盘大小和皇后数量 + * @return {string[][]} - 所有可能的解决方案 + */ +function solveNQueens(n) { + const result = []; + + // 创建一个表示棋盘的数组,初始化为空 + const board = Array(n).fill().map(() => Array(n).fill('.')); + + // 开始回溯 + backtrack(0); + + return result; + + /** + * 回溯函数,尝试在每一行放置皇后 + * @param {number} row - 当前处理的行 + */ + function backtrack(row) { + // 如果已经放置了n个皇后,找到一个解决方案 + if (row === n) { + // 将当前棋盘状态转换为所需的格式并添加到结果中 + const solution = board.map((row) => row.join('')); + result.push(solution); + return; + } + + // 尝试在当前行的每一列放置皇后 + for (let col = 0; col < n; col++) { + // 检查当前位置是否可以放置皇后 + if (isValid(row, col)) { + // 放置皇后 + board[row][col] = 'Q'; + + // 递归到下一行 + backtrack(row + 1); + + // 回溯,移除皇后 + board[row][col] = '.'; + } + } + } + + /** + * 检查在[row, col]位置放置皇后是否有效 + * @param {number} row - 行索引 + * @param {number} col - 列索引 + * @return {boolean} - 如果位置有效返回true,否则返回false + */ + function isValid(row, col) { + // 检查同一列是否有皇后 + for (let i = 0; i < row; i++) { + if (board[i][col] === 'Q') { + return false; + } + } + + // 检查左上对角线是否有皇后 + for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] === 'Q') { + return false; + } + } + + // 检查右上对角线是否有皇后 + for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { + if (board[i][j] === 'Q') { + return false; + } + } + + return true; + } +} + +/** +* 打印棋盘 +* @param {string[]} board - 表示棋盘的字符串数组 +*/ +function printBoard(board) { + for (const row of board) { + console.log(row); + } + console.log('---'); +} + +// 测试代码 +function testNQueens(n) { + console.log(`求解${n}皇后问题:`); + const solutions = solveNQueens(n); + console.log(`找到${solutions.length}个解决方案`); + + // 打印前3个解决方案 + for (let i = 0; i < Math.min(3, solutions.length); i++) { + console.log(`解决方案 ${i + 1}:`); + printBoard(solutions[i]); + } +} + +// 测试8皇后问题 +testNQueens(8); diff --git a/top-interview-leetcode150/numbers-and-strings/169多数元素.js b/top-interview-leetcode150/numbers-and-strings/169多数元素.js new file mode 100644 index 0000000..b6e9117 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/169多数元素.js @@ -0,0 +1,115 @@ +/** + *https://leetcode.cn/problems/majority-element/description/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @return {number} + */ +const majorityElement = function (nums) { + f1(nums); + f2(nums); + f3(nums, 0, nums.length - 1); + f4(nums); +}; + +/* +暴力法,遍历所有元素,统计每一个元素出现的次数,之后再遍历统计的结果,统计结果中大于数组长度二分之一的就是我们要找 +的多数元素 +*/ +function f1(nums) { + const countMap = new Map(); + + // 遍历数组,统计数组元素中每个元素出现的次数 + for (const num of nums) { + countMap.set(num, (countMap.get(num) || 0) + 1); + } + + // 遍历countMap 找到出现次数大于数组长度一半的元素就是多数元素 + const majorityCount = nums.length / 2; + for (const [num, count] of countMap) { + if (count > majorityCount) return num; + } + + // 如果没有就返回, 根据题目要求,不会发生这一情况 + return null; +} + +/* +排序法, 按照题目要求,多数元素一定会大于数组长度的一半, 所以只要把数组拍一个需,那么在1/2位置的元素一定是多数 +元素,why? 我们可以这样思考,想象大脑里面有一个进度条,进度条现在是51%,移动进度条的颜色部分,无论怎么移动,这个 +颜色部分总是会覆盖中间位置, 这里以数组长度3和4为例,如果数组长度是3那么中间元素就是 3/2 = 1.5,但在js中不会自动 +丢弃小数位,所以要Math.floor()向下取整,再来看看四,如果有多数元素,这个多数元素一定有三个,4/2 = 2,刚好也符合,所以 +奇偶无需特殊处理 +*/ +function f2(nums) { + // 先对数组排序 + nums.sort((a, b) => a - b); + return nums[Math.floor(nums.length / 2)]; +} + +/* +分治法,把大问题化成小问题,把原数组分成左右两部分,找出左右俩部分多数的数,如果这两个数相同就返回这个数,如果 +不相同,再比较谁多,返回多的那个 +*/ + +/** + * + * @param {number[]} nums 原数组 + * @param {number} left 开始的部分 + * @param {number} right 结束的部分 + */ +function f3(nums, left, right) { + // 如果left和right相等表明只有一个元素,直接返回即可 + if (left === right) return nums[left]; + + // 递归计算左右两部分的多数元素 + const mid = Math.floor((left + right) / 2); + const leftMajority = f3(nums, left, mid); + const rightMajority = f3(nums, mid + 1, right); + + // 如果左右两边的元素相同,则返回这个元素 + if (leftMajority === rightMajority) return leftMajority; + + // 如果两个元素不相等,就比较它们谁出现的次数多 + const leftMajorityCount = countInRange(nums, left, mid, leftMajority); + const rightMajorityCount = countInRange(nums, mid + 1, right, rightMajority); + return leftMajorityCount > rightMajorityCount ? leftMajority : rightMajority; +} + +/** + * + * @param {number[]} nums 要处理的元素组 + * @param {number} left 左边界 + * @param {number} right 右边界 + * @param {number} target 要统计的目标元素 + * @description 统计目标元素出现的次数 + */ +function countInRange(nums, left, right, target) { + let count = 0; + for (let i = left; i <= right; i++) { + if (nums[i] === target)count++; + } + return count; +} + +/* +投票法:假设数组中举行选择,这样的话,多数元素肯定会选举胜利,如果当前选择的元素和自己是一样的那么就投票,不是就反对 +是对手票减少一个 +*/ + +function f4(nums) { + if (nums.length === 1) return nums[0]; + let count = 1; + let num = nums[0]; // 候选的 + for (let i = 1; i < nums.length; i++) { + if (nums[i] === num) { + count++; + } else if (count > 0) { + count--; + } else { + num = nums[i]; + count = 1; + } + } + return num; +} + +majorityElement([2, 2, 1, 1, 1, 2, 2]); diff --git a/top-interview-leetcode150/numbers-and-strings/189轮转数组.js b/top-interview-leetcode150/numbers-and-strings/189轮转数组.js new file mode 100644 index 0000000..826b631 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/189轮转数组.js @@ -0,0 +1,54 @@ +/** + *https://leetcode.cn/problems/rotate-array/description/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +const rotate = function (nums, k) { + f1(nums, k); + // f2(nums, k); +}; + +/* +暴力法:直接copy整个数组,然后遍历复制后的数组从k位开始填充原数组 + */ +function f1(nums, k) { + const copyNums = [...nums]; + const size = nums.length; // 数组大小 + for (let i = 0; i < size; i++) { + nums[(i + k) % size] = copyNums[i]; + } +} + +/* +反转法:观察发现,只要把数组选中,之后再选择前k个元素和剩下的n-k个元素即可实现题目要求的效果 +*/ + +function f2(nums, k) { + const size = nums.length; + // 如果k等于0直接结束即可,反转是由性能损耗的 + if (k === 0 || k === size) return; + k %= size; // 预防k>size + // 1. 反转整个数组 + reverse(nums, 0, size - 1); + // 反转前面k个部分 + reverse(nums, 0, k - 1); + // 反转剩下的n-k个部分 + reverse(nums, k, size - 1); +} + +// 反转数组的一个部分 +function reverse(arr, start, end) { + while (start < end) { + // [arr[start], arr[end]] = [arr[end], arr[start]]; 交换效率低 + const temp = arr[start]; + arr[start] = arr[end]; + arr[end] = temp; + start++; + end--; + } +} + +const arr = [1, 2, 3, 4]; +rotate(arr, 3); +console.log(arr); diff --git a/top-interview-leetcode150/numbers-and-strings/26删除有序数组中的重复项.js b/top-interview-leetcode150/numbers-and-strings/26删除有序数组中的重复项.js new file mode 100644 index 0000000..3f93320 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/26删除有序数组中的重复项.js @@ -0,0 +1,30 @@ +/** + * https://leetcode.cn/problems/remove-duplicates-from-sorted-array/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @return {number} 不重复元素的数量 + */ +const removeDuplicates = function (nums) { + // 直接利用Set的去重特性(hash) + const numSet = new Set(nums); + let i = 0; + for (const num of numSet) { + nums[i] = num; + i++; + } + return numSet.size; +}; + +// 上面的方法虽然好理解,但是需要遍历整个数组才能转换成Set,加上hash操作,并不是最优解。因为提供的nums已经是一个“非严格递增的数组”, +// 只需利用双指针处理即可,首先定义两个指针p1,p2,p1指向第一个元素,p2指向第二个元素(如果有的话),p2开始遍历,如果遍历到得元素和nums[p1] +// 不相等,p1++ 并把这个值赋值到p1的位置,最后返回p1+1 + +const removeDuplicates2 = function (nums) { + if (nums.length < 2) return 1; + let p1 = 0; + for (let p2 = 1; p2 < nums.length; p2++) { + if (nums[p1] !== nums[p2]) { + nums[++p1] = nums[p2]; + } + } + return p1 + 1; // 下标加1表示个数 +}; diff --git a/top-interview-leetcode150/numbers-and-strings/27移除元素.js b/top-interview-leetcode150/numbers-and-strings/27移除元素.js new file mode 100644 index 0000000..ba7f5ec --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/27移除元素.js @@ -0,0 +1,41 @@ +/** + *https://leetcode.cn/problems/remove-element/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @param {number} val + * @return {number} + */ +/** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ +const removeElement = function (nums, val) { + let p1 = 0; // 记录有效元素,在这个题目中我们需要把遍历到的所有有效元素依此放入这个位置 + let k = 0; // 记录有效元素的个数 + for (let p2 = 0; p2 < nums.length; p2++) { + if (nums[p2] !== val) { + // [nums[p1], nums[p2]] = [nums[p2], nums[p1]]; // 语法糖执行效率低 + const temp = nums[p1]; + nums[p1] = nums[p2]; + nums[p2] = temp; + p1++; + k++; + } + } + return k; +}; + +const removeElement2 = function (nums, val) { + let p1 = 0; // 记录有效元素,在这个题目中我们需要把遍历到的所有有效元素依此放入这个位置 + for (let p2 = 0; p2 < nums.length; p2++) { + if (nums[p2] !== val) { + const temp = nums[p1]; + nums[p1] = nums[p2]; + nums[p2] = temp; + p1++; + } + } + return p1; // p1记录的是有效元素的位置,通过上面的循环可以发现p1和k的行为一致,所以不需要k来记录,只需返回p1即可 +}; + +// 上面 diff --git a/top-interview-leetcode150/numbers-and-strings/80删除有序数组中重复项2.js b/top-interview-leetcode150/numbers-and-strings/80删除有序数组中重复项2.js new file mode 100644 index 0000000..871f956 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/80删除有序数组中重复项2.js @@ -0,0 +1,46 @@ +/** + * https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @return {number} + */ +const removeDuplicates = function (nums) { + if (nums.length === 0) return 0; + + let i = 1; // 慢指针,初始化为1,表示第一项是已经有效的 + let count = 1; // 计数器,用于统计当前元素出现的次数 + + for (let j = 1; j < nums.length; j++) { // 快指针 + if (nums[j] === nums[j - 1]) { + count++; // 当前元素和前一个元素相同,计数器加1 + } else { + count = 1; // 如果不同,重置计数器 + } + + if (count <= 2) { + nums[i] = nums[j]; // 如果当前元素不超过2次,移动到有效位置 + i++; // 慢指针向前移动 + } + } + + return i; // 最终慢指针的位置即为去重后的元素个数 +}; + +// var removeDuplicates = function(nums) { +// const n = nums.length; +// if (n <= 2) { +// return n; +// } +// let slow = 2, fast = 2; +// while (fast < n) { +// if (nums[slow - 2] != nums[fast]) { +// nums[slow] = nums[fast]; +// ++slow; +// } +// ++fast; +// } +// return slow; +// }; + +const numss = [0, 0, 1, 1, 1, 1, 2, 3, 3]; +console.log(removeDuplicates(numss)); +console.log(numss); diff --git a/top-interview-leetcode150/numbers-and-strings/88合并两个有序数组.js b/top-interview-leetcode150/numbers-and-strings/88合并两个有序数组.js new file mode 100644 index 0000000..c3d982d --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/88合并两个有序数组.js @@ -0,0 +1,38 @@ +/** + * https://leetcode.cn/problems/merge-sorted-array/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + +const merge = function (nums1, m, nums2, n) { + let p1 = m - 1; // 指向nums1的末尾 + let p2 = n - 1; // 指向nums2的末尾 + let p = m + n - 1; // 指向nums1 填充部分的末尾 + + while (p1 >= 0 && p2 >= 0) { + if (nums1[p1] > nums2[p2]) { + nums1[p] = nums1[p1]; + p1--; + } else { + nums1[p] = nums2[p2]; + p2--; + } + p--; + } + + // 如果nums2中有未移动的数据,全部移动到nums1中 + + while (p2 >= 0) { + nums1[p] = nums2[p2]; + p2--; + p--; + } +}; + +// 思考:如果p1>=0,还需要全部移动一遍吗?答案是否定的,因为原本就是有序的,如果nums2处理完毕之后,就表示整个拼接好的nums1 +// 就是有序的,只需考虑 p2>=0 的情况 + +// 其他方法,直接合并两个数组,再对其排序,简单粗暴