diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..496ee2ca --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/BinaryHeap.js b/BinaryHeap.js new file mode 100644 index 00000000..23c40222 --- /dev/null +++ b/BinaryHeap.js @@ -0,0 +1,154 @@ +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆中插入多个元素 + insertMultiple(arr) { + for (let i = 0; i < arr.length; i++) { + this.insert(arr[i]); + } + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } + + printHeap() { + console.log('nHeap = '); + console.log(this.data); + } +} + +let maxHeap = new BinaryHeap((a, b) => b - a); +maxHeap.insert(10); +maxHeap.insert(4); +maxHeap.insert(9); +maxHeap.insert(1); +maxHeap.insert(7); +maxHeap.insert(5); +maxHeap.insert(3); + +maxHeap.printHeap(); +maxHeap.delete(0); +maxHeap.printHeap(); +maxHeap.delete(2); +maxHeap.printHeap(); +maxHeap.deleteItem(7); +maxHeap.printHeap(); diff --git a/BinarySearch.js b/BinarySearch.js new file mode 100644 index 00000000..fb3eede4 --- /dev/null +++ b/BinarySearch.js @@ -0,0 +1,15 @@ +let left = 0; +let right = array.length - 1; + +while (left <= right) { + let mid = (left + right) >> 1; + + if (array[mid] === target) { + /*find the target*/ + return; + } else if (array[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } +} diff --git "a/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\346\214\207\351\222\210.js" "b/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\346\214\207\351\222\210.js" new file mode 100644 index 00000000..5dd2144c --- /dev/null +++ "b/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\346\214\207\351\222\210.js" @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function (nums, target) { + let map = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (map.has(nums[i])) { + map.get(nums[i]).push(i); + } else { + map.set(nums[i], [i]); + } + } + + nums.sort((a, b) => a - b); + + for (let i = 0, j = nums.length - 1; i < j; ) { + const sum = nums[i] + nums[j]; + if (sum < target) { + i++; + } else if (sum > target) { + j--; + } else { + return [map.get(nums[i]).shift(), map.get(nums[j]).shift()]; + } + } +}; +// @lc code=end diff --git "a/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..005b820a --- /dev/null +++ "b/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function (nums, target) { + let map = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (map.has(nums[i])) { + return [map.get(nums[i]), i]; + } + map.set(target - nums[i], i); + } +}; +// @lc code=end diff --git "a/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\346\232\264\345\212\233.js" "b/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\346\232\264\345\212\233.js" new file mode 100644 index 00000000..2bfcf328 --- /dev/null +++ "b/Week_01/1.\344\270\244\346\225\260\344\271\213\345\222\214\357\274\214\346\232\264\345\212\233.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = function (nums, target) { + for (let i = 0; i < nums.length - 1; i++) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] === target) { + return [i, j]; + } + } + } +}; +// @lc code=end diff --git "a/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214for\345\276\252\347\216\257\345\217\214\346\214\207\351\222\210.js" "b/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214for\345\276\252\347\216\257\345\217\214\346\214\207\351\222\210.js" new file mode 100644 index 00000000..d894e802 --- /dev/null +++ "b/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214for\345\276\252\347\216\257\345\217\214\346\214\207\351\222\210.js" @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=11 lang=javascript + * + * [11] 盛最多水的容器 + */ + +// @lc code=start +/** + * @param {number[]} height + * @return {number} + */ +var maxArea = function (height) { + let max = -Infinity; + + for (let i = 0, j = height.length - 1; i < j; ) { + max = Math.max( + max, + (height[i] < height[j] ? height[i++] : height[j--]) * (j - i + 1), + ); + } + + return max; +}; +// @lc code=end diff --git "a/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214while\345\276\252\347\216\257\345\217\214\346\214\207\351\222\210.js" "b/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214while\345\276\252\347\216\257\345\217\214\346\214\207\351\222\210.js" new file mode 100644 index 00000000..348664be --- /dev/null +++ "b/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214while\345\276\252\347\216\257\345\217\214\346\214\207\351\222\210.js" @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=11 lang=javascript + * + * [11] 盛最多水的容器 + */ + +// @lc code=start +/** + * @param {number[]} height + * @return {number} + */ +var maxArea = function (height) { + let max = -Infinity; + let i = 0; + let j = height.length - 1; + + while (i < j) { + let minHeight = height[i] < height[j] ? height[i++] : height[j--]; + max = Math.max(minHeight * (j - i + 1), max); + } + + return max; +}; +// @lc code=end diff --git "a/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233\346\263\225.js" "b/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233\346\263\225.js" new file mode 100644 index 00000000..c64d2376 --- /dev/null +++ "b/Week_01/11. \347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233\346\263\225.js" @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=11 lang=javascript + * + * [11] 盛最多水的容器 + */ + +// @lc code=start +/** + * @param {number[]} height + * @return {number} + */ +var maxArea = function (height) { + let max = -Infinity; + + for (let i = 0; i < height.length - 1; i++) { + for (let j = i + 1; j < height.length; j++) { + max = Math.max(max, (j - i) * Math.min(height[i], height[j])); + } + } + + return max; +}; +// @lc code=end diff --git "a/Week_01/141. \347\216\257\345\275\242\351\223\276\350\241\250\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_01/141. \347\216\257\345\275\242\351\223\276\350\241\250\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..5e1e87b9 --- /dev/null +++ "b/Week_01/141. \347\216\257\345\275\242\351\223\276\350\241\250\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,26 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {boolean} + */ +var hasCycle = function(head) { + let set = new Set + let node = head + + while (node) { + if (set.has(node)) { + return true + } + set.add(node) + node = node.next + } + + return false +}; \ No newline at end of file diff --git "a/Week_01/141. \347\216\257\345\275\242\351\223\276\350\241\250\357\274\214\345\277\253\346\205\242\346\214\207\351\222\210.js" "b/Week_01/141. \347\216\257\345\275\242\351\223\276\350\241\250\357\274\214\345\277\253\346\205\242\346\214\207\351\222\210.js" new file mode 100644 index 00000000..f904ef67 --- /dev/null +++ "b/Week_01/141. \347\216\257\345\275\242\351\223\276\350\241\250\357\274\214\345\277\253\346\205\242\346\214\207\351\222\210.js" @@ -0,0 +1,26 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {boolean} + */ +var hasCycle = function(head) { + let slow = head + let fast = head + + while (fast && fast.next) { + fast = fast.next.next + slow = slow.next + if (fast === slow) { + return true + } + } + + return false +}; \ No newline at end of file diff --git "a/Week_01/142. \347\216\257\345\275\242\351\223\276\350\241\250 II\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_01/142. \347\216\257\345\275\242\351\223\276\350\241\250 II\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..a2ecd384 --- /dev/null +++ "b/Week_01/142. \347\216\257\345\275\242\351\223\276\350\241\250 II\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,26 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {ListNode} + */ +var detectCycle = function(head) { + let set = new Set + let node = head + + while (node) { + if (set.has(node)) { + return node + } + set.add(node) + node = node.next + } + + return null +}; \ No newline at end of file diff --git "a/Week_01/142. \347\216\257\345\275\242\351\223\276\350\241\250 II\357\274\214\345\277\253\346\205\242\346\214\207\351\222\210.js" "b/Week_01/142. \347\216\257\345\275\242\351\223\276\350\241\250 II\357\274\214\345\277\253\346\205\242\346\214\207\351\222\210.js" new file mode 100644 index 00000000..22002a31 --- /dev/null +++ "b/Week_01/142. \347\216\257\345\275\242\351\223\276\350\241\250 II\357\274\214\345\277\253\346\205\242\346\214\207\351\222\210.js" @@ -0,0 +1,40 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {ListNode} + */ +var detectCycle = function(head) { + let slow = head + let fast = head + let meet = false + + while (fast && fast.next) { + fast = fast.next.next + slow = slow.next + + if (slow === fast) { + meet = true + break + } + } + + if (!meet) { + return null + } + + let node = head + + while (node !== fast) { + node = node.next + fast = fast.next + } + + return fast +}; \ No newline at end of file diff --git "a/Week_01/15. \344\270\211\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\345\276\252\347\216\257+HashMap.js" "b/Week_01/15. \344\270\211\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\345\276\252\347\216\257+HashMap.js" new file mode 100644 index 00000000..49151863 --- /dev/null +++ "b/Week_01/15. \344\270\211\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\345\276\252\347\216\257+HashMap.js" @@ -0,0 +1,46 @@ +/* + * @lc app=leetcode.cn id=15 lang=javascript + * + * [15] 三数之和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function (nums) { + let result = []; + nums.sort((a, b) => a - b); + + for (let i = 0; i < nums.length - 2; i++) { + if (nums[i] === nums[i - 1]) { + continue; + } + + let target = 0 - nums[i]; + let map = new Map(); + + for (let j = i + 1; j < nums.length; j++) { + if (map.has(nums[j])) { + if (result.length) { + const last = result.length - 1; + + if ( + result[last][1] === map.get(nums[j]) && + result[last][2] === nums[j] + ) { + continue; + } + } + + result.push([nums[i], map.get(nums[j]), nums[j]]); + } + + map.set(target - nums[j], nums[j]); + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_01/15. \344\270\211\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\345\276\252\347\216\257+\345\217\214\346\214\207\351\222\210.js" "b/Week_01/15. \344\270\211\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\345\276\252\347\216\257+\345\217\214\346\214\207\351\222\210.js" new file mode 100644 index 00000000..267e9ffa --- /dev/null +++ "b/Week_01/15. \344\270\211\346\225\260\344\271\213\345\222\214\357\274\214\345\217\214\345\276\252\347\216\257+\345\217\214\346\214\207\351\222\210.js" @@ -0,0 +1,71 @@ +/* + * @lc app=leetcode.cn id=15 lang=javascript + * + * [15] 三数之和 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function (nums) { + let result = []; + nums.sort((a, b) => a - b); + + for (let i = 0; i < nums.length - 2; i++) { + if (nums[i] > 0) { + break; + } + if (nums[i] === nums[i - 1]) { + continue; + } + + const target = 0 - nums[i]; + + for (let j = i + 1, k = nums.length - 1; j < k; ) { + if (result.length) { + const last = result.length - 1; + + if ( + result[last][0] === nums[i] && + result[last][1] === nums[j] && + result[last][2] === nums[k] + ) { + continue; + } + } + + const sum = nums[j] + nums[k]; + + if (sum < target) { + j++; + while (nums[j] === nums[j - 1]) { + j++; + } + } + + if (sum > target) { + k--; + while (nums[k] === nums[k + 1]) { + k--; + } + } + + if (sum === target) { + result.push([nums[i], nums[j], nums[k]]); + j++; + while (nums[j] === nums[j - 1]) { + j++; + } + k--; + while (nums[k] === nums[k + 1]) { + k--; + } + } + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\351\223\276\350\241\250\344\273\243\346\233\277\346\240\210.js" "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\351\223\276\350\241\250\344\273\243\346\233\277\346\240\210.js" new file mode 100644 index 00000000..01289b42 --- /dev/null +++ "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\351\223\276\350\241\250\344\273\243\346\233\277\346\240\210.js" @@ -0,0 +1,66 @@ +/** + * initialize your data structure here. + */ +function Node(val = null, min = null) { + this.val = val + this.min = min + this.next = null +} + +var MinStack = function() { + this.head = null +}; + +/** + * @param {number} x + * @return {void} + */ +MinStack.prototype.push = function(x) { + if (this.head) { + const node = new Node(x, Math.min(this.head.min, x)) + node.next = this.head + this.head = node + } else { + this.head = new Node(x, x) + } +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function() { + if (this.head) { + this.head = this.head.next + } +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function() { + if (this.head) { + return this.head.val + } + + return null +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function() { + if (this.head) { + return this.head.min + } + + return null +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ \ No newline at end of file diff --git "a/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\351\223\276\350\241\250\344\273\243\346\233\277\346\240\210\357\274\214\351\223\276\350\241\250\345\217\214\345\220\221\351\223\276\346\216\245.js" "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\351\223\276\350\241\250\344\273\243\346\233\277\346\240\210\357\274\214\351\223\276\350\241\250\345\217\214\345\220\221\351\223\276\346\216\245.js" new file mode 100644 index 00000000..f3c5c6fe --- /dev/null +++ "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\351\223\276\350\241\250\344\273\243\346\233\277\346\240\210\357\274\214\351\223\276\350\241\250\345\217\214\345\220\221\351\223\276\346\216\245.js" @@ -0,0 +1,63 @@ +/** + * initialize your data structure here. + */ +function Node(val = null, min = null) { + this.val = val + this.min = min + this.prev = null + this.next = null +} + +var MinStack = function() { + this.list = null +}; + +/** + * @param {number} x + * @return {void} + */ +MinStack.prototype.push = function(x) { + if (this.list) { + const node = new Node(x, Math.min(this.list.min, x)) + this.list.next = node + node.prev = this.list + this.list = node + } else { + this.list = new Node(x, x) + } +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function() { + if (this.list) { + const node = this.list + this.list = this.list.prev + this.list && (this.list.next = null) + node.prev = null + } +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function() { + return this.list.val +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function() { + return this.list.min +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ \ No newline at end of file diff --git "a/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210+\345\257\271\350\261\241\345\255\230\345\202\250.js" "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210+\345\257\271\350\261\241\345\255\230\345\202\250.js" new file mode 100644 index 00000000..70bd9f68 --- /dev/null +++ "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210+\345\257\271\350\261\241\345\255\230\345\202\250.js" @@ -0,0 +1,47 @@ +/** + * initialize your data structure here. + */ +var MinStack = function() { + this.stack = [] +}; + +/** + * @param {number} x + * @return {void} + */ +MinStack.prototype.push = function(x) { + this.stack.push({ + val: x, + min: this.stack.length ? Math.min(this.stack[this.stack.length - 1].min, x) : x + }) +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function() { + this.stack.pop() +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function() { + return this.stack[this.stack.length - 1].val +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function() { + return this.stack[this.stack.length - 1].min +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ \ No newline at end of file diff --git "a/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210\345\220\214\346\227\266\345\255\230\345\202\250\346\234\200\345\260\217\345\200\274.js" "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210\345\220\214\346\227\266\345\255\230\345\202\250\346\234\200\345\260\217\345\200\274.js" new file mode 100644 index 00000000..9731385c --- /dev/null +++ "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210\345\220\214\346\227\266\345\255\230\345\202\250\346\234\200\345\260\217\345\200\274.js" @@ -0,0 +1,55 @@ +/** + * initialize your data structure here. + */ +var MinStack = function() { + this.stack = [] + this.min = Infinity +}; + +/** + * @param {number} x + * @return {void} + */ +MinStack.prototype.push = function(x) { + if (this.min >= x) { + this.stack.push(this.min, x) + this.min = x + } else { + this.stack.push(x) + } +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function() { + if (this.min === this.stack[this.stack.length - 1]) { + this.stack.pop() + this.min = this.stack.pop() + } else { + this.stack.pop() + } +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function() { + return this.stack[this.stack.length - 1] +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function() { + return this.min +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ \ No newline at end of file diff --git "a/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210\345\255\230\345\202\250\345\205\245\346\240\210\345\205\203\347\264\240\344\270\216\346\234\200\345\260\217\345\200\274\344\271\213\345\267\256.js" "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210\345\255\230\345\202\250\345\205\245\346\240\210\345\205\203\347\264\240\344\270\216\346\234\200\345\260\217\345\200\274\344\271\213\345\267\256.js" new file mode 100644 index 00000000..872e12a0 --- /dev/null +++ "b/Week_01/155. \346\234\200\345\260\217\346\240\210\357\274\214\345\215\225\344\270\252\346\240\210\345\255\230\345\202\250\345\205\245\346\240\210\345\205\203\347\264\240\344\270\216\346\234\200\345\260\217\345\200\274\344\271\213\345\267\256.js" @@ -0,0 +1,64 @@ +/** + * initialize your data structure here. + */ +var MinStack = function() { + this.stack = [] + this.min = 0 +}; + +/** + * @param {number} x + * @return {void} + */ +MinStack.prototype.push = function(x) { + if (this.stack.length) { + this.stack.push(x - this.min) + + if (x < this.min) { + this.min = x + } + } else { + this.min = x + this.stack.push(x - this.min) + } +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function() { + const pop = this.stack.pop() + + if (pop < 0) { + this.min = this.min - pop + } +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function() { + const top = this.stack[this.stack.length - 1] + + if (top < 0) { + return this.min + } else { + return this.min + top + } +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function() { + return this.min +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ \ No newline at end of file diff --git "a/Week_01/155.\346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\344\270\244\344\270\252\346\240\210.js" "b/Week_01/155.\346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\344\270\244\344\270\252\346\240\210.js" new file mode 100644 index 00000000..9f1d7688 --- /dev/null +++ "b/Week_01/155.\346\234\200\345\260\217\346\240\210\357\274\214\344\275\277\347\224\250\344\270\244\344\270\252\346\240\210.js" @@ -0,0 +1,47 @@ +/** + * initialize your data structure here. + */ +var MinStack = function() { + this.stack = [] + this.min = [Infinity] +}; + +/** + * @param {number} x + * @return {void} + */ +MinStack.prototype.push = function(x) { + this.stack.push(x) + this.min.push(Math.min(this.min[this.min.length - 1], x)) +}; + +/** + * @return {void} + */ +MinStack.prototype.pop = function() { + this.stack.pop() + this.min.pop() +}; + +/** + * @return {number} + */ +MinStack.prototype.top = function() { + return this.stack[this.stack.length - 1] +}; + +/** + * @return {number} + */ +MinStack.prototype.getMin = function() { + return this.min[this.min.length -1] +}; + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ \ No newline at end of file diff --git "a/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\2143\346\254\241\347\277\273\350\275\254.js" "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\2143\346\254\241\347\277\273\350\275\254.js" new file mode 100644 index 00000000..c5cadbf2 --- /dev/null +++ "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\2143\346\254\241\347\277\273\350\275\254.js" @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=189 lang=javascript + * + * [189] 旋转数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function (nums, k) { + k = k % nums.length; + + function reverse(start, end) { + while (start < end) { + const temp = nums[start]; + + nums[start++] = nums[end]; + nums[end--] = temp; + } + } + + reverse(0, nums.length - 1); + reverse(0, k - 1); + reverse(k, nums.length - 1); +}; +// @lc code=end diff --git "a/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214JavaScript.js" "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214JavaScript.js" new file mode 100644 index 00000000..8d6e623f --- /dev/null +++ "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214JavaScript.js" @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=189 lang=javascript + * + * [189] 旋转数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function (nums, k) { + for (let i = 0; i < k; i++) { + let target = nums[nums.length - 1]; + + for (let j = 0; j < nums.length; j++) { + let temp = nums[j]; + nums[j] = target; + target = temp; + } + } +}; +// @lc code=end diff --git "a/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214pop+unshift\344\270\200\350\241\214.js" "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214pop+unshift\344\270\200\350\241\214.js" new file mode 100644 index 00000000..86f3a97d --- /dev/null +++ "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214pop+unshift\344\270\200\350\241\214.js" @@ -0,0 +1,18 @@ +/* + * @lc app=leetcode.cn id=189 lang=javascript + * + * [189] 旋转数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function (nums, k) { + for (let i = 0; i < k; i++) { + nums.unshift(nums.pop()); + } +}; +// @lc code=end diff --git "a/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214\344\275\277\347\224\250\346\226\260\346\225\260\347\273\204Copy.js" "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214\344\275\277\347\224\250\346\226\260\346\225\260\347\273\204Copy.js" new file mode 100644 index 00000000..dee2bfb8 --- /dev/null +++ "b/Week_01/189. \346\227\213\350\275\254\346\225\260\347\273\204\357\274\214\344\275\277\347\224\250\346\226\260\346\225\260\347\273\204Copy.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=189 lang=javascript + * + * [189] 旋转数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function (nums, k) { + let arr = []; + + for (let i = 0; i < nums.length; i++) { + arr[(i + k) % nums.length] = nums[i]; + } + + nums.splice(0, nums.length, ...arr); +}; +// @lc code=end diff --git "a/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214for\345\276\252\347\216\257replace.js" "b/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214for\345\276\252\347\216\257replace.js" new file mode 100644 index 00000000..9bf92e22 --- /dev/null +++ "b/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214for\345\276\252\347\216\257replace.js" @@ -0,0 +1,22 @@ +/** + * @param {string} s + * @return {boolean} + */ +var isValid = function(s) { + if (s.length % 2) { + return false + } + + let halfLen = s.length / 2 + + for (let i = 0; i < halfLen; i++) { + let lastStr = s + s = s.replace(/(\(\))|(\[\])|(\{\})/, '') + + if (lastStr === s) { + return false + } + } + + return !s.length +}; diff --git "a/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214while\345\276\252\347\216\257replace.js" "b/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214while\345\276\252\347\216\257replace.js" new file mode 100644 index 00000000..a3992156 --- /dev/null +++ "b/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214while\345\276\252\347\216\257replace.js" @@ -0,0 +1,16 @@ +/** + * @param {string} s + * @return {boolean} + */ +var isValid = function(s) { + while (s.length) { + let temp = s + s = s.replace(/(\(\))|(\[\])|(\{\})/, '') + + if (s === temp) { + return false + } + } + + return !s.length +}; \ No newline at end of file diff --git "a/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214\346\240\210.js" "b/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214\346\240\210.js" new file mode 100644 index 00000000..26e5ad4f --- /dev/null +++ "b/Week_01/20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\214\346\240\210.js" @@ -0,0 +1,26 @@ +/** + * @param {string} s + * @return {boolean} + */ +var isValid = function(s) { + let stack = [] + let map = { + '(': ')', + '[': ']', + '{': '}' + } + + for (const char of s) { + if (map[char]) { + stack.push(map[char]) + } else { + const pop = stack.pop() + + if (pop !== char) { + return false + } + } + } + + return !stack.length +}; \ No newline at end of file diff --git "a/Week_01/206.\345\217\215\350\275\254\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243.js" "b/Week_01/206.\345\217\215\350\275\254\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243.js" new file mode 100644 index 00000000..8a61e2b7 --- /dev/null +++ "b/Week_01/206.\345\217\215\350\275\254\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=206 lang=javascript + * + * [206] 反转链表 + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/**33 + * @param {ListNode} head + * @return {ListNode} + */ +var reverseList = function (head) { + let node = head; + let prev = null; + + while (node) { + const next = node.next; + node.next = prev; + prev = node; + node = next; + } + + return prev; +}; +// @lc code=end diff --git "a/Week_01/206.\345\217\215\350\275\254\351\223\276\350\241\250\357\274\214\351\200\222\345\275\222.js" "b/Week_01/206.\345\217\215\350\275\254\351\223\276\350\241\250\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..d5164e1b --- /dev/null +++ "b/Week_01/206.\345\217\215\350\275\254\351\223\276\350\241\250\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=206 lang=javascript + * + * [206] 反转链表 + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/**33 + * @param {ListNode} head + * @return {ListNode} + */ +var reverseList = function (head) { + if (!head || !head.next) { + return head; + } + + let node = reverseList(head.next); + head.next.next = head; + head.next = null; + + return node; +}; +// @lc code=end diff --git "a/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\345\210\251\347\224\250\346\225\260\347\273\204\346\216\222\345\272\217.js" "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\345\210\251\347\224\250\346\225\260\347\273\204\346\216\222\345\272\217.js" new file mode 100644 index 00000000..234defef --- /dev/null +++ "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\345\210\251\347\224\250\346\225\260\347\273\204\346\216\222\345\272\217.js" @@ -0,0 +1,36 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var mergeTwoLists = function(l1, l2) { + let dummy = new ListNode + let newList = dummy + let arr = [] + + while (l1) { + arr.push(l1) + l1 = l1.next + } + + while (l2) { + arr.push(l2) + l2 = l2.next + } + + arr.sort((a, b) => a.val - b.val) + + for (let i = 0; i < arr.length; i++) { + newList.next = arr[i] + newList = newList.next + } + + return dummy.next +}; \ No newline at end of file diff --git "a/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243\357\274\214\347\224\250\344\270\216\345\210\244\346\226\255.js" "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243\357\274\214\347\224\250\344\270\216\345\210\244\346\226\255.js" new file mode 100644 index 00000000..596603cf --- /dev/null +++ "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243\357\274\214\347\224\250\344\270\216\345\210\244\346\226\255.js" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=21 lang=javascript + * + * [21] 合并两个有序链表 + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var mergeTwoLists = function (l1, l2) { + let dummy = new ListNode(); + let node = dummy; + + while (l1 && l2) { + if (l1.val <= l2.val) { + node.next = l1; + l1 = l1.next; + } else { + node.next = l2; + l2 = l2.next; + } + node = node.next; + } + + if (l1) { + node.next = l1; + } + + if (l2) { + node.next = l2; + } + + return dummy.next; +}; +// @lc code=end diff --git "a/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243\357\274\214\347\224\250\346\210\226\345\210\244\346\226\255.js" "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243\357\274\214\347\224\250\346\210\226\345\210\244\346\226\255.js" new file mode 100644 index 00000000..c580efc7 --- /dev/null +++ "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243\357\274\214\347\224\250\346\210\226\345\210\244\346\226\255.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=21 lang=javascript + * + * [21] 合并两个有序链表 + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var mergeTwoLists = function (l1, l2) { + let dummy = new ListNode(); + let node = dummy; + + while (l1 || l2) { + if (!l2 || (l1 && l1.val <= l2.val)) { + node.next = l1; + l1 = l1.next; + node = node.next; + } else if (!l1 || (l2 && l2.val < l1.val)) { + node.next = l2; + l2 = l2.next; + node = node.next; + } + } + + return dummy.next; +}; +// @lc code=end diff --git "a/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\351\200\222\345\275\222.js" "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..618f1621 --- /dev/null +++ "b/Week_01/21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=21 lang=javascript + * + * [21] 合并两个有序链表 + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var mergeTwoLists = function (l1, l2) { + if (!l1 || !l2) { + return l1 || l2; + } + + if (l1.val <= l2.val) { + l1.next = mergeTwoLists(l1.next, l2); + + return l1; + } else { + l2.next = mergeTwoLists(l1, l2.next); + + return l2; + } +}; +// @lc code=end diff --git "a/Week_01/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\215\225\350\260\203\351\230\237\345\210\227.js" "b/Week_01/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\215\225\350\260\203\351\230\237\345\210\227.js" new file mode 100644 index 00000000..a01710d0 --- /dev/null +++ "b/Week_01/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\215\225\350\260\203\351\230\237\345\210\227.js" @@ -0,0 +1,27 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var maxSlidingWindow = function(nums, k) { + let result = [] + let dequeue = [] + + for (let i = 0; i < nums.length; i++) { + if (nums[i - k] === dequeue[0]) { + dequeue.shift() + } + + while (nums[i] > dequeue[dequeue.length - 1]) { + dequeue.pop() + } + + dequeue.push(nums[i]) + + if (i >= k - 1) { + result.push(dequeue[0]) + } + } + + return result +}; \ No newline at end of file diff --git "a/Week_01/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" "b/Week_01/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" new file mode 100644 index 00000000..894c20c6 --- /dev/null +++ "b/Week_01/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=239 lang=javascript + * + * [239] 滑动窗口最大值 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var maxSlidingWindow = function (nums, k) { + let result = []; + + for (let i = 0; i < nums.length - k + 1; i++) { + let max = -Infinity; + + for (let j = 0; j < k; j++) { + max = Math.max(max, nums[i + j]); + } + + result.push(max); + } + + return result; +}; +// @lc code=end diff --git "a/Week_01/24. \344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271\357\274\214\350\277\255\344\273\243.js" "b/Week_01/24. \344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271\357\274\214\350\277\255\344\273\243.js" new file mode 100644 index 00000000..d42c2025 --- /dev/null +++ "b/Week_01/24. \344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271\357\274\214\350\277\255\344\273\243.js" @@ -0,0 +1,28 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +var swapPairs = function(head) { + let dummy = new ListNode + let prev = dummy + dummy.next = head + let node = head + + while (node && node.next) { + const next = node.next.next + node.next.next = node + prev.next = node.next + node.next = next + prev = node + node = next + } + + return dummy.next +}; \ No newline at end of file diff --git "a/Week_01/24. \344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271\357\274\214\351\200\222\345\275\222.js" "b/Week_01/24. \344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..4aecfac1 --- /dev/null +++ "b/Week_01/24. \344\270\244\344\270\244\344\272\244\346\215\242\351\223\276\350\241\250\344\270\255\347\232\204\350\212\202\347\202\271\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,23 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +var swapPairs = function (head) { + if (!head || !head.next) { + return head; + } + + const node = swapPairs(head.next.next); + const temp = head.next; + head.next.next = head; + head.next = node; + + return temp; +}; \ No newline at end of file diff --git "a/Week_01/25. K \344\270\252\344\270\200\347\273\204\347\277\273\350\275\254\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243.js" "b/Week_01/25. K \344\270\252\344\270\200\347\273\204\347\277\273\350\275\254\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243.js" new file mode 100644 index 00000000..c9c3aef4 --- /dev/null +++ "b/Week_01/25. K \344\270\252\344\270\200\347\273\204\347\277\273\350\275\254\351\223\276\350\241\250\357\274\214\350\277\255\344\273\243.js" @@ -0,0 +1,55 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ +var reverseKGroup = function (head, k) { + function reverse(head, tail) { + let prev = tail.next; + let node = head; + + while (prev !== tail) { + const next = node.next; + node.next = prev; + prev = node; + node = next; + } + + return [tail, head]; + } + + let dummy = new ListNode(); + dummy.next = head; + let prev = dummy; + + while (head) { + let tail = prev; + + for (let i = 0; i < k; i++) { + tail = tail.next; + + if (!tail) { + return dummy.next; + } + } + + let next = tail.next; + + [head, tail] = reverse(head, tail); + + prev.next = head; + tail.next = next; + + prev = tail; + head = tail.next; + } + + return dummy.next; +}; diff --git "a/Week_01/26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\214\345\217\214\346\214\207\351\222\210.js" "b/Week_01/26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\214\345\217\214\346\214\207\351\222\210.js" new file mode 100644 index 00000000..dfaa83b3 --- /dev/null +++ "b/Week_01/26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\214\345\217\214\346\214\207\351\222\210.js" @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=26 lang=javascript + * + * [26] 删除排序数组中的重复项 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function (nums) { + let moveIndex = 0; + + nums.forEach((value, index) => { + if (value !== nums[moveIndex]) { + nums[++moveIndex] = value; + } + }); + + return moveIndex + 1; +}; +// @lc code=end diff --git "a/Week_01/283.\347\247\273\345\212\250\351\233\266.js" "b/Week_01/283.\347\247\273\345\212\250\351\233\266.js" new file mode 100644 index 00000000..940636bb --- /dev/null +++ "b/Week_01/283.\347\247\273\345\212\250\351\233\266.js" @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=283 lang=javascript + * + * [283] 移动零 + */ +// @lc code=start +/** + * @param {number[]} numbers + * @return {void} Do not return anything, modify numbers in-place instead. + */ +var moveZeroes = function (numbers) { + let moveIndex = 0; + + numbers.forEach((value, index) => { + if (value !== 0) { + if (moveIndex !== index) { + numbers[moveIndex] = value; + numbers[index] = 0; + } + moveIndex++; + } + }); +}; +// @lc code=end diff --git "a/Week_01/34. \345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.js" "b/Week_01/34. \345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.js" new file mode 100644 index 00000000..54b27f78 --- /dev/null +++ "b/Week_01/34. \345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\346\237\245\346\211\276\345\205\203\347\264\240\347\232\204\347\254\254\344\270\200\344\270\252\345\222\214\346\234\200\345\220\216\344\270\200\344\270\252\344\275\215\347\275\256.js" @@ -0,0 +1,19 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var searchRange = function (nums, target) { + let result = [-1, -1]; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] === target) { + if (result[0] === -1) { + result[0] = i; + } + result[1] = i; + } + } + + return result; +}; diff --git "a/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\345\217\230\351\207\217.js" "b/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\345\217\230\351\207\217.js" new file mode 100644 index 00000000..74c85ace --- /dev/null +++ "b/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\345\217\230\351\207\217.js" @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=509 lang=javascript + * + * [509] 斐波那契数 + */ + +// @lc code=start +/** + * @param {number} N + * @return {number} + */ +var fib = function (N) { + if (N <= 1) { + return N; + } + let prev1 = 1; + let prev2 = 0; + let curr = 0; + + for (let i = 2; i < N + 1; i++) { + curr = prev1 + prev2; + prev2 = prev1; + prev1 = curr; + } + + return curr; +}; +// @lc code=end diff --git "a/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\346\225\260\347\273\204.js" "b/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\346\225\260\347\273\204.js" new file mode 100644 index 00000000..2a0b3674 --- /dev/null +++ "b/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\346\225\260\347\273\204.js" @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=509 lang=javascript + * + * [509] 斐波那契数 + */ + +// @lc code=start +/** + * @param {number} N + * @return {number} + */ +var fib = function (N) { + let arr = new Array(N + 1); + + for (let i = 0; i < arr.length; i++) { + if (i <= 1) { + arr[i] = i; + } else { + arr[i] = arr[i - 1] + arr[i - 2]; + } + } + + return arr[arr.length - 1]; +}; +// @lc code=end diff --git "a/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\351\200\222\345\275\222.js" "b/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..58bc174d --- /dev/null +++ "b/Week_01/509.\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=509 lang=javascript + * + * [509] 斐波那契数 + */ + +// @lc code=start +/** + * @param {number} N + * @return {number} + */ +let map = new Map(); +var fib = function (N) { + if (N <= 1) { + return N; + } + + if (map.has(N)) { + return map.get(N); + } + + const sum = fib(N - 1) + fib(N - 2); + map.set(N, sum); + return sum; +}; +// @lc code=end diff --git "a/Week_01/622. \350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\345\217\214\345\220\221\351\223\276\350\241\250.js" "b/Week_01/622. \350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\345\217\214\345\220\221\351\223\276\350\241\250.js" new file mode 100644 index 00000000..908225ca --- /dev/null +++ "b/Week_01/622. \350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\345\217\214\345\220\221\351\223\276\350\241\250.js" @@ -0,0 +1,113 @@ +/** + * Initialize your data structure here. Set the size of the queue to be k. + * @param {number} k + */ +function Node(val = -1) { + this.val = val + this.next = null + this.prev = null +} + +var MyCircularQueue = function(k) { + this.tail = new Node + this.head = this.tail + this.capacity = k + this.index = 0 +}; + +MyCircularQueue.prototype.addIndex = function(index) { + return (index + 1) % this.capacity +}; + +MyCircularQueue.prototype.reduceIndex = function(index) { + return (index + this.capacity - 1) % this.capacity +}; + +/** + * Insert an element into the circular queue. Return true if the operation is successful. + * @param {number} value + * @return {boolean} + */ +MyCircularQueue.prototype.enQueue = function(value) { + if (this.isFull()) { + return false + } + + this.tail.val = value + const node = new Node() + this.tail.next = node + node.prev = this.tail + this.tail = node + this.index++ + + return true +}; + +/** + * Delete an element from the circular queue. Return true if the operation is successful. + * @return {boolean} + */ +MyCircularQueue.prototype.deQueue = function() { + if (this.isEmpty()) { + return false + } + + const node = this.head.next + node.prev = null + this.head.next = null + this.head = node + this.index-- + + return true +}; + +/** + * Get the front item from the queue. + * @return {number} + */ +MyCircularQueue.prototype.Front = function() { + if (this.isEmpty()) { + return -1 + } + + return this.head.val +}; + +/** + * Get the last item from the queue. + * @return {number} + */ +MyCircularQueue.prototype.Rear = function() { + if (this.isEmpty()) { + return -1 + } + + return this.tail.prev.val +}; + +/** + * Checks whether the circular queue is empty or not. + * @return {boolean} + */ +MyCircularQueue.prototype.isEmpty = function() { + return this.index === 0 +}; + +/** + * Checks whether the circular queue is full or not. + * @return {boolean} + */ +MyCircularQueue.prototype.isFull = function() { + return this.index === this.capacity +}; + +/** + * Your MyCircularQueue object will be instantiated and called as such: + * var obj = new MyCircularQueue(k) + * var param_1 = obj.enQueue(value) + * var param_2 = obj.deQueue() + * var param_3 = obj.Front() + * var param_4 = obj.Rear() + * var param_5 = obj.isEmpty() + * var param_6 = obj.isFull() + */ \ No newline at end of file diff --git "a/Week_01/622. \350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" "b/Week_01/622. \350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" new file mode 100644 index 00000000..15f95ef0 --- /dev/null +++ "b/Week_01/622. \350\256\276\350\256\241\345\276\252\347\216\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" @@ -0,0 +1,92 @@ +/** + * Initialize your data structure here. Set the size of the queue to be k. + * @param {number} k + */ +var MyCircularQueue = function(k) { + this.queue = new Array(k).fill(-1) + this.capacity = k + this.head = 0 + this.tail = 0 +}; + +MyCircularQueue.prototype.addIndex = function(index) { + return (index + 1) % this.capacity +}; + +MyCircularQueue.prototype.reduceIndex = function(index) { + return (index + this.capacity - 1) % this.capacity +}; + +/** + * Insert an element into the circular queue. Return true if the operation is successful. + * @param {number} value + * @return {boolean} + */ +MyCircularQueue.prototype.enQueue = function(value) { + if (this.isFull()) { + return false + } + + this.queue[this.tail] = value + this.tail = this.addIndex(this.tail) + + return true +}; + +/** + * Delete an element from the circular queue. Return true if the operation is successful. + * @return {boolean} + */ +MyCircularQueue.prototype.deQueue = function() { + if (this.isEmpty()) { + return false + } + + this.queue[this.head] = -1 + this.head = this.addIndex(this.head) + + return true +}; + +/** + * Get the front item from the queue. + * @return {number} + */ +MyCircularQueue.prototype.Front = function() { + return this.queue[this.head] +}; + +/** + * Get the last item from the queue. + * @return {number} + */ +MyCircularQueue.prototype.Rear = function() { + return this.queue[this.reduceIndex(this.tail)] +}; + +/** + * Checks whether the circular queue is empty or not. + * @return {boolean} + */ +MyCircularQueue.prototype.isEmpty = function() { + return this.head === this.tail && this.queue[this.head] === -1 +}; + +/** + * Checks whether the circular queue is full or not. + * @return {boolean} + */ +MyCircularQueue.prototype.isFull = function() { + return this.head === this.tail && this.queue[this.head] !== -1 +}; + +/** + * Your MyCircularQueue object will be instantiated and called as such: + * var obj = new MyCircularQueue(k) + * var param_1 = obj.enQueue(value) + * var param_2 = obj.deQueue() + * var param_3 = obj.Front() + * var param_4 = obj.Rear() + * var param_5 = obj.isEmpty() + * var param_6 = obj.isFull() + */ \ No newline at end of file diff --git "a/Week_01/641. \350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\345\217\214\345\220\221\351\223\276\350\241\250.js" "b/Week_01/641. \350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\345\217\214\345\220\221\351\223\276\350\241\250.js" new file mode 100644 index 00000000..6497be40 --- /dev/null +++ "b/Week_01/641. \350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\345\217\214\345\220\221\351\223\276\350\241\250.js" @@ -0,0 +1,144 @@ +/** + * Initialize your data structure here. Set the size of the deque to be k. + * @param {number} k + */ +function Node(val = -1) { + this.val = val + this.next = null + this.prev = null +} + +var MyCircularDeque = function(k) { + this.head = new Node + this.tail = this.head + this.index = 0 + this.capacity = k +}; + +/** + * Adds an item at the front of Deque. Return true if the operation is successful. + * @param {number} value + * @return {boolean} + */ +MyCircularDeque.prototype.insertFront = function(value) { + if (this.isFull()) { + return false + } + + const node = new Node(value) + node.next = this.head + this.head.prev = node + this.head = node + this.index++ + + return true +}; + +/** + * Adds an item at the rear of Deque. Return true if the operation is successful. + * @param {number} value + * @return {boolean} + */ +MyCircularDeque.prototype.insertLast = function(value) { + if (this.isFull()) { + return false + } + + this.tail.val = value + const node = new Node + this.tail.next = node + node.prev = this.tail + this.tail = node + this.index++ + + return true +}; + +/** + * Deletes an item from the front of Deque. Return true if the operation is successful. + * @return {boolean} + */ +MyCircularDeque.prototype.deleteFront = function() { + if (this.isEmpty()) { + return false + } + + const node = this.head.next + this.head.next = null + node.prev = null + this.head = node + this.index-- + + return true +}; + +/** + * Deletes an item from the rear of Deque. Return true if the operation is successful. + * @return {boolean} + */ +MyCircularDeque.prototype.deleteLast = function() { + if (this.isEmpty()) { + return false + } + + const node = this.tail.prev + this.tail.prev = null + node.next = null + this.tail = node + this.index-- + + return true +}; + +/** + * Get the front item from the deque. + * @return {number} + */ +MyCircularDeque.prototype.getFront = function() { + if (this.isEmpty()) { + return -1 + } + + return this.head.val +}; + +/** + * Get the last item from the deque. + * @return {number} + */ +MyCircularDeque.prototype.getRear = function() { + if (this.isEmpty()) { + return -1 + } + + return this.tail.prev.val +}; + +/** + * Checks whether the circular deque is empty or not. + * @return {boolean} + */ +MyCircularDeque.prototype.isEmpty = function() { + return this.index === 0 +}; + +/** + * Checks whether the circular deque is full or not. + * @return {boolean} + */ +MyCircularDeque.prototype.isFull = function() { + return this.index === this.capacity +}; + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * var obj = new MyCircularDeque(k) + * var param_1 = obj.insertFront(value) + * var param_2 = obj.insertLast(value) + * var param_3 = obj.deleteFront() + * var param_4 = obj.deleteLast() + * var param_5 = obj.getFront() + * var param_6 = obj.getRear() + * var param_7 = obj.isEmpty() + * var param_8 = obj.isFull() + */ \ No newline at end of file diff --git "a/Week_01/641. \350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" "b/Week_01/641. \350\256\276\350\256\241\345\276\252\347\216\257\345\217\214\347\253\257\351\230\237\345\210\227\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_01/66.\345\212\240\344\270\200\357\274\214BigInt.js" "b/Week_01/66.\345\212\240\344\270\200\357\274\214BigInt.js" new file mode 100644 index 00000000..8a36c1da --- /dev/null +++ "b/Week_01/66.\345\212\240\344\270\200\357\274\214BigInt.js" @@ -0,0 +1,19 @@ +/* + * @lc app=leetcode.cn id=66 lang=javascript + * + * [66] 加一 + */ + +// @lc code=start +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function (digits) { + let newDigits = (BigInt(digits.join('')) + 1n).toString().split(''); + while (digits.length - newDigits.length > 0) { + newDigits.unshift(0); + } + return newDigits; +}; +// @lc code=end diff --git "a/Week_01/66.\345\212\240\344\270\200\357\274\214\345\200\222\345\272\217\351\201\215\345\216\206+\345\217\257\344\270\255\351\200\224\351\200\200\345\207\272.js" "b/Week_01/66.\345\212\240\344\270\200\357\274\214\345\200\222\345\272\217\351\201\215\345\216\206+\345\217\257\344\270\255\351\200\224\351\200\200\345\207\272.js" new file mode 100644 index 00000000..7f662020 --- /dev/null +++ "b/Week_01/66.\345\212\240\344\270\200\357\274\214\345\200\222\345\272\217\351\201\215\345\216\206+\345\217\257\344\270\255\351\200\224\351\200\200\345\207\272.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=66 lang=javascript + * + * [66] 加一 + */ + +// @lc code=start +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function (digits) { + let plus = 1; + + for (let i = digits.length - 1; i >= 0; i--) { + if (plus) { + if (++digits[i] === 10) { + digits[i] = 0; + } else { + plus = 0; + break; + } + } + } + + if (plus) { + digits.unshift(1); + } + + return digits; +}; +// @lc code=end diff --git "a/Week_01/66.\345\212\240\344\270\200\357\274\214\345\216\237\346\225\260\347\273\204\346\261\202\345\222\214\345\206\215\347\277\273\350\275\254.js" "b/Week_01/66.\345\212\240\344\270\200\357\274\214\345\216\237\346\225\260\347\273\204\346\261\202\345\222\214\345\206\215\347\277\273\350\275\254.js" new file mode 100644 index 00000000..044aa061 --- /dev/null +++ "b/Week_01/66.\345\212\240\344\270\200\357\274\214\345\216\237\346\225\260\347\273\204\346\261\202\345\222\214\345\206\215\347\277\273\350\275\254.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=66 lang=javascript + * + * [66] 加一 + */ + +// @lc code=start +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function (digits) { + digits.reverse(); + let plus = 1; + + for (let i = 0; i < digits.length; i++) { + if (plus) { + if (digits[i] === 9) { + digits[i] = 0; + } else { + digits[i]++; + plus = 0; + break; + } + } + } + + if (plus) { + digits.push(1); + } + + digits.reverse(); + return digits; +}; +// @lc code=end diff --git "a/Week_01/66.\345\212\240\344\270\200\357\274\214\346\226\260\346\225\260\347\273\204\346\261\202\345\222\214\345\206\215\347\277\273\350\275\254.js" "b/Week_01/66.\345\212\240\344\270\200\357\274\214\346\226\260\346\225\260\347\273\204\346\261\202\345\222\214\345\206\215\347\277\273\350\275\254.js" new file mode 100644 index 00000000..fcae4c50 --- /dev/null +++ "b/Week_01/66.\345\212\240\344\270\200\357\274\214\346\226\260\346\225\260\347\273\204\346\261\202\345\222\214\345\206\215\347\277\273\350\275\254.js" @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=66 lang=javascript + * + * [66] 加一 + */ + +// @lc code=start +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function (digits) { + let arr = []; + let plus = 1; + + for (let i = digits.length - 1; i >= 0; i--) { + const sum = digits[i] + plus; + if (sum === 10) { + arr.push(0); + } else { + arr.push(sum); + plus = 0; + } + } + + if (plus) { + arr.push(1); + } + + arr.reverse(); + + return arr; +}; +// @lc code=end diff --git "a/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\345\217\230\351\207\217.js" "b/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\345\217\230\351\207\217.js" new file mode 100644 index 00000000..47d175ec --- /dev/null +++ "b/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\345\217\230\351\207\217.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + */ + +// @lc code=start + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function (n) { + let prev1 = 1; + let prev2 = 1; + let curr = 1; + + for (let i = 2; i < n + 1; i++) { + curr = prev1 + prev2; + prev2 = prev1; + prev1 = curr; + } + + return curr; +}; +// @lc code=end diff --git "a/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\346\225\260\347\273\204.js" "b/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\346\225\260\347\273\204.js" new file mode 100644 index 00000000..cf90ce2e --- /dev/null +++ "b/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\346\225\260\347\273\204.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + */ + +// @lc code=start + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function (n) { + let arr = new Array(n + 1).fill(1); + + for (let i = 2; i < arr.length; i++) { + arr[i] = arr[i - 1] + arr[i - 2]; + } + + return arr[arr.length - 1]; +}; +// @lc code=end diff --git "a/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\351\200\222\345\275\222.js" "b/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..fccfcc10 --- /dev/null +++ "b/Week_01/70.\347\210\254\346\245\274\346\242\257\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + */ + +// @lc code=start + +/** + * @param {number} n + * @return {number} + */ +let map = new Map(); +var climbStairs = function (n) { + if (n <= 1) { + return 1; + } + + if (map.has(n)) { + return map.get(n); + } + + let sum = climbStairs(n - 1) + climbStairs(n - 2); + map.set(n, sum); + return sum; +}; +// @lc code=end diff --git "a/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" "b/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" new file mode 100644 index 00000000..1ac0e287 --- /dev/null +++ "b/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=84 lang=javascript + * + * [84] 柱状图中最大的矩形 + */ + +// @lc code=start +/** + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function (heights) { + let max = 0; + + for (let i = 0; i < heights.length; i++) { + let minHeight = heights[i]; + + for (let j = i; j < heights.length; j++) { + minHeight = Math.min(minHeight, heights[j]); + max = Math.max((j - i + 1) * minHeight, max); + } + } + + return max; +}; +// @lc code=end diff --git "a/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\345\276\252\347\216\257+\345\217\214\346\214\207\351\222\210\346\232\264\345\212\233.js" "b/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\345\276\252\347\216\257+\345\217\214\346\214\207\351\222\210\346\232\264\345\212\233.js" new file mode 100644 index 00000000..00fdf22c --- /dev/null +++ "b/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\345\276\252\347\216\257+\345\217\214\346\214\207\351\222\210\346\232\264\345\212\233.js" @@ -0,0 +1,24 @@ +/** + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function(heights) { + let max = 0 + + for (let i = 0; i < heights.length; i++) { + let j = i + let k = i + + while (heights[j - 1] >= heights[i]) { + j-- + } + + while (heights[k + 1] >= heights[i]) { + k++ + } + + max = Math.max(max, heights[i] * (k - j + 1)) + } + + return max +}; \ No newline at end of file diff --git "a/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\346\240\210.js" "b/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\346\240\210.js" new file mode 100644 index 00000000..c0bc3ec0 --- /dev/null +++ "b/Week_01/84. \346\237\261\347\212\266\345\233\276\344\270\255\346\234\200\345\244\247\347\232\204\347\237\251\345\275\242\357\274\214\346\240\210.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=84 lang=javascript + * + * [84] 柱状图中最大的矩形 + */ + +// @lc code=start +/** + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function (heights) { + let max = 0; + let stack = [-1]; + + for (let i = 0; i < heights.length; i++) { + while (heights[stack[stack.length - 1]] > heights[i]) { + max = Math.max( + heights[stack.pop()] * (i - stack[stack.length - 1] - 1), + max, + ); + } + stack.push(i); + } + + while (stack.length > 1) { + max = Math.max( + heights[stack.pop()] * (heights.length - stack[stack.length - 1] - 1), + max, + ); + } + + return max; +}; +// @lc code=end diff --git "a/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214for\345\276\252\347\216\257\345\220\210\345\271\266\346\225\260\347\273\204+sort\346\216\222\345\272\217.js" "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214for\345\276\252\347\216\257\345\220\210\345\271\266\346\225\260\347\273\204+sort\346\216\222\345\272\217.js" new file mode 100644 index 00000000..4936c6d1 --- /dev/null +++ "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214for\345\276\252\347\216\257\345\220\210\345\271\266\346\225\260\347\273\204+sort\346\216\222\345\272\217.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=88 lang=javascript + * + * [88] 合并两个有序数组 + */ + +// @lc code=start +/** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function (nums1, m, nums2, n) { + for (let i = m, j = 0; i < nums1.length; i++, j++) { + nums1[i] = nums2[j]; + } + + nums1.sort((a, b) => a - b); +}; +// @lc code=end diff --git "a/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214splice\345\220\210\345\271\266\346\225\260\347\273\204+sort\346\216\222\345\272\217.js" "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214splice\345\220\210\345\271\266\346\225\260\347\273\204+sort\346\216\222\345\272\217.js" new file mode 100644 index 00000000..6e7ee64f --- /dev/null +++ "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214splice\345\220\210\345\271\266\346\225\260\347\273\204+sort\346\216\222\345\272\217.js" @@ -0,0 +1,19 @@ +/* + * @lc app=leetcode.cn id=88 lang=javascript + * + * [88] 合并两个有序数组 + */ + +// @lc code=start +/** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function (nums1, m, nums2, n) { + nums1.splice(m, n, ...nums2); + nums1.sort((a, b) => a - b); +}; +// @lc code=end diff --git "a/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214\345\217\214\346\214\207\351\222\210+\344\273\216\345\211\215\345\276\200\345\220\216+\344\275\277\347\224\250\346\226\260\346\225\260\347\273\204Copy.js" "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214\345\217\214\346\214\207\351\222\210+\344\273\216\345\211\215\345\276\200\345\220\216+\344\275\277\347\224\250\346\226\260\346\225\260\347\273\204Copy.js" new file mode 100644 index 00000000..85a532ce --- /dev/null +++ "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214\345\217\214\346\214\207\351\222\210+\344\273\216\345\211\215\345\276\200\345\220\216+\344\275\277\347\224\250\346\226\260\346\225\260\347\273\204Copy.js" @@ -0,0 +1,36 @@ +/* + * @lc app=leetcode.cn id=88 lang=javascript + * + * [88] 合并两个有序数组 + */ + +// @lc code=start +/** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function (nums1, m, nums2, n) { + let arr = []; + let i = 0; + let j = 0; + + while (i < m && j < n) { + if (nums1[i] <= nums2[j]) { + arr.push(nums1[i++]); + } else { + arr.push(nums2[j++]); + } + } + + if (i !== m) { + arr.push(...nums1.slice(i, m)); + } else { + arr.push(...nums2.slice(j)); + } + + nums1.splice(0, m + n, ...arr); +}; +// @lc code=end diff --git "a/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214\345\217\214\346\214\207\351\222\210+\344\273\216\345\220\216\345\276\200\345\211\215.js" "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214\345\217\214\346\214\207\351\222\210+\344\273\216\345\220\216\345\276\200\345\211\215.js" new file mode 100644 index 00000000..611b8e15 --- /dev/null +++ "b/Week_01/88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\214\345\217\214\346\214\207\351\222\210+\344\273\216\345\220\216\345\276\200\345\211\215.js" @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=88 lang=javascript + * + * [88] 合并两个有序数组 + */ + +// @lc code=start +/** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function (nums1, m, nums2, n) { + let i = m - 1; + let j = n - 1; + let k = m + n - 1; + + while (i >= 0 && j >= 0) { + nums1[k--] = nums1[i] >= nums2[j] ? nums1[i--] : nums2[j--]; + } + + if (j >= 0) { + nums1.splice(0, j + 1, ...nums2.slice(0, j + 1)); + } +}; +// @lc code=end diff --git "a/Week_01/92.\345\217\215\350\275\254\351\223\276\350\241\250-ii\357\274\214\350\277\255\344\273\243.js" "b/Week_01/92.\345\217\215\350\275\254\351\223\276\350\241\250-ii\357\274\214\350\277\255\344\273\243.js" new file mode 100644 index 00000000..8c3c8391 --- /dev/null +++ "b/Week_01/92.\345\217\215\350\275\254\351\223\276\350\241\250-ii\357\274\214\350\277\255\344\273\243.js" @@ -0,0 +1,52 @@ +/* + * @lc app=leetcode.cn id=92 lang=javascript + * + * [92] 反转链表 II + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @param {number} m + * @param {number} n + * @return {ListNode} + */ +var reverseBetween = function (head, m, n) { + let prev = null; + let curr = head; + + while (m > 1) { + prev = curr; + curr = curr.next; + m--; + n--; + } + + let firstListTail = prev; + let middleListTail = curr; + + while (n > 0) { + let next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + n--; + } + + if (!firstListTail) { + head = prev; + } else { + firstListTail.next = prev; + } + middleListTail.next = curr; + + return head; +}; +// @lc code=end diff --git "a/Week_01/92.\345\217\215\350\275\254\351\223\276\350\241\250-ii\357\274\214\351\200\222\345\275\222.js" "b/Week_01/92.\345\217\215\350\275\254\351\223\276\350\241\250-ii\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..5e293623 --- /dev/null +++ "b/Week_01/92.\345\217\215\350\275\254\351\223\276\350\241\250-ii\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,55 @@ +/* + * @lc app=leetcode.cn id=92 lang=javascript + * + * [92] 反转链表 II + */ + +// @lc code=start +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @param {number} m + * @param {number} n + * @return {ListNode} + */ +var reverseBetween = function (head, m, n) { + let left = head; + let reverse = true; + + function recursion(right, m, n) { + if (n === 1) { + return right; + } + + right = right.next; + n--; + + if (m > 1) { + left = left.next; + m--; + } + + recursion(right, m, n); + + if (right === left || right.next === left) { + reverse = false; + } + + if (reverse) { + const temp = right.val; + right.val = left.val; + left.val = temp; + left = left.next; + } + } + recursion(head, m, n); + + return head; +}; +// @lc code=end diff --git a/Week_01/README.md b/Week_01/README.md index 50de3041..00b4cfb2 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -1 +1,72 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +本周的学习主要都集中在刷题,由于身体不适加上工作有点忙,影响了刷题进度,之后争取调整状态,努力跟上计划。 + +这些题目都不是第一次刷了,但还是有部分题目没法很好的解出来,我认为原因还是遍数不够。 + +以下是本周的做题记录: + +https://shimo.im/sheets/hE9AXYhkH7EL3fOg/MODOC + +我为部分题目写了题解,如下: + +1. [LeetCode 题解:11. 盛最多水的容器,for 循环双指针,JavaScript,详细注释](https://leetcode-cn.com/problems/container-with-most-water/solution/leetcodeti-jie-11-sheng-zui-duo-shui-de-rong-qi-fo) +2. [LeetCode 题解:11. 盛最多水的容器,while 循环双指针,JavaScript,详细注释](https://leetcode-cn.com/problems/container-with-most-water/solution/leetcodeti-jie-11-sheng-zui-duo-shui-de-rong-qi-wh) +3. [LeetCode 题解:11. 盛最多水的容器,双循环暴力法,JavaScript,详细注释](https://leetcode-cn.com/problems/container-with-most-water/solution/leetcodeti-jie-11-sheng-zui-duo-shui-de-rong-qi-sh) +4. [LeetCode 题解:283. 移动零,JavaScript,双指针一次遍历,详细注释](https://leetcode-cn.com/problems/move-zeroes/solution/283-yi-dong-ling-javascriptyi-ci-bian-li-by-185592) +5. [LeetCode 题解:70. 爬楼梯,DP 遍历,变量缓存结果,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-bian-liang-huan-cun-jie-guo) +6. [LeetCode 题解:70. 爬楼梯,DP 遍历数组,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-shu-zu-javascriptxiang-xi-z) +7. [LeetCode 题解:70. 爬楼梯,递归+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-di-gui-ha-xi-biao-javascriptxiang-xi-) +8. [LeetCode 题解:15. 三数之和,JavaScript 双循环+双指针,详细注释](https://leetcode-cn.com/problems/3sum/solution/javascriptpai-xu-shuang-zhi-zhen-xiang-xi-zhu-shi-) +9. [LeetCode 题解:15. 三数之和,JavaScript 双循环+HashMap,详细注释](https://leetcode-cn.com/problems/3sum/solution/shuang-xun-huan-mapbao-li-qiu-jie-by-18559231815) +10. [LeetCode 题解:92. 反转链表 II,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/leetcodeti-jie-92-fan-zhuan-lian-biao-iidie-dai-ja) +11. [LeetCode 题解:92. 反转链表 II,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/leetcodeti-jie-92-fan-zhuan-lian-biao-iidi-gui-jav) +12. [LeetCode 题解:206. 反转链表,双指针,JavaScript,详细注释](https://leetcode-cn.com/problems/reverse-linked-list/solution/leetcodeti-jie-206-fan-zhuan-lian-biao-shuang-zhi-) +13. [LeetCode 题解: 206. 反转链表,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/reverse-linked-list/solution/206-fan-zhuan-lian-biao-javascriptrong-yi-li-jie-d) +14. [LeetCode 题解:206. 反转链表,While 循环迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/reverse-linked-list/solution/206-fan-zhuan-lian-biao-javascriptwhilexun-huan-di) +15. [LeetCode 题解:24. 两两交换链表中的节点,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/swap-nodes-in-pairs/solution/24-liang-liang-jiao-huan-lian-biao-zhong-de-jie-40) +16. [LeetCode 题解:24. 两两交换链表中的节点,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/swap-nodes-in-pairs/solution/24-liang-liang-jiao-huan-lian-biao-zhong-de-jie-39) +17. [LeetCode 题解:142. 环形链表 II,JavaScript,快慢指针,详细注释](https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/142-huan-xing-lian-biao-iijavascriptkuai-man-zhi-z) +18. [LeetCode 题解:142. 环形链表 II,JavaScript,HashMap,详细注释](https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/142-huan-xing-lian-biao-iijavascripthashmapxiang-x) +19. [LeetCode 题解:141. 环形链表,JavaScript,快慢指针,详细注释](https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-javascriptkuai-man-zhi-zhe) +20. [LeetCode 题解:141. 环形链表,JavaScript HashMap,详细注释](https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-javascript-hashmapxiang-xi) +21. [LeetCode 题解:142. 环形链表 II,JavaScript,快慢指针,详细注释](https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/142-huan-xing-lian-biao-iijavascriptkuai-man-zhi-z) +22. [LeetCode 题解:142. 环形链表 II,JavaScript,HashMap,详细注释](https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/142-huan-xing-lian-biao-iijavascripthashmapxiang-x) +23. [LeetCode 题解:25. K 个一组翻转链表,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/reverse-nodes-in-k-group/solution/leetcodeti-jie-25-k-ge-yi-zu-fan-zhuan-lian-biao-d) +24. [LeetCode 题解:20. 有效的括号,栈,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-parentheses/solution/leetcodeti-jie-20-you-xiao-de-gua-hao-zhan-javascr) +25. [LeetCode 题解:20. 有效的括号,while 循环 replace,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-parentheses/solution/leetcodeti-jie-20-you-xiao-de-gua-hao-whilexun-hua) +26. [LeetCode 题解:20. 有效的括号,for 循环 replace,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-parentheses/solution/leetcodeti-jie-20-you-xiao-de-gua-hao-forxun-huan-) +27. [LeetCode 题解:155. 最小栈,使用链表代替栈,JavaScript,详细注释](https://leetcode-cn.com/problems/min-stack/solution/leetcodeti-jie-155-zui-xiao-zhan-shi-yong-lian-bia) +28. [LeetCode 题解:155. 最小栈,单个栈存储入栈元素与最小值之差,JavaScript,详细注释](https://leetcode-cn.com/problems/min-stack/solution/leetcodeti-jie-155-zui-xiao-zhan-dan-ge-zhan-cun-c) +29. [LeetCode 题解:155. 最小栈,单个栈同时存储最小值,JavaScript,详细注释](https://leetcode-cn.com/problems/min-stack/solution/155-zui-xiao-zhan-dan-ge-zhan-bian-liang-javascrip) +30. [LeetCode 题解:155. 最小栈,单个栈+对象存储,JavaScript,详细注释](https://leetcode-cn.com/problems/min-stack/solution/155-zui-xiao-zhan-dan-ge-zhan-dui-xiang-cun-chu-ja) +31. [LeetCode 题解:155.最小栈,使用两个栈,详细注释](https://leetcode-cn.com/problems/min-stack/solution/leetcodeti-jie-155zui-xiao-zhan-fu-zhu-zhan-guan-f) +32. [LeetCode 题解:84. 柱状图中最大的矩形,栈,JavaScript,详细注释](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/leetcodeti-jie-84-zhu-zhuang-tu-zhong-zui-da-de--2) +33. [LeetCode 题解:84. 柱状图中最大的矩形,循环+双指针暴力,JavaScript,详细注释](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/leetcodeti-jie-84-zhu-zhuang-tu-zhong-zui-da-de-2) +34. [LeetCode 题解:84. 柱状图中最大的矩形,双循环暴力,JavaScript,详细注释](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/leetcodeti-jie-84-zhu-zhuang-tu-zhong-zui-da-de-ju) +35. [LeetCode 题解:239. 滑动窗口最大值,单调队列,JavaScript,详细注释](https://leetcode-cn.com/problems/sliding-window-maximum/solution/leetcodeti-jie-239-hua-dong-chuang-kou-zui-da-zh-2) +36. [LeetCode 题解:239. 滑动窗口最大值,双循环暴力,JavaScript,详细注释](https://leetcode-cn.com/problems/sliding-window-maximum/solution/leetcodeti-jie-239-hua-dong-chuang-kou-zui-da-zhi-) +37. [LeetCode 题解:26. 删除排序数组中的重复项,双指针,JavaScript,详细注释](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/26-shan-chu-pai-xu-shu-zu-zhong-de-zhong-fu-xia-76) +38. [LeetCode 题解:189. 旋转数组,环状替换,JavaScript,详细注释](https://leetcode-cn.com/problems/rotate-array/solution/189-xuan-zhuan-shu-zu-huan-zhuang-ti-huan-javascri) +39. [LeetCode 题解:189. 旋转数组,3 次翻转,JavaScript,详细注释](https://leetcode-cn.com/problems/rotate-array/solution/189-xuan-zhuan-shu-zu-3ci-fan-zhuan-javascriptxian) +40. [LeetCode 题解:189. 旋转数组,使用新数组 Copy,JavaScript,详细注释](https://leetcode-cn.com/problems/rotate-array/solution/189-xuan-zhuan-shu-zu-shi-yong-xin-shu-zu-copyjava) +41. [LeetCode 题解:189. 旋转数组,pop+unshift 一行,JavaScript,详细注释](https://leetcode-cn.com/problems/rotate-array/solution/189-xuan-zhuan-shu-zu-popunshiftyi-xing-javascript) +42. [LeetCode 题解:189. 旋转数组,JavaScript,暴力法,详细注释](https://leetcode-cn.com/problems/rotate-array/solution/189-xuan-zhuan-shu-zu-javascriptbao-li-fa-xiang-xi) +43. [LeetCode 题解:21. 合并两个有序链表,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/21-he-bing-liang-ge-you-xu-lian-biao-die-dai-javas) +44. [LeetCode 题解:21. 合并两个有序链表,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/21-he-bing-liang-ge-you-xu-lian-biao-di-gui-javasc) +45. [LeetCode 题解:21. 合并两个有序链表,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/21-he-bing-liang-ge-you-xu-lian-biao-shuang-zhi-zh) +46. [LeetCode 题解:21. 合并两个有序链表,利用数组排序,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/21-he-bing-liang-ge-you-xu-lian-biao-li-yong-shu-z) +47. [LeetCode 题解:88. 合并两个有序数组,双指针+从后往前,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-sorted-array/solution/88-he-bing-liang-ge-you-xu-shu-zu-shuang-zhi-zhe-5) +48. [LeetCode 题解:88. 合并两个有序数组,双指针遍历+从前往后,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-sorted-array/solution/leetcodeti-jie-88-he-bing-liang-ge-you-xu-shu-zu-2) +49. [LeetCode 题解:88. 合并两个有序数组,splice 合并数组+sort 排序,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-sorted-array/solution/leetcodeti-jie-88-he-bing-liang-ge-you-xu-shu-zu-s) +50. [LeetCode 题解:88. 合并两个有序数组,for 循环合并数组+sort 排序,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-sorted-array/solution/leetcodeti-jie-88-he-bing-liang-ge-you-xu-shu-zu-f) +51. [LeetCode 题解:88. 合并两个有序数组,双指针+从前往后+使用新数组 Copy,JavaScript,详细注释](https://leetcode-cn.com/problems/merge-sorted-array/solution/88-he-bing-liang-ge-you-xu-shu-zu-die-dai-shi-yong) +52. [LeetCode 题解:1. 两数之和,Map+队列+双指针,JavaScript,详细注释](https://leetcode-cn.com/problems/two-sum/solution/leetcodeti-jie-1-liang-shu-zhi-he-mapdui-lie-shuan) +53. [LeetCode 题解:1. 两数之和,双循环暴力解法,JavaScript,详细注释](https://leetcode-cn.com/problems/two-sum/solution/1-liang-shu-zhi-he-javascriptshuang-xun-huan-bao-l) +54. [LeetCode 题解:1. 两数之和,HashMap 单次遍历,JavaScript,详细注释](https://leetcode-cn.com/problems/two-sum/solution/1-liang-shu-zhi-he-javascripthashmapdan-ci-bian-li) +55. [LeetCode 题解:66. 加一,BigInt,JavaScript,详细注释](https://leetcode-cn.com/problems/plus-one/solution/leetcodeti-jie-66-jia-yi-bigintjavascriptxiang-xi-) +56. [LeetCode 题解:66. 加一,倒序遍历+可中途退出,JavaScript,详细注释](https://leetcode-cn.com/problems/plus-one/solution/leetcodeti-jie-66-jia-yi-dao-xu-bian-li-ke-zhong-t) +57. [LeetCode 题解:66. 加一,新数组求和再翻转,JavaScript,详细注释](https://leetcode-cn.com/problems/plus-one/solution/leetcodeti-jie-66-jia-yi-xin-shu-zu-qiu-he-zai-fan) +58. [LeetCode 题解:641. 设计循环双端队列,使用双向链表,JavaScript,详细注释](https://leetcode-cn.com/problems/design-circular-deque/solution/leetcodeti-jie-641-she-ji-xun-huan-shuang-duan-d-2) +59. [LeetCode 题解:641. 设计循环双端队列,使用数组,JavaScript,详细注释](https://leetcode-cn.com/problems/design-circular-deque/solution/leetcodeti-jie-641-she-ji-xun-huan-shuang-duan-dui) +60. [LeetCode 题解:42. 接雨水,暴力法,JavaScript,详细注释](https://leetcode-cn.com/problems/trapping-rain-water/solution/leetcodeti-jie-42-jie-yu-shui-bao-li-fa-2ppof) diff --git "a/Week_02/144. \344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\344\275\277\347\224\250\346\240\210.js" "b/Week_02/144. \344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\344\275\277\347\224\250\346\240\210.js" new file mode 100644 index 00000000..90b0e271 --- /dev/null +++ "b/Week_02/144. \344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\344\275\277\347\224\250\346\240\210.js" @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=144 lang=javascript + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var preorderTraversal = function (root) { + let result = []; + let stack = []; + root && stack.push(root); + + while (stack.length) { + const pop = stack.pop(); + + result.push(pop.val); + pop.right && stack.push(pop.right); + pop.left && stack.push(pop.left); + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/144. \344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" "b/Week_02/144. \344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..05410f12 --- /dev/null +++ "b/Week_02/144. \344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=144 lang=javascript + * + * [144] 二叉树的前序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var preorderTraversal = function (root) { + let result = []; + + function traversal(node) { + if (!node) { + return node; + } + + result.push(node.val); + traversal(node.left); + traversal(node.right); + } + traversal(root); + + return result; +}; +// @lc code=end diff --git "a/Week_02/145. \344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" "b/Week_02/145. \344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" new file mode 100644 index 00000000..de4623d7 --- /dev/null +++ "b/Week_02/145. \344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=145 lang=javascript + * + * [145] 二叉树的后序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +var postorderTraversal = function (root) { + let result = []; + let stack = []; + root && stack.push(root); + + while (stack.length) { + const pop = stack.pop(); + + result.unshift(pop.val); + pop.left && stack.push(pop.left); + pop.right && stack.push(pop.right); + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/145. \344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" "b/Week_02/145. \344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..7c16f90f --- /dev/null +++ "b/Week_02/145. \344\272\214\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,36 @@ +/* + * @lc app=leetcode.cn id=145 lang=javascript + * + * [145] 二叉树的后序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +var postorderTraversal = function (root) { + let result = []; + + function traversal(node) { + if (!node) { + return node; + } + + traversal(node.left); + traversal(node.right); + result.push(node.val); + } + traversal(root); + + return result; +}; +// @lc code=end diff --git "a/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\344\272\214\345\217\211\345\240\206.js" "b/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\344\272\214\345\217\211\345\240\206.js" new file mode 100644 index 00000000..af075ce0 --- /dev/null +++ "b/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\344\272\214\345\217\211\345\240\206.js" @@ -0,0 +1,143 @@ +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } +} + +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var maxSlidingWindow = function (nums, k) { + let result = []; // 存储结果 + let maxHeap = new BinaryHeap((a, b) => b - a); // 创建一个大顶堆 + + for (let i = 0; i < nums.length; i++) { + const start = i - k; // 找到需要从堆中删除元素的索引,即刚移出窗口的元素 + + // 如果start>=0,表示当前已有元素移出了窗口,需要从堆中删除,保证堆中元素都是窗口内的元素 + if (start >= 0) { + maxHeap.deleteItem(nums[start]); + } + + // 将当前元素插入堆进行排序 + maxHeap.insert(nums[i]); + + // 当堆中有k个元素时,表示当前窗口已完全在数组中,可以进行取值 + if (maxHeap.size() === k) { + result.push(maxHeap.peek()); + } + } + + return result; +}; diff --git "a/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\215\225\350\260\203\351\230\237\345\210\227.js" "b/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\215\225\350\260\203\351\230\237\345\210\227.js" new file mode 100644 index 00000000..ada9d456 --- /dev/null +++ "b/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\215\225\350\260\203\351\230\237\345\210\227.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=239 lang=javascript + * + * [239] 滑动窗口最大值 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var maxSlidingWindow = function (nums, k) { + let result = []; + let dequeue = []; + + for (let i = 0; i < nums.length; i++) { + if (dequeue[0] === nums[i - k]) { + dequeue.shift(); + } + + while (dequeue[dequeue.length - 1] < nums[i]) { + dequeue.pop(); + } + + dequeue.push(nums[i]); + + if (i >= k - 1) { + result.push(dequeue[0]); + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" "b/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" new file mode 100644 index 00000000..16bc07b1 --- /dev/null +++ "b/Week_02/239. \346\273\221\345\212\250\347\252\227\345\217\243\346\234\200\345\244\247\345\200\274\357\274\214\345\217\214\345\276\252\347\216\257\346\232\264\345\212\233.js" @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=239 lang=javascript + * + * [239] 滑动窗口最大值 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var maxSlidingWindow = function (nums, k) { + let result = []; + + for (let i = 0; i < nums.length - k + 1; i++) { + let max = -Infinity; + const len = i + k; + + for (let j = i; j < len; j++) { + max = Math.max(max, nums[j]); + } + + result.push(max); + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\345\223\210\345\270\214\350\241\250\344\270\200\346\254\241\345\276\252\347\216\257.js" "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\345\223\210\345\270\214\350\241\250\344\270\200\346\254\241\345\276\252\347\216\257.js" new file mode 100644 index 00000000..02736507 --- /dev/null +++ "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\345\223\210\345\270\214\350\241\250\344\270\200\346\254\241\345\276\252\347\216\257.js" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + if (s.length !== t.length) { + return false; + } + + let map = new Map(); + + for (let i = 0; i < s.length; i++) { + if (map.has(s[i])) { + map.set(s[i], map.get(s[i]) + 1); + } else { + map.set(s[i], 1); + } + + if (map.has(t[i])) { + map.set(t[i], map.get(t[i]) - 1); + } else { + map.set(t[i], -1); + } + } + + for (const [, num] of map) { + if (num !== 0) { + return false; + } + } + + return true; +}; +// @lc code=end diff --git "a/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\345\223\210\345\270\214\350\241\250\344\270\244\346\254\241\345\276\252\347\216\257.js" "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\345\223\210\345\270\214\350\241\250\344\270\244\346\254\241\345\276\252\347\216\257.js" new file mode 100644 index 00000000..b34fad7c --- /dev/null +++ "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\345\223\210\345\270\214\350\241\250\344\270\244\346\254\241\345\276\252\347\216\257.js" @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + if (s.length !== t.length) { + return false; + } + + let map = new Map(); + + for (const char of s) { + map.set(char, map.has(char) ? map.get(char) + 1 : 1); + } + + for (const char of t) { + if (map.get(char)) { + map.set(char, map.get(char) - 1); + } else { + return false; + } + } + + return true; +}; +// @lc code=end diff --git "a/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\346\225\260\347\273\204\346\216\222\345\272\217.js" "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\346\225\260\347\273\204\346\216\222\345\272\217.js" new file mode 100644 index 00000000..92a686a5 --- /dev/null +++ "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\346\225\260\347\273\204\346\216\222\345\272\217.js" @@ -0,0 +1,16 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + return s.split('').sort().join('') === t.split('').sort().join(''); +}; +// @lc code=end diff --git "a/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260.js" "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260.js" new file mode 100644 index 00000000..9f19846e --- /dev/null +++ "b/Week_02/242. \346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + if (s.length !== t.length) { + return false; + } + + let arr = new Array(26).fill(0); + const baseIndex = 'a'.codePointAt(0); + + for (const char of s) { + const index = char.codePointAt(0) - baseIndex; + arr[index] ? arr[index]++ : (arr[index] = 0); + } + + for (const char of t) { + const index = char.codePointAt(0) - baseIndex; + + if (!arr[index]) { + return false; + } + + arr[index]--; + } + + return true; +}; +// @lc code=end diff --git "a/Week_02/347. \345\211\215 K \344\270\252\351\253\230\351\242\221\345\205\203\347\264\240\357\274\214\344\272\214\345\217\211\345\240\206.js" "b/Week_02/347. \345\211\215 K \344\270\252\351\253\230\351\242\221\345\205\203\347\264\240\357\274\214\344\272\214\345\217\211\345\240\206.js" new file mode 100644 index 00000000..a7fcc853 --- /dev/null +++ "b/Week_02/347. \345\211\215 K \344\270\252\351\253\230\351\242\221\345\205\203\347\264\240\357\274\214\344\272\214\345\217\211\345\240\206.js" @@ -0,0 +1,146 @@ +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆中插入多个元素 + insertMultiple(arr) { + for (let i = 0; i < arr.length; i++) { + this.insert(arr[i]); + } + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } + + // 读取所有堆元素 + getData() { + return this.data; + } +} + +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var topKFrequent = function (nums, k) { + let result = []; // 存储结果 + let map = new Map(); // 用于统计元素出现频次 + // 使用堆将元素按出现频次从大到小排序 + let heap = new BinaryHeap((a, b) => b[1] - a[1]); + + // 遍历数组,统计元素出现的频次,并将其存入Map + for (let i = 0; i < nums.length; i++) { + map.has(nums[i]) + ? map.set(nums[i], map.get(nums[i]) + 1) + : map.set(nums[i], 1); + } + + // 将统计好的元素与频次取出,依次插入堆进行排序 + for (const element of map) { + heap.insert(element); + } + + // 从堆中依次取出k个元素,存入结果 + for (let i = 0; i < k; i++) { + result.push(heap.deleteHead()[0]); + } + + return result; +}; diff --git "a/Week_02/347. \345\211\215 K \344\270\252\351\253\230\351\242\221\345\205\203\347\264\240\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.js" "b/Week_02/347. \345\211\215 K \344\270\252\351\253\230\351\242\221\345\205\203\347\264\240\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.js" new file mode 100644 index 00000000..3783b295 --- /dev/null +++ "b/Week_02/347. \345\211\215 K \344\270\252\351\253\230\351\242\221\345\205\203\347\264\240\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.js" @@ -0,0 +1,75 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var topKFrequent = function (nums, k) { + let map = new Map(); // 用Map记录所有数字出现的频率 + + // 遍历数组,记录每个数字的出现频率,将其保存在Map中 + for (let i = 0; i < nums.length; i++) { + if (map.has(nums[i])) { + map.set(nums[i], map.get(nums[i]) + 1); + } else { + map.set(nums[i], 1); + } + } + + // 将数字和频率的关系转换成数组,用于排序。 + const numsWithFrequency = [...map.entries()]; + + // 使用快速排序,将数组按频率从大到小排序,并返回前k个数字 + return quickSort(numsWithFrequency, 0, numsWithFrequency.length - 1, k); +}; + +const quickSort = (nums, left, right, k) => { + // 如果nums的长度小等于1,无需排序,直接将数字取出返回 + if (nums.length <= 1) return nums.map(([num]) => num); + + // 如果nums有长度,就需要排序 + if (left < right) { + // 将nums从left到right的部分分为两段,其中间值是pivot + // pivot左侧的值都小于nums[pivot],pivot右侧的值都大于nums[pivot] + const pivot = partition(nums, left, right); + + // 如果pivot刚好等于k,表示排序提前完成,可以直接返回结果 + if (pivot === k) { + // 将前k个元素转换为数字返回 + return nums.slice(0, k).map(([num]) => num); + } + + // 按pivot将nums拆分成两端分别排序,由于该题并未要求将数组完全排序,因此只需要将前k个元素整理到一侧即可 + // 由于我们事先不知道pivot与k的大小关系,因此需要根据k的位置进行递归 + pivot > k + ? quickSort(nums, left, pivot - 1, k) + : quickSort(nums, pivot + 1, right, k); + } + + // 完成排序后,将前k个元素转换为数字返回即可 + return nums.slice(0, k).map(([num]) => num); +}; + +const partition = (nums, left, right) => { + let pivot = left; // 以left为中值,比它大的都放在其左侧,比它大的都放在其右侧 + let index = left + 1; // 从left+1开始遍历数组,同时index表示了频率比nums[pivot]大的值存放的位置 + + // 遍历从left+1到right的元素,将其以nums[pivot]为中值拆分成两半 + for (let i = index; i <= right; i++) { + // 如果当前频率比nums[pivot]大,则将其移动到index的位置 + if (nums[i][1] > nums[pivot][1]) { + // 将nums[i]与nums[index]对调 + [nums[i], nums[index]] = [nums[index], nums[i]]; + // 将index移动一位,用于存储下一个值 + index++; + } + } + + // 完成循环时,index多移动了一位,要将其回退 + index--; + // 循环停止时,index位置存储的是频率大于nums[pivot]的值,而nums[pivot]还在原地 + // 将两者对调,中值会被移动到index的位置,在其左侧的频率都比它大 + [nums[pivot], nums[index]] = [nums[index], nums[pivot]]; + + // 将新的中值返回,下层递归根据它将数组拆分成两段继续排序 + return index; +}; diff --git "a/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..13ad9597 --- /dev/null +++ "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,38 @@ +/** + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let result = 0; // 存储结果 + // 创建存储每个捅左边界的数组,设置初始值为0 + let maxLeft = new Array(height.length).fill(0); + + // 循环生成每个桶的左边界,由于最左侧没有桶壁,无法接雨水,因此从1开始生成,第一个左桶壁高度为0 + for (let i = 1; i < maxLeft.length - 1; i++) { + // 当前桶左侧从0到i-1所有桶壁中最高的一个已经被保存,只需要对比其与i-1桶的高度,即可知道桶i的最高左桶壁 + maxLeft[i] = Math.max(maxLeft[i - 1], height[i - 1]); + } + + // 创建存储每个捅右边界的数组 + let maxRight = new Array(height.length).fill(0); + + // 循环生成每个桶的右边界,由于最右侧没有桶壁,无法接雨水,因此从最后一个索引开始生成,最后一个右桶壁高度为0 + for (let j = maxRight.length - 2; j > 0; j--) { + // 当前桶右侧从最后到j+1所有桶壁中最高的一个已经被保存,只需要对比其与j+1桶的高度,即可知道桶j的最高右桶壁 + maxRight[j] = Math.max(maxRight[j + 1], height[j + 1]); + } + + // 现在一只每个桶的左桶壁和右桶壁的高度,将其减去当前桶底高度,即为可以装水的高度 + for (let k = 0; k < height.length; k++) { + // 每个桶能装水的最大高度,是两个桶壁的最小值 + let bucketHeight = Math.min(maxLeft[k], maxRight[k]); + + // 由于当前bucketHeight保存的是桶壁最大高度,并未计算当前桶底的高度,在此需要判断一下桶壁是否高于桶底 + if (bucketHeight > height[k]) { + // 将当前可装水的高度进入结果 + result += bucketHeight - height[k]; + } + } + + return result; +}; diff --git "a/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\217\214\346\214\207\351\222\210.js" "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\217\214\346\214\207\351\222\210.js" new file mode 100644 index 00000000..1ebab8fd --- /dev/null +++ "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\217\214\346\214\207\351\222\210.js" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=42 lang=javascript + * + * [42] 接雨水 + */ + +// @lc code=start +/** + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let result = 0; // 存储结果 + // 当前桶桶壁高度 + let leftMax = height[0]; // 当前桶左侧最高桶壁 + let rightMax = height[height.length - 1]; // 当前桶右侧最高桶壁 + // 当前桶桶壁索引,同时也是用于计算当前装水数量的水桶索引 + let left = 1; // 当前桶左侧最高桶壁的索引 + let right = height.length - 2; // 当前桶右侧最高桶壁的索引 + + // 两个指针向内推进,计算装水总量 + // 两个指针相等时也要计算,否则会遗漏一个水桶 + while (left <= right) { + // 对于单个水桶来说,它其实只关心最低的桶壁是哪个 + // 而已知的左右桶壁中较小的那个,必然是当前桶的最小桶壁 + // 同时较小的指针指向的就是当前要计算的桶,直接计算当前桶的可装水量即可 + // 完成计算后将当前指针向内移动一步,当前所使用的桶壁高度,需要同时更新 + if (leftMax < rightMax) { + // 在移动之前先更新当前桶壁的高度 + // 如果当前桶底高度大于桶壁,计算的转水量会为0 + leftMax = Math.max(height[left], leftMax); + // 计算当前转水量之后,将指针向内移动一位 + result += leftMax - height[left++]; + } else { + rightMax = Math.max(height[right], rightMax); + result += rightMax - height[right--]; + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\217\214\346\214\207\351\222\210\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\217\214\346\214\207\351\222\210\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..214ab435 --- /dev/null +++ "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\345\217\214\346\214\207\351\222\210\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode.cn id=42 lang=javascript + * + * [42] 接雨水 + */ + +// @lc code=start +/** + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let sum = 0; + let left = 1; + let leftMax = height[0]; + let right = height.length - 2; + let rightMax = height[height.length - 1]; + + while (left <= right) { + if (leftMax < rightMax) { + leftMax = Math.max(leftMax, height[left]); + sum += leftMax - height[left++]; + } else { + rightMax = Math.max(rightMax, height[right]); + sum += rightMax - height[right--]; + } + } + + return sum; +}; +// @lc code=end diff --git "a/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\232\264\345\212\233.js" "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\232\264\345\212\233.js" new file mode 100644 index 00000000..04ba9fca --- /dev/null +++ "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\232\264\345\212\233.js" @@ -0,0 +1,33 @@ +/* + * @lc app=leetcode.cn id=42 lang=javascript + * + * [42] 接雨水 + */ + +// @lc code=start +/** + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let result = 0; + + for (let i = 0; i < height.length; i++) { + let leftMax = 0; + + for (let j = i; j >= 0; j--) { + leftMax = Math.max(leftMax, height[j]); + } + + let rightMax = 0; + + for (let k = i; k < height.length; k++) { + rightMax = Math.max(rightMax, height[k]); + } + + result += Math.min(leftMax, rightMax) - height[i]; + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\240\210.js" "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\240\210.js" new file mode 100644 index 00000000..3add022f --- /dev/null +++ "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\240\210.js" @@ -0,0 +1,28 @@ +/** + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let result = 0; + let stack = []; + + for (let i = 0; i < height.length; i++) { + while (stack.length && height[i] > height[stack[stack.length - 1]]) { + const pop = stack.pop(); + + if (!stack.length) { + break; + } + + const bottom = height[pop]; + const left = height[stack[stack.length - 1]]; + const right = height[i]; + const width = i - stack[stack.length - 1] - 1; + const bucketHeight = Math.min(left, right) - bottom; + result += width * bucketHeight; + } + stack.push(i); + } + + return result; +}; diff --git "a/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\240\210\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\240\210\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..7fa1e3bd --- /dev/null +++ "b/Week_02/42. \346\216\245\351\233\250\346\260\264\357\274\214\346\240\210\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,44 @@ +/* + * @lc app=leetcode.cn id=42 lang=javascript + * + * [42] 接雨水 + */ + +// @lc code=start +/** + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let result = 0; // 储存总转水量 + let stack = []; // 栈中存储的是水桶的左边界和桶底 + + // 遍历所有的柱子,在入栈之前,柱子都是作为水桶的右边界 + for (let right = 0; right < height.length; right++) { + // 如果右边界比栈顶元素高,表示其与栈中元素形成了一个水桶,可以计算水桶的装水量 + while (height[right] > height[stack[stack.length - 1]]) { + // 栈顶元素即为当前水桶的桶底,将其弹出并计算桶底高度 + const bucketBottom = height[stack.pop()]; + + // 将桶底出栈之后,栈如果为空则表示不存在水桶,退出循环 + if (!stack.length) { + break; + } + + const left = stack[stack.length - 1]; // 剩余的栈顶元素为水桶的左边界,获取其索引 + const leftHeight = height[left]; // 获取水桶左边界的高度 + const rightHeight = height[right]; // 获取水桶右边界的高度 + const bucketTop = Math.min(leftHeight, rightHeight); // 水桶左右边界的较小值,即为桶顶高度 + const bucketHeight = bucketTop - bucketBottom; // 水桶的可装水高度为桶顶减去桶底高度 + const width = right - left - 1; // 水桶的实际宽度为左右边界之差减一 + const area = width * bucketHeight; // 计算当前水桶可装水量 + result += area; // 将水量加入总量 + } + + // 当前右边界可以作为下一个水桶的左边界,因此需要入栈 + stack.push(right); + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/429. N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214BFS.js" "b/Week_02/429. N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214BFS.js" new file mode 100644 index 00000000..c3c57b8b --- /dev/null +++ "b/Week_02/429. N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214BFS.js" @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode.cn id=429 lang=javascript + * + * [429] N叉树的层序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ + +/** + * @param {Node} root + * @return {number[][]} + */ +var levelOrder = function (root) { + let result = []; + let queue = []; + root && queue.push(root); + + while (queue.length) { + let len = queue.length; + let last = result.length; + result.push([]); + + while (--len >= 0) { + let node = queue.shift(); + + result[last].push(node.val); + + node.children && queue.push(...node.children); + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/429. N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" "b/Week_02/429. N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..6d54db19 --- /dev/null +++ "b/Week_02/429. N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=429 lang=javascript + * + * [429] N叉树的层序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ + +/** + * @param {Node} root + * @return {number[][]} + */ +var levelOrder = function (root) { + let result = []; + + function traversal(node, level) { + if (!node) { + return node; + } + + result[level] ? result[level].push(node.val) : (result[level] = [node.val]); + + if (node.children) { + node.children.forEach((child) => { + traversal(child, level + 1); + }); + } + } + traversal(root, 0); + + return result; +}; +// @lc code=end diff --git "a/Week_02/49. \345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204\357\274\214\346\225\260\347\273\204\346\216\222\345\272\217+\345\223\210\345\270\214\350\241\250.js" "b/Week_02/49. \345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204\357\274\214\346\225\260\347\273\204\346\216\222\345\272\217+\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..8c08d94c --- /dev/null +++ "b/Week_02/49. \345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204\357\274\214\346\225\260\347\273\204\346\216\222\345\272\217+\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=49 lang=javascript + * + * [49] 字母异位词分组 + */ + +// @lc code=start +/** + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = function (strs) { + let map = new Map(); + + for (const str of strs) { + const key = str.split('').sort().join(''); + map.has(key) ? map.get(key).push(str) : map.set(key, [str]); + } + + return [...map.values()]; +}; +// @lc code=end diff --git "a/Week_02/49. \345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260+\345\223\210\345\270\214\350\241\250.js" "b/Week_02/49. \345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260+\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..96bb2552 --- /dev/null +++ "b/Week_02/49. \345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260+\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=49 lang=javascript + * + * [49] 字母异位词分组 + */ + +// @lc code=start +/** + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = function (strs) { + let map = new Map(); + const baseIndex = 'a'.codePointAt(0); + + for (const str of strs) { + let arr = new Array(26).fill(0); + + for (const char of str) { + const index = char.codePointAt(0) - baseIndex; + + arr[index]++; + } + + const key = JSON.stringify(arr); + + map.has(key) ? map.get(key).push(str) : map.set(key, [str]); + } + + return [...map.values()]; +}; +// @lc code=end diff --git "a/Week_02/589. N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" "b/Week_02/589. N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" new file mode 100644 index 00000000..8477bfad --- /dev/null +++ "b/Week_02/589. N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" @@ -0,0 +1,39 @@ +/* + * @lc app=leetcode.cn id=589 lang=javascript + * + * [589] N叉树的前序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val, children) { + * this.val = val; + * this.children = children; + * }; + */ + +/** + * @param {Node} root + * @return {number[]} + */ +var preorder = function (root) { + let result = []; + let stack = []; + root && stack.push(root); + + while (stack.length) { + const pop = stack.pop(); + + result.push(pop.val); + + if (pop.children) { + for (let i = pop.children.length - 1; i >= 0; i--) { + stack.push(pop.children[i]); + } + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/589. N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" "b/Week_02/589. N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..51c181fd --- /dev/null +++ "b/Week_02/589. N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=589 lang=javascript + * + * [589] N叉树的前序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val, children) { + * this.val = val; + * this.children = children; + * }; + */ + +/** + * @param {Node} root + * @return {number[]} + */ +var preorder = function (root) { + let result = []; + + function traversal(node) { + if (!node) { + return; + } + + result.push(node.val); + + if (node.children) { + node.children.forEach((child) => { + traversal(child); + }); + } + } + traversal(root); + + return result; +}; +// @lc code=end diff --git "a/Week_02/590. N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" "b/Week_02/590. N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" new file mode 100644 index 00000000..96c5b586 --- /dev/null +++ "b/Week_02/590. N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\346\240\210.js" @@ -0,0 +1,39 @@ +/* + * @lc app=leetcode.cn id=590 lang=javascript + * + * [590] N叉树的后序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ + +/** + * @param {Node} root + * @return {number[]} + */ +var postorder = function (root) { + let result = []; + let stack = []; + root && stack.push(root); + + while (stack.length) { + const node = stack.pop(); + + result.unshift(node.val); + + if (node.children) { + node.children.forEach((child) => { + stack.push(child); + }); + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/590. N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" "b/Week_02/590. N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..4558e415 --- /dev/null +++ "b/Week_02/590. N\345\217\211\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=590 lang=javascript + * + * [590] N叉树的后序遍历 + */ + +// @lc code=start +/** + * // Definition for a Node. + * function Node(val,children) { + * this.val = val; + * this.children = children; + * }; + */ + +/** + * @param {Node} root + * @return {number[]} + */ +var postorder = function (root) { + let result = []; + + function traversal(node) { + if (!node) { + return; + } + + if (node.children) { + node.children.forEach((child) => { + traversal(child); + }); + } + + result.push(node.val); + } + traversal(root); + + return result; +}; +// @lc code=end diff --git "a/Week_02/94. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206\357\274\214\344\275\277\347\224\250\346\240\210.js" "b/Week_02/94. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206\357\274\214\344\275\277\347\224\250\346\240\210.js" new file mode 100644 index 00000000..18f16731 --- /dev/null +++ "b/Week_02/94. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206\357\274\214\344\275\277\347\224\250\346\240\210.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=94 lang=javascript + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +var inorderTraversal = function (root) { + let result = []; + let stack = []; + let curr = root; + + while (curr || stack.length) { + while (curr) { + stack.push(curr); + curr = curr.left; + } + + curr = stack.pop(); + result.push(curr.val); + curr = curr.right; + } + + return result; +}; +// @lc code=end diff --git "a/Week_02/94. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" "b/Week_02/94. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..e0f21f57 --- /dev/null +++ "b/Week_02/94. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,36 @@ +/* + * @lc app=leetcode.cn id=94 lang=javascript + * + * [94] 二叉树的中序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {number[]} + */ +var inorderTraversal = function (root) { + let result = []; + + function traversal(node) { + if (!node) { + return node; + } + + traversal(node.left); + result.push(node.val); + traversal(node.right); + } + traversal(root); + + return result; +}; +// @lc code=end diff --git a/Week_02/README.md b/Week_02/README.md index 50de3041..7609155a 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -1 +1,35 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +以下是本周的做题记录: + +https://shimo.im/sheets/hE9AXYhkH7EL3fOg/MODOC + +我为部分题目写了题解,如下: + +1. [LeetCode 题解:242. 有效的字母异位词,数组计数,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-s-2) +2. [LeetCode 题解:242. 有效的字母异位词,哈希表两次循环,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-h-2) +3. [LeetCode 题解:242. 有效的字母异位词,哈希表一次循环,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-ha-) +4. [LeetCode 题解:242. 有效的字母异位词,数组排序,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-shu) +5. [LeetCode 题解:49. 字母异位词分组,数组计数+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/group-anagrams/solution/leetcodeti-jie-49-zi-mu-yi-wei-ci-fen-zu-shu-zu-ji) +6. [LeetCode 题解:49. 字母异位词分组,数组排序,JavaScript,详细注释](https://leetcode-cn.com/problems/group-anagrams/solution/leetcodeti-jie-49-zi-mu-yi-wei-ci-fen-zu-shu-zu-pa) +7. [LeetCode 题解:1. 两数之和,Map+队列+双指针,JavaScript,详细注释](https://leetcode-cn.com/problems/two-sum/solution/leetcodeti-jie-1-liang-shu-zhi-he-mapdui-lie-shuan) +8. [LeetCode 题解:1. 两数之和,双循环暴力解法,JavaScript,详细注释](https://leetcode-cn.com/problems/two-sum/solution/1-liang-shu-zhi-he-javascriptshuang-xun-huan-bao-l) +9. [LeetCode 题解:1. 两数之和,HashMap 单次遍历,JavaScript,详细注释](https://leetcode-cn.com/problems/two-sum/solution/1-liang-shu-zhi-he-javascripthashmapdan-ci-bian-li) +10. [LeetCode 题解:94. 二叉树的中序遍历,使用栈,JavaScript,详细注释](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/leetcodeti-jie-94-er-cha-shu-de-zhong-xu-bian-li-s) +11. [LeetCode 题解:94. 二叉树的中序遍历,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/leetcodeti-jie-94-er-cha-shu-de-zhong-xu-bian-li-d) +12. [LeetCode 题解:144. 二叉树的前序遍历,使用栈,JavaScript,详细注释](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/leetcodeti-jie-144-er-cha-shu-de-qian-xu-bian-li-s) +13. [LeetCode 题解:144. 二叉树的前序遍历,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/leetcodeti-jie-144-er-cha-shu-de-qian-xu-bian-li-d) +14. [LeetCode 题解:590. N 叉树的后序遍历,栈,JavaScript,详细注释](https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/solution/leetcodeti-jie-590-ncha-shu-de-hou-xu-bian-li-zhan) +15. [LeetCode 题解:590. N 叉树的后序遍历,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/solution/leetcodeti-jie-590-ncha-shu-de-hou-xu-bian-li-java) +16. [LeetCode 题解:589. N 叉树的前序遍历,栈,JavaScript,详细注释](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/solution/leetcodeti-jie-589-ncha-shu-de-qian-xu-bian-li-zha) +17. [LeetCode 题解:589. N 叉树的前序遍历,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/solution/leetcodeti-jie-589-ncha-shu-de-qian-xu-bian-li-di-) +18. [LeetCode 题解:429. N 叉树的层序遍历,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/solution/leetcodeti-jie-429-ncha-shu-de-ceng-xu-bian-li-bfs) +19. [LeetCode 题解:429. N 叉树的层序遍历,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/solution/leetcodeti-jie-429-ncha-shu-de-ceng-xu-bian-li-di-) +20. [LeetCode 题解:剑指 Offer 40. 最小的 k 个数,二叉堆,JavaScript,详细注释](https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/leetcodeti-jie-jian-zhi-offer-40-zui-xia-oj7n) +21. [LeetCode 题解:剑指 Offer 40. 最小的 k 个数,快速排序,JavaScript,详细注释](https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/leetcodeti-jie-jian-zhi-offer-40-zui-xia-r0ak) +22. [LeetCode 题解:剑指 Offer 40. 最小的 k 个数,sort,JavaScript,详细注释](https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/leetcodeti-jie-jian-zhi-offer-40-zui-xia-65i9) +23. [LeetCode 题解:239. 滑动窗口最大值,二叉堆,JavaScript,详细注释](https://leetcode-cn.com/problems/sliding-window-maximum/solution/leetcodeti-jie-239-hua-dong-chuang-kou-z-qqdg) +24. [LeetCode 题解:239. 滑动窗口最大值,单调队列,JavaScript,详细注释](https://leetcode-cn.com/problems/sliding-window-maximum/solution/leetcodeti-jie-239-hua-dong-chuang-kou-zui-da-zh-2) +25. [LeetCode 题解:239. 滑动窗口最大值,双循环暴力,JavaScript,详细注释](https://leetcode-cn.com/problems/sliding-window-maximum/solution/leetcodeti-jie-239-hua-dong-chuang-kou-zui-da-zhi-) +26. [LeetCode 题解:剑指 Offer 49. 丑数,暴力法,JavaScript,详细注释](https://leetcode-cn.com/problems/chou-shu-lcof/solution/leetcodeti-jie-jian-zhi-offer-49-chou-sh-xgfu) +27. [LeetCode 题解:347. 前 K 个高频元素,二叉堆,JavaScript,详细注释](https://leetcode-cn.com/problems/top-k-frequent-elements/solution/leetcodeti-jie-347-qian-k-ge-gao-pin-yua-j4w7) diff --git "a/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214sort.js" "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214sort.js" new file mode 100644 index 00000000..6a0ffe1a --- /dev/null +++ "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214sort.js" @@ -0,0 +1,16 @@ +/** + * @param {number[]} arr + * @param {number} k + * @return {number[]} + */ +var getLeastNumbers = function (arr, k) { + let result = []; // 存储结果 + arr.sort((a, b) => a - b); // 将数组从小到大排序 + + // 从已排序的数组中取出k个元素 + for (let i = 0; i < k; i++) { + result.push(arr[i]); + } + + return result; +}; diff --git "a/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214sort\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214sort\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..f07d9f1c --- /dev/null +++ "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214sort\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,8 @@ +/** + * @param {number[]} arr + * @param {number} k + * @return {number[]} + */ +var getLeastNumbers = function (arr, k) { + return arr.sort((a, b) => a - b).splice(0, k); +}; diff --git "a/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214\344\272\214\345\217\211\345\240\206.js" "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214\344\272\214\345\217\211\345\240\206.js" new file mode 100644 index 00000000..40ff047d --- /dev/null +++ "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214\344\272\214\345\217\211\345\240\206.js" @@ -0,0 +1,115 @@ +/** + * @param {number[]} arr + * @param {number} k + * @return {number[]} + */ +var getLeastNumbers = function (arr, k) { + let result = []; // 存储结果 + let heap = new BinaryHeap((a, b) => a - b); // 创建一个堆,元素由小到大排序 + + // 将数组元素都插入堆 + for (let i = 0; i < arr.length; i++) { + heap.insert(arr[i]); + } + + // 从堆中按顺序取出k个元素 + for (let j = 0; j < k; j++) { + result.push(heap.deleteHead()); + } + + return result; +}; + +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用传入的对比函数比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } +} diff --git "a/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.js" "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.js" new file mode 100644 index 00000000..7737f082 --- /dev/null +++ "b/Week_02/\345\211\221\346\214\207 Offer 40. \346\234\200\345\260\217\347\232\204k\344\270\252\346\225\260\357\274\214\345\277\253\351\200\237\346\216\222\345\272\217.js" @@ -0,0 +1,59 @@ +/** + * @param {number[]} arr + * @param {number} k + * @return {number[]} + */ +var getLeastNumbers = function (arr, k) { + return quickSort(arr, 0, arr.length - 1, k); +}; + +const quickSort = (nums, left, right, k) => { + // 如果nums的长度小等于1,无需排序,直接作为结果返回 + if (nums.length <= 1) return nums; + + // 如果nums有长度,就需要排序 + if (left < right) { + // 将nums从left到right的部分分为两段,其中间值是pivot + // pivot左侧的值都小于nums[pivot],pivot右侧的值都大于nums[pivot] + const pivot = partition(nums, left, right); + + // 如果pivot刚好等于k,表示排序提前完成,可以直接返回结果 + if (pivot === k) { + return nums.slice(0, k); + } + + // 按pivot将nums拆分成两端分别排序,由于该题并未要求将数组完全排序,因此只需要将前k个元素整理到一侧即可 + // 由于我们事先不知道pivot与k的大小关系,因此需要根据k的位置进行递归 + pivot > k + ? quickSort(nums, left, pivot - 1, k) + : quickSort(nums, pivot + 1, right, k); + } + + // 完成排序后,将前k个元素返回即可 + return nums.slice(0, k); +}; + +const partition = (nums, left, right) => { + let pivot = left; // 以left为中值,比它小的都放在其左侧,比它大的都放在其右侧 + let index = left + 1; // 从left+1开始遍历数组,同时index表示了比nums[pivot]小的值存放的位置 + + // 遍历从left+1到right的元素,将其以nums[pivot]为中值拆分成两半 + for (let i = index; i <= right; i++) { + // 如果当前值比nums[pivot]小,则将其移动到index的位置 + if (nums[i] < nums[pivot]) { + // 将nums[i]与nums[index]对调 + [nums[i], nums[index]] = [nums[index], nums[i]]; + // 将index移动一位,用于存储下一个值 + index++; + } + } + + // 完成循环时,index多移动了一位,要将其回退 + index--; + // 循环停止时,index位置存储的是小于nums[pivot]的值,而nums[pivot]还在原地 + // 将两者对调,中值会被移动到index的位置,在其左侧的值都比它小 + [nums[pivot], nums[index]] = [nums[index], nums[pivot]]; + + // 将新的中值返回,下层递归根据它将数组拆分成两段继续排序 + return index; +}; diff --git "a/Week_02/\345\211\221\346\214\207 Offer 49. \344\270\221\346\225\260\357\274\214\344\270\211\346\214\207\351\222\210.js" "b/Week_02/\345\211\221\346\214\207 Offer 49. \344\270\221\346\225\260\357\274\214\344\270\211\346\214\207\351\222\210.js" new file mode 100644 index 00000000..a3ee01a3 --- /dev/null +++ "b/Week_02/\345\211\221\346\214\207 Offer 49. \344\270\221\346\225\260\357\274\214\344\270\211\346\214\207\351\222\210.js" @@ -0,0 +1,173 @@ +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆中插入多个元素 + insertMultiple(arr) { + for (let i = 0; i < arr.length; i++) { + this.insert(arr[i]); + } + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } + + // 读取所有堆元素 + getData() { + return this.data; + } +} +/** + * @param {number} n + * @return {number} + */ +var nthUglyNumber = function (n) { + let heap = new BinaryHeap((a, b) => a - b); // 创建一个小顶堆 + let arr = [2, 3, 5]; // 将质因数保存到数组,方便进行遍历计算 + let set = new Set(); // 由于计算结果可能重复,如2*3和3*2,因此需要用Set标记丑数是否已被保存过 + let result = 1; // 1的质因数也可为2、3、4,因此设置初始结果为1 + let count = 0; // 统计计算出的丑数个数 + heap.insert(1); // 将1插入小顶堆,用于启动遍历 + + // 循环计算n个丑数 + while (count < n) { + // 堆顶元素即为丑数,每次取出丑数都比上一次取出的大 + // 由于堆的性质,取出之后依然会保持从小到大的排序 + // 退出循环后,result保存的就是第n个丑数 + result = heap.deleteHead(); + count++; // 每取出一个丑数,计数一次 + + // 分别将取出的丑数,乘以2、3、4,即为下一批的丑数 + for (let i = 0; i < arr.length; i++) { + let product = result * arr[i]; // 计算出下一个丑数 + + // 如果计算出的丑数没有被保存过,则将其插入堆 + if (!set.has(product)) { + // 丑数插入堆后,自然按照从小到大排序 + heap.insert(product); + // 标记当前丑数已被插入堆中 + set.add(product); + } + } + } + + return result; +}; diff --git "a/Week_02/\345\211\221\346\214\207 Offer 49. \344\270\221\346\225\260\357\274\214\346\232\264\345\212\233\346\263\225.js" "b/Week_02/\345\211\221\346\214\207 Offer 49. \344\270\221\346\225\260\357\274\214\346\232\264\345\212\233\346\263\225.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_03/104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\214BFS.js" "b/Week_03/104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\214BFS.js" new file mode 100644 index 00000000..a8bdcbec --- /dev/null +++ "b/Week_03/104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\214BFS.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=104 lang=javascript + * + * [104] 二叉树的最大深度 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var maxDepth = function (root) { + let queue = []; // 存储层序遍历结果 + let level = 0; // 记录遍历的二叉树层级 + root && queue.push(root); // 如果树存在,才进行遍历 + + // 不断遍历队列,直到队列为空时,完成树的层序遍历 + while (queue.length) { + // 缓存当前层的节点数量,避免队列操作过程中数量不断改变,对for循环次数造成影响 + let queueLength = queue.length; + + // 每次只遍历当前一层的节点 + while (--queueLength >= 0) { + // 将当前层的节点依次从队列取出 + const node = queue.shift(); + + // 如果子节点存在,则将子节点入队,作为下一层的节点 + node.left && queue.push(node.left); + node.right && queue.push(node.right); + } + + // 遍历完一层之后,层数加1 + level++; + } + + // 由于每次都会遍历一层,当完成二叉树遍历时,最后的层数就是最大深度 + return level; +}; +// @lc code=end diff --git "a/Week_03/104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\214\351\200\222\345\275\222.js" "b/Week_03/104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..1f2bb9d8 --- /dev/null +++ "b/Week_03/104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,36 @@ +/* + * @lc app=leetcode.cn id=104 lang=javascript + * + * [104] 二叉树的最大深度 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {number} + */ +var maxDepth = function (root) { + let max = 0; + + function traversal(node, level) { + if (!node) { + max = Math.max(level, max); + return; + } + + traversal(node.left, level + 1); + traversal(node.right, level + 1); + } + traversal(root, 0); + + return max; +}; +// @lc code=end diff --git "a/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214Simple O(n) without map.js" "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214Simple O(n) without map.js" new file mode 100644 index 00000000..e544f8a1 --- /dev/null +++ "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214Simple O(n) without map.js" @@ -0,0 +1,47 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +/* + */ +var buildTree = function (preorder, inorder) { + /** + * @description 通过不断移动前、中序遍历的索引,生成二叉树 + * @param {number} stop // 停止递归的值 + * @return {TreeNode} + */ + let preorderIndex = 0; // 前序遍历的索引指针 + let inorderIndex = 0; // 中序遍历的索引指针 + + function build( + stop, // 已生成节点的值 + ) { + // 如果当前中序遍历对应的值已被生成过节点,则无需重复生成 + if (inorder[inorderIndex] === stop) { + return null; + } + + // 使用谦虚遍历的值创建一个节点,同时将指针向前移动一位 + const root = new TreeNode(preorder[preorderIndex++]); + + // 标记当前值已被生成过节点,继续向左子节点递归 + root.left = build(root.val); + // 中序遍历指针向前移动,不断查找可以生成右子节点的值 + inorderIndex++; + // 将上层递归的结点值传入右子节点的递归,标记其已生成过节点 + root.right = build(stop); + + // 将当前生成好的节点返回到上一层递归,供连接到根节点 + return root; + } + + return build(); +}; diff --git "a/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214Simple O(n) without map\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214Simple O(n) without map\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..66b31413 --- /dev/null +++ "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214Simple O(n) without map\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=105 lang=javascript + * + * [105] 从前序与中序遍历序列构造二叉树 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +var buildTree = function (preorder, inorder) { + let pre = 0; + let i = 0; + + function build(stop) { + if (stop === inorder[i]) { + return null; + } + + const node = new TreeNode(preorder[pre++]); + + node.left = build(node.val); + i++; + node.right = build(stop); + + return node; + } + + return build(); +}; +// @lc code=end diff --git "a/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\344\275\277\347\224\250\347\264\242\345\274\225.js" "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\344\275\277\347\224\250\347\264\242\345\274\225.js" new file mode 100644 index 00000000..6cf4e93a --- /dev/null +++ "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\344\275\277\347\224\250\347\264\242\345\274\225.js" @@ -0,0 +1,56 @@ +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +var buildTree = function (preorder, inorder) { + /** + * @description 使用前中序遍历结果的片段,递归生成二叉树 + * @param {number} preLeft // 每个子树在前序遍历中的左边界 + * @param {number} preRight // 每个子树在前序遍历中的右边界 + * @param {number} inLeft // 每个子树在中序遍历中的左边界 + * @param {number} inRight // 每个子树在中序遍历中的右边界 + * @return {TreeNode} + */ + function buildBinaryTree(preLeft, preRight, inLeft, inRight) { + // 当截取数组片段的左边界大于右边界时,表示当前用于构造子树的数组为空,退出循环 + if (preLeft > preRight) { + // 数组为空时,表示当前已经无节点需要生成,返回null + // 让上一层节点的left和right指针指向null + return null; + } + + // 缓存根节点的值 + const rootVal = preorder[preLeft]; + // 使用当前值创建一个节点,即为一个树的根节点 + const root = new TreeNode(rootVal); + // 查找到当前根节点在数组中的位置 + const rootIndex = inorder.indexOf(rootVal); + // 中序遍历的左边界到根节点之差是子树的节点数量 + // 由于左边界始终会存在,因此向左统计总是可以得到子树的节点数量 + const nodeCount = rootIndex - inLeft; + + // 计算数组对应左子树的索引,向下递归 + // 将当前根节点与已生成好的左子树连接 + root.left = buildBinaryTree( + preLeft + 1, // 左子树的前序遍历左边界 + preLeft + nodeCount, // 当前左边界加上子树节点数量,即为左子树的前序遍历右边界 + inLeft, // 左子树的中序遍历左边界 + rootIndex - 1, // 左子树的中序遍历右边界 + ); + // 计算数组对应右子树的索引,向下递归 + // 将当前根节点与已生成好的右子树连接 + root.right = buildBinaryTree( + preLeft + nodeCount + 1, // 当前左边界加上子树节点数量再加1,即为右子树的前序遍历左边界 + preRight, // 右子树的前序遍历右边界 + rootIndex + 1, // 右子树的中序遍历左边界 + inRight, // 右子树的中序遍历右边界 + ); + + // 将根节点返回供上层节点连接 + return root; + } + + // 生成二叉树并返回 + return buildBinaryTree(0, preorder.length - 1, 0, preorder.length - 1); +}; diff --git "a/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\345\223\210\345\270\214\350\241\250.js" "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..02b01410 --- /dev/null +++ "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,63 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +var buildTree = function (preorder, inorder) { + /** + * @description 使用前中序遍历结果的片段,递归生成二叉树 + * @param {number} preLeft // 每个子树在前序遍历中的左边界 + * @param {number} preRight // 每个子树在前序遍历中的右边界 + * @param {number} inLeft // 每个子树在中序遍历中的左边界 + * @param {number} inRight // 每个子树在中序遍历中的右边界 + * @return {TreeNode} + */ + function buildBinaryTree(preLeft, preRight, inLeft, inRight) { + // 当截取数组片段的左边界大于右边界时,表示当前用于构造子树的数组为空,退出循环 + if (preLeft > preRight) { + // 数组为空时,表示当前已经无节点需要生成,返回null + // 让上一层节点的left和right指针指向null + return null; + } + + // 缓存根节点的值 + const rootVal = preorder[preLeft]; + // 使用当前值创建一个节点,即为一个树的根节点 + const root = new TreeNode(rootVal); + // 查找到当前根节点在数组中的位置 + const rootIndex = inorder.indexOf(rootVal); + // 中序遍历的左边界到根节点之差是子树的节点数量 + // 由于左边界始终会存在,因此向左统计总是可以得到子树的节点数量 + const nodeCount = rootIndex - inLeft; + + // 计算数组对应左子树的索引,向下递归 + // 将当前根节点与已生成好的左子树连接 + root.left = buildBinaryTree( + preLeft + 1, // 左子树的前序遍历左边界 + preLeft + nodeCount, // 当前左边界加上子树节点数量,即为左子树的前序遍历右边界 + inLeft, // 左子树的中序遍历左边界 + rootIndex - 1, // 左子树的中序遍历右边界 + ); + // 计算数组对应右子树的索引,向下递归 + // 将当前根节点与已生成好的右子树连接 + root.right = buildBinaryTree( + preLeft + nodeCount + 1, // 当前左边界加上子树节点数量再加1,即为右子树的前序遍历左边界 + preRight, // 右子树的前序遍历右边界 + rootIndex + 1, // 右子树的中序遍历左边界 + inRight, // 右子树的中序遍历右边界 + ); + + // 将根节点返回供上层节点连接 + return root; + } + + // 生成二叉树并返回 + return buildBinaryTree(0, preorder.length - 1, 0, preorder.length - 1); +}; diff --git "a/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\346\225\260\347\273\204\345\210\207\345\211\262.js" "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\346\225\260\347\273\204\345\210\207\345\211\262.js" new file mode 100644 index 00000000..d5fb0878 --- /dev/null +++ "b/Week_03/105. \344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222+\346\225\260\347\273\204\345\210\207\345\211\262.js" @@ -0,0 +1,41 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +var buildTree = function (preorder, inorder) { + // 递归切割数组到最后,数组会为空,此时退出循环 + if (preorder.length === 0) { + // 数组为空时,表示当前已经无节点需要生成,返回null + // 让上一层节点的left和right指针指向null + return null; + } + + // 使用当前值创建一个节点,即为一个树的根节点 + const root = new TreeNode(preorder[0]); + // 查找到当前根节点在数组中的位置 + const rootIndex = inorder.findIndex((value) => preorder[0] === value); + + // 将数组对应左子树的部分切割,向下递归 + // 将当前根节点与已生成好的左子树连接 + root.left = buildTree( + preorder.slice(1, rootIndex + 1), + inorder.slice(0, rootIndex), + ); + // 将数组对应右子树的部分切割,向下递归 + // 将当前根节点与已生成好的右子树连接 + root.right = buildTree( + preorder.slice(rootIndex + 1), + inorder.slice(rootIndex + 1), + ); + + // 将根节点返回供上层节点连接 + return root; +}; diff --git "a/Week_03/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246\357\274\214BFS.JS" "b/Week_03/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246\357\274\214BFS.JS" new file mode 100644 index 00000000..0e302dab --- /dev/null +++ "b/Week_03/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246\357\274\214BFS.JS" @@ -0,0 +1,54 @@ +/* + * @lc app=leetcode.cn id=111 lang=javascript + * + * [111] 二叉树的最小深度 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var minDepth = function (root) { + let queue = []; // 存储层序遍历结果 + let level = 0; // 记录遍历的二叉树层级 + root && queue.push(root); // 如果树存在,才进行遍历 + + // 不断遍历队列,直到队列为空时,完成树的层序遍历 + while (queue.length) { + // 缓存当前层的节点数量,避免队列操作过程中数量不断改变,对for循环次数造成影响 + let queueLength = queue.length; + + // 每次只遍历当前一层的节点 + while (--queueLength >= 0) { + // 将当前层的节点依次从队列取出 + const node = queue.shift(); + + // 没有子节点的节点就是叶子节点 + // 第一次遇到叶子节点时,当前深度就是最小二叉树深度 + if (!node.left && !node.right) { + // 由于当前深度还未记录,返回时需要加1 + return level + 1; + } + + // 如果子节点存在,则将子节点入队,作为下一层的节点 + node.left && queue.push(node.left); + node.right && queue.push(node.right); + } + + // 遍历完一层之后,层数加1 + level++; + } + + // 如果没有进入循环,当前深度为0 + return level; +}; +// @lc code=end diff --git "a/Week_03/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246\357\274\214\351\200\222\345\275\222.JS" "b/Week_03/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246\357\274\214\351\200\222\345\275\222.JS" new file mode 100644 index 00000000..d1f48955 --- /dev/null +++ "b/Week_03/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246\357\274\214\351\200\222\345\275\222.JS" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=111 lang=javascript + * + * [111] 二叉树的最小深度 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var minDepth = function (root) { + let min = 0; + + function traversal(node, level) { + let newLevel = level + 1; + + if (!node.left && !node.right) { + if (min === 0) { + min = newLevel; + } else { + min = Math.min(min, newLevel); + } + return; + } + + node.left && traversal(node.left, newLevel); + node.right && traversal(node.right, newLevel); + } + root && traversal(root, 0); + + return min; +}; +// @lc code=end diff --git "a/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\345\210\206\346\262\273.js" "b/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\345\210\206\346\262\273.js" new file mode 100644 index 00000000..d987398e --- /dev/null +++ "b/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\345\210\206\346\262\273.js" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=169 lang=javascript + * + * [169] 多数元素 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var majorityElement = function (nums) { + function countMax(left, right, max) { + let count = 0; + + for (let i = left; i <= right; i++) { + if (nums[i] === max) { + count++; + } + } + + return count; + } + + function divide(left, right) { + if (left === right) { + return nums[left]; + } + + let middle = left + Math.floor((right - left) / 2); + let leftMax = divide(left, middle); + let rightMax = divide(middle + 1, right); + + let leftCount = countMax(left, right, leftMax); + let rightCount = countMax(left, right, rightMax); + + return leftCount > rightCount ? leftMax : rightMax; + } + + return divide(0, nums.length - 1); +}; +// @lc code=end diff --git "a/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..de733baf --- /dev/null +++ "b/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=169 lang=javascript + * + * [169] 多数元素 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var majorityElement = function (nums) { + let map = new Map(); + + for (let i = 0; i < nums.length; i++) { + const value = nums[i]; + + if (map.has(value)) { + map.set(value, map.get(value) + 1); + } else { + map.set(value, 1); + } + + if (map.get(value) >= nums.length / 2) { + return value; + } + } +}; +// @lc code=end diff --git "a/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\346\216\222\345\272\217.js" "b/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\346\216\222\345\272\217.js" new file mode 100644 index 00000000..d99aa7ec --- /dev/null +++ "b/Week_03/169. \345\244\232\346\225\260\345\205\203\347\264\240\357\274\214\346\216\222\345\272\217.js" @@ -0,0 +1,15 @@ +/* + * @lc app=leetcode.cn id=169 lang=javascript + * + * [169] 多数元素 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var majorityElement = function (nums) { + return nums.sort((a, b) => a - b)[Math.floor(nums.length / 2)]; +}; +// @lc code=end diff --git "a/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214BFS.js" "b/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214BFS.js" new file mode 100644 index 00000000..53e17acc --- /dev/null +++ "b/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214BFS.js" @@ -0,0 +1,54 @@ +/* + * @lc app=leetcode.cn id=17 lang=javascript + * + * [17] 电话号码的字母组合 + */ + +// @lc code=start +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function (digits) { + // 如果无输入号码,直接返回空数组 + if (!digits || !digits.length) { + return []; + } + + let queue = ['']; // 使用队列进行BFS遍历,队列中先存入一个空字符串,用于启动遍历 + let level = 0; // 将生成所有可能按键的过程,当做遍历一个n叉树,用level记录遍历的层数 + // 使用Map存储按键对应的字母 + const map = new Map([ + ['2', 'abc'], + ['3', 'def'], + ['4', 'ghi'], + ['5', 'jkl'], + ['6', 'mno'], + ['7', 'pqrs'], + ['8', 'tuv'], + ['9', 'wxyz'], + ]); + + // 当遍历层数达到输入号码层数时,退出循环 + while (level < digits.length) { + let queueLen = queue.length; // 缓存当前队列节点数,用于控制遍历当前层的节点 + + // 每一次循环都只遍历当前一层的节点 + while (--queueLen >= 0) { + const str = queue.shift(); // 将每个节点出队 + const chars = map.get(digits[level]); // 根据按键数字,查找下一层可能的字母 + + // 遍历下一个按键的所有可能字母,生成下一层节点 + for (let i = 0; i < chars.length; i++) { + queue.push(str + chars[i]); + } + } + + // 完成一层节点遍历后,将层数加1 + level++; + } + + // 完成循环后,队列中剩下的节点就是所有可能的字母组合 + return queue; +}; +// @lc code=end diff --git "a/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214\345\233\236\346\272\257.js" "b/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214\345\233\236\346\272\257.js" new file mode 100644 index 00000000..38c502c8 --- /dev/null +++ "b/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214\345\233\236\346\272\257.js" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=17 lang=javascript + * + * [17] 电话号码的字母组合 + */ + +// @lc code=start +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function (digits) { + if (!digits || !digits.length) { + return []; + } + + let result = []; + const map = new Map([ + ['2', 'abc'], + ['3', 'def'], + ['4', 'ghi'], + ['5', 'jkl'], + ['6', 'mno'], + ['7', 'pqrs'], + ['8', 'tuv'], + ['9', 'wxyz'], + ]); + + function dfs(str, current) { + if (current === digits.length) { + result.push(str); + return; + } + + const chars = map.get(digits[current]); + + for (let i = 0; i < chars.length; i++) { + dfs(str + chars[i], current + 1); + } + } + dfs('', 0); + + return result; +}; +// @lc code=end diff --git "a/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214\351\230\237\345\210\227.js" "b/Week_03/17. \347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\345\255\227\346\257\215\347\273\204\345\220\210\357\274\214\351\230\237\345\210\227.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214BFS.js" "b/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214BFS.js" new file mode 100644 index 00000000..4ace234d --- /dev/null +++ "b/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214BFS.js" @@ -0,0 +1,43 @@ +/* + * @lc app=leetcode.cn id=22 lang=javascript + * + * [22] 括号生成 + */ + +// @lc code=start +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function (n) { + let queue = [['(', 1, 0]]; + let result = []; + + while (queue.length) { + let len = queue.length; + + while (--len >= 0) { + const item = queue.shift(); + + if (item[1] < n) { + let newItem = item.slice(); + newItem[0] += '('; + newItem[1]++; + queue.push(newItem); + } + if (item[2] < item[1]) { + let newItem = item.slice(); + newItem[0] += ')'; + newItem[2]++; + if (newItem[2] === n) { + result.push(newItem[0]); + } else { + queue.push(newItem); + } + } + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214\351\200\222\345\275\222\347\224\237\346\210\220\345\220\214\346\227\266\350\277\207\346\273\244.js" "b/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214\351\200\222\345\275\222\347\224\237\346\210\220\345\220\214\346\227\266\350\277\207\346\273\244.js" new file mode 100644 index 00000000..fd5015e7 --- /dev/null +++ "b/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214\351\200\222\345\275\222\347\224\237\346\210\220\345\220\214\346\227\266\350\277\207\346\273\244.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=22 lang=javascript + * + * [22] 括号生成 + */ + +// @lc code=start +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function (n) { + let result = []; + + function dfs(str, left, right) { + if (left === n && right === n) { + result.push(str); + return; + } + + if (left < n) { + dfs(str + '(', left + 1, right); + } + if (right < left) { + dfs(str + ')', left, right + 1); + } + } + dfs('', 0, 0); + + return result; +}; +// @lc code=end diff --git "a/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214\351\200\222\345\275\222\347\224\237\346\210\220\345\220\216\350\277\207\346\273\244.js" "b/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214\351\200\222\345\275\222\347\224\237\346\210\220\345\220\216\350\277\207\346\273\244.js" new file mode 100644 index 00000000..fb1b7716 --- /dev/null +++ "b/Week_03/22. \346\213\254\345\217\267\347\224\237\346\210\220\357\274\214\351\200\222\345\275\222\347\224\237\346\210\220\345\220\216\350\277\207\346\273\244.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=22 lang=javascript + * + * [22] 括号生成 + */ + +// @lc code=start +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function (n) { + let result = []; + + function judge(str) { + let count = 0; + + for (const char of str) { + if (char === '(') { + count++; + } else { + if (!count) { + return false; + } + count--; + } + } + + return !count; + } + + function dfs(str) { + if (str.length === n * 2) { + if (judge(str)) { + result.push(str); + } + return; + } + + dfs(str + '('); + dfs(str + ')'); + } + dfs(''); + + return result; +}; +// @lc code=end diff --git "a/Week_03/226. \347\277\273\350\275\254\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222.js" "b/Week_03/226. \347\277\273\350\275\254\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..d64e7a1b --- /dev/null +++ "b/Week_03/226. \347\277\273\350\275\254\344\272\214\345\217\211\346\240\221\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=226 lang=javascript + * + * [226] 翻转二叉树 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {TreeNode} + */ +var invertTree = function (root) { + function traversal(node) { + if (!node) { + return; + } + + [node.left, node.right] = [node.right, node.left]; + + traversal(node.left); + traversal(node.right); + } + traversal(root); + + return root; +}; +// @lc code=end diff --git "a/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\345\255\230\345\202\250\347\210\266\350\212\202\347\202\271.js" "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\345\255\230\345\202\250\347\210\266\350\212\202\347\202\271.js" new file mode 100644 index 00000000..fc479b54 --- /dev/null +++ "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\345\255\230\345\202\250\347\210\266\350\212\202\347\202\271.js" @@ -0,0 +1,53 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +// 保存每个节点的父节点 +let parentMap = new Map(); + +// 递归遍历所有节点,并保存其父节点 +function dfs(node) { + if (node.left) { + // 如果子节点存在,则将其父节点保存到Map中 + parentMap.set(node.left, node); + // 继续递归遍历子节点 + dfs(node.left); + } + if (node.right) { + parentMap.set(node.right, node); + dfs(node.right); + } +} +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function (root, p, q) { + dfs(root); // 遍历二叉树的所有节点,保存其子父关系 + + // 用Set保存p的所有父节点 + let visitedSet = new Set(); + + // 遍历并保存所有p的父节点 + while (p) { + // 每次遍历的p都是一个父节点,将其保存到Set中 + visitedSet.add(p); + // 不断在Map中查找p的父节点,并将其更新到p,用于下一次遍历 + p = parentMap.get(p); + } + + // 遍历q的父节点,如果父节点已在visitedSet中保存过,则它就是最近公共祖先 + while (q) { + // 当第一次发现q在visitedSet中,那么它就是最近公共祖先,将其返回即可 + if (visitedSet.has(q)) { + return q; + } + // 每次遍历都查找q的父节点,并将其更新,继续循环 + q = parentMap.get(q); + } +}; diff --git "a/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\345\255\230\345\202\250\347\210\266\350\212\202\347\202\271\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\345\255\230\345\202\250\347\210\266\350\212\202\347\202\271\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..7c2d9a61 --- /dev/null +++ "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\345\255\230\345\202\250\347\210\266\350\212\202\347\202\271\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,50 @@ +/* + * @lc app=leetcode.cn id=236 lang=javascript + * + * [236] 二叉树的最近公共祖先 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +let map = new Map(); + +function dfs(node) { + if (node.left) { + map.set(node.left, node); + dfs(node.left); + } + if (node.right) { + map.set(node.right, node); + dfs(node.right); + } +} +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function (root, p, q) { + dfs(root); + + let set = new Set(); + + while (p) { + set.add(p); + p = map.get(p); + } + + while (q) { + if (set.has(q)) { + return q; + } + q = map.get(q); + } +}; +// @lc code=end diff --git "a/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\351\200\222\345\275\222.js" "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..a29010b2 --- /dev/null +++ "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode.cn id=236 lang=javascript + * + * [236] 二叉树的最近公共祖先 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function (root, p, q) { + // 如果root不存在,直接退出循环 + // 如果查询到p或q,则将其返回,供上层递归判断 + if (!root || root === p || root === q) { + return root; + } + + // 获取在子树中匹配到的p和q节点 + const left = lowestCommonAncestor(root.left, p, q); + const right = lowestCommonAncestor(root.right, p, q); + + // 如果p和q同时匹配到,当前节点即为最近公共祖先 + if (left && right) { + return root; + } + + // 如果没有同时匹配到p和q,就将匹配到的节点返回给上层判断 + // 最近公共祖先是其本身的情况,会通过该步骤返回 + return left || right; +}; +// @lc code=end diff --git "a/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\351\200\222\345\275\222\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\351\200\222\345\275\222\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..8228c8ca --- /dev/null +++ "b/Week_03/236. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210\357\274\214\351\200\222\345\275\222\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=236 lang=javascript + * + * [236] 二叉树的最近公共祖先 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function (root, p, q) { + if (!root || root === p || root === q) { + return root; + } + + const left = lowestCommonAncestor(root.left, p, q); + const right = lowestCommonAncestor(root.right, p, q); + + if (left && right) { + return root; + } + + return left || right; +}; +// @lc code=end diff --git "a/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214ASCII\347\240\201\346\261\202\345\222\214\357\274\214JavaScript\357\274\214\350\257\246\347\273\206\346\263\250\351\207\212.js" "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214ASCII\347\240\201\346\261\202\345\222\214\357\274\214JavaScript\357\274\214\350\257\246\347\273\206\346\263\250\351\207\212.js" new file mode 100644 index 00000000..7fbcf1c1 --- /dev/null +++ "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214ASCII\347\240\201\346\261\202\345\222\214\357\274\214JavaScript\357\274\214\350\257\246\347\273\206\346\263\250\351\207\212.js" @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=389 lang=javascript + * + * [389] 找不同 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {character} + */ +var findTheDifference = function (s, t) { + let code = 0; // 保存ASCII码的值 + + // 遍历将t中所有字符ASCII码的值求和 + for (const char of t) { + code += char.charCodeAt(0); + } + + // 因为t比s多一个字符,因此只要将t中字符ASCII码减去s的ASCII码,剩下的一个就是被添加的字符 + for (const char of s) { + code -= char.charCodeAt(0); + } + + // 将ASCII码转换成字符串,就得到了结果 + return String.fromCharCode(code); +}; +// @lc code=end diff --git "a/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\344\275\215\350\277\220\347\256\227.js" "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\344\275\215\350\277\220\347\256\227.js" new file mode 100644 index 00000000..dd8a7ab7 --- /dev/null +++ "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\344\275\215\350\277\220\347\256\227.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=389 lang=javascript + * + * [389] 找不同 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {character} + */ +var findTheDifference = function (s, t) { + let code = 0; // 保存ASCII码的值 + const str = s + t; // 将s和t组成一个新字符串 + + // 将字符串中的所有字符转成ASCII码进行异或运算,相同的字符会被抵消 + // 最后剩下的就是多余字符的ASCII码 + for (let i = 0; i < str.length; i++) { + code ^= str.charCodeAt(i); + } + + // 将ASCII码转换成字符串返回 + return String.fromCharCode(code); +}; +// @lc code=end diff --git "a/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..36e12bc9 --- /dev/null +++ "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=389 lang=javascript + * + * [389] 找不同 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {character} + */ +var findTheDifference = function (s, t) { + let map = new Map(); + + for (const char of s) { + map.has(char) ? map.set(char, map.get(char) + 1) : map.set(char, 1); + } + + for (const char of t) { + if (map.has(char)) { + map.set(char, map.get(char) - 1); + + if (map.get(char) === -1) { + return char; + } + } else { + return char; + } + } +}; +// @lc code=end diff --git "a/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260.js" "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260.js" new file mode 100644 index 00000000..c3a58c8c --- /dev/null +++ "b/Week_03/389. \346\211\276\344\270\215\345\220\214\357\274\214\346\225\260\347\273\204\350\256\241\346\225\260.js" @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=389 lang=javascript + * + * [389] 找不同 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {character} + */ +var findTheDifference = function (s, t) { + let count = new Array(26).fill(0); + const base = 'a'.charCodeAt(0); + + for (const char of s) { + count[char.charCodeAt(0) - base]++; + } + + for (const char of t) { + const index = char.charCodeAt(0) - base; + count[index]--; + + if (count[index] < 0) { + return char; + } + } +}; +// @lc code=end diff --git "a/Week_03/46. \345\205\250\346\216\222\345\210\227\357\274\214\345\233\236\346\272\257.js" "b/Week_03/46. \345\205\250\346\216\222\345\210\227\357\274\214\345\233\236\346\272\257.js" new file mode 100644 index 00000000..7e91dad9 --- /dev/null +++ "b/Week_03/46. \345\205\250\346\216\222\345\210\227\357\274\214\345\233\236\346\272\257.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=46 lang=javascript + * + * [46] 全排列 + */ + +// @lc code=start + +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permute = function (nums) { + let result = []; + let used = new Array(nums.length).fill(false); + let permutation = []; + + function dfs() { + if (permutation.length === nums.length) { + result.push(permutation.slice()); + return; + } + + for (let i = 0; i < used.length; i++) { + if (used[i]) { + continue; + } + + permutation.push(nums[i]); + used[i] = true; + dfs(); + permutation.pop(); + used[i] = false; + } + } + dfs(0); + + return result; +}; +// @lc code=end diff --git "a/Week_03/50. Pow(x, n)\357\274\214\346\232\264\345\212\233.js" "b/Week_03/50. Pow(x, n)\357\274\214\346\232\264\345\212\233.js" new file mode 100644 index 00000000..fe7cc693 --- /dev/null +++ "b/Week_03/50. Pow(x, n)\357\274\214\346\232\264\345\212\233.js" @@ -0,0 +1,33 @@ +/* + * @lc app=leetcode.cn id=50 lang=javascript + * + * [50] Pow(x, n) + */ + +// @lc code=start +/** + * @param {number} x + * @param {number} n + * @return {number} + */ +var myPow = function (x, n) { + if (x === 0 || x === 1) { + return x; + } + if (x === -1) { + return n % 2 ? -1 : 1; + } + if (x === -0x7fffffff) { + return 0; + } + + let result = 1; + const pow = Math.abs(n); + + for (let i = 0; i < pow; i++) { + result *= x; + } + + return n < 0 ? 1 / result : result; +}; +// @lc code=end diff --git "a/Week_03/50. Pow(x, n)\357\274\214\350\277\255\344\273\243\345\210\206\346\262\273.js" "b/Week_03/50. Pow(x, n)\357\274\214\350\277\255\344\273\243\345\210\206\346\262\273.js" new file mode 100644 index 00000000..611fa804 --- /dev/null +++ "b/Week_03/50. Pow(x, n)\357\274\214\350\277\255\344\273\243\345\210\206\346\262\273.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=50 lang=javascript + * + * [50] Pow(x, n) + */ + +// @lc code=start +/** + * @param {number} x + * @param {number} n + * @return {number} + */ +var myPow = function (x, n) { + if (x === 0 || x === 1) { + return x; + } + if (x === -1) { + return n % 2 ? -1 : 1; + } + + let result = 1; + let subRes = x; + let pow = Math.abs(n); + + while (pow > 0) { + if (pow % 2) { + result *= subRes; + } + subRes *= subRes; + pow = Math.floor(pow / 2); + } + + return n < 0 ? 1 / result : result; +}; +// @lc code=end diff --git "a/Week_03/50. Pow(x, n)\357\274\214\351\200\222\345\275\222\345\210\206\346\262\273.js" "b/Week_03/50. Pow(x, n)\357\274\214\351\200\222\345\275\222\345\210\206\346\262\273.js" new file mode 100644 index 00000000..89998bce --- /dev/null +++ "b/Week_03/50. Pow(x, n)\357\274\214\351\200\222\345\275\222\345\210\206\346\262\273.js" @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode.cn id=50 lang=javascript + * + * [50] Pow(x, n) + */ + +// @lc code=start +/** + * @param {number} x + * @param {number} n + * @return {number} + */ +var myPow = function (x, n) { + function dfs(pow) { + if (pow === 0) { + return 1; + } + + if (pow % 2) { + pow = Math.floor(pow / 2); + const res = dfs(pow); + return res * res * x; + } else { + const res = dfs(pow / 2); + return res * res; + } + } + + return n < 0 ? 1 / dfs(Math.abs(n)) : dfs(n); +}; +// @lc code=end diff --git "a/Week_03/51. N \347\232\207\345\220\216\357\274\214\345\233\236\346\272\257+\345\223\210\345\270\214\350\241\250.js" "b/Week_03/51. N \347\232\207\345\220\216\357\274\214\345\233\236\346\272\257+\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..641fcf0d --- /dev/null +++ "b/Week_03/51. N \347\232\207\345\220\216\357\274\214\345\233\236\346\272\257+\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,72 @@ +/* + * @lc app=leetcode.cn id=51 lang=javascript + * + * [51] N 皇后 + */ + +// @lc code=start +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = function (n) { + let positions = []; + let colPosition = []; + let pieSet = new Set(); + let naSet = new Set(); + let colSet = new Set(); + + function dfs(row) { + if (row === n) { + positions.push(colPosition.slice()); + return; + } + + for (let col = 0; col < n; col++) { + const pie = row + col; + const na = row - col; + + if (pieSet.has(pie) || naSet.has(na) || colSet.has(col)) { + continue; + } + + pieSet.add(pie); + naSet.add(na); + colSet.add(col); + colPosition.push(col); + dfs(row + 1); + pieSet.delete(pie); + naSet.delete(na); + colSet.delete(col); + colPosition.pop(); + } + } + dfs(0); + + function generate() { + let patterns = []; + + for (let i = 0; i < positions.length; i++) { + let pattern = []; + + for (let j = 0; j < n; j++) { + let subPattern = ''; + for (let k = 0; k < n; k++) { + if (positions[i][j] === k) { + subPattern += 'Q'; + } else { + subPattern += '.'; + } + } + pattern.push(subPattern); + } + + patterns.push(pattern); + } + + return patterns; + } + + return generate(); +}; +// @lc code=end diff --git "a/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214DP\351\201\215\345\216\206\346\225\260\347\273\204.js" "b/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214DP\351\201\215\345\216\206\346\225\260\347\273\204.js" new file mode 100644 index 00000000..74c321a9 --- /dev/null +++ "b/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214DP\351\201\215\345\216\206\346\225\260\347\273\204.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + */ + +// @lc code=start + +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function (n) { + let arr = new Array(n + 1).fill(1); + + for (let i = 2; i < arr.length; i++) { + arr[i] = arr[i - 2] + arr[i - 1]; + } + + return arr[n]; +}; +// @lc code=end diff --git "a/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214DP\351\201\215\345\216\206\357\274\214\345\217\230\351\207\217\347\274\223\345\255\230\347\273\223\346\236\234.js" "b/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214DP\351\201\215\345\216\206\357\274\214\345\217\230\351\207\217\347\274\223\345\255\230\347\273\223\346\236\234.js" new file mode 100644 index 00000000..62115860 --- /dev/null +++ "b/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214DP\351\201\215\345\216\206\357\274\214\345\217\230\351\207\217\347\274\223\345\255\230\347\273\223\346\236\234.js" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + */ + +// @lc code=start + +/** + * @param {number} n + * @return {number} + */ +let map = new Map(); +var climbStairs = function (n) { + let prev1 = 1; + let prev2 = 1; + let curr = 1; + + for (let i = 2; i < n + 1; i++) { + curr = prev1 + prev2; + prev2 = prev1; + prev1 = curr; + } + + return curr; +}; +// @lc code=end diff --git "a/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214\351\200\222\345\275\222+\345\223\210\345\270\214\350\241\250.js" "b/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214\351\200\222\345\275\222+\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..df35c059 --- /dev/null +++ "b/Week_03/70. \347\210\254\346\245\274\346\242\257\357\274\214\351\200\222\345\275\222+\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + */ + +// @lc code=start + +/** + * @param {number} n + * @return {number} + */ +let map = new Map(); +var climbStairs = function (n) { + if (n <= 1) { + return 1; + } + + if (map.has(n)) { + return map.get(n); + } + const sum = climbStairs(n - 1) + climbStairs(n - 2); + map.set(n, sum); + + return sum; +}; +// @lc code=end diff --git "a/Week_03/77. \347\273\204\345\220\210\357\274\214\345\233\236\346\272\257+for\345\276\252\347\216\257.js" "b/Week_03/77. \347\273\204\345\220\210\357\274\214\345\233\236\346\272\257+for\345\276\252\347\216\257.js" new file mode 100644 index 00000000..f0cd4d93 --- /dev/null +++ "b/Week_03/77. \347\273\204\345\220\210\357\274\214\345\233\236\346\272\257+for\345\276\252\347\216\257.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=77 lang=javascript + * + * [77] 组合 + */ + +// @lc code=start +/** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ +var combine = function (n, k) { + let result = []; + let subset = []; + let used = new Array(n + 1).fill(false); + + function dfs(current) { + if (subset.length === k) { + result.push(subset.slice()); + return; + } + + for (let i = current; i < used.length; i++) { + if (used[i]) { + continue; + } + + subset.push(i); + used[i] = true; + dfs(i); + subset.pop(); + used[i] = false; + } + } + dfs(1); + + return result; +}; +// @lc code=end diff --git "a/Week_03/77. \347\273\204\345\220\210\357\274\214\351\200\222\345\275\222\345\233\236\346\272\257.js" "b/Week_03/77. \347\273\204\345\220\210\357\274\214\351\200\222\345\275\222\345\233\236\346\272\257.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_03/78. \345\255\220\351\233\206\357\274\214\350\277\255\344\273\243+\344\275\215\350\277\220\347\256\227.js" "b/Week_03/78. \345\255\220\351\233\206\357\274\214\350\277\255\344\273\243+\344\275\215\350\277\220\347\256\227.js" new file mode 100644 index 00000000..fd02b80b --- /dev/null +++ "b/Week_03/78. \345\255\220\351\233\206\357\274\214\350\277\255\344\273\243+\344\275\215\350\277\220\347\256\227.js" @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=78 lang=javascript + * + * [78] 子集 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number[][]} + */ +var subsets = function (nums) { + let result = []; + + for (let i = 0; i < 1 << nums.length; i++) { + let subset = []; + + for (let j = 0; j < nums.length; j++) { + if (i & (1 << j)) { + subset.push(nums[j]); + } + } + + result.push(subset); + } + + return result; +}; +// @lc code=end diff --git "a/Week_03/78. \345\255\220\351\233\206\357\274\214\350\277\255\344\273\243.js" "b/Week_03/78. \345\255\220\351\233\206\357\274\214\350\277\255\344\273\243.js" new file mode 100644 index 00000000..b05ab3a7 --- /dev/null +++ "b/Week_03/78. \345\255\220\351\233\206\357\274\214\350\277\255\344\273\243.js" @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=78 lang=javascript + * + * [78] 子集 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number[][]} + */ +var subsets = function (nums) { + let result = [[]]; + + for (let i = 0; i < nums.length; i++) { + const resultLength = result.length; + + for (let j = 0; j < resultLength; j++) { + result.push([...result[j], nums[i]]); + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_03/78. \345\255\220\351\233\206\357\274\214\351\200\222\345\275\222+for\345\276\252\347\216\257+\345\233\236\346\272\257.js" "b/Week_03/78. \345\255\220\351\233\206\357\274\214\351\200\222\345\275\222+for\345\276\252\347\216\257+\345\233\236\346\272\257.js" new file mode 100644 index 00000000..fb4e4f3c --- /dev/null +++ "b/Week_03/78. \345\255\220\351\233\206\357\274\214\351\200\222\345\275\222+for\345\276\252\347\216\257+\345\233\236\346\272\257.js" @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=78 lang=javascript + * + * [78] 子集 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number[][]} + */ +var subsets = function (nums) { + let result = []; + let subset = []; + + function dfs(current) { + result.push(subset.slice()); + + for (let i = current; i < nums.length; i++) { + subset.push(nums[i]); + dfs(i + 1); + subset.pop(); + } + } + dfs(0); + + return result; +}; +// @lc code=end diff --git "a/Week_03/78. \345\255\220\351\233\206\357\274\214\351\200\222\345\275\222\345\233\236\346\272\257.js" "b/Week_03/78. \345\255\220\351\233\206\357\274\214\351\200\222\345\275\222\345\233\236\346\272\257.js" new file mode 100644 index 00000000..8e9d1cdc --- /dev/null +++ "b/Week_03/78. \345\255\220\351\233\206\357\274\214\351\200\222\345\275\222\345\233\236\346\272\257.js" @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode.cn id=78 lang=javascript + * + * [78] 子集 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number[][]} + */ +var subsets = function (nums) { + let result = []; + let subset = []; + + function dfs(current) { + if (current === nums.length) { + result.push(subset.slice()); + return; + } + + subset.push(nums[current]); + dfs(current + 1); + subset.pop(); + dfs(current + 1); + } + dfs(0); + + return result; +}; +// @lc code=end diff --git "a/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\344\275\277\347\224\250\346\240\210\344\270\255\345\272\217\351\201\215\345\216\206.js" "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\344\275\277\347\224\250\346\240\210\344\270\255\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 00000000..14437b7b --- /dev/null +++ "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\344\275\277\347\224\250\346\240\210\344\270\255\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode.cn id=98 lang=javascript + * + * [98] 验证二叉搜索树 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function (root) { + let stack = []; + let curr = root; + let prev = -Infinity; + + while (stack.length || curr) { + while (curr) { + stack.push(curr); + curr = curr.left; + } + + curr = stack.pop(); + if (prev >= curr.val) { + return false; + } + prev = curr.val; + curr = curr.right; + } + + return true; +}; +// @lc code=end diff --git "a/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222.js" "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..89acafc6 --- /dev/null +++ "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=98 lang=javascript + * + * [98] 验证二叉搜索树 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function (root) { + function traversal(node, left, right) { + if (!node) { + return true; + } + + if (left >= node.val || node.val >= right) { + return false; + } + + return ( + traversal(node.left, left, node.val) && + traversal(node.right, node.val, right) + ); + } + return traversal(root, -Infinity, Infinity); +}; +// @lc code=end diff --git "a/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222\344\270\255\345\272\217\351\201\215\345\216\206\345\256\214\346\210\220\345\220\216\345\206\215\345\210\244\346\226\255.js" "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222\344\270\255\345\272\217\351\201\215\345\216\206\345\256\214\346\210\220\345\220\216\345\206\215\345\210\244\346\226\255.js" new file mode 100644 index 00000000..8ce4518e --- /dev/null +++ "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222\344\270\255\345\272\217\351\201\215\345\216\206\345\256\214\346\210\220\345\220\216\345\206\215\345\210\244\346\226\255.js" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=98 lang=javascript + * + * [98] 验证二叉搜索树 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function (root) { + let arr = []; + + function traversal(node) { + if (!node) { + return; + } + + traversal(node.left); + arr.push(node.val); + traversal(node.right); + } + traversal(root); + + for (let i = 0; i < arr.length - 1; i++) { + if (arr[i] >= arr[i + 1]) { + return false; + } + } + + return true; +}; +// @lc code=end diff --git "a/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222\344\270\255\345\272\217\351\201\215\345\216\206\350\277\207\347\250\213\344\270\255\345\210\244\346\226\255.js" "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222\344\270\255\345\272\217\351\201\215\345\216\206\350\277\207\347\250\213\344\270\255\345\210\244\346\226\255.js" new file mode 100644 index 00000000..05137e55 --- /dev/null +++ "b/Week_03/98. \351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\214\351\200\222\345\275\222\344\270\255\345\272\217\351\201\215\345\216\206\350\277\207\347\250\213\344\270\255\345\210\244\346\226\255.js" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=98 lang=javascript + * + * [98] 验证二叉搜索树 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function (root) { + let prev = -Infinity; + let judge = true; + + function traversal(node) { + if (!node) { + return; + } + + traversal(node.left); + if (prev >= node.val) { + judge = false; + return; + } + prev = node.val; + traversal(node.right); + } + + traversal(root); + + return judge; +}; +// @lc code=end diff --git a/Week_03/README.md b/Week_03/README.md index 50de3041..fb496dbe 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,60 @@ -学习笔记 \ No newline at end of file +# 学习总结 + +希望能在直播的时候讲解一下[90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/)和[47. 全排列 II](https://leetcode-cn.com/problems/permutations-ii/)。 +对这种去重的题目,感觉有点无从下手,虽然看了题解能做出来,但换一题可能就不会了。 +目前能总结出的规律是: +1. 需要进行排序,排序后相同值都排列在一起 +2. 只在相同值中选取第一个或最后一个值,用其生成所有可能情况,其他的值都排除掉 + +我为部分题目写了题解,如下: + +> 从本周之后的题目我大都没做过,之后的题解会减少。我会尽量多写一些题解,虽然很影响刷题速度,但我觉得这是很好的学习方式。 + +1. [LeetCode题解:70. 爬楼梯,DP遍历,变量缓存结果,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-bian-liang-huan-cun-jie-guo) +2. [LeetCode题解:70. 爬楼梯,DP遍历数组,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-shu-zu-javascriptxiang-xi-z) +3. [LeetCode题解:70. 爬楼梯,递归+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-di-gui-ha-xi-biao-javascriptxiang-xi-) +4. [LeetCode题解:22. 括号生成,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-bfsjia-fa-ja) +5. [LeetCode题解:22. 括号生成,递归生成同时过滤,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-di-gui-sheng) +6. [LeetCode题解:22. 括号生成,递归先生成再过滤,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-xian-sheng-c) +7. [LeetCode题解:98. 验证二叉搜索树,递归中序遍历过程中判断,JavaScript,详细注释](https://leetcode-cn.com/problems/validate-binary-search-tree/solution/leetcodeti-jie-98-yan-zheng-er-cha-sou-suo-shu-d-3) +8. [LeetCode题解:98. 验证二叉搜索树,递归中序遍历完成后再判断,JavaScript,详细注释](https://leetcode-cn.com/problems/validate-binary-search-tree/solution/leetcodeti-jie-98-yan-zheng-er-cha-sou-suo-shu-d-2) +9. [LeetCode题解:98. 验证二叉搜索树,使用栈中序遍历,JavaScript,详细注释](https://leetcode-cn.com/problems/validate-binary-search-tree/solution/leetcodeti-jie-98-yan-zheng-er-cha-sou-suo-shu-zho) +10. [LeetCode题解:98. 验证二叉搜索树,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/validate-binary-search-tree/solution/leetcodeti-jie-98-yan-zheng-er-cha-sou-suo-shu-di-) +11. [LeetCode题解:104. 二叉树的最大深度,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/solution/leetcodeti-jie-104-er-cha-shu-de-zui-da-c1do9) +12. [LeetCode题解:104. 二叉树的最大深度,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/solution/leetcodeti-jie-104-er-cha-shu-de-zui-da-shen-du-di) +13. [ LeetCode题解:111. 二叉树的最小深度,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/solution/leetcodeti-jie-111-er-cha-shu-de-zui-xia-9qxk) +14. [LeetCode题解:111. 二叉树的最小深度,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/solution/leetcodeti-jie-111-er-cha-shu-de-zui-xiao-shen-du-) +15. [LeetCode题解:50. Pow(x, n),迭代分治,JavaScript,详细注释](https://leetcode-cn.com/problems/powx-n/solution/leetcodeti-jie-50-powx-ndie-dai-fen-zhi-javascript) +16. [LeetCode题解:50. Pow(x, n),递归分治,JavaScript,详细注释](https://leetcode-cn.com/problems/powx-n/solution/leetcodeti-jie-50-powx-nfen-zhi-javascriptxiang-xi) +17. [LeetCode题解:50. Pow(x, n),暴力法,JavaScript,详细注释](https://leetcode-cn.com/problems/powx-n/solution/leetcodeti-jie-50-powx-nbao-li-fa-javascriptxiang-) +18. [LeetCode题解:90. 子集 II,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets-ii/solution/leetcodeti-jie-90-zi-ji-iidie-dai-javascriptxiang-) +19. [LeetCode题解:90. 子集 II,迭代+位运算,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets-ii/solution/leetcodeti-jie-90-zi-ji-iidie-dai-wei-yun-suan-jav) +20. [LeetCode题解:90. 子集 II,递归+for循环+回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets-ii/solution/leetcodeti-jie-90-zi-ji-iidi-gui-forxun-huan-hui-s) +21. [LeetCode题解:90. 子集 II,回溯+哈希表去重,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets-ii/solution/leetcodeti-jie-90-zi-ji-iihui-su-ha-xi-biao-qu-zho) +22. [LeetCode题解:78. 子集,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets/solution/leetcodeti-jie-78-zi-ji-die-dai-javascriptxiang-2) +23. [LeetCode题解:78. 子集,递归+for循环+回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets/solution/leetcodeti-jie-78-zi-ji-di-gui-forxun-huan-hui-su-) +24. [LeetCode题解:78. 子集,迭代+位运算,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets/solution/leetcodeti-jie-78-zi-ji-die-dai-javascriptxiang-xi) +25. [LeetCode题解:78. 子集,递归回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/subsets/solution/leetcodeti-jie-78-zi-ji-di-gui-hui-su-javascriptxi) +26. [LeetCode题解:169. 多数元素,分治,JavaScript,详细注释](https://leetcode-cn.com/problems/majority-element/solution/leetcodeti-jie-169-duo-shu-yuan-su-fen-zhi-javascr) +27. [LeetCode题解:169. 多数元素,排序,JavaScript,详细注释](https://leetcode-cn.com/problems/majority-element/solution/leetcodeti-jie-169-duo-shu-yuan-su-pai-xu-javascri) +28. [LeetCode题解:169. 多数元素,哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/majority-element/solution/leetcodeti-jie-169-duo-shu-yuan-su-ha-xi-biao-java) +29. [LeetCode题解:17. 电话号码的字母组合,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/leetcodeti-jie-17-dian-hua-hao-ma-de-zi-fjmys) +30. [LeetCode题解:17. 电话号码的字母组合,队列,JavaScript,详细注释](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/leetcodeti-jie-17-dian-hua-hao-ma-de-zi-mu-zu-he-d) +31. [LeetCode题解:17. 电话号码的字母组合,回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/leetcodeti-jie-17-dian-hua-hao-ma-de-zi-mu-zu-he-h) +32. [LeetCode题解:52. N皇后 II,回溯+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/n-queens-ii/solution/leetcodeti-jie-52-nhuang-hou-iihui-su-ha-xi-biao-j) +33. [LeetCode题解:51. N 皇后,回溯+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/n-queens/solution/leetcodeti-jie-51-n-huang-hou-hui-su-ha-xi-biao-ja) +34. [LeetCode题解:236. 二叉树的最近公共祖先,存储父节点,JavaScript,详细注释](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/solution/leetcodeti-jie-236-er-cha-shu-de-zui-jin-p8zb) +35. [LeetCode题解:236. 二叉树的最近公共祖先,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/solution/leetcodeti-jie-236-er-cha-shu-de-zui-jin-4llp) +36. [LeetCode题解:105. 从前序与中序遍历序列构造二叉树,Simple O(n) without map,JavaScript,详细注释](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/leetcodeti-jie-105-cong-qian-xu-yu-zhong-rozn) +37. [LeetCode题解:105. 从前序与中序遍历序列构造二叉树,递归+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/leetcodeti-jie-105-cong-qian-xu-yu-zhong-ns9q) +38. [LeetCode题解:105. 从前序与中序遍历序列构造二叉树,递归+使用索引,JavaScript,详细注释](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/leetcodeti-jie-105-cong-qian-xu-yu-zhong-jasc) +39. [LeetCode题解:105. 从前序与中序遍历序列构造二叉树,递归+数组切割,JavaScript,详细注释](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/leetcodeti-jie-105-cong-qian-xu-yu-zhong-r52p) +40. [LeetCode题解:17. 电话号码的字母组合,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/leetcodeti-jie-17-dian-hua-hao-ma-de-zi-fjmys) +41. [LeetCode题解:17. 电话号码的字母组合,队列,JavaScript,详细注释](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/leetcodeti-jie-17-dian-hua-hao-ma-de-zi-mu-zu-he-d) +42. [LeetCode题解:17. 电话号码的字母组合,回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/leetcodeti-jie-17-dian-hua-hao-ma-de-zi-mu-zu-he-h) +43. [LeetCode题解:77. 组合,递归回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/combinations/solution/leetcodeti-jie-77-zu-he-di-gui-hui-su-javascriptxi) +44. [LeetCode题解:77. 组合,回溯+for循环,JavaScript,详细注释](https://leetcode-cn.com/problems/combinations/solution/leetcodeti-jie-77-zu-he-hui-su-javascriptxiang-xi-) +45. [LeetCode题解:47. 全排列 II,回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/permutations-ii/solution/leetcodeti-jie-47-quan-pai-lie-iihui-su-javascript) +46. [LeetCode题解:46. 全排列,回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/permutations/solution/leetcodeti-jie-46-quan-pai-lie-hui-su-javascriptxi) +47. [LeetCode题解:47. 全排列 II,回溯,JavaScript,详细注释](https://leetcode-cn.com/problems/permutations-ii/solution/leetcodeti-jie-47-quan-pai-lie-iihui-su-javascript) + diff --git "a/Week_04/102. \344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214BFS.js" "b/Week_04/102. \344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214BFS.js" new file mode 100644 index 00000000..2102370f --- /dev/null +++ "b/Week_04/102. \344\272\214\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206\357\274\214BFS.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=102 lang=javascript + * + * [102] 二叉树的层序遍历 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @return {number[][]} + */ +var levelOrder = function (root) { + let queue = []; + let result = []; + root && queue.push(root); + + while (queue.length) { + let len = queue.length; + let curr = result.length; + result.push([]); + + while (--len >= 0) { + const node = queue.shift(); + + result[curr].push(node.val); + node.left && queue.push(node.left); + node.right && queue.push(node.right); + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_04/122. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 II.js" "b/Week_04/122. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 II.js" new file mode 100644 index 00000000..14bedb29 --- /dev/null +++ "b/Week_04/122. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 II.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=122 lang=javascript + * + * [122] 买卖股票的最佳时机 II + */ +// @lc code=start +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (prices) { + let result = 0; + + for (let i = 0; i < prices.length - 1; i++) { + if (prices[i + 1] > prices[i]) { + result += prices[i + 1] - prices[i]; + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214BFS+\347\224\237\346\210\220\346\211\200\346\234\211\345\217\257\350\203\275\346\226\260\345\215\225\350\257\215\345\206\215\345\214\271\351\205\215.js" "b/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214BFS+\347\224\237\346\210\220\346\211\200\346\234\211\345\217\257\350\203\275\346\226\260\345\215\225\350\257\215\345\206\215\345\214\271\351\205\215.js" new file mode 100644 index 00000000..83580c59 --- /dev/null +++ "b/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214BFS+\347\224\237\346\210\220\346\211\200\346\234\211\345\217\257\350\203\275\346\226\260\345\215\225\350\257\215\345\206\215\345\214\271\351\205\215.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=127 lang=javascript + * + * [127] 单词接龙 + */ + +// @lc code=start +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function (beginWord, endWord, wordList) { + let queue = [[beginWord, 1]]; + let set = new Set(wordList); + + while (queue.length) { + const [word, level] = queue.shift(); + + for (let i = 0; i < word.length; i++) { + for (let j = 97; j < 123; j++) { + const newWord = `${word.slice(0, i)}${String.fromCharCode( + j, + )}${word.slice(i + 1)}`; + + if (set.has(newWord)) { + if (endWord === newWord) { + return level + 1; + } + queue.push([newWord, level + 1]); + set.delete(newWord); + } + } + } + } + + return 0; +}; +// @lc code=end diff --git "a/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214BFS+\347\273\237\350\256\241\345\215\225\350\257\215\345\217\230\345\214\226\346\254\241\346\225\260.js" "b/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214BFS+\347\273\237\350\256\241\345\215\225\350\257\215\345\217\230\345\214\226\346\254\241\346\225\260.js" new file mode 100644 index 00000000..f2df7f15 --- /dev/null +++ "b/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214BFS+\347\273\237\350\256\241\345\215\225\350\257\215\345\217\230\345\214\226\346\254\241\346\225\260.js" @@ -0,0 +1,50 @@ +/* + * @lc app=leetcode.cn id=127 lang=javascript + * + * [127] 单词接龙 + */ + +// @lc code=start +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function (beginWord, endWord, wordList) { + let queue = [beginWord]; + let level = 1; + let set = new Set(wordList); + + while (queue.length) { + let len = queue.length; + + while (--len >= 0) { + const word = queue.shift(); + + for (const next of set) { + let diff = 0; + + for (let i = 0; i < next.length; i++) { + if (word[i] !== next[i]) { + diff++; + } + } + + if (diff === 1) { + if (next === endWord) { + return ++level; + } + + set.delete(next); + queue.push(next); + } + } + } + + level++; + } + + return 0; +}; +// @lc code=end diff --git "a/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214\345\217\214\345\220\221BFS.js" "b/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214\345\217\214\345\220\221BFS.js" new file mode 100644 index 00000000..7c61b422 --- /dev/null +++ "b/Week_04/127. \345\215\225\350\257\215\346\216\245\351\276\231\357\274\214\345\217\214\345\220\221BFS.js" @@ -0,0 +1,50 @@ +/* + * @lc app=leetcode.cn id=127 lang=javascript + * + * [127] 单词接龙 + */ + +// @lc code=start +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function (beginWord, endWord, wordList) { + let queue = [[beginWord, 1]]; + let map = new Map([[endWord, 1]]); + let set = new Set(wordList); + + if (!set.has(endWord)) { + return 0; + } + + while (queue.length && map.size) { + if (queue.length > map.size) { + [queue, map] = [Array.from(map), new Map(queue)]; + } + + const [word, level] = queue.shift(); + + for (let i = 0; i < word.length; i++) { + for (let j = 97; j < 123; j++) { + const newWord = `${word.slice(0, i)}${String.fromCodePoint( + j, + )}${word.slice(i + 1)}`; + + if (map.has(newWord)) { + return map.get(newWord) + level; + } + + if (set.has(newWord)) { + set.delete(newWord); + queue.push([newWord, level + 1]); + } + } + } + } + + return 0; +}; +// @lc code=end diff --git "a/Week_04/153. \345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" "b/Week_04/153. \345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" new file mode 100644 index 00000000..1f14de8a --- /dev/null +++ "b/Week_04/153. \345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=153 lang=javascript + * + * [153] 寻找旋转排序数组中的最小值 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var findMin = function (nums) { + let left = 0; // 二分查找左边界 + let right = nums.length - 1; // 二分查找右边界 + + // 由于该题没有目标值可供判断,因此循环需要在left等于right之前退出 + // 两个指针相遇时,表示找到了结果 + while (left < right) { + // 取两个指针的中点 + const mid = (left + right) >> 1; + + // nums[mid] < nums[right]时,表示旋转点在左半边 + if (nums[mid] < nums[right]) { + // 由于mid总是向下取整,因此在极限情况,区间只剩两个值时,mid=left,对应用例[3,1,2] + // 并且该题没有中点等于目标值的判断,因此中点必须始终包含在区间内 + right = mid; + } + // nums[mid] > nums[right]时,表示旋转点在右半边 + else if (nums[mid] > nums[right]) { + // 此时中点的值必然不是最小值,因此left可以等于mid+1 + left = mid + 1; + } + } + + // 退出循环时,左右指针必然相遇,并且指向最小值,将其返回即可 + return nums[left]; +}; +// @lc code=end diff --git "a/Week_04/200. \345\262\233\345\261\277\346\225\260\351\207\217\357\274\214DFS.js" "b/Week_04/200. \345\262\233\345\261\277\346\225\260\351\207\217\357\274\214DFS.js" new file mode 100644 index 00000000..0d545b86 --- /dev/null +++ "b/Week_04/200. \345\262\233\345\261\277\346\225\260\351\207\217\357\274\214DFS.js" @@ -0,0 +1,57 @@ +/* + * @lc app=leetcode.cn id=200 lang=javascript + * + * [200] 岛屿数量 + */ + +// @lc code=start +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + let count = 0; // 统计岛屿数量 + + // 遍历地图的行 + for (let i = 0; i < grid.length; i++) { + // 遍历地图的列 + for (let j = 0; j < grid[i].length; j++) { + // 当遇到1时,表示遇到了岛屿 + if (grid[i][j] === '1') { + // 将岛屿计数加1 + count++; + // 使用DFS将当前坐标相邻的1全部改为0,也就是将整个岛屿沉没 + // 岛屿所有的坐标都改为0后,之后的遍历不会再遇到这个岛屿的1,确保了不会对岛屿重复计数 + sinkIsland(i, j); + } + } + } + + // 使用DFS,将当前岛屿所有坐标值都改为0,让当前岛屿沉没 + function sinkIsland(i, j) { + if ( + // 当坐标超出地图时,停止沉岛 + i < 0 || + j < 0 || + i >= grid.length || + j >= grid[i].length || + // 当遇到水时,停止沉岛 + grid[i][j] === '0' + ) { + return; + } + + // 将当前坐标的值修改为0 + grid[i][j] = '0'; + + // 将坐标向上下左右4个方向移动,继续修改岛屿的值 + sinkIsland(i - 1, j); + sinkIsland(i, j - 1); + sinkIsland(i + 1, j); + sinkIsland(i, j + 1); + } + + // 返回统计的岛屿数量 + return count; +}; +// @lc code=end diff --git "a/Week_04/200. \345\262\233\345\261\277\346\225\260\351\207\217\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_04/200. \345\262\233\345\261\277\346\225\260\351\207\217\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..1fe5fe6e --- /dev/null +++ "b/Week_04/200. \345\262\233\345\261\277\346\225\260\351\207\217\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,38 @@ +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + let count = 0; + let dx = [1, 0, -1, 0]; + let dy = [0, 1, 0, -1]; + + for (let i = 0; i < grid.length; i++) { + for (let j = 0; j < grid[i].length; j++) { + if (grid[i][j] === '1') { + count++; + slink(i, j); + } + } + } + + function slink(i, j) { + if ( + i < 0 || + j < 0 || + i >= grid.length || + j >= grid[i].length || + grid[i][j] === '0' + ) { + return; + } + + grid[i][j] = '0'; + + for (let k = 0; k < 4; k++) { + slink(i + dx[k], j + dy[k]); + } + } + + return count; +}; diff --git "a/Week_04/33. \346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" "b/Week_04/33. \346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" new file mode 100644 index 00000000..1d417356 --- /dev/null +++ "b/Week_04/33. \346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" @@ -0,0 +1,54 @@ +/* + * @lc app=leetcode.cn id=33 lang=javascript + * + * [33] 搜索旋转排序数组 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var search = function (nums, target) { + let left = 0; // 查找的左边界 + let right = nums.length - 1; // 查找的右边界 + + // 进行二分查找,当左右指针相遇时退出循环 + while (left <= right) { + // 取中间值,答案必然中间值的左右其中一方 + const mid = (left + right) >> 1; + + // 当中值与目标相等时,表示查找成功,返回索引 + if (nums[mid] === target) { + return mid; + } + + // 判断target是否在前半部分 + if ( + // 如果数组在前半部分被旋转 + // nums[0]大于nums[mid],表示旋转点在前半部分 + (nums[mid] < nums[0] && + // target在0到旋转点之间 + (nums[0] <= target || + // target在旋转点到中点之间 + target <= nums[mid])) || + // 如果数组在后半部分被旋转 + // 数组在前半部分是单调递增 + (nums[0] < nums[mid] && + // target在nums[0]和nums[mid]之间 + nums[0] <= target && + target < nums[mid]) + ) { + // 向前半部分查找target + right = mid - 1; + } else { + // 向后半部分查找target + left = mid + 1; + } + } + + // 如果正常退出循环,表示没有找到target,返回-1 + return -1; +}; +// @lc code=end diff --git "a/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214BFS+\347\224\237\346\210\220\346\211\200\346\234\211\345\217\257\350\203\275\346\226\260\345\237\272\345\233\240\345\206\215\345\214\271\351\205\215.js" "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214BFS+\347\224\237\346\210\220\346\211\200\346\234\211\345\217\257\350\203\275\346\226\260\345\237\272\345\233\240\345\206\215\345\214\271\351\205\215.js" new file mode 100644 index 00000000..8b9e7cd1 --- /dev/null +++ "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214BFS+\347\224\237\346\210\220\346\211\200\346\234\211\345\217\257\350\203\275\346\226\260\345\237\272\345\233\240\345\206\215\345\214\271\351\205\215.js" @@ -0,0 +1,68 @@ +/* + * @lc app=leetcode.cn id=433 lang=javascript + * + * [433] 最小基因变化 + */ + +// @lc code=start +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function (start, end, bank) { + let level = 0; // 统计BFS遍历的深度 + let queue = [start]; // 在队列中存储起始基因序列,用于开始循环 + let bankSet = new Set(bank); // 存储未被访问过的基因序列 + let charBank = ['A', 'T', 'C', 'G']; // 每个基因可变化的字母 + + // 不断遍历队列,直到队列为空时,完成BFS + while (queue.length) { + // 缓存当前队列中的元素数量,即为当前层的元素数量 + let queueLength = queue.length; + + // 进行queueLength次遍历 + while (--queueLength >= 0) { + // 将队列中的当前基因出队 + const currGene = queue.pop(); + + // 遍历当前基因的字母 + for (let i = 0; i < currGene.length; i++) { + // 从当前可变化字母中寻找一个可用的字母 + for (let j = 0; j < charBank.length; j++) { + // 避免生成与当前基因重复的序列 + if (charBank[j] === currGene[i]) { + continue; + } + + // 生成一个新基因序列 + const newGene = `${currGene.slice(0, i)}${ + charBank[j] + }${currGene.slice(i + 1)}`; + + // 判断新基因是否使用 + if (bankSet.has(newGene)) { + // 如果第一次发现,新基因与目标相同,表示查找到了最短变化路径 + if (newGene === end) { + // 由于当前变化没有被计数,返回结果时需要加1 + return ++level; + } + + // 将当前基因从Set中删除,表示其被访问过 + bankSet.delete(newGene); + // 将当前基因存入数组,用于下一次变化 + queue.push(newGene); + } + } + } + } + + // 每完成一层遍历之后,变化数量就加1 + level++; + } + + // 如果退出循环,表示未找到变化路径,直接返回-1 + return -1; +}; +// @lc code=end diff --git "a/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214BFS+\347\273\237\350\256\241\345\237\272\345\233\240\345\217\230\345\214\226\346\254\241\346\225\260.js" "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214BFS+\347\273\237\350\256\241\345\237\272\345\233\240\345\217\230\345\214\226\346\254\241\346\225\260.js" new file mode 100644 index 00000000..9af6a5aa --- /dev/null +++ "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214BFS+\347\273\237\350\256\241\345\237\272\345\233\240\345\217\230\345\214\226\346\254\241\346\225\260.js" @@ -0,0 +1,64 @@ +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function (start, end, bank) { + let count = 0; // 统计BFS遍历的深度 + let queue = [start]; // 在队列中存储起始基因序列,用于开始循环 + let visited = new Set(); // 使用Set标识访问过的bank中基因 + + // 不断遍历队列,直到队列为空时,完成BFS + while (queue.length) { + // 缓存当前队列中的元素数量,即为当前层的元素数量 + let queueLength = queue.length; + + // 进行queueLength次遍历 + while (--queueLength >= 0) { + // 将队列元素出队 + const str = queue.shift(); + + // 遍历bank,查找只变化一次的基因序列 + for (let i = 0; i < bank.length; i++) { + // 如果当前序列已被使用过,则跳过 + if (visited.has(bank[i])) { + continue; + } + + let diff = 0; // 统计基因变化的数量 + + // 对比str和bank[i]的每个字符 + for (let j = 0; j < bank[i].length; j++) { + // 当str与bank[i]的字符不一致时,记录基因变化次数 + if (str[j] !== bank[i][j]) { + diff++; + // 如果差异大于1,表示变化了2次,bank[i]不是可能的变化,退出循环 + if (diff > 1) { + break; + } + } + } + + // 由于题目要求基因每次只能变化一个字符,因此只有diff为1才可进入下一层递归 + if (diff === 1) { + // 如果bank[i]等于目标序列,那么此时一定是第一次到达目标,且此时的变化数量一定是最小值 + // 如果bank中存在从起始序列到目标序列的变化方案,一定会从此处退出循环,并返回结果 + if (bank[i] === end) { + return count + 1; + } + // 标识bank[i]已被使用过 + visited.add(bank[i]); + // bank[i]即为新的基因序列,将其入队,作为下一层基因序列使用 + queue.push(bank[i]); + } + } + } + + // 每完成一层遍历之后,变化数量就加1 + count++; + } + + // 如果正常退出循环,说明bank中不存在从其实序列到目标序列的变化方案 + return -1; +}; diff --git "a/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214DFS.js" "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214DFS.js" new file mode 100644 index 00000000..9fe58abb --- /dev/null +++ "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214DFS.js" @@ -0,0 +1,48 @@ +/* + * @lc app=leetcode.cn id=433 lang=javascript + * + * [433] 最小基因变化 + */ + +// @lc code=start +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function (start, end, bank) { + let set = new Set(); + let min = Infinity; + + function dfs(str, change) { + if (str === end) { + min = Math.min(min, change); + return; + } + + for (const next of bank) { + if (set.has(next)) { + continue; + } + + let count = 0; + + for (let i = 0; i < next.length; i++) { + if (str[i] !== next[i]) { + count++; + } + } + + if (count === 1) { + set.add(next); + dfs(next, change + 1); + set.delete(next); + } + } + } + dfs(start, 0); + + return min === Infinity ? -1 : min; +}; +// @lc code=end diff --git "a/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..45dede3b --- /dev/null +++ "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,48 @@ +/* + * @lc app=leetcode.cn id=433 lang=javascript + * + * [433] 最小基因变化 + */ + +// @lc code=start +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function (start, end, bank) { + let min = Infinity; + let set = new Set(); + + function dfs(str, level) { + if (str === end) { + min = Math.min(min, level); + return; + } + + for (const next of bank) { + if (set.has(next)) { + continue; + } + + let diff = 0; + + for (let i = 0; i < next.length; i++) { + if (next[i] !== str[i]) { + diff++; + } + } + + if (diff === 1) { + set.add(next); + dfs(next, level + 1); + set.delete(next); + } + } + } + dfs(start, 0); + + return min === Infinity ? -1 : min; +}; +// @lc code=end diff --git "a/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214\345\217\214\345\220\221BFS.js" "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214\345\217\214\345\220\221BFS.js" new file mode 100644 index 00000000..5c52bd05 --- /dev/null +++ "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214\345\217\214\345\220\221BFS.js" @@ -0,0 +1,42 @@ +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function (start, end, bank) { + let level = 0; + let set = new Set(bank); + let queue = [start]; + + while (queue.length) { + let queueLen = queue.length; + + while (--queueLen >= 0) { + const gene = queue.shift(); + + for (const newGene of set) { + let diff = 0; + + for (let i = 0; i < gene.length; i++) { + if (gene[i] !== newGene[i]) { + diff++; + } + } + + if (diff === 1) { + if (newGene === end) { + return level + 1; + } + + set.delete(newGene); + queue.push(newGene); + } + } + } + + level++; + } + + return -1; +}; diff --git "a/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214\345\217\214\345\220\221BFS\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214\345\217\214\345\220\221BFS\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..a5ffa0b5 --- /dev/null +++ "b/Week_04/433. \346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226\357\274\214\345\217\214\345\220\221BFS\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,66 @@ +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function (start, end, bank) { + // 使用Set判断bank中的单词是否被使用过 + let bankSet = new Set(bank); + + // 如果end不存在于bank,一定无法变化,直接返回-1 + if (!bankSet.has(end)) { + return -1; + } + + // 每次都遍历队列,初始时存入start,对应层级为0 + let queue = [[start, 0]]; + // 虽然两端的遍历都需要使用队列,实际操作时可以用Map来加速判断是否相遇的过程 + // 每次遍历时,只需要取queue和map中长度较小的一个,将其转换为一个队列进行遍历即可 + let map = new Map([[end, 0]]); + // 每个基因可变化的字母 + const geneBank = ['A', 'T', 'C', 'G']; + + // 如果queue和map中任意一个被清空,表示双向BFS不会相遇,即为无法进行转换 + while (queue.length && map.size) { + // 选取queue和map中较短的一个进行遍历,优化搜索速度 + if (queue.length > map.size) { + // 将queue和map对调,保证每次遍历的都是queue + [queue, map] = [Array.from(map), new Map(queue)]; + } + + // 将queue中元素出队,搜索下一个转换的基因 + const [gene, level] = queue.shift(); + + // 遍历当前基因的每个字符 + for (let i = 0; i < gene.length; i++) { + // 选择一个可变化的字母,生成 + for (let j = 0; j < geneBank.length; j++) { + // 如果新字母与当前的相同,一定不是下一个可变的基因,直接跳过 + if (geneBank[j] === gene[i]) { + continue; + } + + // 将每一位字母都用新字母替换,生成新基因 + const newGene = `${gene.slice(0, i)}${geneBank[j]}${gene.slice(i + 1)}`; + + // 如果新基因在Map中存在,表示双向BFS相遇,即为找到了最短变化路径 + if (map.has(newGene)) { + // 将两端的变化次数想加,加上当次移动的变化,即为总的变化次数 + return map.get(newGene) + level + 1; + } + + // 如果新基因在Set中,表示它是下一个可变化的基因 + if (bankSet.has(newGene)) { + // 将其从Set中删除,避免重复选择 + bankSet.delete(newGene); + // 将其插入到队列中,进行下一次变化 + queue.push([newGene, level + 1]); + } + } + } + } + + // 如果推出循环,表示没有找到可变化的路径,返回-1 + return -1; +}; diff --git "a/Week_04/45. \350\267\263\350\267\203\346\270\270\346\210\217 II\357\274\214\350\264\252\345\277\203\344\273\216\345\220\216\345\220\221\345\211\215.js" "b/Week_04/45. \350\267\263\350\267\203\346\270\270\346\210\217 II\357\274\214\350\264\252\345\277\203\344\273\216\345\220\216\345\220\221\345\211\215.js" new file mode 100644 index 00000000..2a477892 --- /dev/null +++ "b/Week_04/45. \350\267\263\350\267\203\346\270\270\346\210\217 II\357\274\214\350\264\252\345\277\203\344\273\216\345\220\216\345\220\221\345\211\215.js" @@ -0,0 +1,28 @@ +/* + * @lc app=leetcode.cn id=45 lang=javascript + * + * [45] 跳跃游戏 II + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var jump = function (nums) { + let target = nums.length - 1; + let step = 0; + + while (target > 0) { + for (let i = 0; i < target; i++) { + if (i + nums[i] >= target) { + target = i; + step++; + break; + } + } + } + + return step; +}; +// @lc code=end diff --git "a/Week_04/45. \350\267\263\350\267\203\346\270\270\346\210\217 II\357\274\214\350\264\252\345\277\203\346\255\243\345\220\221\346\237\245\346\211\276.js" "b/Week_04/45. \350\267\263\350\267\203\346\270\270\346\210\217 II\357\274\214\350\264\252\345\277\203\346\255\243\345\220\221\346\237\245\346\211\276.js" new file mode 100644 index 00000000..f8ad703c --- /dev/null +++ "b/Week_04/45. \350\267\263\350\267\203\346\270\270\346\210\217 II\357\274\214\350\264\252\345\277\203\346\255\243\345\220\221\346\237\245\346\211\276.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=45 lang=javascript + * + * [45] 跳跃游戏 II + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var jump = function (nums) { + let step = 0; + let target = 0; + let max = 0; + + for (let i = 0; i < nums.length - 1; i++) { + max = Math.max(max, i + nums[i]); + + if (target === i) { + target = max; + step++; + + if (target >= nums.length - 1) { + break; + } + } + } + + return step; +}; +// @lc code=end diff --git "a/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203for\345\276\252\347\216\257.js" "b/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203for\345\276\252\347\216\257.js" new file mode 100644 index 00000000..99b5b961 --- /dev/null +++ "b/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203for\345\276\252\347\216\257.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=455 lang=javascript + * + * [455] 分发饼干 + */ + +// @lc code=start +/** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ +var findContentChildren = function (g, s) { + s.sort((a, b) => a - b); + g.sort((a, b) => a - b); + let i = 0; + + for (let j = 0; j < s.length; j++) { + if (s[j] >= g[i]) { + i++; + } + } + + return i; +}; +// @lc code=end diff --git "a/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203for\345\276\252\347\216\257\357\274\214\344\274\230\345\214\226.js" "b/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203for\345\276\252\347\216\257\357\274\214\344\274\230\345\214\226.js" new file mode 100644 index 00000000..b286f3c8 --- /dev/null +++ "b/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203for\345\276\252\347\216\257\357\274\214\344\274\230\345\214\226.js" @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=455 lang=javascript + * + * [455] 分发饼干 + */ + +// @lc code=start +/** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ +var findContentChildren = function (g, s) { + s.sort((a, b) => a - b); + g.sort((a, b) => a - b); + + for (var i = 0, j = 0; j < s.length; j++) { + if (s[j] >= g[i]) { + i++; + } + } + + return i; +}; +// @lc code=end diff --git "a/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203while\345\276\252\347\216\257.js" "b/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203while\345\276\252\347\216\257.js" new file mode 100644 index 00000000..9b596bf5 --- /dev/null +++ "b/Week_04/455. \345\210\206\345\217\221\351\245\274\345\271\262\357\274\214\350\264\252\345\277\203while\345\276\252\347\216\257.js" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=455 lang=javascript + * + * [455] 分发饼干 + */ + +// @lc code=start +/** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ +var findContentChildren = function (g, s) { + s.sort((a, b) => a - b); + g.sort((a, b) => a - b); + let i = 0; + let j = 0; + + while (i < g.length && j < s.length) { + if (s[j++] >= g[i]) { + i++; + } + } + + return i; +}; +// @lc code=end diff --git "a/Week_04/515. \345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274\357\274\214BFS.js" "b/Week_04/515. \345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274\357\274\214BFS.js" new file mode 100644 index 00000000..91d7380c --- /dev/null +++ "b/Week_04/515. \345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274\357\274\214BFS.js" @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode.cn id=515 lang=javascript + * + * [515] 在每个树行中找最大值 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var largestValues = function (root) { + let result = []; + let queue = []; + root && queue.push(root); + + while (queue.length) { + let len = queue.length; + let index = result.length; + result.push(-Infinity); + + while (--len >= 0) { + const node = queue.shift(); + + result[index] = Math.max(node.val, result[index]); + node.left && queue.push(node.left); + node.right && queue.push(node.right); + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_04/515. \345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274\357\274\214DFS.js" "b/Week_04/515. \345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274\357\274\214DFS.js" new file mode 100644 index 00000000..ee30621a --- /dev/null +++ "b/Week_04/515. \345\234\250\346\257\217\344\270\252\346\240\221\350\241\214\344\270\255\346\211\276\346\234\200\345\244\247\345\200\274\357\274\214DFS.js" @@ -0,0 +1,40 @@ +/* + * @lc app=leetcode.cn id=515 lang=javascript + * + * [515] 在每个树行中找最大值 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ +var largestValues = function (root) { + let result = []; + + function dfs(node, level) { + if (!node) { + return; + } + + typeof result[level] === 'number' + ? (result[level] = Math.max(node.val, result[level])) + : (result[level] = node.val); + + dfs(node.left, level + 1); + dfs(node.right, level + 1); + } + + dfs(root, 0); + + return result; +}; +// @lc code=end diff --git "a/Week_04/529. \346\211\253\351\233\267\346\270\270\346\210\217\357\274\214BFS.js" "b/Week_04/529. \346\211\253\351\233\267\346\270\270\346\210\217\357\274\214BFS.js" new file mode 100644 index 00000000..83eba373 --- /dev/null +++ "b/Week_04/529. \346\211\253\351\233\267\346\270\270\346\210\217\357\274\214BFS.js" @@ -0,0 +1,90 @@ +/* + * @lc app=leetcode.cn id=529 lang=javascript + * + * [529] 扫雷游戏 + */ + +// @lc code=start +/** + * @param {character[][]} board + * @param {number[]} click + * @return {character[][]} + */ +var updateBoard = function (board, click) { + // 规则1 + // 如果第一次点击就踩到雷,游戏结束 + if (board[click[0]][click[1]] === 'M') { + // 将当前雷的位置改为X + board[click[0]][click[1]] = 'X'; + // 返回新矩阵 + return board; + } + + // 获取矩阵行数 + const xLength = board.length; + // 获取矩阵列数 + const yLength = board[0].length; + // 当前坐标周围8个点的行方向坐标,从正上方开始,按顺时针方向排列 + const xDirection = [0, 1, 1, 1, 0, -1, -1, -1]; + // 当前坐标周围8个点的列方向坐标,从正上方开始,按顺时针方向排列 + const yDirection = [1, 1, 0, -1, -1, -1, 0, 1]; + let queue = [click]; + + // 判断当前坐标是否在矩阵内 + function isInMatrix(x, y) { + return x >= 0 && y >= 0 && x < xLength && y < yLength; + } + + // 不断将坐标点从队列取出进行处理,队列被清空后,表示所有点都处理完毕 + while (queue.length) { + // 出队一个点,判断要如何显示 + const [x, y] = queue.shift(); + + // 如果当前坐标已被处理过,则跳过 + if (board[x][y] !== 'E') { + continue; + } + + // 统计当前坐标周围的地雷数量 + let count = 0; + // 统计当前坐标周围8个方向的坐标点 + let aroundPoints = []; + + for (let i = 0; i < 8; i++) { + // 生成当前坐标周围的8个点坐标 + let aroundX = x + xDirection[i]; + let aroundY = y + yDirection[i]; + + // 判断当前坐标是否在矩阵内部 + if (isInMatrix(aroundX, aroundY)) { + // 只有周围点的坐标不为E,才需要入队,供下一次处理 + if (board[aroundX][aroundY] === 'E') { + aroundPoints.push([aroundX, aroundY]); + } + + // 如果当前坐标周围有地雷,则统计数量 + if (board[aroundX][aroundY] === 'M') { + count++; + } + } + } + // 规则2 + // 如果当前坐标周围没有地雷,则修改为B + if (count === 0) { + board[x][y] = 'B'; + + // 继续查找周围的坐标是否需要标记地雷数量 + for (let i = 0; i < aroundPoints.length; i++) { + queue.push(aroundPoints[i]); + } + } else { + // 规则3 + // 如果当前坐标周围的地雷数量大于0,则将其修改为地雷数量 + board[x][y] = count.toString(); + } + } + + // 将新矩阵返回 + return board; +}; +// @lc code=end diff --git "a/Week_04/529. \346\211\253\351\233\267\346\270\270\346\210\217\357\274\214DFS.js" "b/Week_04/529. \346\211\253\351\233\267\346\270\270\346\210\217\357\274\214DFS.js" new file mode 100644 index 00000000..ce4bb8fc --- /dev/null +++ "b/Week_04/529. \346\211\253\351\233\267\346\270\270\346\210\217\357\274\214DFS.js" @@ -0,0 +1,93 @@ +/* + * @lc app=leetcode.cn id=529 lang=javascript + * + * [529] 扫雷游戏 + */ + +// @lc code=start +/** + * @param {character[][]} board + * @param {number[]} click + * @return {character[][]} + */ +var updateBoard = function (board, click) { + const [clickX, clickY] = click; // 先解析出当前点就的坐标 + + // 规则1 + // 如果第一次点击就踩到雷,游戏结束 + if (board[clickX][clickY] === 'M') { + // 将当前雷的位置改为X + board[clickX][clickY] = 'X'; + // 返回新矩阵 + return board; + } + + // 获取矩阵行数 + const xLength = board.length; + // 获取矩阵列数 + const yLength = board[0].length; + // 当前坐标周围8个点的行方向坐标,从正上方开始,按顺时针方向排列 + const xDirection = [0, 1, 1, 1, 0, -1, -1, -1]; + // 当前坐标周围8个点的列方向坐标,从正上方开始,按顺时针方向排列 + const yDirection = [1, 1, 0, -1, -1, -1, 0, 1]; + + // 判断当前坐标是否超出矩阵范围 + function isOut(x, y) { + return x < 0 || y < 0 || x >= xLength || y >= yLength; + } + + function dfs(x, y) { + // 如果当前坐标超出矩阵,无法进行处理,退出递归 + // 如果当前坐标不是E,表示已经处理过,无需重复处理,退出递归 + if (isOut(x, y) || board[x][y] !== 'E') { + return; + } + + // 统计当前坐标周围的地 雷数量 + let count = 0; + // 统计当前坐标周围8个方向的坐标点 + let aroundPoints = []; + + for (let i = 0; i < 8; i++) { + // 生成当前坐标周围的8个点坐标 + let aroundX = x + xDirection[i]; + let aroundY = y + yDirection[i]; + + // 只有在矩阵内部的坐标才需要判断 + if (!isOut(aroundX, aroundY)) { + // 在矩阵内部的坐标,可能需要进一步递归统计周围地 雷数量,先将其缓存 + // 只有周围点的坐标不为E,才需要入队,供下一次处理 + if (board[aroundX][aroundY] === 'E') { + aroundPoints.push([aroundX, aroundY]); + } + + // 如果当前坐标周围有地 雷,则统计数量 + if (board[aroundX][aroundY] === 'M') { + count++; + } + } + } + + // 规则2 + // 如果当前坐标周围没有地 雷,则修改为B + if (count === 0) { + board[x][y] = 'B'; + + // 继续查找周围的坐标是否需要标记地 雷数量 + for (let i = 0; i < aroundPoints.length; i++) { + dfs(...aroundPoints[i]); + } + } else { + // 规则3 + // 如果当前坐标周围的地 雷数量大于0,则将其修改为地 雷数量 + board[x][y] = count.toString(); + } + } + + // 从当前点击位置开始,向周围扩散判断每个位置的显示方式 + dfs(clickX, clickY); + + // 将新矩阵返回 + return board; +}; +// @lc code=end diff --git "a/Week_04/55. \350\267\263\350\267\203\346\270\270\346\210\217\357\274\214\350\264\252\345\277\203.js" "b/Week_04/55. \350\267\263\350\267\203\346\270\270\346\210\217\357\274\214\350\264\252\345\277\203.js" new file mode 100644 index 00000000..2f5cb313 --- /dev/null +++ "b/Week_04/55. \350\267\263\350\267\203\346\270\270\346\210\217\357\274\214\350\264\252\345\277\203.js" @@ -0,0 +1,19 @@ +/** + * @param {number[]} nums + * @return {boolean} + */ +var canJump = function (nums) { + let max = 0; + + for (let i = 0; i < nums.length; i++) { + if (i > max) { + return false; + } + + max = Math.max(max, i + nums[i]); + + if (max >= nums.length - 1) { + return true; + } + } +}; diff --git "a/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" "b/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" new file mode 100644 index 00000000..3a6311e4 --- /dev/null +++ "b/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" @@ -0,0 +1,41 @@ +/* + * @lc app=leetcode.cn id=69 lang=javascript + * + * [69] x 的平方根 + */ + +// @lc code=start +/** + * @param {number} x + * @return {number} + */ +var mySqrt = function (x) { + let left = 0; // 查找的左边界 + let right = x; // 查找的右边界 + + // 进行二分查找,当左右指针相遇时退出循环 + while (left <= right) { + // 取中间值,答案必然中间值的左右其中一方 + // 使用这段代码也是同样效果 + // const mid = Math.floor((right + left) / 2); + const mid = (left + right) >> 1; + + // 如果mid的平方大于x,表示最终结果在右半部分 + if (mid ** 2 > x) { + // 重新确定右边界,下次循环在右半部分查找结果 + right = mid - 1; + } else { + // 如果mid的平方小于等于x,表示最终结果在左半部分 + // 重新确定左边界,下次循环在左半部分查找结果 + left = mid + 1; + } + } + + // 由于最后一次循环前,left和right相等,即mid=left=right + // 此时mid的频繁只有两种可能情况,即为mid的平方大于或等于x + // mid大于x时,最终结果在mid到mid-1之间,经过四舍五入后为mid,也就是退出循环后的right的值 + // mid等于x时,退出循环后,right即为mid的值 + // 两种情况退出后,right都是最终结果,因此只要返回right即可 + return right; +}; +// @lc code=end diff --git "a/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\347\211\233\351\241\277\350\277\255\344\273\243\346\263\225+\350\277\255\344\273\243.js" "b/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\347\211\233\351\241\277\350\277\255\344\273\243\346\263\225+\350\277\255\344\273\243.js" new file mode 100644 index 00000000..d5d0b3d2 --- /dev/null +++ "b/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\347\211\233\351\241\277\350\277\255\344\273\243\346\263\225+\350\277\255\344\273\243.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=69 lang=javascript + * + * [69] x 的平方根 + */ + +// @lc code=start +/** + * @param {number} x + * @return {number} + */ +var mySqrt = function (x) { + let x0 = x; // 缓存每次迭代的结果,从x开始迭代 + + // 用Math.floor将x0*x0向下取整 + // 避免例如TestCase: 5,会出现x0 * x0=5.000000000000001,造成死循环 + while (Math.floor(x0 * x0) > x) { + // 套用分析得到的迭代公式,不断迭代 + // 当迭代结束时,得到的就是最接近结果的值 + x0 = (x0 + x / x0) / 2; + } + + // 将结果的整数部分返回 + return Math.floor(x0); +}; +// @lc code=end diff --git "a/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\347\211\233\351\241\277\350\277\255\344\273\243\346\263\225+\351\200\222\345\275\222.js" "b/Week_04/69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\214\347\211\233\351\241\277\350\277\255\344\273\243\346\263\225+\351\200\222\345\275\222.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_04/74. \346\220\234\347\264\242\344\272\214\347\273\264\347\237\251\351\230\265\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" "b/Week_04/74. \346\220\234\347\264\242\344\272\214\347\273\264\347\237\251\351\230\265\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" new file mode 100644 index 00000000..35cab6e0 --- /dev/null +++ "b/Week_04/74. \346\220\234\347\264\242\344\272\214\347\273\264\347\237\251\351\230\265\357\274\214\344\272\214\345\210\206\346\237\245\346\211\276.js" @@ -0,0 +1,35 @@ +/** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ +var searchMatrix = function (matrix, target) { + const colLength = matrix[0].length; // 缓存一行的元素数量,用于计算真实索引 + let left = 0; // 二分查找左边界 + let right = matrix.length * colLength - 1; // 二分查找右边界 + + // 当左右边界相遇时,查找结束 + while (left <= right) { + // 计算当前查找区域的中点 + const mid = (left + right) >> 1; + // 计算当前中点在矩阵中的哪一行 + const row = Math.floor(mid / colLength); + // 计算当前中点在矩阵中的哪一列 + const col = mid % colLength; + + // 如果当前中点值等于目标,查找成功 + if (matrix[row][col] === target) { + return true; + } + + // 如果中值大于目标,表示目标在左半边,将右边界移动到左半边继续查找 + if (matrix[row][col] > target) { + right = mid - 1; + } else { + // 如果中值小于目标,表示目标在右半边,将左边界移动到右半边继续查找 + left = mid + 1; + } + } + + return false; +}; diff --git "a/Week_04/860. \346\237\240\346\252\254\346\260\264\346\211\276\351\233\266\357\274\214\346\250\241\346\213\237\346\203\205\345\242\203.js" "b/Week_04/860. \346\237\240\346\252\254\346\260\264\346\211\276\351\233\266\357\274\214\346\250\241\346\213\237\346\203\205\345\242\203.js" new file mode 100644 index 00000000..e72f3be8 --- /dev/null +++ "b/Week_04/860. \346\237\240\346\252\254\346\260\264\346\211\276\351\233\266\357\274\214\346\250\241\346\213\237\346\203\205\345\242\203.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=860 lang=javascript + * + * [860] 柠檬水找零 + */ + +// @lc code=start +/** + * @param {number[]} bills + * @return {boolean} + */ +var lemonadeChange = function (bills) { + let five = 0; + let ten = 0; + + for (const bill of bills) { + if (bill === 5) { + five++; + } else if (bill === 10) { + ten++; + five--; + } else if (bill === 20) { + if (ten > 0) { + ten--; + five--; + } else { + five -= 3; + } + } + + if (five < 0 || ten < 0) { + return false; + } + } + + return true; +}; +// @lc code=end diff --git "a/Week_04/874. \346\250\241\346\213\237\350\241\214\350\265\260\346\234\272\345\231\250\344\272\272\357\274\214\346\250\241\346\213\237\346\203\205\345\242\203.js" "b/Week_04/874. \346\250\241\346\213\237\350\241\214\350\265\260\346\234\272\345\231\250\344\272\272\357\274\214\346\250\241\346\213\237\346\203\205\345\242\203.js" new file mode 100644 index 00000000..516a5b4b --- /dev/null +++ "b/Week_04/874. \346\250\241\346\213\237\350\241\214\350\265\260\346\234\272\345\231\250\344\272\272\357\274\214\346\250\241\346\213\237\346\203\205\345\242\203.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=874 lang=javascript + * + * [874] 模拟行走机器人 + */ + +// @lc code=start +/** + * @param {number[]} commands + * @param {number[][]} obstacles + * @return {number} + */ +var robotSim = function (commands, obstacles) { + let dx = [0, 1, 0, -1]; + let dy = [1, 0, -1, 0]; + let d = 0; + + let x = 0; + let y = 0; + let max = 0; + + let set = new Set(obstacles.map((item) => item.toString())); + + for (const command of commands) { + if (command === -1) { + d = (d + 1) % 4; + } else if (command === -2) { + d = (d + 3) % 4; + } else { + for (let i = 0; i < command; i++) { + let newX = x + dx[d]; + let newY = y + dy[d]; + + if (set.has(`${newX},${newY}`)) { + break; + } + + x = newX; + y = newY; + max = Math.max(max, x ** 2 + y ** 2); + } + } + } + + return max; +}; +// @lc code=end diff --git a/Week_04/README.md b/Week_04/README.md index 50de3041..04881337 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,40 @@ -学习笔记 \ No newline at end of file +# 学习总结 + +希望能在直播的时候讲解一下[126. 单词接龙 II](https://leetcode-cn.com/problems/word-ladder-ii/)。 + +这周的题目大部分对我来说都是新题,刷题量有所下降,但是做新题还是比较令人开心的。 + +我为部分题目写了题解,如下: + +1. [LeetCode 题解:102. 二叉树的层序遍历,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/leetcodeti-jie-102-er-cha-shu-de-ceng-xu-bian-li-b) +2. [LeetCode 题解:102. 二叉树的层序遍历,DFS,JavaScript,详细注释](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/leetcodeti-jie-102-er-cha-shu-de-ceng-xu-bian-li-d) +3. [LeetCode 题解:433. 最小基因变化,双向 BFS(beats 99%),JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-pwwwg) +4. [LeetCode 题解:433. 最小基因变化,BFS+生成所有可能新基因再匹配,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-0hd8n) +5. [LeetCode 题解:433. 最小基因变化,BFS+统计基因变化次数,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-hua-bfsjav) +6. [LeetCode 题解:433. 最小基因变化,DFS,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-hua-dfsjav) +7. [LeetCode 题解:22. 括号生成,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-bfsjia-fa-ja) +8. [LeetCode 题解:22. 括号生成,递归生成同时过滤,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-di-gui-sheng) +9. [LeetCode 题解:22. 括号生成,递归先生成再过滤,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-xian-sheng-c) +10. [LeetCode 题解:515. 在每个树行中找最大值,DFS,JavaScript,详细注释](https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/solution/leetcodeti-jie-515-zai-mei-ge-shu-xing-zhong-zha-2) +11. [LeetCode 题解:515. 在每个树行中找最大值,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/solution/leetcodeti-jie-515-zai-mei-ge-shu-xing-zhong-zhao-) +12. [LeetCode 题解:69. x 的平方根,牛顿迭代法+递归,JavaScript,详细注释](https://leetcode-cn.com/problems/sqrtx/solution/leetcodeti-jie-69-x-de-ping-fang-gen-niu-9ale) +13. [LeetCode 题解:69. x 的平方根,牛顿迭代法+迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/sqrtx/solution/leetcodeti-jie-69-x-de-ping-fang-gen-niu-t5xj) +14. [LeetCode 题解:69. x 的平方根,二分查找,JavaScript,详细注释](https://leetcode-cn.com/problems/sqrtx/solution/leetcodeti-jie-69-x-de-ping-fang-gen-er-6hyhb) +15. [牛顿迭代法](https://leetcode-cn.com/problems/sqrtx/solution/niu-dun-die-dai-fa-by-loafer) +16. [LeetCode 题解:860. 柠檬水找零,模拟情境,JavaScript,详细注释](https://leetcode-cn.com/problems/lemonade-change/solution/leetcodeti-jie-860-ning-meng-shui-zhao-ling-mo-ni-) +17. [LeetCode 题解:122. 买卖股票的最佳时机 II,JavaScript,一遍循环,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/122-mai-mai-gu-piao-de-zui-jia-shi-ji-iijavascript) +18. [LeetCode 题解:455. 分发饼干,贪心 while 循环,JavaScript,详细注释](https://leetcode-cn.com/problems/assign-cookies/solution/leetcodeti-jie-455-fen-fa-bing-gan-tan-xin-whilexu) +19. [LeetCode 题解:455. 分发饼干,贪心 for 循环,JavaScript,详细注释](https://leetcode-cn.com/problems/assign-cookies/solution/leetcodeti-jie-455-fen-fa-bing-gan-tan-xin-javascr) +20. [LeetCode 题解:874. 模拟行走机器人,模拟情境,JavaScript,详细注释](https://leetcode-cn.com/problems/walking-robot-simulation/solution/leetcodeti-jie-874-mo-ni-xing-zou-ji-qi-ren-mo-ni-) +21. [LeetCode 题解:127. 单词接龙,双向 BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder/solution/leetcodeti-jie-127-dan-ci-jie-long-shuang-xiang-bf) +22. [LeetCode 题解:127. 单词接龙,BFS+生成所有可能新单词再匹配,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder/solution/leetcodeti-jie-127-dan-ci-jie-long-bfssheng-cheng-) +23. [LeetCode 题解:127. 单词接龙,BFS+统计单词变化次数,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder/solution/leetcodeti-jie-127-dan-ci-jie-long-bfsjavascriptxi) +24. [LeetCode 题解:200. 岛屿数量,DFS,JavaScript,详细注释](https://leetcode-cn.com/problems/number-of-islands/solution/leetcodeti-jie-200-dao-yu-shu-liang-dfsj-w48p) +25. [LeetCode 题解:529. 扫雷游戏,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/minesweeper/solution/leetcodeti-jie-529-sao-lei-you-xi-bfsjav-y6tk) +26. [LeetCode 题解:529. 扫雷游戏,DFS,JavaScript,详细注释](https://leetcode-cn.com/problems/minesweeper/solution/leetcodeti-jie-529-sao-lei-you-xi-dfsjav-1cwz) +27. [LeetCode 题解:45. 跳跃游戏 II,贪心从后向前,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/leetcodeti-jie-tan-xin-cong-hou-xiang-qian-javascr) +28. [LeetCode 题解:45. 跳跃游戏 II,贪心正向查找,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/45-tiao-yue-you-xi-iitan-xin-zheng-xiang-cha-zhao-) +29. [LeetCode 题解:55. 跳跃游戏,贪心,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game/solution/leetcodeti-jie-55-tiao-yue-you-xi-tan-xin-javascri) +30. [简洁+容易理解](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/jian-ji-rong-yi-li-jie-java-er-fen-fa-by-breezean) +31. [LeetCode 题解:45. 跳跃游戏 II,贪心从后向前,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/leetcodeti-jie-tan-xin-cong-hou-xiang-qian-javascr) +32. [LeetCode 题解:45. 跳跃游戏 II,贪心正向查找,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/45-tiao-yue-you-xi-iitan-xin-zheng-xiang-cha-zhao-) diff --git a/Week_05/README.md b/Week_05/README.md index 50de3041..73e95c47 100644 --- a/Week_05/README.md +++ b/Week_05/README.md @@ -1 +1,23 @@ -学习笔记 \ No newline at end of file +# 学习总结 + +这周前几天都在补前4周落下的题目,影响了本周的做题进度,下周争取补上。 +动态规划的题目,目前看来只要找到了方法,就不是特别难。 + +做题记录如下: +[https://shimo.im/sheets/hE9AXYhkH7EL3fOg/MODOC](https://shimo.im/sheets/hE9AXYhkH7EL3fOg/MODOC) + +我为部分题目写了题解,如下: + +1. [LeetCode题解:62. 不同路径,动态规划,JavaScript,详细注释](https://leetcode-cn.com/problems/unique-paths/solution/leetcodeti-jie-62-bu-tong-lu-jing-dong-t-5p7s) +2. [LeetCode题解:70. 爬楼梯,DP遍历,变量缓存结果,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-bian-liang-huan-cun-jie-guo) +3. [LeetCode题解:70. 爬楼梯,DP遍历数组,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-shu-zu-javascriptxiang-xi-z) +4. [LeetCode题解:70. 爬楼梯,递归+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-di-gui-ha-xi-biao-javascriptxiang-xi-) +5. [LeetCode题解:121. 买卖股票的最佳时机,暴力法,JavaScript,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/leetcodeti-jie-121-mai-mai-gu-piao-de-zui-jia-sh-2) +6. [LeetCode题解:121. 买卖股票的最佳时机,一次遍历,JavaScript,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/leetcodeti-jie-121-mai-mai-gu-piao-de-zui-jia-shi-) +7. [LeetCode题解:122. 买卖股票的最佳时机 II,JavaScript,一遍循环,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/122-mai-mai-gu-piao-de-zui-jia-shi-ji-iijavascript) +8. [LeetCode题解:122. 买卖股票的最佳时机 II,JavaScript,一遍循环,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/122-mai-mai-gu-piao-de-zui-jia-shi-ji-iijavascript) +9. [LeetCode题解:45. 跳跃游戏 II,贪心从后向前,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/leetcodeti-jie-tan-xin-cong-hou-xiang-qian-javascr) +10. [LeetCode题解:45. 跳跃游戏 II,贪心正向查找,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/45-tiao-yue-you-xi-iitan-xin-zheng-xiang-cha-zhao-) +11. [LeetCode题解:55. 跳跃游戏,贪心,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game/solution/leetcodeti-jie-55-tiao-yue-you-xi-tan-xin-javascri) +12. [LeetCode题解:45. 跳跃游戏 II,贪心从后向前,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/leetcodeti-jie-tan-xin-cong-hou-xiang-qian-javascr) +13. [LeetCode题解:45. 跳跃游戏 II,贪心正向查找,JavaScript,详细注释](https://leetcode-cn.com/problems/jump-game-ii/solution/45-tiao-yue-you-xi-iitan-xin-zheng-xiang-cha-zhao-) diff --git "a/Week_06/1143.\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.js" "b/Week_06/1143.\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.js" new file mode 100644 index 00000000..ae01cd06 --- /dev/null +++ "b/Week_06/1143.\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.js" @@ -0,0 +1,94 @@ +/* + * @lc app=leetcode.cn id=1143 lang=javascript + * + * [1143] 最长公共子序列 + * + * https://leetcode-cn.com/problems/longest-common-subsequence/description/ + * + * algorithms + * Medium (60.69%) + * Likes: 326 + * Dislikes: 0 + * Total Accepted: 61.7K + * Total Submissions: 101.7K + * Testcase Example: '"abcde"\n"ace"' + * + * 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。 + * + * 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 + * 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" + * 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 + * + * 若这两个字符串没有公共子序列,则返回 0。 + * + * + * + * 示例 1: + * + * 输入:text1 = "abcde", text2 = "ace" + * 输出:3 + * 解释:最长公共子序列是 "ace",它的长度为 3。 + * + * + * 示例 2: + * + * 输入:text1 = "abc", text2 = "abc" + * 输出:3 + * 解释:最长公共子序列是 "abc",它的长度为 3。 + * + * + * 示例 3: + * + * 输入:text1 = "abc", text2 = "def" + * 输出:0 + * 解释:两个字符串没有公共子序列,返回 0。 + * + * + * + * + * 提示: + * + * + * 1 <= text1.length <= 1000 + * 1 <= text2.length <= 1000 + * 输入的字符串只含有小写英文字符。 + * + * + */ + +// @lc code=start +/** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = function (text1, text2) { + // 分别缓存两个字符串的长度 + const m = text1.length; + const n = text2.length; + // 初始化DP数组,默认值为第一行,对应text1为空字符串的情况,初始值都为0 + let dp = [new Array(n + 1).fill(0)]; + + // 遍历text1 + for (let i = 1; i <= m; i++) { + // 存入当前行的状态,第一列对应了text2为空字符串的情况,初始值都为0 + dp.push(new Array(n + 1).fill(0)); + + // 遍历text2 + for (let j = 1; j <= n; j++) { + // 如果当前字母相等,当前位置一定可以加1,那么只需从text1[0]到text1[i-2]和text2[0]到text2[j-2]的公共子序列推导到此处即可 + if (text1[i - 1] === text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + // 当前字母不相等 + // 但从text1[0]到text1[i - 1]与text2[0]到text2[j - 2],或者从text1[0]到text1[i - 2]与text2[0]到text2[j - 1]进行对比的话,都可能会出现公共子序列 + // 由于涉及到两种情况,此处需要取最大值 + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + // 推导完两个字符串长度,最后得到的就是最长公共子序列 + return dp[m][n]; +}; +// @lc code=end diff --git "a/Week_06/1143.\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/1143.\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..de0daf63 --- /dev/null +++ "b/Week_06/1143.\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,32 @@ +/* + * @lc app=leetcode.cn id=1143 lang=javascript + * + * [1143] 最长公共子序列 + */ + +// @lc code=start +/** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = function (text1, text2) { + let m = text1.length + 1; + let n = text2.length + 1; + let dp = [new Array(n).fill(0)]; + + for (let i = 1; i < m; i++) { + dp.push([0]); + + for (let j = 1; j < n; j++) { + if (text1[i - 1] === text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + return dp[m - 1][n - 1]; +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221.js" new file mode 100644 index 00000000..cfe5970d --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221.js" @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + let dp = triangle[triangle.length - 1].slice(); + + for (let i = triangle.length - 2; i >= 0; i--) { + for (let j = 0; j < triangle[i].length; j++) { + dp[j] = Math.min(dp[j], dp[j + 1]) + triangle[i][j]; + } + } + + return dp[0]; +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..44776392 --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,76 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + * + * https://leetcode-cn.com/problems/triangle/description/ + * + * algorithms + * Medium (66.98%) + * Likes: 668 + * Dislikes: 0 + * Total Accepted: 124K + * Total Submissions: 185.1K + * Testcase Example: '[[2],[3,4],[6,5,7],[4,1,8,3]]' + * + * 给定一个三角形 triangle ,找出自顶向下的最小路径和。 + * + * 每一步只能移动到下一行中相邻的结点上。 + * + * 相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。 + * + * + * + * 示例 1: + * + * + * 输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]] + * 输出:11 + * 解释:自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + * + * + * 示例 2: + * + * + * 输入:triangle = [[-10]] + * 输出:-10 + * + * + * + * + * 提示: + * + * + * 1 + * triangle[0].length == 1 + * triangle[i].length == triangle[i - 1].length + 1 + * -10^4 + * + * + * + * + * 进阶: + * + * + * 你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题吗? + * + * + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + let dp = [...triangle[triangle.length - 1]]; + + for (let i = triangle.length - 2; i >= 0; i--) { + for (let j = 0; j < triangle[i].length; j++) { + dp[j] = Math.min(dp[j], dp[j + 1]) + triangle[i][j]; + } + } + + return dp[0]; +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221.js" new file mode 100644 index 00000000..3f1a955f --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221.js" @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + let dp = [triangle[0][0]]; + + for (let i = 1; i < triangle.length; i++) { + dp[i] = dp[i - 1]; + for (let j = triangle[i].length - 1; j >= 0; j--) { + dp[j] = Math.min(dp[j], dp[j - 1] || Infinity) + triangle[i][j]; + } + } + + return dp.reduce((prev, curr) => { + return Math.min(prev, curr); + }, Infinity); +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..0ab022ae --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,81 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + * + * https://leetcode-cn.com/problems/triangle/description/ + * + * algorithms + * Medium (66.98%) + * Likes: 668 + * Dislikes: 0 + * Total Accepted: 124K + * Total Submissions: 185.1K + * Testcase Example: '[[2],[3,4],[6,5,7],[4,1,8,3]]' + * + * 给定一个三角形 triangle ,找出自顶向下的最小路径和。 + * + * 每一步只能移动到下一行中相邻的结点上。 + * + * 相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。 + * + * + * + * 示例 1: + * + * + * 输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]] + * 输出:11 + * 解释:自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + * + * + * 示例 2: + * + * + * 输入:triangle = [[-10]] + * 输出:-10 + * + * + * + * + * 提示: + * + * + * 1 + * triangle[0].length == 1 + * triangle[i].length == triangle[i - 1].length + 1 + * -10^4 + * + * + * + * + * 进阶: + * + * + * 你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题吗? + * + * + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + let dp = new Array(triangle[triangle.length - 1].length).fill(Infinity); + dp[0] = triangle[0][0]; + + for (let i = 1; i < triangle.length; i++) { + for (let j = triangle[i].length - 1; j >= 0; j--) { + dp[j] = + Math.min(dp[j], typeof dp[j - 1] === 'number' ? dp[j - 1] : Infinity) + + triangle[i][j]; + } + } + + return dp.reduce((prev, curr) => { + return Math.min(prev, curr); + }, Infinity); +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221.js" new file mode 100644 index 00000000..826ed111 --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221.js" @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + let dp = [triangle[triangle.length - 1].slice()]; + + for (let i = triangle.length - 2; i >= 0; i--) { + dp.unshift([]); + + for (let j = 0; j < triangle[i].length; j++) { + dp[0][j] = Math.min(dp[1][j], dp[1][j + 1]) + triangle[i][j]; + } + } + + return dp[0][0]; +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..bc8f2142 --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\345\217\215\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,78 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + * + * https://leetcode-cn.com/problems/triangle/description/ + * + * algorithms + * Medium (66.98%) + * Likes: 668 + * Dislikes: 0 + * Total Accepted: 124K + * Total Submissions: 185.1K + * Testcase Example: '[[2],[3,4],[6,5,7],[4,1,8,3]]' + * + * 给定一个三角形 triangle ,找出自顶向下的最小路径和。 + * + * 每一步只能移动到下一行中相邻的结点上。 + * + * 相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。 + * + * + * + * 示例 1: + * + * + * 输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]] + * 输出:11 + * 解释:自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + * + * + * 示例 2: + * + * + * 输入:triangle = [[-10]] + * 输出:-10 + * + * + * + * + * 提示: + * + * + * 1 + * triangle[0].length == 1 + * triangle[i].length == triangle[i - 1].length + 1 + * -10^4 + * + * + * + * + * 进阶: + * + * + * 你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题吗? + * + * + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + let dp = [[...triangle[triangle.length - 1]]]; + + for (let i = triangle.length - 2; i >= 0; i--) { + dp.unshift([]); + + for (let j = 0; j < triangle[i].length; j++) { + dp[0][j] = Math.min(dp[1][j], dp[1][j + 1]) + triangle[i][j]; + } + } + + return dp[0][0]; +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221.js" new file mode 100644 index 00000000..044aa4c6 --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221.js" @@ -0,0 +1,29 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + for (let i = 1; i < triangle.length; i++) { + for (let j = 0; j < triangle[i].length; j++) { + triangle[i][j] = + Math.min( + triangle[i - 1][j] === undefined ? Infinity : triangle[i - 1][j], + triangle[i - 1][j - 1] === undefined + ? Infinity + : triangle[i - 1][j - 1], + ) + triangle[i][j]; + } + } + + return triangle[triangle.length - 1].reduce((prev, curr) => { + return Math.min(prev, curr); + }, Infinity); +}; +// @lc code=end diff --git "a/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..1883b041 --- /dev/null +++ "b/Week_06/120.\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\346\255\243\345\220\221\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,83 @@ +/* + * @lc app=leetcode.cn id=120 lang=javascript + * + * [120] 三角形最小路径和 + * + * https://leetcode-cn.com/problems/triangle/description/ + * + * algorithms + * Medium (66.98%) + * Likes: 668 + * Dislikes: 0 + * Total Accepted: 124K + * Total Submissions: 185.1K + * Testcase Example: '[[2],[3,4],[6,5,7],[4,1,8,3]]' + * + * 给定一个三角形 triangle ,找出自顶向下的最小路径和。 + * + * 每一步只能移动到下一行中相邻的结点上。 + * + * 相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。 + * + * + * + * 示例 1: + * + * + * 输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]] + * 输出:11 + * 解释:自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + * + * + * 示例 2: + * + * + * 输入:triangle = [[-10]] + * 输出:-10 + * + * + * + * + * 提示: + * + * + * 1 + * triangle[0].length == 1 + * triangle[i].length == triangle[i - 1].length + 1 + * -10^4 + * + * + * + * + * 进阶: + * + * + * 你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题吗? + * + * + */ + +// @lc code=start +/** + * @param {number[][]} triangle + * @return {number} + */ +var minimumTotal = function (triangle) { + let dp = [triangle[0]]; + + for (let i = 1; i < triangle.length; i++) { + dp.push([]); + for (let j = 0; j < triangle[i].length; j++) { + dp[i][j] = + Math.min( + typeof dp[i - 1][j] === 'number' ? dp[i - 1][j] : Infinity, + typeof dp[i - 1][j - 1] === 'number' ? dp[i - 1][j - 1] : Infinity, + ) + triangle[i][j]; + } + } + + return dp[triangle.length - 1].reduce((prev, curr) => { + return Math.min(prev, curr); + }, Infinity); +}; +// @lc code=end diff --git "a/Week_06/121.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/121.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..8218a4df --- /dev/null +++ "b/Week_06/121.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,64 @@ +/* + * @lc app=leetcode.cn id=121 lang=javascript + * + * [121] 买卖股票的最佳时机 + * + * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/description/ + * + * algorithms + * Easy (55.01%) + * Likes: 1243 + * Dislikes: 0 + * Total Accepted: 299.3K + * Total Submissions: 543.9K + * Testcase Example: '[7,1,5,3,6,4]' + * + * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + * + * 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。 + * + * 注意:你不能在买入股票前卖出股票。 + * + * + * + * 示例 1: + * + * 输入: [7,1,5,3,6,4] + * 输出: 5 + * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 + * ⁠ 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 + * + * + * 示例 2: + * + * 输入: [7,6,4,3,1] + * 输出: 0 + * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 + * + * + */ + +// @lc code=start +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (prices) { + // 使用一个数组进行递推 + // 0位置保存的是当前的最大利润,1位置保存的是当前的 + let dp = [0, prices[0]]; + + // 从1开始遍历prices进行递推 + for (let i = 1; i < prices.length; i++) { + dp = [ + // 到i为止产生的最大利润,是i之前产生的最大利润,与当前卖出的最大利润中的较大者 + Math.max(dp[0], prices[i] - dp[1]), + // 到i为止已知的最小价格,是i之前已知的最小价格,与当前价格之间的较小者 + Math.min(dp[1], prices[i]), + ]; + } + + // 0位置保存的就是遍历所有价格之后能够产生的最大利润 + return dp[0]; +}; +// @lc code=end diff --git "a/Week_06/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..0620cbc2 --- /dev/null +++ "b/Week_06/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,88 @@ +/* + * @lc app=leetcode.cn id=122 lang=javascript + * + * [122] 买卖股票的最佳时机 II + * + * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/description/ + * + * algorithms + * Easy (63.60%) + * Likes: 896 + * Dislikes: 0 + * Total Accepted: 233.7K + * Total Submissions: 366.8K + * Testcase Example: '[7,1,5,3,6,4]' + * + * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + * + * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 + * + * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + * + * + * + * 示例 1: + * + * 输入: [7,1,5,3,6,4] + * 输出: 7 + * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + * 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 + * + * + * 示例 2: + * + * 输入: [1,2,3,4,5] + * 输出: 4 + * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 + * 。 + * 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 + * 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 + * + * + * 示例 3: + * + * 输入: [7,6,4,3,1] + * 输出: 0 + * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 + * + * + * + * 提示: + * + * + * 1 <= prices.length <= 3 * 10 ^ 4 + * 0 <= prices[i] <= 10 ^ 4 + * + * + */ + +// @lc code=start +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (prices) { + dp = [ + 0, // 此处保存的是当前完成卖出交易后,赚到的利润最大值,开始时没有交易,因此为0 + -prices[0], // 此处保存的是当前完成买入交易后,赚到的利润最大值,开始时买入股票,因此为负 + ]; + + for (let i = 1; i < prices.length; i++) { + dp = [ + // 计算当前卖出后的最大利润 + Math.max( + dp[0], // 如果上一次已卖出,这次没有股票可卖 + dp[1] + prices[i], // 如果上次有买入,那么这次就按当前价格卖出,获得收益 + ), + // 计算当前买入后的最大利润 + Math.max( + dp[1], // 上次已经买过,这次没必要再买,否则会损失收益 + dp[0] - prices[i], // 如果上次卖出过,这次就买入,等待下次卖出机会 + ), + ]; + } + + // 最后一次操作后,取最终利润的最大值 + return Math.max(...dp); +}; +// @lc code=end diff --git "a/Week_06/123. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 III\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/123. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 III\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..1ed8a017 --- /dev/null +++ "b/Week_06/123. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 III\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,50 @@ +/* + * @lc app=leetcode.cn id=123 lang=javascript + * + * [123] 买卖股票的最佳时机 III + */ + +// @lc code=start +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (prices) { + let dp = []; // 记录每一天的各种操作状态 + dp[0] = 0; // 状态0:不进行任何操作,初始为0 + dp[1] = -prices[0]; // 状态1:进行1次买入操作,支出prices[0] + dp[2] = 0; // 状态2:进行1次卖出操作,第一次无法卖出,为0 + dp[3] = -prices[0]; // 状态3:进行第2次买入操作,初始最多只能买入一次,支出prices[0] + dp[4] = 0; // 状态4:进行第2次卖出操作,第一次无法卖出,为8 + + for (let i = 1; i < prices.length; i++) { + dp = [ + // 状态0 + dp[0], // 每次都不做任何操作 + // 状态1 + Math.max( + dp[0] - prices[i], // 如果之前从没有买过,当前尝试购买1次 + dp[1], // 如果之前买过1次,无法再买 + ), + // 状态2 + Math.max( + dp[1] + prices[i], // 如果之前买过1次,现在可以卖出 + dp[2], // 如果之前卖出过1次,现在无法再卖 + ), + // 状态3 + Math.max( + dp[2] - prices[i], // 如果之前卖过一次,现在可以再买 + dp[3], // 如果已经买过2次,就不可以再买了 + ), + // 状态4 + Math.max( + dp[3] + prices[i], // 如果之前买过2次,现在可以卖出 + dp[4], // 如果已经卖出过2次,现在无法再卖出 + ), + ]; + } + + // 递推完成后,状态4就是卖出2次能获得的最大利润 + return dp[4]; +}; +// @lc code=end diff --git "a/Week_06/123. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 III\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\233\264\346\216\245\351\200\222\346\216\250.js" "b/Week_06/123. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 III\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\233\264\346\216\245\351\200\222\346\216\250.js" new file mode 100644 index 00000000..e41dbf3e --- /dev/null +++ "b/Week_06/123. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 III\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\233\264\346\216\245\351\200\222\346\216\250.js" @@ -0,0 +1,90 @@ +/* + * @lc app=leetcode.cn id=123 lang=javascript + * + * [123] 买卖股票的最佳时机 III + * + * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/description/ + * + * algorithms + * Hard (47.66%) + * Likes: 593 + * Dislikes: 0 + * Total Accepted: 72.5K + * Total Submissions: 152.1K + * Testcase Example: '[3,3,5,0,0,3,1,4]' + * + * 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。 + * + * 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 + * + * 注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + * + * 示例 1: + * + * 输入: [3,3,5,0,0,3,1,4] + * 输出: 6 + * 解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。 + * 随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。 + * + * 示例 2: + * + * 输入: [1,2,3,4,5] + * 输出: 4 + * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 + * 。 + * 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 + * 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 + * + * + * 示例 3: + * + * 输入: [7,6,4,3,1] + * 输出: 0 + * 解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。 + * + */ + +// @lc code=start +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (prices) { + // 如果没有价格信息,利润为0 + if (!prices && !prices.length) { + return 0; + } + + // 最多进行2次交易,交易有买入和卖出两种状态,因此递推2*2种情况 + // 偶数位置是买入状态,奇数位置是卖出状态 + // 实际上第一次交易时,买入为-prices[0],卖出为0(因为还没有买过,就无法卖出) + // 将递推状态都初始化为-prices[0],从prices[0]开始递推,就会自然创建出第一次交易的状态 + let dp = new Array(4).fill(-prices[0]); + + // 按每天的价格进行递推 + for (let i = 0; i < prices.length; i++) { + // 递推每次交易的价格 + for (let j = 0; j < dp.length; j++) { + // 当前价格是根据上一次交易的结果推出 + dp[j] = Math.max( + dp[j], // 如果这次交易没有收益,不进行交易,保持原样 + // 在j-1,即上一次交易的基础上,进行当次交易 + // j为0时,表示第一次交易,它的前一次无交易,为0 + (dp[j - 1] || 0) + + // 偶数位置是买入状态,奇数位置是卖出状态,进行相应交易 + (j & 1 ? prices[i] : -prices[i]), + ); + } + // 也可以完全按照上一次递推的状态,推导下一次的状态,两者结果相同 + /* dp = dp.map((val, j) => { + return Math.max( + dp[j], + (dp[j - 1] || 0) + (j & 1 ? prices[i] : -prices[i]), + ); + }); */ + } + + // 卖出次数最多,就会拿到最多利润 + return dp[dp.length - 1]; +}; +// @lc code=end diff --git "a/Week_06/126.\345\215\225\350\257\215\346\216\245\351\276\231-ii\357\274\214BFS.js" "b/Week_06/126.\345\215\225\350\257\215\346\216\245\351\276\231-ii\357\274\214BFS.js" new file mode 100644 index 00000000..b4734e3a --- /dev/null +++ "b/Week_06/126.\345\215\225\350\257\215\346\216\245\351\276\231-ii\357\274\214BFS.js" @@ -0,0 +1,66 @@ +/* + * @lc app=leetcode.cn id=126 lang=javascript + * + * [126] 单词接龙 II + */ + +// @lc code=start +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {string[][]} + */ +var findLadders = function (beginWord, endWord, wordList) { + let result = []; + let queue = [[beginWord]]; + let set = new Set(); + + while (queue.length) { + let queueLength = queue.length; + let found = false; + const levelSet = new Set(); + + while (--queueLength >= 0) { + let subRes = queue.shift(); + const last = subRes.length - 1; + + for (let i = 0; i < wordList.length; i++) { + if (set.has(wordList[i])) { + continue; + } + + let diff = 0; + + for (let j = 0; j < wordList[i].length; j++) { + if (subRes[last][j] !== wordList[i][j]) { + diff++; + if (diff > 1) { + break; + } + } + } + + if (diff === 1) { + if (wordList[i] === endWord) { + found = true; + result.push([...subRes, wordList[i]]); + } else { + queue.push([...subRes, wordList[i]]); + levelSet.add(wordList[i]); + } + } + } + } + for (const word of levelSet) { + set.add(word); + } + + if (found) { + break; + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" new file mode 100644 index 00000000..b8f3acb7 --- /dev/null +++ "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" @@ -0,0 +1,66 @@ +/* + * @lc app=leetcode.cn id=152 lang=javascript + * + * [152] 乘积最大子数组 + * + * https://leetcode-cn.com/problems/maximum-product-subarray/description/ + * + * algorithms + * Medium (40.78%) + * Likes: 883 + * Dislikes: 0 + * Total Accepted: 110.4K + * Total Submissions: 270.7K + * Testcase Example: '[2,3,-2,4]' + * + * 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 + * + * + * + * 示例 1: + * + * 输入: [2,3,-2,4] + * 输出: 6 + * 解释: 子数组 [2,3] 有最大乘积 6。 + * + * + * 示例 2: + * + * 输入: [-2,0,-1] + * 输出: 0 + * 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxProduct = function (nums) { + let max = nums[0]; + // 存储子数组的最大乘积,默认为第一项 + // 由于乘积可能会出现负负得正的情况,因此需要同时存储最大和最小值 + // 这样能避免只存储最大值,导致负值被抛弃的情况 + // 但实际上prevMin和prevMax都可能存在负值 + let prevMin = max; + let prevMax = max; + + // 遍历数组,计算最大乘积 + for (let i = 1; i < nums.length; i++) { + // 对于nums[i]来说,它一定会被计算到乘积中,但它之前已知的乘积是可以被抛弃的 + // 假设上一步的最大值和最小值需要使用,分别计算当前的最大和最小乘积 + const currMin = prevMin * nums[i]; + const currMax = prevMax * nums[i]; + + // 将currMin和currMax与当前值对比,找出i位置的真正最大和最小值,存入数组 + prevMin = Math.min(currMin, currMax, nums[i]); + prevMax = Math.max(currMin, currMax, nums[i]); + + // 不断对比当前最大值,即可找出整个数组中的最大乘积 + max = Math.max(max, prevMax); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..04f5ec15 --- /dev/null +++ "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,56 @@ +/* + * @lc app=leetcode.cn id=152 lang=javascript + * + * [152] 乘积最大子数组 + * + * https://leetcode-cn.com/problems/maximum-product-subarray/description/ + * + * algorithms + * Medium (40.78%) + * Likes: 883 + * Dislikes: 0 + * Total Accepted: 110.4K + * Total Submissions: 270.7K + * Testcase Example: '[2,3,-2,4]' + * + * 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 + * + * + * + * 示例 1: + * + * 输入: [2,3,-2,4] + * 输出: 6 + * 解释: 子数组 [2,3] 有最大乘积 6。 + * + * + * 示例 2: + * + * 输入: [-2,0,-1] + * 输出: 0 + * 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxProduct = function (nums) { + let prevMin = nums[0]; + let prevMax = nums[0]; + let max = nums[0]; + + for (let i = 1; i < nums.length; i++) { + let currMin = prevMin * nums[i]; + let currMax = prevMax * nums[i]; + + prevMin = Math.min(currMin, currMax, nums[i]); + prevMax = Math.max(currMin, currMax, nums[i]); + max = Math.max(prevMax, max); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" new file mode 100644 index 00000000..8b34b9ef --- /dev/null +++ "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" @@ -0,0 +1,64 @@ +/* + * @lc app=leetcode.cn id=152 lang=javascript + * + * [152] 乘积最大子数组 + * + * https://leetcode-cn.com/problems/maximum-product-subarray/description/ + * + * algorithms + * Medium (40.78%) + * Likes: 883 + * Dislikes: 0 + * Total Accepted: 110.4K + * Total Submissions: 270.7K + * Testcase Example: '[2,3,-2,4]' + * + * 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 + * + * + * + * 示例 1: + * + * 输入: [2,3,-2,4] + * 输出: 6 + * 解释: 子数组 [2,3] 有最大乘积 6。 + * + * + * 示例 2: + * + * 输入: [-2,0,-1] + * 输出: 0 + * 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxProduct = function (nums) { + let max = nums[0]; // 存储子数组的最大乘积,默认为第一项 + // 由于乘积可能会出现负负得正的情况,因此需要同时存储最大和最小值 + // 这样能避免只存储最大值,导致负值被抛弃的情况 + // 但实际上minProducts和maxProducts都可能存在负值 + let minProducts = [max]; // 使用数组存储已计算出的最小值 + let maxProducts = [max]; // 使用数组存储已计算出的最大值 + + // 遍历数组,计算最大乘积 + // 遍历数组,计算最大乘积 + for (let i = 1; i < nums.length; i++) { + // 对于nums[i]来说,它一定会被计算到乘积中,但它之前已知的乘积是可以被抛弃的 + // 假设上一步的最大值和最小值需要使用,分别计算当前的最大和最小乘积 + const currMin = minProducts[i - 1] * nums[i]; + const currMax = maxProducts[i - 1] * nums[i]; + // 将currMin和currMax与当前值对比,找出i位置的真正最大和最小值,存入数组 + minProducts[i] = Math.min(currMin, currMax, nums[i]); + maxProducts[i] = Math.max(currMin, currMax, nums[i]); + // 不断对比当前最大值,即可找出整个数组中的最大乘积 + max = Math.max(max, maxProducts[i]); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..d4f38c6b --- /dev/null +++ "b/Week_06/152. \344\271\230\347\247\257\346\234\200\345\244\247\345\255\220\346\225\260\347\273\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,55 @@ +/* + * @lc app=leetcode.cn id=152 lang=javascript + * + * [152] 乘积最大子数组 + * + * https://leetcode-cn.com/problems/maximum-product-subarray/description/ + * + * algorithms + * Medium (40.78%) + * Likes: 883 + * Dislikes: 0 + * Total Accepted: 110.4K + * Total Submissions: 270.7K + * Testcase Example: '[2,3,-2,4]' + * + * 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 + * + * + * + * 示例 1: + * + * 输入: [2,3,-2,4] + * 输出: 6 + * 解释: 子数组 [2,3] 有最大乘积 6。 + * + * + * 示例 2: + * + * 输入: [-2,0,-1] + * 输出: 0 + * 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxProduct = function (nums) { + let minRes = [nums[0]]; + let maxRes = [nums[0]]; + let max = nums[0]; + + for (let i = 1; i < nums.length; i++) { + let currMin = minRes[i - 1] * nums[i]; + let currMax = maxRes[i - 1] * nums[i]; + minRes[i] = Math.min(currMin, currMax, nums[i]); + maxRes[i] = Math.max(currMin, currMax, nums[i]); + max = Math.max(maxRes[i], max); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/188. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 IV\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\345\205\210\345\210\235\345\247\213\345\214\226\345\206\215\351\200\222\346\216\250.js" "b/Week_06/188. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 IV\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\345\205\210\345\210\235\345\247\213\345\214\226\345\206\215\351\200\222\346\216\250.js" new file mode 100644 index 00000000..45f0a731 --- /dev/null +++ "b/Week_06/188. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 IV\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\345\205\210\345\210\235\345\247\213\345\214\226\345\206\215\351\200\222\346\216\250.js" @@ -0,0 +1,45 @@ +/** + * @param {number} k + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (k, prices) { + let dp = [0]; // 0位置表示当前没有进行交易 + + // 初始化第一次交易的情况 + for (let i = 0; i < k * 2; i++) { + // 奇数位置为买入,第一次买入需要支出prices[0] + // 偶数位置为卖出,由于没有买入过股票,没法卖出,为0 + dp.push(i % 2 ? 0 : -prices[0]); + } + + // 从第二次交易开始递推 + for (let i = 1; i < prices.length; i++) { + // 递推每一次交易后的状态 + dp = dp.map((value, j) => { + if (j === 0) { + // 0位置表示一直都没有交易过 + return dp[j]; + } else if (j % 2 === 1) { + // 奇数位置是买入操作 + return Math.max( + // 如果上一次有卖出,这次可以买入 + dp[j - 1] - prices[i], + // 如果这次交易没有收益,不进行交易,保持原样 + dp[j], + ); + } else { + // 偶数位置是卖出操作 + return Math.max( + // 如果上次买入过,这次可以卖出 + dp[j - 1] + prices[i], + // 如果这次交易没有收益,不进行交易,保持原样 + dp[j], + ); + } + }); + } + + // 进行最多次卖出,会获得最多利润,直接返回即可 + return dp[dp.length - 1]; +}; diff --git "a/Week_06/188. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 IV\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\233\264\346\216\245\351\200\222\346\216\250.js" "b/Week_06/188. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 IV\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\233\264\346\216\245\351\200\222\346\216\250.js" new file mode 100644 index 00000000..fcc84039 --- /dev/null +++ "b/Week_06/188. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272 IV\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\233\264\346\216\245\351\200\222\346\216\250.js" @@ -0,0 +1,44 @@ +/** + * @param {number} k + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (k, prices) { +/* +2\n[3,2,6,5,0,3] +2\n[] +0\n[1,3] +*/ + // 最多进行k次交易,交易有买入和卖出两种状态,因此递推k*2种情况 + // 偶数位置是买入状态,奇数位置是卖出状态 + // 实际上第一次交易时,买入为-prices[0],卖出为0(因为还没有买过,就无法卖出) + // 将递推状态都初始化为-prices[0],从prices[0]开始递推,就会自然创建出第一次交易的状态 + let dp = new Array(k << 1).fill(-prices[0]); + + // 按每天的价格进行递推 + for (let i = 0; i < prices.length; i++) { + // 递推每次交易的价格 + for (let j = 0; j < dp.length; j++) { + // 当前价格是根据上一次交易的结果推出 + dp[j] = Math.max( + dp[j], // 如果这次交易没有收益,不进行交易,保持原样 + // 在j-1,即上一次交易的基础上,进行当次交易 + // j为0时,表示第一次交易,它的前一次无交易,为0 + (dp[j - 1] || 0) + + // 偶数位置是买入状态,奇数位置是卖出状态,进行相应交易 + (j & 1 ? prices[i] : -prices[i]), + ); + } + // 也可以完全按照上一次递推的状态,推导下一次的状态,两者结果相同 + /* dp = dp.map((val, j) => { + return Math.max( + dp[j], + (dp[j - 1] || 0) + (j & 1 ? prices[i] : -prices[i]), + ); + }); */ + } + + // 卖出次数最多,就会拿到最多利润 + // 遇到2\n[]和0\n[1,3]这两个case,会无法正确递推,此时利润都为0 + return dp[dp.length - 1] || 0; +}; diff --git "a/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" new file mode 100644 index 00000000..a4db0954 --- /dev/null +++ "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" @@ -0,0 +1,84 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + * + * https://leetcode-cn.com/problems/house-robber/description/ + * + * algorithms + * Easy (47.24%) + * Likes: 1232 + * Dislikes: 0 + * Total Accepted: 225.9K + * Total Submissions: 478K + * Testcase Example: '[1,2,3,1]' + * + * + * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + * + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + * + * + * + * 示例 1: + * + * 输入:[1,2,3,1] + * 输出:4 + * 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 + * 偷窃到的最高金额 = 1 + 3 = 4 。 + * + * 示例 2: + * + * 输入:[2,7,9,3,1] + * 输出:12 + * 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 + * 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 + * + * + * + * + * 提示: + * + * + * 0 <= nums.length <= 100 + * 0 <= nums[i] <= 400 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + /** + * 对于第i个房子,有两种场景,偷或不偷 + * 如果不偷的话,dp[i] = dp[i - 1] + 0 + * 如果偷的话,那么i之前的所有的可能情况,也就是dp[i] = Math.max(nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...) + * 如此可知dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...),如此可知,每一位都取了之前所有可能的最大值 + * 那么就可以推导出dp[i - 2] > dp[i - 3] > dp[i - 4]...,因此dp[i - 2]之后的对比都可以省略,递推公式就变成了 + * dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2]) + */ + // 由于要向前递推两层,一次初始化时要创建两个元素,并且都要取最大值 + let dp = [ + // 偷了第0户 + nums[0] || 0, + // 第0和1户只能选一户偷 + Math.max(nums[0] || 0, nums[1] || 0), + ]; + + // 从第2户开始递推 + for (let i = 2; i < nums.length; i++) { + dp[i] = Math.max( + // 如果不偷第i户,只需考虑i-1户的情况 + dp[i - 1], + // 如果偷第i户,需要考虑i-2到0户的情况,而i-2必然大于之前所有结果,此处只需要考虑i-2即可 + nums[i] + dp[i - 2], + ); + } + + // 递推到最后一个值,就是最大偷到的金额 + return dp[dp.length - 1]; +}; +// @lc code=end diff --git "a/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\345\217\230\351\207\217.js" "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\345\217\230\351\207\217.js" new file mode 100644 index 00000000..c223b64f --- /dev/null +++ "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\345\217\230\351\207\217.js" @@ -0,0 +1,77 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + * + * https://leetcode-cn.com/problems/house-robber/description/ + * + * algorithms + * Easy (47.24%) + * Likes: 1232 + * Dislikes: 0 + * Total Accepted: 225.9K + * Total Submissions: 478K + * Testcase Example: '[1,2,3,1]' + * + * + * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + * + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + * + * + * + * 示例 1: + * + * 输入:[1,2,3,1] + * 输出:4 + * 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 + * 偷窃到的最高金额 = 1 + 3 = 4 。 + * + * 示例 2: + * + * 输入:[2,7,9,3,1] + * 输出:12 + * 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 + * 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 + * + * + * + * + * 提示: + * + * + * 0 <= nums.length <= 100 + * 0 <= nums[i] <= 400 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + /** + * 对于第i个房子,有两种场景,偷或不偷 + * 如果不偷的话,dp[i] = dp[i - 1] + 0 + * 如果偷的话,那么i之前的所有的可能情况,也就是dp[i] = Math.max(nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...) + * 如此可知dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...),如此可知,每一位都取了之前所有可能的最大值 + * 那么就可以推导出dp[i - 2] > dp[i - 3] > dp[i - 4]...,因此dp[i - 2]之后的对比都可以省略,递推公式就变成了 + * dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2]) + */ + // 由于要向前递推两层,一次初始化时要创建两个元素,并且都要取最大值 + let prev = nums[0] || 0; + let curr = Math.max(nums[0] || 0, nums[1] || 0); + + // 直接带入递推公式即可 + for (let i = 2; i < nums.length; i++) { + const temp = curr; + curr = Math.max(curr, prev + nums[i]); + prev = temp; + } + + // 递推到最后一个值,就是最大偷到的金额 + return curr; +}; +// @lc code=end diff --git "a/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204.js" "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204.js" new file mode 100644 index 00000000..bad7acce --- /dev/null +++ "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204.js" @@ -0,0 +1,76 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + * + * https://leetcode-cn.com/problems/house-robber/description/ + * + * algorithms + * Easy (47.24%) + * Likes: 1232 + * Dislikes: 0 + * Total Accepted: 225.9K + * Total Submissions: 478K + * Testcase Example: '[1,2,3,1]' + * + * + * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + * + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + * + * + * + * 示例 1: + * + * 输入:[1,2,3,1] + * 输出:4 + * 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 + * 偷窃到的最高金额 = 1 + 3 = 4 。 + * + * 示例 2: + * + * 输入:[2,7,9,3,1] + * 输出:12 + * 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 + * 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 + * + * + * + * + * 提示: + * + * + * 0 <= nums.length <= 100 + * 0 <= nums[i] <= 400 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + /** + * 对于第i个房子,有两种场景,偷或不偷 + * 如果不偷的话,dp[i] = dp[i - 1] + 0 + * 如果偷的话,那么i之前的所有的可能情况,也就是dp[i] = Math.max(nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...) + * 如此可知dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...),如此可知,每一位都取了之前所有可能的最大值 + * 那么就可以推导出dp[i - 2] > dp[i - 3] > dp[i - 4]...,因此dp[i - 2]之后的对比都可以省略,递推公式就变成了 + * dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2]) + */ + // 由于要向前递推两层,一次初始化时要创建两个元素,并且都要取最大值 + let dp = [nums[0] || 0, Math.max(nums[0] || 0, nums[1] || 0)]; + + // 直接带入递推公式即可 + for (let i = 2; i < nums.length; i++) { + const temp = dp[1]; + dp[1] = Math.max(dp[1], nums[i] + dp[0]); + dp[0] = temp; + } + + // 递推到最后一个值,就是最大偷到的金额 + return dp[1]; +}; +// @lc code=end diff --git "a/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204\357\274\214\344\270\215\345\210\233\345\273\272\345\244\264\344\270\244\344\270\252\345\200\274.js" "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204\357\274\214\344\270\215\345\210\233\345\273\272\345\244\264\344\270\244\344\270\252\345\200\274.js" new file mode 100644 index 00000000..ce24956f --- /dev/null +++ "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204\357\274\214\344\270\215\345\210\233\345\273\272\345\244\264\344\270\244\344\270\252\345\200\274.js" @@ -0,0 +1,73 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + * + * https://leetcode-cn.com/problems/house-robber/description/ + * + * algorithms + * Easy (47.24%) + * Likes: 1232 + * Dislikes: 0 + * Total Accepted: 225.9K + * Total Submissions: 478K + * Testcase Example: '[1,2,3,1]' + * + * + * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + * + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + * + * + * + * 示例 1: + * + * 输入:[1,2,3,1] + * 输出:4 + * 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 + * 偷窃到的最高金额 = 1 + 3 = 4 。 + * + * 示例 2: + * + * 输入:[2,7,9,3,1] + * 输出:12 + * 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 + * 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 + * + * + * + * + * 提示: + * + * + * 0 <= nums.length <= 100 + * 0 <= nums[i] <= 400 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + /** + * 对于第i个房子,有两种场景,偷或不偷 + * 如果不偷的话,dp[i] = dp[i - 1] + 0 + * 如果偷的话,那么i之前的所有的可能情况,也就是dp[i] = Math.max(nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...) + * 如此可知dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...),如此可知,每一位都取了之前所有可能的最大值 + * 那么就可以推导出dp[i - 2] > dp[i - 3] > dp[i - 4]...,因此dp[i - 2]之后的对比都可以省略,递推公式就变成了 + * dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2]) + */ + let dp = [0, 0]; + + for (let i = 0; i < nums.length; i++) { + const temp = dp[1]; + dp[1] = Math.max(dp[1], dp[0] + nums[i]); + dp[0] = temp; + } + + return dp[1]; +}; +// @lc code=end diff --git "a/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204\357\274\214\344\275\277\347\224\250reduce.js" "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204\357\274\214\344\275\277\347\224\250reduce.js" new file mode 100644 index 00000000..cc6b99ce --- /dev/null +++ "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201\357\274\214O(1)\346\225\260\347\273\204\357\274\214\344\275\277\347\224\250reduce.js" @@ -0,0 +1,68 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + * + * https://leetcode-cn.com/problems/house-robber/description/ + * + * algorithms + * Easy (47.24%) + * Likes: 1232 + * Dislikes: 0 + * Total Accepted: 225.9K + * Total Submissions: 478K + * Testcase Example: '[1,2,3,1]' + * + * + * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + * + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + * + * + * + * 示例 1: + * + * 输入:[1,2,3,1] + * 输出:4 + * 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 + * 偷窃到的最高金额 = 1 + 3 = 4 。 + * + * 示例 2: + * + * 输入:[2,7,9,3,1] + * 输出:12 + * 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 + * 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 + * + * + * + * + * 提示: + * + * + * 0 <= nums.length <= 100 + * 0 <= nums[i] <= 400 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + /** + * 对于第i个房子,有两种场景,偷或不偷 + * 如果不偷的话,dp[i] = dp[i - 1] + 0 + * 如果偷的话,那么i之前的所有的可能情况,也就是dp[i] = Math.max(nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...) + * 如此可知dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2], nums[i] + dp[i - 3], ...),如此可知,每一位都取了之前所有可能的最大值 + * 那么就可以推导出dp[i - 2] > dp[i - 3] > dp[i - 4]...,因此dp[i - 2]之后的对比都可以省略,递推公式就变成了 + * dp[i] = Math.max(dp[i - 1] + 0, nums[i] + dp[i - 2]) + */ + return nums.reduce((dp, curr) => [dp[1], Math.max(dp[1], dp[0] + curr)], [ + 0, + 0, + ])[1]; +}; +// @lc code=end diff --git "a/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\345\210\206\345\210\253\345\255\230\345\202\250\345\201\267\345\222\214\344\270\215\345\201\267\347\212\266\346\200\201.js" "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\345\210\206\345\210\253\345\255\230\345\202\250\345\201\267\345\222\214\344\270\215\345\201\267\347\212\266\346\200\201.js" new file mode 100644 index 00000000..e430b723 --- /dev/null +++ "b/Week_06/198. \346\211\223\345\256\266\345\212\253\350\210\215\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\345\210\206\345\210\253\345\255\230\345\202\250\345\201\267\345\222\214\344\270\215\345\201\267\347\212\266\346\200\201.js" @@ -0,0 +1,72 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + * + * https://leetcode-cn.com/problems/house-robber/description/ + * + * algorithms + * Easy (47.24%) + * Likes: 1232 + * Dislikes: 0 + * Total Accepted: 225.9K + * Total Submissions: 478K + * Testcase Example: '[1,2,3,1]' + * + * + * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + * + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + * + * + * + * 示例 1: + * + * 输入:[1,2,3,1] + * 输出:4 + * 解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 + * 偷窃到的最高金额 = 1 + 3 = 4 。 + * + * 示例 2: + * + * 输入:[2,7,9,3,1] + * 输出:12 + * 解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 + * 偷窃到的最高金额 = 2 + 9 + 1 = 12 。 + * + * + * + * + * 提示: + * + * + * 0 <= nums.length <= 100 + * 0 <= nums[i] <= 400 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + // 对于第一户人家,存在偷和不偷两种情况 + // 需要注意nums为[]的情况,此时偷的情况也为0 + let dp = [[nums[0] || 0, 0]]; + + // 判断每一户人家的偷和不偷状态,不断累加偷到的金额 + for (let i = 1; i < nums.length; i++) { + dp[i] = [ + // 如果偷当前人家,那么上一户人家就不能偷,只能取不偷的值 + dp[i - 1][1] + nums[i], + // 如果不偷当前人家,上一户人家偷不偷都可以,只需要取一个最大值 + Math.max(dp[i - 1][0], dp[i - 1][1]), + ]; + } + + // 数组最后一位,分别存储了偷和不偷最后一户的总金额,取最大值即可 + return Math.max(...dp[dp.length - 1]); +}; +// @lc code=end diff --git "a/Week_06/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" "b/Week_06/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" new file mode 100644 index 00000000..b2b34080 --- /dev/null +++ "b/Week_06/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\344\270\215\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=213 lang=javascript + * + * [213] 打家劫舍 II + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + if (nums.length === 0) { + return 0; + } + if (nums.length === 1) { + return nums[0]; + } + + function robBetween(start, end) { + let dp = [0, 0]; + + for (let i = start; i <= end; i++) { + const temp = dp[1]; + dp[1] = Math.max(dp[0] + nums[i], dp[1]); + dp[0] = temp; + } + + return dp[1]; + } + + return Math.max( + robBetween(0, nums.length - 2), + robBetween(1, nums.length - 1), + ); +}; +// @lc code=end diff --git "a/Week_06/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" "b/Week_06/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" new file mode 100644 index 00000000..f069c5c4 --- /dev/null +++ "b/Week_06/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=213 lang=javascript + * + * [213] 打家劫舍 II + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + if (nums.length === 0) { + return 0; + } + if (nums.length === 1) { + return nums[0]; + } + + function robBetween(start, end) { + let dp = [ + nums[start] || 0, + Math.max(nums[start] || 0, nums[start + 1] || 0), + ]; + + for (let i = start + 2; i <= end; i++) { + dp = [Math.max(dp[0], dp[1]), dp[0] + nums[i]]; + } + + return Math.max(dp[0], dp[1]); + } + + return Math.max( + robBetween(0, nums.length - 2), + robBetween(1, nums.length - 1), + ); +}; +// @lc code=end diff --git "a/Week_06/221. \346\234\200\345\244\247\346\255\243\346\226\271\345\275\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/221. \346\234\200\345\244\247\346\255\243\346\226\271\345\275\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..a16a6922 --- /dev/null +++ "b/Week_06/221. \346\234\200\345\244\247\346\255\243\346\226\271\345\275\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=221 lang=javascript + * + * [221] 最大正方形 + */ + +// @lc code=start +/** + * @param {character[][]} matrix + * @return {number} + */ +var maximalSquare = function (matrix) { + // 参考了:https://leetcode-cn.com/problems/maximal-square/solution/li-jie-san-zhe-qu-zui-xiao-1-by-lzhlyle/ + let m = matrix.length; // 缓存矩阵行数 + let n = matrix[0].length; // 缓存矩阵列数 + // 创建一个m+1行n+1列的矩阵用于递推 + // 由于递推matrix的时候的第一行和第一列,需要判断-1行和-1列的最小值 + // 因此初始化dp的第一行和第一列都为0,dp的i+1行和j+1列对应的是matrix的i行i列 + // 创建dp时只要存入第一行的值 + let dp = [new Array(n + 1).fill(0)]; + let max = 0; // 存储遇到的最大正方形边长 + + // 递推matrix的每个位置,计算每个位置能形成的正方形最大边长 + for (let i = 0; i < m; i++) { + // 每次循环dp中存入当前行的值 + dp.push(new Array(n + 1).fill(0)); + + for (let j = 0; j < n; j++) { + // 当遇到矩阵值为1时,表示当前存在正方形 + if (matrix[i][j] === '1') { + // 计算当前位置能形成的最大正方形边长 + dp[i + 1][j + 1] = + // 当前位置是和左、上、左上3个方向形成正方形 + // 能形成的最大正方形会受到这3个位置的限制,因此取最小值 + Math.min(dp[i][j + 1], dp[i + 1][j], dp[i][j]) + + // 当前位置的自身的边长 + 1; + // 记录所有边长的最大值 + max = Math.max(max, dp[i + 1][j + 1]); + } + } + } + + // 最大边长的平方是最大面积 + return max * max; +}; +// @lc code=end diff --git "a/Week_06/279. \345\256\214\345\205\250\345\271\263\346\226\271\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/279. \345\256\214\345\205\250\345\271\263\346\226\271\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..b3f37bf3 --- /dev/null +++ "b/Week_06/279. \345\256\214\345\205\250\345\271\263\346\226\271\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=279 lang=javascript + * + * [279] 完全平方数 + */ + +// @lc code=start +/** + * @param {number} n + * @return {number} + */ +var numSquares = function (n) { + // 该题类似于[322. 零钱兑换](https://leetcode-cn.com/problems/coin-change/) + // 只是硬币不是固定金额 + // 需要计算到n,因此需要从0开始递推到n + // 由于要计算的是最小值,因此初始化为Infinity,避免计算错误 + let dp = new Array(n + 1).fill(Infinity); + dp[0] = 0; // 0对应的可组合数字为0 + + // 从1开始才会有组合个数,一直递推到n + for (let i = 1; i < dp.length; i++) { + // 每次计算可用的完全平方数 + // 判断可用的方法是j*j不超过当前数量i + for (let j = 1; j * j <= i; j++) { + dp[i] = Math.min( + dp[i], // 当前已储存了之前完全平方数的组合数量 + // 当前的完全平方数数量,可由i - j * j加一个j * j而来 + // 因此等于dp[i - j * j]的数量加1个j * j + dp[i - j * j] + 1, + ); + } + } + + // 递推到n时,就计算出了所有可能的数量 + return dp[n]; +}; +// @lc code=end diff --git "a/Week_06/297. \344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226\357\274\214BFS.js" "b/Week_06/297. \344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226\357\274\214BFS.js" new file mode 100644 index 00000000..3cf8cd21 --- /dev/null +++ "b/Week_06/297. \344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226\357\274\214BFS.js" @@ -0,0 +1,91 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * Encodes a tree to a single string. + * + * @param {TreeNode} root + * @return {string} + */ +var serialize = function (root) { + let queue = [root]; // 使用队列遍历二叉树 + let serialized = []; // 用数组存储序列化后的值 + + // 队列被清空时,表示完成了二叉树的遍历 + while (queue.length) { + // 缓存当前一行的节点数量 + let queueLength = queue.length; + + // 将当前一行的节点出队,进行处理 + for (let i = 0; i < queueLength; i++) { + const node = queue.shift(); + + if (node) { + // 如果当前节点不为null,将其值存入serialized + serialized.push(node.val); + // 将左右子节点存入队列,供下一行遍历处理 + // 队列中的节点,总是保持根、左、右的顺序排列 + queue.push(node.left, node.right); + } else { + // 如果当前节点为null,将X存入serialized + serialized.push('X'); + } + } + } + + // 将数组转换成字符串返回 + return serialized.join(','); +}; + +/** + * Decodes your encoded data to tree. + * + * @param {string} data + * @return {TreeNode} + */ +var deserialize = function (data) { + // 如果根节点为null,那么data为X,直接返回null即可 + if (data === 'X') { + return null; + } + + // 将data转换为数组方便依次处理 + const valList = data.split(','); + // 从数组中出队根节点的值,创建一个根节点 + const root = new TreeNode(valList.shift()); + // 将根节点存入队列,使用队列辅助创建二叉树 + let queue = [root]; + + // valList被清空,表示所有值都已用来创建二叉树 + while (valList.length) { + // 从队列中出队一个节点 + const node = queue.shift(); + // 因为valList中的值都是按照根、左、右排列,所以可以从valList连续出队左右子节点的值 + const left = valList.shift(); + const right = valList.shift(); + + // 如果值不为空,就创建一个节点 + // 此时意味着valList还存有其子节点的值,此时只需要将节点存入queue,即可在下一次循环中处理 + if (left !== 'X') { + node.left = new TreeNode(left); + queue.push(node.left); + } + if (right !== 'X') { + node.right = new TreeNode(right); + queue.push(node.right); + } + } + + // 将生成的二叉树根节点返回即可 + return root; +}; + +/** + * Your functions will be called as such: + * deserialize(serialize(root)); + */ diff --git "a/Week_06/297. \344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226\357\274\214DFS.js" "b/Week_06/297. \344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226\357\274\214DFS.js" new file mode 100644 index 00000000..89c9fff5 --- /dev/null +++ "b/Week_06/297. \344\272\214\345\217\211\346\240\221\347\232\204\345\272\217\345\210\227\345\214\226\344\270\216\345\217\215\345\272\217\345\210\227\345\214\226\357\274\214DFS.js" @@ -0,0 +1,61 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ + +/** + * Encodes a tree to a single string. + * + * @param {TreeNode} root + * @return {string} + */ +var serialize = function (root) { + // 如果节点为空,使用一个特定的字符标识 + if (!root) { + return 'X'; + } + + // 每次递归都获取左右子树的序列化结果 + const left = serialize(root.left); + const right = serialize(root.right); + + // 将当前二叉树按照根,左,右的方式拼接 + return `${root.val},${left},${right}`; +}; + +/** + * Decodes your encoded data to tree. + * + * @param {string} data + * @return {TreeNode} + */ +var deserialize = function (data) { + // 将序列化的字符串,转换为数组 + const valList = data.split(','); + + // 生成二叉树的方法 + function build() { + // 由于二叉树已经按照根、左、右的顺序序列化,每次递归只需要按顺序取出每个节点的值即可 + const val = valList.shift(); + + // 如果当前值为X,表示此节点为空,直接返回null + if (val === 'X') { + return null; + } + + // 使用当前值生成一个节点 + const node = new TreeNode(val); + + // 由于子节点都是按照先左后右的顺序取出,因此按照同样顺序将子节点连接到根节点即可 + node.left = build(); + node.right = build(); + + // 将当前生成的节点返回,供上一层递归生成树 + return node; + } + + return build(); +}; diff --git "a/Week_06/309.\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/309.\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..bc22c31f --- /dev/null +++ "b/Week_06/309.\346\234\200\344\275\263\344\271\260\345\215\226\350\202\241\347\245\250\346\227\266\346\234\272\345\220\253\345\206\267\345\206\273\346\234\237\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,62 @@ +/* + * @lc app=leetcode.cn id=309 lang=javascript + * + * [309] 最佳买卖股票时机含冷冻期 + */ + +// @lc code=start +/** + * @param {number[]} prices + * @return {number} + */ +var maxProfit = function (prices) { + // 如果价格没有两天的价格,则无法进行交易,返回0即可 + if (prices <= 1) { + return 0; + } + + // 创建dp数组进行递推,并且需要根据前天的结果推断当天的情况 + // 假设现在是第i天,dp[0]保存的是i - 2天买卖后的结果,dp[1]保存的是i - 1天的结果 + // dp[0][0]保存的是进行卖出操作的结果,dp[0][1]保存的是买入后的结果。 + dp = [ + // 第一天的结果 + [ + 0, // 还没有买过,因此无法卖出,没有收入 + -prices[0], // 买入,支出prices[0]元 + ], + // 第二天的结果 + // 无论进行卖出还是买入操作,第一天可能进行了买入,或者没有任何操作 + [ + Math.max( + 0, // 前一天没有操作 + prices[1] - prices[0], // 前一天买入股票,当前才可以卖出 + ), + // 因两天同时买入,肯定比任何一天买入小,因此无需判断 + Math.max( + -prices[0], // 第一天买入了股票 + -prices[1], // 第一天没有买入,第二天买入 + ), + ], + ]; + + for (let i = 2; i < prices.length; i++) { + dp = [ + dp[1], // i-1天的状态,在下次递推时会变成i-2天的状态 + [ + // 第i天的卖出状态 + Math.max( + dp[1][0], // i-1天如果有卖出,那么今天没法卖出 + dp[1][1] + prices[i], // 只有i-1天买入,今天才可以卖出 + ), + // 第i天的买入状态 + Math.max( + dp[0][0] - prices[i], // i-2天如果卖出,今天才可以买入 + dp[1][1], // 如果i-1天有买入,今天无法买入 + ), + ], + ]; + } + + return Math.max(dp[1][0], dp[1][1]); +}; +// @lc code=end diff --git "a/Week_06/322. \351\233\266\351\222\261\345\205\221\346\215\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/322. \351\233\266\351\222\261\345\205\221\346\215\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..82f11a8e --- /dev/null +++ "b/Week_06/322. \351\233\266\351\222\261\345\205\221\346\215\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,42 @@ +/* + * @lc app=leetcode.cn id=322 lang=javascript + * + * [322] 零钱兑换 + */ + +// @lc code=start +/** + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ +var coinChange = function (coins, amount) { + // 兑换的金额是amount,因此需要从0开始递推到amount + // 由于要计算的是最小值,因此初始化为Infinity,避免计算错误 + let dp = new Array(amount + 1).fill(Infinity); + dp[0] = 0; // 金额为0时,硬币数为0,用于第一次使用硬币 + + // 从1开始才会累计硬币个数 + for (let i = 1; i < dp.length; i++) { + // 需要计算各种硬币的组合情况 + for (const coin of coins) { + // 向前查找硬币的使用情况,当前金额是由金额i - coin加上coin而来,需要至少保证金额大于0 + // i - coin === 0 时,是第一次使用当前硬币 + if (i - coin >= 0) { + // 如果要凑成当前金额,例如11,它可能的是由10+1,9+2,6+5组合而成 + dp[i] = Math.min( + dp[i], // 表示已存储了之前所有硬币组合的最小值 + // 表示当前硬币组合的硬币个数 + // 当前硬币个数,是由金额i - coin加上一个coin硬币而来 + // 因此数量就是dp[i - coin]的硬币数加1 + dp[i - coin] + 1, + ); + } + } + } + + // 如果硬币无法凑成所需金额,就会出现从amount向前查找任意硬币金额都只能找到Infinity的情况 + // 因此最后一位必然还是Infinity + return dp[amount] === Infinity ? -1 : dp[amount]; +}; +// @lc code=end diff --git "a/Week_06/322. \351\233\266\351\222\261\345\205\221\346\215\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/322. \351\233\266\351\222\261\345\205\221\346\215\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..237dc5a9 --- /dev/null +++ "b/Week_06/322. \351\233\266\351\222\261\345\205\221\346\215\242\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,89 @@ +/* + * @lc app=leetcode.cn id=322 lang=javascript + * + * [322] 零钱兑换 + * + * https://leetcode-cn.com/problems/coin-change/description/ + * + * algorithms + * Medium (42.19%) + * Likes: 1003 + * Dislikes: 0 + * Total Accepted: 167.9K + * Total Submissions: 397.8K + * Testcase Example: '[1,2,5]\n11' + * + * 给定不同面额的硬币 coins 和一个总金额 + * amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 + * + * 你可以认为每种硬币的数量是无限的。 + * + * + * + * 示例 1: + * + * + * 输入:coins = [1, 2, 5], amount = 11 + * 输出:3 + * 解释:11 = 5 + 5 + 1 + * + * 示例 2: + * + * + * 输入:coins = [2], amount = 3 + * 输出:-1 + * + * 示例 3: + * + * + * 输入:coins = [1], amount = 0 + * 输出:0 + * + * + * 示例 4: + * + * + * 输入:coins = [1], amount = 1 + * 输出:1 + * + * + * 示例 5: + * + * + * 输入:coins = [1], amount = 2 + * 输出:2 + * + * + * + * + * 提示: + * + * + * 1 + * 1 + * 0 + * + * + */ + +// @lc code=start +/** + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ +var coinChange = function (coins, amount) { + let dp = new Array(amount + 1).fill(Infinity); + dp[0] = 0; + + for (let i = 1; i < dp.length; i++) { + for (const coin of coins) { + if (i - coin >= 0) { + dp[i] = Math.min(dp[i - coin] + 1, dp[i]); + } + } + } + + return dp[amount] === Infinity ? -1 : dp[amount]; +}; +// @lc code=end diff --git "a/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" new file mode 100644 index 00000000..6620d767 --- /dev/null +++ "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" @@ -0,0 +1,53 @@ +/* + * @lc app=leetcode.cn id=53 lang=javascript + * + * [53] 最大子序和 + * + * https://leetcode-cn.com/problems/maximum-subarray/description/ + * + * algorithms + * Easy (53.00%) + * Likes: 2778 + * Dislikes: 0 + * Total Accepted: 391.6K + * Total Submissions: 738.8K + * Testcase Example: '[-2,1,-3,4,-1,2,1,-5,4]' + * + * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + * + * 示例: + * + * 输入: [-2,1,-3,4,-1,2,1,-5,4] + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * + * + * 进阶: + * + * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function (nums) { + let sum = nums[0]; // 用sum统计第i位之前的最大子序和 + let max = sum; // 第一个值即为最初的最大值 + + for (let i = 1; i < nums.length; i++) { + // 对于第i个数来说,nums[i]一定会被加入新的子序和中 + // 因此只需要判断nums[i]加上之前最大的子序和,是否比不加上更大即可 + sum = + nums[i] + + // 之前的子序和是否需要使用,若无需使用则为0 + Math.max(sum, 0); + // 每一位产生的最大子序和都被存储在原位置,因此需要同步比对找出所有子序和中的最大值 + max = Math.max(max, sum); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..d966d3e6 --- /dev/null +++ "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=53 lang=javascript + * + * [53] 最大子序和 + * + * https://leetcode-cn.com/problems/maximum-subarray/description/ + * + * algorithms + * Easy (53.00%) + * Likes: 2778 + * Dislikes: 0 + * Total Accepted: 391.6K + * Total Submissions: 738.8K + * Testcase Example: '[-2,1,-3,4,-1,2,1,-5,4]' + * + * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + * + * 示例: + * + * 输入: [-2,1,-3,4,-1,2,1,-5,4] + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * + * + * 进阶: + * + * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function (nums) { + let prevMax = nums[0]; + let max = nums[0]; + + for (let i = 1; i < nums.length; i++) { + prevMax = Math.max(prevMax, 0) + nums[i]; + max = Math.max(prevMax, max); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" new file mode 100644 index 00000000..d3d71761 --- /dev/null +++ "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" @@ -0,0 +1,56 @@ +/* + * @lc app=leetcode.cn id=53 lang=javascript + * + * [53] 最大子序和 + * + * https://leetcode-cn.com/problems/maximum-subarray/description/ + * + * algorithms + * Easy (53.00%) + * Likes: 2778 + * Dislikes: 0 + * Total Accepted: 391.6K + * Total Submissions: 738.8K + * Testcase Example: '[-2,1,-3,4,-1,2,1,-5,4]' + * + * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + * + * 示例: + * + * 输入: [-2,1,-3,4,-1,2,1,-5,4] + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * + * + * 进阶: + * + * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function (nums) { + let max = nums[0]; // 第一个值即为最初的最大值 + + // 由于需要判断上一个子序和是否需要取用,因此从1开始遍历数组,避免出现i为-1的问题 + // 此处复用了nums来存储每个位置的最大子序和,因此直接遍历nums即可 + for (let i = 1; i < nums.length; i++) { + // 对于第i个数来说,nums[i]一定会被加入新的子序和中 + // 因此只需要判断nums[i]加上之前最大的子序和,是否比不加上更大即可 + nums[i] = Math.max( + // 假设之前的子序和需要被使用 + nums[i] + nums[i - 1], + // 假设之前的子序和需要废弃,则为0 + nums[i] + 0, + ); + // 每一位产生的最大子序和都被存储在原位置,因此需要同步比对找出所有子序和中的最大值 + max = Math.max(max, nums[i]); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..19b81228 --- /dev/null +++ "b/Week_06/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=53 lang=javascript + * + * [53] 最大子序和 + * + * https://leetcode-cn.com/problems/maximum-subarray/description/ + * + * algorithms + * Easy (53.00%) + * Likes: 2778 + * Dislikes: 0 + * Total Accepted: 391.6K + * Total Submissions: 738.8K + * Testcase Example: '[-2,1,-3,4,-1,2,1,-5,4]' + * + * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + * + * 示例: + * + * 输入: [-2,1,-3,4,-1,2,1,-5,4] + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * + * + * 进阶: + * + * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function (nums) { + let dp = [nums[0]]; + let max = nums[0]; + + for (let i = 1; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], 0) + nums[i]; + max = Math.max(dp[i], max); + } + + return max; +}; +// @lc code=end diff --git "a/Week_06/605.\347\247\215\350\212\261\351\227\256\351\242\230.js" "b/Week_06/605.\347\247\215\350\212\261\351\227\256\351\242\230.js" new file mode 100644 index 00000000..7fc7706a --- /dev/null +++ "b/Week_06/605.\347\247\215\350\212\261\351\227\256\351\242\230.js" @@ -0,0 +1,35 @@ +/* + * @lc app=leetcode.cn id=605 lang=javascript + * + * [605] 种花问题 + */ + +// @lc code=start +/** + * @param {number[]} flowerbed + * @param {number} n + * @return {boolean} + */ +var canPlaceFlowers = function (flowerbed, n) { + if (n === 0) { + return true; + } + + for (let i = 0; i < flowerbed.length; i++) { + if ( + flowerbed[i] === 0 && + (i === 0 || flowerbed[i - 1] === 0) && + (i === flowerbed.length - 1 || flowerbed[i + 1] === 0) + ) { + flowerbed[i] = 1; + n--; + + if (n === 0) { + return true; + } + } + } + + return !n; +}; +// @lc code=end diff --git "a/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" new file mode 100644 index 00000000..10a1fb72 --- /dev/null +++ "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" @@ -0,0 +1,23 @@ +/** + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function (m, n) { + // 创建第一行,且第一行只有一种走法 + let arr = new Array(n).fill(1); + + // 第一行的走法都是1,因此从第二行开始计算 + for (let i = 1; i < m; i++) { + // 第一列的走法都为1,因此从第二列开始计算 + for (let j = 1; j < n; j++) { + // 每个位置都是上方和左方两个点走过来的 + // 那么当前走法数量,就是上方和左方个位置的走法之和 + // 当前位置原本存储的是上一行的结果,存储新结果之后,它就变成了下一位置的左边结果 + arr[j] = arr[j] + arr[j - 1]; + } + } + + // 数组的最后一位存储的就是最终结果 + return arr[n - 1]; +}; diff --git "a/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..10a1fb72 --- /dev/null +++ "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,23 @@ +/** + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function (m, n) { + // 创建第一行,且第一行只有一种走法 + let arr = new Array(n).fill(1); + + // 第一行的走法都是1,因此从第二行开始计算 + for (let i = 1; i < m; i++) { + // 第一列的走法都为1,因此从第二列开始计算 + for (let j = 1; j < n; j++) { + // 每个位置都是上方和左方两个点走过来的 + // 那么当前走法数量,就是上方和左方个位置的走法之和 + // 当前位置原本存储的是上一行的结果,存储新结果之后,它就变成了下一位置的左边结果 + arr[j] = arr[j] + arr[j - 1]; + } + } + + // 数组的最后一位存储的就是最终结果 + return arr[n - 1]; +}; diff --git "a/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" new file mode 100644 index 00000000..f195a0bd --- /dev/null +++ "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" @@ -0,0 +1,31 @@ +/** + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function (m, n) { + // 按行数创建一个数组,用来存储整个网格,每个格子都存储到达当前位置所需的路径数 + let arr = new Array(m); + + // 遍历网格的行 + for (let i = 0; i < arr.length; i++) { + // 只有网格超过一行,才需要计算 + if (i > 0) { + // 网格第一列,就只有一种走法,因此存入1 + arr[i] = [1]; + + // 从1开始遍历网格的列 + for (let j = 1; j < n; j++) { + // 每个位置都是上方和左方两个点走过来的 + // 那么当前走法数量,就是上方和左方个位置的走法之和 + arr[i][j] = arr[i - 1][j] + arr[i][j - 1]; + } + } else { + // 网格的第一行只有一种走法,因此全部为1 + arr[i] = new Array(n).fill(1); + } + } + + // 网格的最后一个位置,存储的就是最终结果 + return arr[m - 1][n - 1]; +}; diff --git "a/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..4d20a1e7 --- /dev/null +++ "b/Week_06/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=62 lang=javascript + * + * [62] 不同路径 + */ + +// @lc code=start +/** + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function (m, n) { + let dp = new Array(); + + for (let i = 0; i < m; i++) { + dp.push([1]); + + for (let j = 1; j < n; j++) { + if (i === 0) { + dp[i][j] = 1; + } else { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + } + + return dp[m - 1][n - 1]; +}; +// @lc code=end diff --git "a/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" "b/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" new file mode 100644 index 00000000..21cbfec2 --- /dev/null +++ "b/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" @@ -0,0 +1,99 @@ +/* + * @lc app=leetcode.cn id=63 lang=javascript + * + * [63] 不同路径 II + * + * https://leetcode-cn.com/problems/unique-paths-ii/description/ + * + * algorithms + * Medium (37.62%) + * Likes: 490 + * Dislikes: 0 + * Total Accepted: 122.2K + * Total Submissions: 325K + * Testcase Example: '[[0,0,0],[0,1,0],[0,0,0]]' + * + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + * + * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + * + * 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? + * + * + * + * 网格中的障碍物和空位置分别用 1 和 0 来表示。 + * + * + * + * 示例 1: + * + * + * 输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] + * 输出:2 + * 解释: + * 3x3 网格的正中间有一个障碍物。 + * 从左上角到右下角一共有 2 条不同的路径: + * 1. 向右 -> 向右 -> 向下 -> 向下 + * 2. 向下 -> 向下 -> 向右 -> 向右 + * + * + * 示例 2: + * + * + * 输入:obstacleGrid = [[0,1],[0,0]] + * 输出:1 + * + * + * + * + * 提示: + * + * + * m == obstacleGrid.length + * n == obstacleGrid[i].length + * 1 + * obstacleGrid[i][j] 为 0 或 1 + * + * + */ + +// @lc code=start +/** + * @param {number[][]} obstacleGrid + * @return {number} + */ +var uniquePathsWithObstacles = function (obstacleGrid) { + // 缓存网格行列的最后一位索引 + const m = obstacleGrid.length - 1; + const n = obstacleGrid[0].length - 1; + + // 如果起点或终点是1,必然无法走到终点,路径数量为0 + if (obstacleGrid[0][0] === 1 || obstacleGrid[m][n] === 1) { + return 0; + } + + // 初始化第一行的dp数组,起点的值为1 + let dp = new Array(n + 1).fill(0); + dp[0] = 1; + + // 从第二行、第二列开始遍历整个网格,计算路径数量 + for (let i = 0; i <= m; i++) { + for (let j = 0; j <= n; j++) { + // 如果当前位置是障碍物,路径数量为0 + if (obstacleGrid[i][j] === 1) { + dp[j] = 0; + continue; + } + + // j为0无需更新,会一直保持上一行的值 + if (j > 0) { + // 如果当前位置可以行走,路径数量为从上、左两个方向走过来的数量之和 + dp[j] = dp[j - 1] + dp[j]; + } + } + } + + // 终点存储的是所有路径数量 + return dp[n]; +}; +// @lc code=end diff --git "a/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" "b/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" new file mode 100644 index 00000000..e8abd27e --- /dev/null +++ "b/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" @@ -0,0 +1,121 @@ +/* + * @lc app=leetcode.cn id=63 lang=javascript + * + * [63] 不同路径 II + * + * https://leetcode-cn.com/problems/unique-paths-ii/description/ + * + * algorithms + * Medium (37.62%) + * Likes: 490 + * Dislikes: 0 + * Total Accepted: 122.2K + * Total Submissions: 325K + * Testcase Example: '[[0,0,0],[0,1,0],[0,0,0]]' + * + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + * + * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + * + * 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? + * + * + * + * 网格中的障碍物和空位置分别用 1 和 0 来表示。 + * + * + * + * 示例 1: + * + * + * 输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] + * 输出:2 + * 解释: + * 3x3 网格的正中间有一个障碍物。 + * 从左上角到右下角一共有 2 条不同的路径: + * 1. 向右 -> 向右 -> 向下 -> 向下 + * 2. 向下 -> 向下 -> 向右 -> 向右 + * + * + * 示例 2: + * + * + * 输入:obstacleGrid = [[0,1],[0,0]] + * 输出:1 + * + * + * + * + * 提示: + * + * + * m == obstacleGrid.length + * n == obstacleGrid[i].length + * 1 + * obstacleGrid[i][j] 为 0 或 1 + * + * + */ + +// @lc code=start +/** + * @param {number[][]} obstacleGrid + * @return {number} + */ +var uniquePathsWithObstacles = function (obstacleGrid) { + // 缓存网格行列的最后一位索引 + const m = obstacleGrid.length - 1; + const n = obstacleGrid[0].length - 1; + + // 如果起点或终点是1,必然无法走到终点,路径数量为0 + if (obstacleGrid[0][0] === 1 || obstacleGrid[m][n] === 1) { + return 0; + } + + // 初始化第一行的dp数组,起点的值为1 + let dp = [new Array(n + 1).fill(0)]; + dp[0][0] = 1; + + // 创建第一列的初始路径数量 + for (let i = 1; i <= m; i++) { + // 初始化当前行的初始路径数量,默认为0 + dp.push(new Array(n + 1).fill(0)); + + if (obstacleGrid[i][0] === 0) { + // 如果当前位置可以行走,数量等于上一行 + dp[i][0] = dp[i - 1][0]; + } else { + // 如果当前位置不可行走,数量为0 + dp[i][0] = 0; + } + } + + // 初始化第一行的路径数量 + for (let i = 0; i <= n; i++) { + if (obstacleGrid[0][i] === 0) { + // 如果当前位置可以行走,数量为1 + dp[0][i] = 1; + } else { + // 如果当前位置不可行走,从这以后的数量都为0 + // 第一行初始化的值都为0,直接退出即可 + break; + } + } + + // 从第二行、第二列开始遍历整个网格,计算路径数量 + for (let i = 1; i <= m; i++) { + for (let j = 1; j <= n; j++) { + if (obstacleGrid[i][j] === 0) { + // 如果当前位置可以行走,路径数量为从上、左两个方向走过来的数量之和 + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } else { + // 如果当前位置不可行走,路径数量为0 + dp[i][j] = 0; + } + } + } + + // 终点存储的是所有路径数量 + return dp[m][n]; +}; +// @lc code=end diff --git "a/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..2773a447 --- /dev/null +++ "b/Week_06/63. \344\270\215\345\220\214\350\267\257\345\276\204 II\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=63 lang=javascript + * + * [63] 不同路径 II + */ + +// @lc code=start +/** + * @param {number[][]} obstacleGrid + * @return {number} + */ +var uniquePathsWithObstacles = function (obstacleGrid) { + obstacleGrid[0][0] = obstacleGrid[0][0] === 0 ? 1 : 0; + const m = obstacleGrid.length; + const n = obstacleGrid[0].length; + + for (let i = 1; i < m; i++) { + if (obstacleGrid[i][0] === 1) { + obstacleGrid[i][0] = 0; + } else { + obstacleGrid[i][0] = obstacleGrid[i - 1][0]; + } + } + + for (let i = 1; i < n; i++) { + if (obstacleGrid[0][i] === 1) { + obstacleGrid[0][i] = 0; + } else { + obstacleGrid[0][i] = obstacleGrid[0][i - 1]; + } + } + + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + if (obstacleGrid[i][j] === 1) { + obstacleGrid[i][j] = 0; + } else { + obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1]; + } + } + } + + return obstacleGrid[m - 1][n - 1]; +}; +// @lc code=end diff --git "a/Week_06/64. \346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/64. \346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..ff090087 --- /dev/null +++ "b/Week_06/64. \346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,43 @@ +/* + * @lc app=leetcode.cn id=64 lang=javascript + * + * [64] 最小路径和 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function (grid) { + let m = grid.length; // 缓存行数量 + let n = grid[0].length; // 缓存列数量 + + // 创建第一列的路径和初始值 + for (let i = 1; i < m; i++) { + // 第一列只能从上一列走过来,因此每个路径和都为上一列加上这一列 + grid[i][0] += grid[i - 1][0]; + } + + // 创建第一行的路径和初始值 + for (let i = 1; i < n; i++) { + // 第一行只能从上一行走过来,因此每个路径和都为上一行加上这一行 + grid[0][i] += grid[0][i - 1]; + } + + // 递推每个位置的路径和 + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + // 当前位置的最小路径和,等于上一步的最小路径,加上当前的路径值 + grid[i][j] = + // 由于要去最小路径和,当前位置只能从上方和左方走过来,因此取前两步的最小值 + Math.min(grid[i - 1][j], grid[i][j - 1]) + + // 加上当前的路径值 + grid[i][j]; + } + } + + // 走到最后位置时,自然就能推出到达终点的最小路径和 + return grid[m - 1][n - 1]; +}; +// @lc code=end diff --git "a/Week_06/647. \345\233\236\346\226\207\345\255\220\344\270\262\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" "b/Week_06/647. \345\233\236\346\226\207\345\255\220\344\270\262\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\270\200\347\273\264\346\225\260\347\273\204.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_06/647. \345\233\236\346\226\207\345\255\220\344\270\262\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" "b/Week_06/647. \345\233\236\346\226\207\345\255\220\344\270\262\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" new file mode 100644 index 00000000..e4b25d36 --- /dev/null +++ "b/Week_06/647. \345\233\236\346\226\207\345\255\220\344\270\262\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\272\214\347\273\264\346\225\260\347\273\204.js" @@ -0,0 +1,72 @@ +/* + * @lc app=leetcode.cn id=647 lang=javascript + * + * [647] 回文子串 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var countSubstrings = function (s) { + /* +如果输入aabaca,双循环生成的所有子串如下: +如果在第一层循环结束时,打印生成的子串,会发现实际上是从左到右按列生成的。 +[ + [ 'a ', 'aa ', 'aab ', 'aaba ', 'aabac ', 'aabaca' ], + [ ' ', 'a ', 'ab ', 'aba ', 'abac ', 'abaca ' ], + [ ' ', ' ', 'b ', 'ba ', 'bac ', 'baca ' ], + [ ' ', ' ', ' ', 'a ', 'ac ', 'aca ' ], + [ ' ', ' ', ' ', ' ', 'c ', 'ca ' ], + [ ' ', ' ', ' ', ' ', ' ', 'a ' ] +] +*/ + let count = 0; // 统计所有回文子串数量 + let dp = []; // 将所有生成的子串用矩阵标记,是回文子串的标记为true,其余为false + // let strs = []; // 用于生成所有子串 + + // 标记每行的子串 + for (let j = 0; j < s.length; j++) { + // 默认一行中所有的子串都是false + dp.push(new Array(s.length).fill(false)); + // strs.push(new Array(s.length).fill(new Array(s.length).fill(' ').join(''))); + // 标记每列的子串, + for (let i = 0; i <= j; i++) { + // 生成子串的代码 + /* strs[i][j] = s.slice(i, j + 1); + while (strs[i][j].length < s.length) { + strs[i][j] += ' '; + } */ + // 两个索引相等时,子串只有一个字母,此时必然是是回文串 + if (j === i) { + count++; // 将子串计数 + dp[i][j] = true; // 在矩阵中标记回文子串 + } + // 有两个字母,且两个字母相等,是回文串 + else if (j - i === 1 && s[i] === s[j]) { + count++; // 将子串计数 + dp[i][j] = true; // 在矩阵中标记回文子串 + } else if ( + // 字符串数量多于两个 + j - i > 1 && + // 首尾两个字母相等 + s[i] === s[j] && + // 且dp[i + 1][j - 1],也就是剔除首尾后的子串是回文子串 + dp[i + 1][j - 1] + ) { + count++; // 将子串计数 + dp[i][j] = true; // 在矩阵中标记回文子串 + } + } + // 打印每次循环生成的结果 + // console.log(strs); + // console.log(dp); + } + // 打印标记好的子串结果 + // console.log(strs); + // console.log(dp); + + return count; +}; +// @lc code=end diff --git "a/Week_06/714. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/714. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..f2a7479a --- /dev/null +++ "b/Week_06/714. \344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272\345\220\253\346\211\213\347\273\255\350\264\271\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,37 @@ +/* + * @lc app=leetcode.cn id=714 lang=javascript + * + * [714] 买卖股票的最佳时机含手续费 + */ + +// @lc code=start +/** + * @param {number[]} prices + * @param {number} fee + * @return {number} + */ +var maxProfit = function (prices, fee) { + dp = [ + 0, // 此处保存的是当前完成卖出交易后,赚到的利润最大值,开始时没有交易,因此为0 + -prices[0], // 此处保存的是当前完成买入交易后,赚到的利润最大值,开始时买入股票,因此为负 + ]; + + for (let i = 1; i < prices.length; i++) { + dp = [ + // 计算当前卖出后的最大利润 + Math.max( + dp[0], // 如果上一次已卖出,这次没有股票可卖 + dp[1] + prices[i] - fee, // 如果上次有买入,那么这次就按当前价格卖出,获得收益,只有卖出才需要手续费 + ), + // 计算当前买入后的最大利润 + Math.max( + dp[1], // 上次已经买过,这次没必要再买,否则会损失收益 + dp[0] - prices[i], // 如果上次卖出过,这次就买入,等待下次卖出机会 + ), + ]; + } + + // 最后一次操作后,取最终利润的最大值 + return Math.max(...dp); +}; +// @lc code=end diff --git "a/Week_06/91. \350\247\243\347\240\201\346\226\271\346\263\225\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_06/91. \350\247\243\347\240\201\346\226\271\346\263\225\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..8f7f4bd1 --- /dev/null +++ "b/Week_06/91. \350\247\243\347\240\201\346\226\271\346\263\225\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,53 @@ +/* + * @lc app=leetcode.cn id=91 lang=javascript + * + * [91] 解码方法 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var numDecodings = function (s) { + // 如果编码不存在或者第一个数字为0,会无法解码,返回0 + if (s.length === 0 || s[0] === '0') { + return 0; + } + // 该题需要向前查找两个数字,因此创建的dp数组长度加1,表示从0开始推导 + // 由于dp长度比s大1,因此递推时dp[i+1]位置才对应的是s[i] + // 实际有效初始化的是数组的前两个值,因为第一个数字必然可以编码 + // 而递推是从第二个数字开始,如果前两个数字可以编码,就需要从前0和1两种情况开始递推 + let dp = new Array(s.length + 1).fill(1); + + // 由于要考虑两个数字的编码,因此从1开始递推 + for (let i = 1; i < s.length; i++) { + // 获取前一个数字 + const prevChar = s[i - 1]; + // 获取连续的两个数字 + const towChar = s[i - 1] + s[i]; + + // 只有编码0是特殊的 + if (s[i] === '0') { + // 如果前一个编码是1或2,那么连续两个编码就有意义 + if (prevChar === '1' || prevChar === '2') { + // 当前位置的编码方式,和上一个编码相同 + dp[i + 1] = dp[i - 1]; + } else { + // 如果前一个编码不是1或2,例如30,它是一个无意义编码,直接返回0 + return 0; + } + } + // 如果是10~26的编码,是有意义的数字 + else if (10 < towChar && towChar <= 26) { + // 当前位置的编码方式,是由前两个位置的编码方式结合而成 + dp[i + 1] = dp[i] + dp[i - 1]; + } else { + // 当前位置的编码方式,和上一个编码相同 + dp[i + 1] = dp[i]; + } + } + + return dp[s.length]; +}; +// @lc code=end diff --git a/Week_06/README.md b/Week_06/README.md index 50de3041..66b75b7b 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -1 +1,9 @@ -学习笔记 \ No newline at end of file +# 学习总结 + +第五周大都在补前4周落下的题目,影响了做题进度。下周课程中,留的作业看起来量并不大,我会在完成下周作业的基础上,争取把动态规划的题目都补上。 + +动态规划的题目,重点在于找到状态转移方程,但可能是熟练度不够的原因,对于这点还是感到很困难。 + +很多题目其实意思不难,但是大多数题解都写的又长又臭,很多时候反而加深了理解难度。反而直接把题解代码跑一遍,逐次打印递推过程,有必要的时候再看文字帮助理解,效果会更好。 + +这周由于都在赶做题进度,没来得及写题解,不过已经在作业中留了详细的注释,有空会补上题解。 diff --git "a/Week_07/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS.js" "b/Week_07/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS.js" new file mode 100644 index 00000000..c78ea958 --- /dev/null +++ "b/Week_07/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS.js" @@ -0,0 +1,84 @@ +/* + * @lc app=leetcode.cn id=1091 lang=javascript + * + * [1091] 二进制矩阵中的最短路径 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + // 缓存矩阵的终点位置 + const m = grid.length - 1; + const n = grid[0].length - 1; + + // 当起点和终点为1时,必然无法到达终点 + if (grid[0][0] === 1 || grid[m][n] === 1) { + return -1; + } + + // 如果矩阵只有1个点,且为0,路径为1 + if (m === 0 && n === 0 && grid[0][0] === 0) { + return 1; + } + + let queue = [[0, 0]]; // 使用队列进行BFS搜索 + let level = 1; // 缓存路径长度,起点的长度为1 + // 可以向四周所有方向行走,缓存8个方向 + const direction = [ + [-1, 1], // 右上 + [0, 1], // 右 + [1, 1], // 右下 + [1, 0], // 下 + [1, -1], // 左下 + // 一下3种都是往回走,无需判断 + // [-1, 0], // 上 + // [0, -1], // 左 + // [-1, -1], // 左上 + ]; + + // 如果队列中有值,则继续搜索 + while (queue.length) { + // 缓存当前层的节点数量 + let queueLength = queue.length; + + // 每次只遍历当前一层的节点 + while (--queueLength >= 0) { + // 出队一个坐标,计算它可以行走的下一步位置 + const [x, y] = queue.shift(); + + for (let i = 0; i < direction.length; i++) { + // 下一步可以向四周行走,计算出相应新坐标 + const newX = x + direction[i][0]; + const newY = y + direction[i][1]; + + // 如果新坐标超出网格,或者被标记为1,表示无法行走,则跳过 + if ( + newX < 0 || + newY < 0 || + newX > m || + newY > m || + grid[newX][newY] === 1 + ) { + continue; + } + + // 如果新坐标是终点,表示找到路径,返回长度即可 + if (newX === m && newY === n) { + return level + 1; + } + // 将走过的位置标记为1,避免重复行走 + grid[newX][newY] = 1; + // 将下一步的坐标存入队列,用于下一层循环 + queue.push([newX, newY]); + } + } + + level++; // 每向前走一层,将步数加1 + } + + return -1; +}; +// @lc code=end diff --git "a/Week_07/130. \350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237\357\274\214\345\271\266\346\237\245\351\233\206.js" "b/Week_07/130. \350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237\357\274\214\345\271\266\346\237\245\351\233\206.js" new file mode 100644 index 00000000..fc48e5fa --- /dev/null +++ "b/Week_07/130. \350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237\357\274\214\345\271\266\346\237\245\351\233\206.js" @@ -0,0 +1,88 @@ +/* + * @lc app=leetcode.cn id=130 lang=javascript + * + * [130] 被围绕的区域 + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solve = function (board) { + if (!board.length) { + return []; + } + const m = board.length; + const n = board[0].length; + const x = m - 1; + const y = n - 1; + const dx = [1, -1, 0, 0]; + const dy = [0, 0, 1, -1]; + const dummy = m * n; + let unionFind = new UnionFind(dummy + 1); + + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (board[i][j] === 'O') { + if (i === 0 || j === 0 || i === x || j === y) { + unionFind.union(i * n + j, dummy); + } else { + for (let k = 0; k < 4; k++) { + const newI = i + dx[k]; + const newJ = j + dy[k]; + + if (board[newI][newJ] === 'O') { + unionFind.union(i * n + j, newI * n + newJ); + } + } + } + } + } + } + + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (board[i][j] === 'O') { + if (!unionFind.isConnected(i * n + j, dummy)) { + board[i][j] = 'X'; + } + } + } + } +}; + +class UnionFind { + constructor(n) { + this.parent = new Array(n); + + for (let i = 0; i < this.parent.length; i++) { + this.parent[i] = i; + } + } + + find(p) { + while (p !== this.parent[p]) { + this.parent[p] = this.parent[this.parent[p]]; + p = this.parent[p]; + } + + return p; + } + + isConnected(p, q) { + return this.find(p) === this.find(q); + } + + union(p, q) { + const rootP = this.find(p); + const rootQ = this.find(q); + + if (rootP === rootQ) { + return; + } + + this.parent[rootP] = rootQ; + } +} +// @lc code=end diff --git "a/Week_07/130. \350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/130. \350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..35898ae5 --- /dev/null +++ "b/Week_07/130. \350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,155 @@ +/* + * @lc app=leetcode.cn id=130 lang=javascript + * + * [130] 被围绕的区域 + * + * https://leetcode-cn.com/problems/surrounded-regions/description/ + * + * algorithms + * Medium (42.29%) + * Likes: 448 + * Dislikes: 0 + * Total Accepted: 84.2K + * Total Submissions: 199K + * Testcase Example: '[["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]' + * + * 给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。 + * + * 找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。 + * + * 示例: + * + * X X X X + * X O O X + * X X O X + * X O X X + * + * + * 运行你的函数后,矩阵变为: + * + * X X X X + * X X X X + * X X X X + * X O X X + * + * + * 解释: + * + * 被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' + * 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。 + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solve = function (board) { + // 如果board为空,则无需搜索 + if (!board || !board.length) { + return; + } + // [["O","X","X","O","X"],["X","O","O","X","O"],["X","O","X","O","X"],["O","X","O","O","O"],["X","X","O","X","O"]] + // [] + + const m = board.length; // 缓存矩阵行数 + const n = board[0].length; // 缓存矩阵列数 + const x = m - 1; // 缓存最后一行 + const y = n - 1; // 缓存最后一列 + const dummy = m * n; // 创建一个虚拟节点,将所有边缘节点与其连接成一个集合 + const unionFind = new UnionFind(dummy + 1); // 创建集合,数量比矩阵节点总数多一个,即多了虚拟节点 + const dx = [1, -1, 0, 0]; // 缓存行方向向量 + const dy = [0, 0, 1, -1]; // 缓存列方向向量 + + // 遍历矩阵的所有点 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + // 如果当前为O,则 表示需要进行合并 + if (board[i][j] === 'O') { + const index = i * n + j; + + // 如果节点在边缘,则合并到虚拟节点,形成一个独立集合 + if (i === 0 || j === 0 || i === x || j === y) { + unionFind.union(index, dummy); + } else { + // 如果是中间节点,则只要与四周的节点合并 + // 与边缘连通的节点,自然会被合并到虚拟节点下 + for (let k = 0; k < 4; k++) { + // 计算四周点的坐标 + const newI = i + dx[k]; + const newJ = j + dy[k]; + + // 如果四周节点为O,才需要进行合并 + if (board[newI][newJ] === 'O') { + unionFind.union(index, newI * n + newJ); + } + } + } + } + } + } + + // 完成集合合并后,再次遍历所有节点,不在虚拟节点集合中的,设置为X + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + // 判断当前节点是否与虚拟节点连接 + if (!unionFind.isConnect(i * n + j, dummy)) { + board[i][j] = 'X'; + } + } + } +}; + +class UnionFind { + constructor(n) { + this.parent = []; // 使用数组保存集合 + + // 初始化所有集合,每个集合的父节点都是自身,也就是它们都是独立集合 + for (let i = 0; i < n; i++) { + this.parent.push(i); + } + } + + // 查找一个节点所在集合的根节点 + find(p) { + // 当前节点的父节点是其自身时,它就是集合的根节点 + while (p != this.parent[p]) { + // 将当前节点父节点指针,直接指向爷爷节点,实现路径压缩 + this.parent[p] = this.parent[this.parent[p]]; + // 将指针向上移动 + p = this.parent[p]; + } + + // 将找到的根节点返回 + return p; + } + + // 判断p和q是否属于同一个集合 + isConnect(p, q) { + // 找到两个节点所在集合的根节点 + const rootP = this.find(p); + const rootQ = this.find(q); + + // 根节点相等,表示它们在同一个集合 + return rootP === rootQ; + } + + // 合并两个集合 + union(p, q) { + // 找到两个节点所在集合的根节点 + let rootP = this.find(p); + let rootQ = this.find(q); + + // 如果他们是同一个节点,表示p和q在同一个集合,无需合并 + if (rootP === rootQ) { + return; + } + + // 将rootP的根节点指向rootQ,完成集合的合并 + this.parent[rootP] = rootQ; + // 减少集合数量 + this.count--; + } +} +// @lc code=end diff --git "a/Week_07/200.\345\262\233\345\261\277\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206.js" "b/Week_07/200.\345\262\233\345\261\277\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206.js" new file mode 100644 index 00000000..1974465b --- /dev/null +++ "b/Week_07/200.\345\262\233\345\261\277\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206.js" @@ -0,0 +1,110 @@ +/* + * @lc app=leetcode.cn id=200 lang=javascript + * + * [200] 岛屿数量 + */ + +// @lc code=start +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + // [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]] + // [["1","0","1","1","0","1","1"]] + const m = grid.length; // 缓存行数量 + const n = grid[0].length; // 缓存列数量 + const dx = [1, -1, 0, 0]; // 缓存行方向 + const dy = [0, 0, 1, -1]; // 缓存列方向 + // 根据grid上的陆地情况,初始化并查集 + let unionFind = new UnionFind(grid, m, n); + + // 遍历整个地图,将陆地都合并到一个集合中 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + // 如果是陆地,就进行集合的合并 + if (grid[i][j] === '1') { + // 将当前位置设置为0,避免重复合并 + grid[i][j] = '0'; + + // 计算当前坐标四周的的点坐标 + for (let k = 0; k < 4; k++) { + // 计算新的i和j + const newI = i + dx[k]; + const newJ = j + dy[k]; + + // 当新坐标在地图内,且也是陆地时,将这些点都合并到一个集合 + if ( + newI >= 0 && + newJ >= 0 && + newI < m && + newJ < n && + grid[newI][newJ] === '1' + ) { + // 将所有泸定合并到一个集合,并进行计数 + unionFind.union(newI * n + newJ, i * n + j); + } + } + } + } + } + + // 所有陆地都合并后,剩下的集合数量就是岛屿数量 + return unionFind.count; +}; + +// 并查集 +class UnionFind { + constructor(grid, m, n) { + this.count = 0; // 统计集合数量,初始化为n + this.parent = new Array(m * n); // 使用数组保存集合 + + // 根据grid的值初始化所有集合 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + // 计算grid索引对应并查集中的索引 + const index = i * n + j; + + // 只有是陆地的位置才是集合元素 + if (grid[i][j] === '1') { + this.parent[index] = index; // 对元素做标记 + this.count++; // 对集合进行计数 + } else { + this.parent[index] = -1; // 非集合元素标记为-1 + } + } + } + } + + // 查找一个节点所在集合的根节点 + find(p) { + // 当前节点的父节点是其自身时,它就是集合的根节点 + while (p != this.parent[p]) { + // 将当前节点父节点指针,直接指向爷爷节点,实现路径压缩 + this.parent[p] = this.parent[this.parent[p]]; + // 将指针向上移动 + p = this.parent[p]; + } + + // 将找到的根节点返回 + return p; + } + + // 合并两个集合 + union(p, q) { + // 找到两个节点所在集合的根节点 + let rootP = this.find(p); + let rootQ = this.find(q); + + // 如果他们是同一个节点,表示p和q在同一个集合,无需合并 + if (rootP === rootQ) { + return; + } + + // 将rootP的根节点指向rootQ,完成集合的合并 + this.parent[rootP] = rootQ; + // 减少集合数量 + this.count--; + } +} +// @lc code=end diff --git "a/Week_07/200.\345\262\233\345\261\277\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/200.\345\262\233\345\261\277\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..1974465b --- /dev/null +++ "b/Week_07/200.\345\262\233\345\261\277\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,110 @@ +/* + * @lc app=leetcode.cn id=200 lang=javascript + * + * [200] 岛屿数量 + */ + +// @lc code=start +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + // [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]] + // [["1","0","1","1","0","1","1"]] + const m = grid.length; // 缓存行数量 + const n = grid[0].length; // 缓存列数量 + const dx = [1, -1, 0, 0]; // 缓存行方向 + const dy = [0, 0, 1, -1]; // 缓存列方向 + // 根据grid上的陆地情况,初始化并查集 + let unionFind = new UnionFind(grid, m, n); + + // 遍历整个地图,将陆地都合并到一个集合中 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + // 如果是陆地,就进行集合的合并 + if (grid[i][j] === '1') { + // 将当前位置设置为0,避免重复合并 + grid[i][j] = '0'; + + // 计算当前坐标四周的的点坐标 + for (let k = 0; k < 4; k++) { + // 计算新的i和j + const newI = i + dx[k]; + const newJ = j + dy[k]; + + // 当新坐标在地图内,且也是陆地时,将这些点都合并到一个集合 + if ( + newI >= 0 && + newJ >= 0 && + newI < m && + newJ < n && + grid[newI][newJ] === '1' + ) { + // 将所有泸定合并到一个集合,并进行计数 + unionFind.union(newI * n + newJ, i * n + j); + } + } + } + } + } + + // 所有陆地都合并后,剩下的集合数量就是岛屿数量 + return unionFind.count; +}; + +// 并查集 +class UnionFind { + constructor(grid, m, n) { + this.count = 0; // 统计集合数量,初始化为n + this.parent = new Array(m * n); // 使用数组保存集合 + + // 根据grid的值初始化所有集合 + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + // 计算grid索引对应并查集中的索引 + const index = i * n + j; + + // 只有是陆地的位置才是集合元素 + if (grid[i][j] === '1') { + this.parent[index] = index; // 对元素做标记 + this.count++; // 对集合进行计数 + } else { + this.parent[index] = -1; // 非集合元素标记为-1 + } + } + } + } + + // 查找一个节点所在集合的根节点 + find(p) { + // 当前节点的父节点是其自身时,它就是集合的根节点 + while (p != this.parent[p]) { + // 将当前节点父节点指针,直接指向爷爷节点,实现路径压缩 + this.parent[p] = this.parent[this.parent[p]]; + // 将指针向上移动 + p = this.parent[p]; + } + + // 将找到的根节点返回 + return p; + } + + // 合并两个集合 + union(p, q) { + // 找到两个节点所在集合的根节点 + let rootP = this.find(p); + let rootQ = this.find(q); + + // 如果他们是同一个节点,表示p和q在同一个集合,无需合并 + if (rootP === rootQ) { + return; + } + + // 将rootP的根节点指向rootQ,完成集合的合并 + this.parent[rootP] = rootQ; + // 减少集合数量 + this.count--; + } +} +// @lc code=end diff --git "a/Week_07/203.\345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221)\357\274\214\347\254\254\344\270\211\351\201\215.js" "b/Week_07/203.\345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221)\357\274\214\347\254\254\344\270\211\351\201\215.js" new file mode 100644 index 00000000..8cb92499 --- /dev/null +++ "b/Week_07/203.\345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221)\357\274\214\347\254\254\344\270\211\351\201\215.js" @@ -0,0 +1,109 @@ +/* + * @lc app=leetcode.cn id=208 lang=javascript + * + * [208] 实现 Trie (前缀树) + * + * https://leetcode-cn.com/problems/implement-trie-prefix-tree/description/ + * + * algorithms + * Medium (69.54%) + * Likes: 498 + * Dislikes: 0 + * Total Accepted: 67.9K + * Total Submissions: 97.7K + * Testcase Example: '["Trie","insert","search","search","startsWith","insert","search"]\n' + + '[[],["apple"],["apple"],["app"],["app"],["app"],["app"]]' + * + * 实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。 + * + * 示例: + * + * Trie trie = new Trie(); + * + * trie.insert("apple"); + * trie.search("apple"); // 返回 true + * trie.search("app"); // 返回 false + * trie.startsWith("app"); // 返回 true + * trie.insert("app"); + * trie.search("app"); // 返回 true + * + * 说明: + * + * + * 你可以假设所有的输入都是由小写字母 a-z 构成的。 + * 保证所有输入均为非空字符串。 + * + * + */ + +// @lc code=start +/** + * Initialize your data structure here. + */ +var Trie = function () { + this.root = {}; + this.EOF = Symbol('EOF'); +}; + +/** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function (word) { + let node = this.root; + + for (const char of word) { + node[char] = node[char] || {}; + node = node[char]; + } + + node[this.EOF] = this.EOF; +}; + +/** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ +Trie.prototype.search = function (word) { + let node = this.root; + + for (const char of word) { + if (!node[char]) { + return false; + } + + node = node[char]; + } + + return node[this.EOF] === this.EOF; +}; + +/** + * Returns if there is any word in the trie that starts with the given prefix. + * @param {string} prefix + * @return {boolean} + */ +Trie.prototype.startsWith = function (prefix) { + let node = this.root; + + for (const char of prefix) { + if (!node[char]) { + return false; + } + + node = node[char]; + } + + return true; +}; + +/** + * Your Trie object will be instantiated and called as such: + * var obj = new Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ +// @lc code=end diff --git "a/Week_07/208. \345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221).js" "b/Week_07/208. \345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221).js" new file mode 100644 index 00000000..e04cbf79 --- /dev/null +++ "b/Week_07/208. \345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221).js" @@ -0,0 +1,95 @@ +/* + * @lc app=leetcode.cn id=208 lang=javascript + * + * [208] 实现 Trie (前缀树) + */ + +// @lc code=start +/** + * Initialize your data structure here. + */ +var Trie = function () { + // 根节点使用一个空对象,单词的每个字母作为对象的一个key + this.root = {}; + // 使用一个固定的标记,标记一个节点为字典树的根节点 + // 此处使用Symbol标记,避免重复 + this.endOfWord = Symbol('EOF'); +}; + +/** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function (word) { + // 当一个单词插入字典树时,都从根节点开始插入 + let node = this.root; + + // 逐个字母遍历单词 + for (const char of word) { + // 如果字母未在字典树中川建国节点,则创建一个新节点 + node[char] = node[char] || {}; + // 移动节点,向下遍历 + node = node[char]; + } + + // 标记当前节点是一个单词的结束位置 + node[this.endOfWord] = this.endOfWord; +}; + +/** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ +Trie.prototype.search = function (word) { + // 单词的搜索从根节点开始 + let node = this.root; + + // 在字典树中查找单词的每个字母 + for (const char of word) { + // 如果当前字母不存在于字典树,该单词也一定不存在 + if (!node[char]) { + return false; + } + + // 移动节点,向下查找 + node = node[char]; + } + + // 如果该单词的最后一个字母对应的节点,在字典树中被标记为单词结尾,才表示这个单词在字典树中存在 + return node[this.endOfWord] === this.endOfWord; +}; + +/** + * Returns if there is any word in the trie that starts with the given prefix. + * @param {string} prefix + * @return {boolean} + */ +Trie.prototype.startsWith = function (prefix) { + // 从根节点开始判断单词是否存在 + let node = this.root; + + // 在字典树中查找单词的每个字母 + for (const char of prefix) { + // 如果当前字母不存在于字典树,该单词也一定不存在 + if (!node[char]) { + return false; + } + + // 移动节点,向下查找 + node = node[char]; + } + + // 此处无需判断单词是否完整的在字典树中存在,只要字典树中存在单词所有字母即可 + return true; +}; + +/** + * Your Trie object will be instantiated and called as such: + * var obj = new Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ +// @lc code=end diff --git "a/Week_07/208. \345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221)\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/208. \345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221)\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..8cb92499 --- /dev/null +++ "b/Week_07/208. \345\256\236\347\216\260 Trie (\345\211\215\347\274\200\346\240\221)\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,109 @@ +/* + * @lc app=leetcode.cn id=208 lang=javascript + * + * [208] 实现 Trie (前缀树) + * + * https://leetcode-cn.com/problems/implement-trie-prefix-tree/description/ + * + * algorithms + * Medium (69.54%) + * Likes: 498 + * Dislikes: 0 + * Total Accepted: 67.9K + * Total Submissions: 97.7K + * Testcase Example: '["Trie","insert","search","search","startsWith","insert","search"]\n' + + '[[],["apple"],["apple"],["app"],["app"],["app"],["app"]]' + * + * 实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。 + * + * 示例: + * + * Trie trie = new Trie(); + * + * trie.insert("apple"); + * trie.search("apple"); // 返回 true + * trie.search("app"); // 返回 false + * trie.startsWith("app"); // 返回 true + * trie.insert("app"); + * trie.search("app"); // 返回 true + * + * 说明: + * + * + * 你可以假设所有的输入都是由小写字母 a-z 构成的。 + * 保证所有输入均为非空字符串。 + * + * + */ + +// @lc code=start +/** + * Initialize your data structure here. + */ +var Trie = function () { + this.root = {}; + this.EOF = Symbol('EOF'); +}; + +/** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function (word) { + let node = this.root; + + for (const char of word) { + node[char] = node[char] || {}; + node = node[char]; + } + + node[this.EOF] = this.EOF; +}; + +/** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ +Trie.prototype.search = function (word) { + let node = this.root; + + for (const char of word) { + if (!node[char]) { + return false; + } + + node = node[char]; + } + + return node[this.EOF] === this.EOF; +}; + +/** + * Returns if there is any word in the trie that starts with the given prefix. + * @param {string} prefix + * @return {boolean} + */ +Trie.prototype.startsWith = function (prefix) { + let node = this.root; + + for (const char of prefix) { + if (!node[char]) { + return false; + } + + node = node[char]; + } + + return true; +}; + +/** + * Your Trie object will be instantiated and called as such: + * var obj = new Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ +// @lc code=end diff --git "a/Week_07/212.\345\215\225\350\257\215\346\220\234\347\264\242 II\357\274\214\345\255\227\345\205\270\346\240\221.js" "b/Week_07/212.\345\215\225\350\257\215\346\220\234\347\264\242 II\357\274\214\345\255\227\345\205\270\346\240\221.js" new file mode 100644 index 00000000..6bb0bffa --- /dev/null +++ "b/Week_07/212.\345\215\225\350\257\215\346\220\234\347\264\242 II\357\274\214\345\255\227\345\205\270\346\240\221.js" @@ -0,0 +1,169 @@ +/* + * @lc app=leetcode.cn id=212 lang=javascript + * + * [212] 单词搜索 II + * + * https://leetcode-cn.com/problems/word-search-ii/description/ + * + * algorithms + * Hard (44.53%) + * Likes: 310 + * Dislikes: 0 + * Total Accepted: 27.3K + * Total Submissions: 61.3K + * Testcase Example: '[["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]\n' + + '["oath","pea","eat","rain"]' + * + * 给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词。 + * + * 单词必须按照字母顺序,通过 相邻的单元格 + * 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。 + * + * + * + * 示例 1: + * + * + * 输入:board = + * [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], + * words = ["oath","pea","eat","rain"] + * 输出:["eat","oath"] + * + * + * 示例 2: + * + * + * 输入:board = [["a","b"],["c","d"]], words = ["abcb"] + * 输出:[] + * + * + * + * + * 提示: + * + * + * m == board.length + * n == board[i].length + * 1 + * board[i][j] 是一个小写英文字母 + * 1 + * 1 + * words[i] 由小写英文字母组成 + * words 中的所有字符串互不相同 + * + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ +var findWords = function (board, words) { + // [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]\n["oath","pea","eat","rain","oathi","oathk","oathf","oate","oathii","oathfi","oathfii"] + // [["a","a"]\n["aaa"] + // [["a","a"]]\n["a"] + let trie = new Trie(); // 创建一个字典树 + let result = new Set(); // 使用Set存储查找到的单词,避免重复 + const dx = [1, -1, 0, 0]; // x轴的4个方向 + const dy = [0, 0, 1, -1]; // y轴的4个方向 + const m = board.length; // 缓存行数 + const n = board[0].length; // 缓存列数 + + // 将单词表中的单词都插入字典树 + for (const word of words) { + trie.insert(word); + } + + // 查找当前位置的字母是否能够组合成单词 + // 使用DFS在矩阵中向四周查找能组成单词的字母 + function dfs( + // 当前查询字母的坐标 + i, + j, + prevWord, // 之前已经生成的单词 + prevDict, // 上一个字母在字典树中的节点 + ) { + const currChar = board[i][j]; // 当前坐标的字母 + const currDict = prevDict[currChar]; // 当前坐标在字典树中的节点 + const currWord = prevWord + currChar; // 当前字母组成的新单词 + + // 查看新单词在字典树中是否有结束标记,有的话表示找到一个单词 + if (currDict[trie.endOfWord] === trie.endOfWord) { + // 将单词存储到结果中,这里不需要退出,因为字典树可能还有后续节点 + result.add(currWord); + } + + // 缓存当前的字母 + const temp = board[i][j]; + // 将当前字母用其他字符标记,避免重复选取 + board[i][j] = '@'; + + // 查找当前坐标四周的字母 + for (let k = 0; k < 4; k++) { + // 计算四周点的坐标 + const x = i + dx[k]; + const y = j + dy[k]; + + // 如果新坐标超出矩阵,则跳过 + if (x < 0 || y < 0 || x >= m || y >= n) { + continue; + } + + // 如果新坐标已被使用过,或者当前字母不在字典树中,无需继续查找 + if (board[x][y] !== '@' && currDict[board[x][y]]) { + // 继续查找新字母是否能组成所需单词 + dfs(x, y, currWord, currDict); + } + } + + // 恢复当前状态,清除选中标记 + board[i][j] = temp; + } + + // 遍历整个矩阵,从每个字母查找单词表中的单词 + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; j++) { + if (trie.root[board[i][j]]) { + dfs(i, j, '', trie.root); + } + } + } + + // 将Set转换为数组返回结果 + return [...result]; +}; + +// 字典树 +class Trie { + constructor() { + // 根节点使用一个空对象,单词的每个字母作为对象的一个key + this.root = {}; + // 使用一个固定的标记,标记一个节点为字典树的根节点 + // 此处使用Symbol标记,避免重复 + this.endOfWord = Symbol('EOF'); + } + + /** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ + insert(word) { + // 当一个单词插入字典树时,都从根节点开始插入 + let node = this.root; + + // 逐个字母遍历单词 + for (const char of word) { + // 如果字母未在字典树中川建国节点,则创建一个新节点 + node[char] = node[char] || {}; + // 移动节点,向下遍历 + node = node[char]; + } + + // 标记当前节点是一个单词的结束位置 + node[this.endOfWord] = this.endOfWord; + } +} +// @lc code=end diff --git "a/Week_07/212.\345\215\225\350\257\215\346\220\234\347\264\242 II\357\274\214\345\255\227\345\205\270\346\240\221\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/212.\345\215\225\350\257\215\346\220\234\347\264\242 II\357\274\214\345\255\227\345\205\270\346\240\221\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..6bb0bffa --- /dev/null +++ "b/Week_07/212.\345\215\225\350\257\215\346\220\234\347\264\242 II\357\274\214\345\255\227\345\205\270\346\240\221\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,169 @@ +/* + * @lc app=leetcode.cn id=212 lang=javascript + * + * [212] 单词搜索 II + * + * https://leetcode-cn.com/problems/word-search-ii/description/ + * + * algorithms + * Hard (44.53%) + * Likes: 310 + * Dislikes: 0 + * Total Accepted: 27.3K + * Total Submissions: 61.3K + * Testcase Example: '[["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]\n' + + '["oath","pea","eat","rain"]' + * + * 给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词。 + * + * 单词必须按照字母顺序,通过 相邻的单元格 + * 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。 + * + * + * + * 示例 1: + * + * + * 输入:board = + * [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], + * words = ["oath","pea","eat","rain"] + * 输出:["eat","oath"] + * + * + * 示例 2: + * + * + * 输入:board = [["a","b"],["c","d"]], words = ["abcb"] + * 输出:[] + * + * + * + * + * 提示: + * + * + * m == board.length + * n == board[i].length + * 1 + * board[i][j] 是一个小写英文字母 + * 1 + * 1 + * words[i] 由小写英文字母组成 + * words 中的所有字符串互不相同 + * + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ +var findWords = function (board, words) { + // [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]\n["oath","pea","eat","rain","oathi","oathk","oathf","oate","oathii","oathfi","oathfii"] + // [["a","a"]\n["aaa"] + // [["a","a"]]\n["a"] + let trie = new Trie(); // 创建一个字典树 + let result = new Set(); // 使用Set存储查找到的单词,避免重复 + const dx = [1, -1, 0, 0]; // x轴的4个方向 + const dy = [0, 0, 1, -1]; // y轴的4个方向 + const m = board.length; // 缓存行数 + const n = board[0].length; // 缓存列数 + + // 将单词表中的单词都插入字典树 + for (const word of words) { + trie.insert(word); + } + + // 查找当前位置的字母是否能够组合成单词 + // 使用DFS在矩阵中向四周查找能组成单词的字母 + function dfs( + // 当前查询字母的坐标 + i, + j, + prevWord, // 之前已经生成的单词 + prevDict, // 上一个字母在字典树中的节点 + ) { + const currChar = board[i][j]; // 当前坐标的字母 + const currDict = prevDict[currChar]; // 当前坐标在字典树中的节点 + const currWord = prevWord + currChar; // 当前字母组成的新单词 + + // 查看新单词在字典树中是否有结束标记,有的话表示找到一个单词 + if (currDict[trie.endOfWord] === trie.endOfWord) { + // 将单词存储到结果中,这里不需要退出,因为字典树可能还有后续节点 + result.add(currWord); + } + + // 缓存当前的字母 + const temp = board[i][j]; + // 将当前字母用其他字符标记,避免重复选取 + board[i][j] = '@'; + + // 查找当前坐标四周的字母 + for (let k = 0; k < 4; k++) { + // 计算四周点的坐标 + const x = i + dx[k]; + const y = j + dy[k]; + + // 如果新坐标超出矩阵,则跳过 + if (x < 0 || y < 0 || x >= m || y >= n) { + continue; + } + + // 如果新坐标已被使用过,或者当前字母不在字典树中,无需继续查找 + if (board[x][y] !== '@' && currDict[board[x][y]]) { + // 继续查找新字母是否能组成所需单词 + dfs(x, y, currWord, currDict); + } + } + + // 恢复当前状态,清除选中标记 + board[i][j] = temp; + } + + // 遍历整个矩阵,从每个字母查找单词表中的单词 + for (let i = 0; i < board.length; i++) { + for (let j = 0; j < board[i].length; j++) { + if (trie.root[board[i][j]]) { + dfs(i, j, '', trie.root); + } + } + } + + // 将Set转换为数组返回结果 + return [...result]; +}; + +// 字典树 +class Trie { + constructor() { + // 根节点使用一个空对象,单词的每个字母作为对象的一个key + this.root = {}; + // 使用一个固定的标记,标记一个节点为字典树的根节点 + // 此处使用Symbol标记,避免重复 + this.endOfWord = Symbol('EOF'); + } + + /** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ + insert(word) { + // 当一个单词插入字典树时,都从根节点开始插入 + let node = this.root; + + // 逐个字母遍历单词 + for (const char of word) { + // 如果字母未在字典树中川建国节点,则创建一个新节点 + node[char] = node[char] || {}; + // 移动节点,向下遍历 + node = node[char]; + } + + // 标记当前节点是一个单词的结束位置 + node[this.endOfWord] = this.endOfWord; + } +} +// @lc code=end diff --git "a/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..a076ea65 --- /dev/null +++ "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,120 @@ +./* + * @lc app=leetcode.cn id=36 lang=javascript + * + * [36] 有效的数独 + * + * https://leetcode-cn.com/problems/valid-sudoku/description/ + * + * algorithms + * Medium (61.85%) + * Likes: 461 + * Dislikes: 0 + * Total Accepted: 113.6K + * Total Submissions: 183.6K + * Testcase Example: '[["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]' + * + * 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 + * + * + * 数字 1-9 在每一行只能出现一次。 + * 数字 1-9 在每一列只能出现一次。 + * 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 + * + * + * + * + * 上图是一个部分填充的有效的数独。 + * + * 数独部分空格内已填入了数字,空白格用 '.' 表示。 + * + * 示例 1: + * + * 输入: + * [ + * ⁠ ["5","3",".",".","7",".",".",".","."], + * ⁠ ["6",".",".","1","9","5",".",".","."], + * ⁠ [".","9","8",".",".",".",".","6","."], + * ⁠ ["8",".",".",".","6",".",".",".","3"], + * ⁠ ["4",".",".","8",".","3",".",".","1"], + * ⁠ ["7",".",".",".","2",".",".",".","6"], + * ⁠ [".","6",".",".",".",".","2","8","."], + * ⁠ [".",".",".","4","1","9",".",".","5"], + * ⁠ [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: true + * + * + * 示例 2: + * + * 输入: + * [ + * ["8","3",".",".","7",".",".",".","."], + * ["6",".",".","1","9","5",".",".","."], + * [".","9","8",".",".",".",".","6","."], + * ["8",".",".",".","6",".",".",".","3"], + * ["4",".",".","8",".","3",".",".","1"], + * ["7",".",".",".","2",".",".",".","6"], + * [".","6",".",".",".",".","2","8","."], + * [".",".",".","4","1","9",".",".","5"], + * [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: false + * 解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 + * ⁠ 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。 + * + * 说明: + * + * + * 一个有效的数独(部分已被填充)不一定是可解的。 + * 只需要根据以上规则,验证已经填入的数字是否有效即可。 + * 给定数独序列只包含数字 1-9 和字符 '.' 。 + * 给定数独永远是 9x9 形式的。 + * + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {boolean} + */ +var isValidSudoku = function (board) { + let colMap = new Map(); // 使用Map缓存每一行的数字情况 + let rowMap = new Map(); // 使用Map缓存每一列的数字情况 + let subBoardMap = new Map(); // 使用Map缓存每个小格里的数字情况 + + // 共有9行、9列、9个小格,分别用一个Set记录 + for (let i = 0; i < 9; i++) { + colMap.set(i, new Set()); + rowMap.set(i, new Set()); + subBoardMap.set(i, new Set()); + } + + // 遍历所有位置,并判断数字是否重复 + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + // 计算每个小格的索引 + const subIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3); + + // 如果数字已经在行、列、或小格中出现过,说明是无效的数独 + if ( + colMap.get(i).has(board[i][j]) || + rowMap.get(j).has(board[i][j]) || + subBoardMap.get(subIndex).has(board[i][j]) + ) { + return false; + } + + // 将数字缓存到Set中,供之后循环判断 + if (board[i][j] !== '.') { + colMap.get(i).add(board[i][j]); + rowMap.get(j).add(board[i][j]); + subBoardMap.get(subIndex).add(board[i][j]); + } + } + } + + // 能够正常退出循环,表示数字都没有重复出现,是有效的数独 + return true; +}; +// @lc code=end diff --git "a/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\344\274\230\345\214\226.js" "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\344\274\230\345\214\226.js" new file mode 100644 index 00000000..6e344ecd --- /dev/null +++ "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\344\274\230\345\214\226.js" @@ -0,0 +1,125 @@ +/* + * @lc app=leetcode.cn id=36 lang=javascript + * + * [36] 有效的数独 + * + * https://leetcode-cn.com/problems/valid-sudoku/description/ + * + * algorithms + * Medium (61.85%) + * Likes: 461 + * Dislikes: 0 + * Total Accepted: 113.6K + * Total Submissions: 183.6K + * Testcase Example: '[["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]' + * + * 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 + * + * + * 数字 1-9 在每一行只能出现一次。 + * 数字 1-9 在每一列只能出现一次。 + * 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 + * + * + * + * + * 上图是一个部分填充的有效的数独。 + * + * 数独部分空格内已填入了数字,空白格用 '.' 表示。 + * + * 示例 1: + * + * 输入: + * [ + * ⁠ ["5","3",".",".","7",".",".",".","."], + * ⁠ ["6",".",".","1","9","5",".",".","."], + * ⁠ [".","9","8",".",".",".",".","6","."], + * ⁠ ["8",".",".",".","6",".",".",".","3"], + * ⁠ ["4",".",".","8",".","3",".",".","1"], + * ⁠ ["7",".",".",".","2",".",".",".","6"], + * ⁠ [".","6",".",".",".",".","2","8","."], + * ⁠ [".",".",".","4","1","9",".",".","5"], + * ⁠ [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: true + * + * + * 示例 2: + * + * 输入: + * [ + * ["8","3",".",".","7",".",".",".","."], + * ["6",".",".","1","9","5",".",".","."], + * [".","9","8",".",".",".",".","6","."], + * ["8",".",".",".","6",".",".",".","3"], + * ["4",".",".","8",".","3",".",".","1"], + * ["7",".",".",".","2",".",".",".","6"], + * [".","6",".",".",".",".","2","8","."], + * [".",".",".","4","1","9",".",".","5"], + * [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: false + * 解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 + * ⁠ 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。 + * + * 说明: + * + * + * 一个有效的数独(部分已被填充)不一定是可解的。 + * 只需要根据以上规则,验证已经填入的数字是否有效即可。 + * 给定数独序列只包含数字 1-9 和字符 '.' 。 + * 给定数独永远是 9x9 形式的。 + * + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {boolean} + */ +var isValidSudoku = function (board) { + /* +行、列,以及小格内,数字是否出现重复的判断是独立的。 +并且整个矩阵有9行、9列、9个小格。 +因此我们只要用双循环遍历这个矩阵,并且保证第二层循环每进行一次,都遍历了一行、一列和一个小格即可。 +*/ + // 遍历每一行、列、小格 + for (let i = 0; i < 9; i++) { + // 每次循环都创建3个Set,分别存储行、列和小格的数字出现情况,只要分别判断其中是否有重复即可 + const sets = [new Set(), new Set(), new Set()]; + + // 遍历行、列、小格中的每个位置 + for (let j = 0; j < 9; j++) { + const nums = [ + board[i][j], // 行的每个值 + board[j][i], // 列的每个值 + // 小格的每个值 + board[ + // 小格的行索引 + 3 * Math.floor(i / 3) + Math.floor(j / 3) + ][ + // 小格的列索引 + 3 * (i % 3) + (j % 3) + ], + ]; + + // 判断每个值是否在对应Set中存在 + for (let k = 0; k < sets.length; k++) { + // 当前值是数字,才需要判断 + if (nums[k] !== '.') { + // 当前值如果在Set中,说明是无效的数独 + if (sets[k].has(nums[k])) { + return false; + } + // 将当前值加入Set,用于下次判断 + sets[k].add(nums[k]); + } + } + } + } + + // 能够正常退出循环,表示数字都没有重复出现,是有效的数独 + return true; +}; +// @lc code=end diff --git "a/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\344\274\230\345\214\226\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\344\274\230\345\214\226\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..1c3d006d --- /dev/null +++ "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\344\274\230\345\214\226\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,105 @@ +/* + * @lc app=leetcode.cn id=36 lang=javascript + * + * [36] 有效的数独 + * + * https://leetcode-cn.com/problems/valid-sudoku/description/ + * + * algorithms + * Medium (61.85%) + * Likes: 461 + * Dislikes: 0 + * Total Accepted: 113.6K + * Total Submissions: 183.6K + * Testcase Example: '[["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]' + * + * 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 + * + * + * 数字 1-9 在每一行只能出现一次。 + * 数字 1-9 在每一列只能出现一次。 + * 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 + * + * + * + * + * 上图是一个部分填充的有效的数独。 + * + * 数独部分空格内已填入了数字,空白格用 '.' 表示。 + * + * 示例 1: + * + * 输入: + * [ + * ⁠ ["5","3",".",".","7",".",".",".","."], + * ⁠ ["6",".",".","1","9","5",".",".","."], + * ⁠ [".","9","8",".",".",".",".","6","."], + * ⁠ ["8",".",".",".","6",".",".",".","3"], + * ⁠ ["4",".",".","8",".","3",".",".","1"], + * ⁠ ["7",".",".",".","2",".",".",".","6"], + * ⁠ [".","6",".",".",".",".","2","8","."], + * ⁠ [".",".",".","4","1","9",".",".","5"], + * ⁠ [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: true + * + * + * 示例 2: + * + * 输入: + * [ + * ["8","3",".",".","7",".",".",".","."], + * ["6",".",".","1","9","5",".",".","."], + * [".","9","8",".",".",".",".","6","."], + * ["8",".",".",".","6",".",".",".","3"], + * ["4",".",".","8",".","3",".",".","1"], + * ["7",".",".",".","2",".",".",".","6"], + * [".","6",".",".",".",".","2","8","."], + * [".",".",".","4","1","9",".",".","5"], + * [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: false + * 解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 + * ⁠ 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。 + * + * 说明: + * + * + * 一个有效的数独(部分已被填充)不一定是可解的。 + * 只需要根据以上规则,验证已经填入的数字是否有效即可。 + * 给定数独序列只包含数字 1-9 和字符 '.' 。 + * 给定数独永远是 9x9 形式的。 + * + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {boolean} + */ +var isValidSudoku = function (board) { + for (let i = 0; i < 9; i++) { + let sets = [new Set(), new Set(), new Set()]; + + for (let j = 0; j < 9; j++) { + const nums = [ + board[i][j], + board[j][i], + board[3 * Math.floor(i / 3) + Math.floor(j / 3)][(i % 3) * 3 + (j % 3)], + ]; + + for (let k = 0; k < sets.length; k++) { + if (nums[k] !== '.') { + if (sets[k].has(nums[k])) { + return false; + } + sets[k].add(nums[k]); + } + } + } + } + + return true; +}; +// @lc code=end diff --git "a/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..1c73b84e --- /dev/null +++ "b/Week_07/36. \346\234\211\346\225\210\347\232\204\346\225\260\347\213\254\357\274\214\345\223\210\345\270\214\350\241\250\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,115 @@ +/* + * @lc app=leetcode.cn id=36 lang=javascript + * + * [36] 有效的数独 + * + * https://leetcode-cn.com/problems/valid-sudoku/description/ + * + * algorithms + * Medium (61.85%) + * Likes: 461 + * Dislikes: 0 + * Total Accepted: 113.6K + * Total Submissions: 183.6K + * Testcase Example: '[["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]' + * + * 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 + * + * + * 数字 1-9 在每一行只能出现一次。 + * 数字 1-9 在每一列只能出现一次。 + * 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 + * + * + * + * + * 上图是一个部分填充的有效的数独。 + * + * 数独部分空格内已填入了数字,空白格用 '.' 表示。 + * + * 示例 1: + * + * 输入: + * [ + * ⁠ ["5","3",".",".","7",".",".",".","."], + * ⁠ ["6",".",".","1","9","5",".",".","."], + * ⁠ [".","9","8",".",".",".",".","6","."], + * ⁠ ["8",".",".",".","6",".",".",".","3"], + * ⁠ ["4",".",".","8",".","3",".",".","1"], + * ⁠ ["7",".",".",".","2",".",".",".","6"], + * ⁠ [".","6",".",".",".",".","2","8","."], + * ⁠ [".",".",".","4","1","9",".",".","5"], + * ⁠ [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: true + * + * + * 示例 2: + * + * 输入: + * [ + * ["8","3",".",".","7",".",".",".","."], + * ["6",".",".","1","9","5",".",".","."], + * [".","9","8",".",".",".",".","6","."], + * ["8",".",".",".","6",".",".",".","3"], + * ["4",".",".","8",".","3",".",".","1"], + * ["7",".",".",".","2",".",".",".","6"], + * [".","6",".",".",".",".","2","8","."], + * [".",".",".","4","1","9",".",".","5"], + * [".",".",".",".","8",".",".","7","9"] + * ] + * 输出: false + * 解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 + * ⁠ 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。 + * + * 说明: + * + * + * 一个有效的数独(部分已被填充)不一定是可解的。 + * 只需要根据以上规则,验证已经填入的数字是否有效即可。 + * 给定数独序列只包含数字 1-9 和字符 '.' 。 + * 给定数独永远是 9x9 形式的。 + * + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {boolean} + */ +var isValidSudoku = function (board) { + let rowMap = new Map(); + let colMap = new Map(); + let boxMap = new Map(); + + for (let i = 0; i < 9; i++) { + rowMap.set(i, new Set()); + colMap.set(i, new Set()); + boxMap.set(i, new Set()); + } + + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + const num = board[i][j]; + const boxIndex = 3 * Math.floor(i / 3) + Math.floor(j / 3); + + if (num !== '.') { + if ( + rowMap.get(i).has(num) || + colMap.get(j).has(num) || + boxMap.get(boxIndex).has(num) + ) { + return false; + } + + rowMap.get(i).add(num); + colMap.get(j).add(num); + boxMap.get(boxIndex).add(num); + } + } + } + + return true; +}; +// @lc code=end diff --git "a/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS.js" "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS.js" new file mode 100644 index 00000000..784852e2 --- /dev/null +++ "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS.js" @@ -0,0 +1,53 @@ +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solveSudoku = function (board) { + // [[".",".","9","7","4","8",".",".","."],["7",".",".",".",".",".",".",".","."],[".","2",".","1",".","9",".",".","."],[".",".","7",".",".",".","2","4","."],[".","6","4",".","1",".","5","9","."],[".","9","8",".",".",".","3",".","."],[".",".",".","8",".","3",".","2","."],[".",".",".",".",".",".",".",".","6"],[".",".",".","2","7","5","9",".","."]] + function solve() { + for (let row = 0; row < 9; row++) { + for (let col = 0; col < 9; col++) { + if (board[row][col] === '.') { + for (let i = 1; i <= 9; i++) { + const char = i.toString(); + + if (isValid(row, col, char)) { + board[row][col] = char; + + if (solve()) { + return true; + } + + board[row][col] = '.'; + } + } + + return false; + } + } + } + + return true; + } + + function isValid(row, col, char) { + const regionRow = 3 * Math.floor(row / 3); //region start row + const regionCol = 3 * Math.floor(col / 3); //region start col + + for (let i = 0; i < 9; i++) { + if (board[row][i] === char) { + return false; + } + if (board[i][col] === char) { + return false; + } + if (board[regionRow + Math.floor(i / 3)][regionCol + (i % 3)] === char) { + return false; + } + } + + return true; + } + + solve(); +}; diff --git "a/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\344\274\230\345\214\226.js" "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\344\274\230\345\214\226.js" new file mode 100644 index 00000000..f52f7bff --- /dev/null +++ "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\344\274\230\345\214\226.js" @@ -0,0 +1,87 @@ +/* + * @lc app=leetcode.cn id=37 lang=javascript + * + * [37] 解数独 + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solveSudoku = function (board) { + // 整个数独有81个格子,因此最多需要递归81个位置,查找可用的数字 + function solve(index) { + // 因为是从0开始搜索,所以索引到达81时,已经完成整个数独的搜索 + // 由于只有solve返回true的时候,才会继续下探一层 + // 反过来说,既然已经完成搜索,那么每个位置结果都是true,数独的解已可以找到 + if (index === 81) { + return true; + } + + // 根据传入的index,计算相应的行列索引 + const row = Math.floor(index / 9); + const col = index % 9; + + // 如果当前位置已经有数字,则向下一个位置查找 + if (board[row][col] !== '.') { + return solve(index + 1); + } + + // 使用一个数组,用索引1~9的布尔值,标记可以使用的数字 + let use = new Array(10).fill(true); + // 计算当前位置可以填入的数字 + // 可填入数字是会根据每次填入的情况不断变化 + // 如果无法查找到可用的值,就无法进入下方的循环,导致当前递归返回false + validate(row, col, use); + + // 遍历1~9,查找可以使用的数字 + for (let i = 1; i <= 9; i++) { + // 如果当前的值可用,就尝试将其填入数独 + if (use[i]) { + board[row][col] = i.toString(); + + // 在填入当前数字的基础上,查找之后的所有位置是否有合法的数字填入 + if (solve(index + 1)) { + // 如果都有合法解,则返回结果 + return true; + } + } + } + + // 如果退出了循环,表示没有找到合法解,将当前位置恢复状态,并返回false + board[row][col] = '.'; + return false; + } + + // 查询当前行、列和子数独能够使用的数字 + function validate(row, col, use) { + // 用于计算子数独行列索引的基础值 + const baseRow = 3 * Math.floor(row / 3); + const baseCol = 3 * Math.floor(col / 3); + + // 遍历当前位置所在的行、列、和子数独的每个位置,它们都有9个位置,因此只需遍历9次 + // 如果其中的某个位置有数字,则将其对应在use中的位置设置为false + for (let i = 0; i < 9; i++) { + // 固定行,遍历列 + if (board[row][i] !== '.') { + use[Number(board[row][i])] = false; + } + // 固定列。遍历行 + if (board[i][col] !== '.') { + use[Number(board[i][col])] = false; + } + // 计算子数独的行列索引 + const boxRow = baseRow + Math.floor(i / 3); + const boxCol = baseCol + (i % 3); + // 遍历子数独 + if (board[boxRow][boxCol] !== '.') { + use[Number(board[boxRow][boxCol])] = false; + } + } + } + + // 从索引0开始查找数独的解 + solve(0); +}; +// @lc code=end diff --git "a/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\345\206\215\344\274\230\345\214\226.js" "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\345\206\215\344\274\230\345\214\226.js" new file mode 100644 index 00000000..4b8b2bc8 --- /dev/null +++ "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\345\206\215\344\274\230\345\214\226.js" @@ -0,0 +1,110 @@ +/* + * @lc app=leetcode.cn id=37 lang=javascript + * + * [37] 解数独 + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solveSudoku = function (board) { + // 参考了:https://leetcode-cn.com/problems/sudoku-solver/solution/pythonsethui-su-chao-guo-95-by-mai-mai-mai-mai-zi/ + // 用Set保存每一行、列、子数独的可用数字 + let rowEnableNumSets = []; + let colEnableNumSets = []; + let boxEnableNumSets = []; + let empty = []; // 保存所有待填入数字行、列、子数独索引 + const chars = ['1', '2', '3', '4', '5', '6', '7', '8', '9']; // 缓存可用的字符 + + // 创建每一行、列、子数独的初始Set + for (let i = 0; i < 9; i++) { + rowEnableNumSets.push(new Set(chars)); + colEnableNumSets.push(new Set(chars)); + boxEnableNumSets.push(new Set(chars)); + } + + // 遍历整个位置 + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + const boxIndex = 3 * Math.floor(i / 3) + Math.floor(j / 3); + // 如果当前需要填写数字,将这个坐标存入empty + if (board[i][j] === '.') { + empty.push([i, j, boxIndex]); + } else { + // 将已存在的数字,从行、列、子数独Set中删除,之后Set中只保存了可用的数字 + rowEnableNumSets[i].delete(board[i][j]); + colEnableNumSets[j].delete(board[i][j]); + boxEnableNumSets[boxIndex].delete(board[i][j]); + } + } + } + + const fillSets = []; // 用Set缓存empty中每个位置可填的数字 + + // 遍历empty,生成每个位置可填的数字集合 + for (let index = 0; index < empty.length; index++) { + const [row, col, boxIndex] = empty[index]; // 取出行、列、子数独索引 + fillSets.push(new Set()); // 存入一个Set,待填充数字 + + for (const char of chars) { + // 只有在行、列、子数独Set中存在的数字,才是当前位置候选数字先将其缓存 + if ( + rowEnableNumSets[row].has(char) && + colEnableNumSets[col].has(char) && + boxEnableNumSets[boxIndex].has(char) + ) { + fillSets[index].add(char); + } + } + } + + // 递归搜索empty,查找数独的解 + function solve(index) { + // 因为是从0开始搜索,所以索引到达empty长度时,已经完成所有可填位置的搜索 + // 由于只有solve返回true的时候,才会继续下探一层 + // 反过来说,既然已经完成搜索,那么每个位置结果都是true,数独的解已可以找到 + if (index === empty.length) { + return true; + } + + // 取出行、列、子数独索引 + const [row, col, boxIndex] = empty[index]; + + // 遍历所有可用的数字 + for (const char of fillSets[index]) { + // 行、列、子数独的可用数字会随着每次递归更新,因此每次需要重新判断 + // 只有当前可用的数字才可以尝试填入 + if ( + rowEnableNumSets[row].has(char) && + colEnableNumSets[col].has(char) && + boxEnableNumSets[boxIndex].has(char) + ) { + // 将当前数字从行、列、子数独的可用数字中删除,向下递归搜索 + rowEnableNumSets[row].delete(char); + colEnableNumSets[col].delete(char); + boxEnableNumSets[boxIndex].delete(char); + board[row][col] = char; + + // 在填入当前数字的基础上,查找之后的所有位置是否有合法的数字填入 + if (solve(index + 1)) { + return true; + } + + // 如果无法找到可用的数字,就恢复状态 + rowEnableNumSets[row].add(char); + colEnableNumSets[col].add(char); + boxEnableNumSets[boxIndex].add(char); + board[row][col] = '.'; + } + } + + // 如果退出了循环,表示没有找到合法解,将当前位置恢复状态,并返回false + return false; + } + + // 从索引0开始查找数独的解 + solve(0); +}; +// @lc code=end diff --git "a/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..c3bc7331 --- /dev/null +++ "b/Week_07/37. \350\247\243\346\225\260\347\213\254\357\274\214DFS\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,114 @@ +/* + * @lc app=leetcode.cn id=37 lang=javascript + * + * [37] 解数独 + * + * https://leetcode-cn.com/problems/sudoku-solver/description/ + * + * algorithms + * Hard (66.81%) + * Likes: 731 + * Dislikes: 0 + * Total Accepted: 66.7K + * Total Submissions: 99.8K + * Testcase Example: '[["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]' + * + * 编写一个程序,通过填充空格来解决数独问题。 + * + * 一个数独的解法需遵循如下规则: + * + * + * 数字 1-9 在每一行只能出现一次。 + * 数字 1-9 在每一列只能出现一次。 + * 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 + * + * + * 空白格用 '.' 表示。 + * + * + * + * 一个数独。 + * + * + * + * 答案被标成红色。 + * + * 提示: + * + * + * 给定的数独序列只包含数字 1-9 和字符 '.' 。 + * 你可以假设给定的数独只有唯一解。 + * 给定数独永远是 9x9 形式的。 + * + * + */ + +// @lc code=start +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solveSudoku = function (board) { + function solve() { + // 遍历整个数独, + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + // 当前字符待填入状态 + if (board[i][j] === '.') { + // 遍历1-9 + for (let c = 1; c <= 9; c++) { + // 将1-9转换成字符串 + const char = c.toString(); + + // 如果当前字母可以填入,才将其填入数独 + if (isValid(i, j, char)) { + board[i][j] = char; + + // 在递归中如果完成了数独的遍历,表示找到了答案,即可退出 + if (solve()) { + return true; + } + + // 如果递归返回为false,表示当前填入的数字不可用,将其恢复 + board[i][j] = '.'; + } + } + + // 退出循环表示未找到可以替换的数字,返回false + return false; + } + } + } + + // 如果能完成数独遍历,表示已经找到正确解法,返回true + return true; + } + + // 判断当前位置如果填入了一个数字,是否符合规则 + function isValid(row, col, char) { + const boxRow = 3 * Math.floor(row / 3); // 子 + const boxCol = 3 * Math.floor(col / 3); // 子 + + for (let i = 0; i < 9; i++) { + // 判断当前位置的同一行是否有相同数字 + if (board[row][i] === char) { + return false; + } + // 判断当前位置的同一列是否有相同数字 + if (board[i][col] === char) { + return false; + } + // 判断当前位置的同一个小格是否有相同数字 + if (board[boxRow + Math.floor(i / 3)][boxCol + (i % 3)] === char) { + return false; + } + } + + // 未查找到相同数字,表示char可以使用 + return true; + } + + // 递归生成数独解法 + solve(); +}; +// @lc code=end diff --git "a/Week_07/547.\347\234\201\344\273\275\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206.js" "b/Week_07/547.\347\234\201\344\273\275\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206.js" new file mode 100644 index 00000000..32c681be --- /dev/null +++ "b/Week_07/547.\347\234\201\344\273\275\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206.js" @@ -0,0 +1,61 @@ +/* + * @lc app=leetcode.cn id=547 lang=javascript + * + * [547] 省份数量 + */ + +// @lc code=start +/** + * @param {number[][]} isConnected + * @return {number} + */ +var findCircleNum = function (isConnected) { + const unionFind = new UnionFind(isConnected); + const m = isConnected.length; + const n = isConnected[0].length; + + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (isConnected[i][j] === 1) { + unionFind.union(i, j); + unionFind.uni; + } + } + } + + return unionFind.count; +}; + +class UnionFind { + constructor(isConnected) { + this.count = 0; + this.parent = new Array(isConnected.length); + + for (let i = 0; i < isConnected.length; i++) { + this.parent[i] = i; + this.count++; + } + } + + find(p) { + while (p !== this.parent[p]) { + this.parent[p] = this.parent[this.parent[p]]; + p = this.parent[p]; + } + + return p; + } + + union(p, q) { + const rootP = this.find(p); + const rootQ = this.find(q); + + if (rootP === rootQ) { + return; + } + + this.parent[rootP] = rootQ; + this.count--; + } +} +// @lc code=end diff --git "a/Week_07/547.\347\234\201\344\273\275\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_07/547.\347\234\201\344\273\275\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..00bb888a --- /dev/null +++ "b/Week_07/547.\347\234\201\344\273\275\346\225\260\351\207\217\357\274\214\345\271\266\346\237\245\351\233\206\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,127 @@ +/* + * @lc app=leetcode.cn id=547 lang=javascript + * + * [547] 省份数量 + * + * https://leetcode-cn.com/problems/number-of-provinces/description/ + * + * algorithms + * Medium (61.17%) + * Likes: 474 + * Dislikes: 0 + * Total Accepted: 109.8K + * Total Submissions: 179.4K + * Testcase Example: '[[1,1,0],[1,1,0],[0,0,1]]' + * + * + * + * 有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c + * 间接相连。 + * + * 省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。 + * + * 给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 + * isConnected[i][j] = 0 表示二者不直接相连。 + * + * 返回矩阵中 省份 的数量。 + * + * + * + * 示例 1: + * + * + * 输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]] + * 输出:2 + * + * + * 示例 2: + * + * + * 输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]] + * 输出:3 + * + * + * + * + * 提示: + * + * + * 1 + * n == isConnected.length + * n == isConnected[i].length + * isConnected[i][j] 为 1 或 0 + * isConnected[i][i] == 1 + * isConnected[i][j] == isConnected[j][i] + * + * + * + * + */ + +// @lc code=start +/** + * @param {number[][]} isConnected + * @return {number} + */ +var findCircleNum = function (isConnected) { + // 由于只有n个城市,因此指出要初始化n个集合 + const unionFind = new UnionFind(isConnected.length); + + // 遍历所有城市之间的关系 + for (let i = 0; i < isConnected.length; i++) { + for (let j = 0; j < isConnected[i].length; j++) { + // 索引相同表示是同一个城市,无需合并 + // 如果两个城市相连,则将两个城市合并 + if (i !== j && isConnected[i][j] === 1) { + unionFind.union(i, j); + } + } + } + + return unionFind.count; +}; + +// 并查集 +class UnionFind { + constructor(n) { + this.count = n; // 统计集合数量,初始化为n + this.parent = []; // 使用数组保存集合 + + // 初始化城市集合 + for (let i = 0; i < n; i++) { + this.parent.push(i); + } + } + + // 查找一个节点所在集合的根节点 + find(p) { + // 当前节点的父节点是其自身时,它就是集合的根节点 + while (p != this.parent[p]) { + // 将当前节点父节点指针,直接指向爷爷节点,实现路径压缩 + this.parent[p] = this.parent[this.parent[p]]; + // 将指针向上移动 + p = this.parent[p]; + } + + // 将找到的根节点返回 + return p; + } + + // 合并两个集合 + union(p, q) { + // 找到两个节点所在集合的根节点 + let rootP = this.find(p); + let rootQ = this.find(q); + + // 如果他们是同一个节点,表示p和q在同一个集合,无需合并 + if (rootP === rootQ) { + return; + } + + // 将rootP的根节点指向rootQ,完成集合的合并 + this.parent[rootP] = rootQ; + // 减少集合数量 + this.count--; + } +} +// @lc code=end diff --git a/Week_07/README.md b/Week_07/README.md index 50de3041..1c5ddcb8 100644 --- a/Week_07/README.md +++ b/Week_07/README.md @@ -1 +1,75 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +双向BFS模板,从[LeetCode题解:127. 单词接龙,双向BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder/solution/leetcodeti-jie-127-dan-ci-jie-long-shuang-xiang-bf)的代码提取而来。 + +``` javascript +var ladderLength = function (start, end, list) { + // 每次都遍历队列,初始时存入start,对应层级为1 + let queue = [[start, 1]]; + // 虽然两端的遍历都需要使用队列,实际操作时可以用Map来加速判断是否相遇的过程 + // 每次遍历时,只需要取queue和map中长度较小的一个,将其转换为一个队列进行遍历即可 + let map = new Map([[end, 1]]); + // 使用Set判断list中的单词是否被使用过 + let set = new Set(list); + + // 由于双向BFS即使end不存在于list中,也有可能会相遇 + // 因此要先判断list中是否有end,若不存在则表示不存在从start到end的路径 + if (!set.has(end)) { + return 0; + } + + // 如果queue和map中任意一个被清空,表示双向BFS不会相遇,即不存在从start到end的路径 + while (queue.length && map.size) { + // 选取queue和map中较短的一个进行遍历,优化搜索速度 + if (queue.length > map.size) { + // 将queue和map对调,保证每次遍历的都是queue + [queue, map] = [Array.from(map), new Map(queue)]; + } + + // 将queue中元素出队,搜索下一个层节点 + const [now, level] = queue.shift(); + + const next = /* 生成下一层节点 */; + + // 如果next在map中存在,表示双向BFS相遇,即为找到了最短路径 + if (map.has(next)) { + // 将两端的level想加,即为总长度 + return map.get(next) + level; + } + + // 如果next存在于list中,表示next可作为下一层节点 + if (set.has(next)) { + // 将next从list中删除,避免重复使用 + set.delete(next); + // 将next入队,进行下一层搜索,同时层级加一 + queue.push([next, level + 1]); + } + } + + // 如果退出循环,表示未找到转换序列,返回0 + return 0; +}; +``` + +我为部分题目写了题解,如下: + +其余没写题解的题目,都在作业中写了详细注释,有空时补上。 + +1. [LeetCode题解:200. 岛屿数量,DFS,JavaScript,详细注释](https://leetcode-cn.com/problems/number-of-islands/solution/leetcodeti-jie-200-dao-yu-shu-liang-dfsj-w48p) +2. [LeetCode题解:70. 爬楼梯,DP遍历,变量缓存结果,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-bian-liang-huan-cun-jie-guo) +3. [LeetCode题解:70. 爬楼梯,DP遍历数组,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-shu-zu-javascriptxiang-xi-z) +4. [LeetCode题解:70. 爬楼梯,递归+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-di-gui-ha-xi-biao-javascriptxiang-xi-) +5. [LeetCode题解:22. 括号生成,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-bfsjia-fa-ja) +6. [LeetCode题解:22. 括号生成,递归生成同时过滤,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-di-gui-sheng) +7. [LeetCode题解:22. 括号生成,递归先生成再过滤,JavaScript,详细注释](https://leetcode-cn.com/problems/generate-parentheses/solution/leetcodeti-jie-22-gua-hao-sheng-cheng-xian-sheng-c) +8. [LeetCode题解:52. N皇后 II,回溯+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/n-queens-ii/solution/leetcodeti-jie-52-nhuang-hou-iihui-su-ha-xi-biao-j) +9. [LeetCode题解:51. N 皇后,回溯+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/n-queens/solution/leetcodeti-jie-51-n-huang-hou-hui-su-ha-xi-biao-ja) +10. [LeetCode题解:126. 单词接龙 II,BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder-ii/solution/leetcodeti-jie-126-dan-ci-jie-long-iibfs-tm9m) +11. [LeetCode题解:127. 单词接龙,双向BFS,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder/solution/leetcodeti-jie-127-dan-ci-jie-long-shuang-xiang-bf) +12. [LeetCode题解:127. 单词接龙,BFS+生成所有可能新单词再匹配,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder/solution/leetcodeti-jie-127-dan-ci-jie-long-bfssheng-cheng-) +13. [LeetCode题解:127. 单词接龙,BFS+统计单词变化次数,JavaScript,详细注释](https://leetcode-cn.com/problems/word-ladder/solution/leetcodeti-jie-127-dan-ci-jie-long-bfsjavascriptxi) +14. [LeetCode题解:433. 最小基因变化,双向BFS(beats 99%),JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-pwwwg) +15. [LeetCode题解:433. 最小基因变化,BFS+生成所有可能新基因再匹配,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-0hd8n) +16. [LeetCode题解:433. 最小基因变化,BFS+统计基因变化次数,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-hua-bfsjav) +17. [LeetCode题解:433. 最小基因变化,DFS,JavaScript,详细注释](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/leetcodeti-jie-433-zui-xiao-ji-yin-bian-hua-dfsjav) + diff --git "a/Week_08/1091. \344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS\357\274\214\347\254\254\344\270\211\351\201\215.js" "b/Week_08/1091. \344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS\357\274\214\347\254\254\344\270\211\351\201\215.js" new file mode 100644 index 00000000..d4630d0f --- /dev/null +++ "b/Week_08/1091. \344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS\357\274\214\347\254\254\344\270\211\351\201\215.js" @@ -0,0 +1,56 @@ +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + const m = grid.length - 1; + + if (grid[0][0] === 1) { + return -1; + } + if (grid[m][m] === 1) { + return -1; + } + if (grid.length === 1) { + return 1; + } + + let queue = [[0, 0]]; + let count = 1; + const d = [ + // [0, -1], + [1, -1], + [1, 0], + [1, 1], + [0, 1], + [-1, 1], + // [-1, 0], + // [-1, -1] + ]; + + while (queue.length) { + let queueLength = queue.length; + + while (--queueLength >= 0) { + const [i, j] = queue.shift(); + + for (let k = 0; k < d.length; k++) { + const x = i + d[k][0]; + const y = j + d[k][1]; + + if (x >= 0 && y >= 0 && x <= m && y <= m && grid[x][y] === 0) { + if (x === m && y === m) { + return count + 1; + } + + grid[x][y] = 1; + queue.push([x, y]); + } + } + } + + count++; + } + + return -1; +}; diff --git "a/Week_08/1122. \346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_08/1122. \346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..edde61bc --- /dev/null +++ "b/Week_08/1122. \346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,77 @@ +/* + * @lc app=leetcode.cn id=1122 lang=javascript + * + * [1122] 数组的相对排序 + * + * https://leetcode-cn.com/problems/relative-sort-array/description/ + * + * algorithms + * Easy (70.94%) + * Likes: 153 + * Dislikes: 0 + * Total Accepted: 53.3K + * Total Submissions: 75.2K + * Testcase Example: '[2,3,1,3,2,4,6,7,9,2,19]\n[2,1,4,3,9,6]' + * + * 给你两个数组,arr1 和 arr2, + * + * + * arr2 中的元素各不相同 + * arr2 中的每个元素都出现在 arr1 中 + * + * + * 对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 + * 的末尾。 + * + * + * + * 示例: + * + * + * 输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6] + * 输出:[2,2,2,1,4,3,3,9,6,7,19] + * + * + * + * + * 提示: + * + * + * 1 + * 0 + * arr2 中的元素 arr2[i] 各不相同 + * arr2 中的每个元素 arr2[i] 都出现在 arr1 中 + * + * + */ + +// @lc code=start +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function (arr1, arr2) { + let map = new Map(); + let result = []; + + arr1.forEach((value) => { + map.set(value, map.has(value) ? map.get(value) + 1 : 1); + }); + + arr2.forEach((value) => { + if (map.has(value)) { + result.push(...new Array(map.get(value)).fill(value)); + map.delete(value); + } + }); + + Array.from(map.keys()) + .sort((a, b) => a - b) + .forEach((value) => { + result.push(...new Array(map.get(value)).fill(value)); + }); + + return result; +}; +// @lc code=end diff --git "a/Week_08/1122. \346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/1122. \346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..91fa2964 --- /dev/null +++ "b/Week_08/1122. \346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,75 @@ +/* + * @lc app=leetcode.cn id=1122 lang=javascript + * + * [1122] 数组的相对排序 + * + * https://leetcode-cn.com/problems/relative-sort-array/description/ + * + * algorithms + * Easy (70.94%) + * Likes: 153 + * Dislikes: 0 + * Total Accepted: 53.3K + * Total Submissions: 75.2K + * Testcase Example: '[2,3,1,3,2,4,6,7,9,2,19]\n[2,1,4,3,9,6]' + * + * 给你两个数组,arr1 和 arr2, + * + * + * arr2 中的元素各不相同 + * arr2 中的每个元素都出现在 arr1 中 + * + * + * 对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 + * 的末尾。 + * + * + * + * 示例: + * + * + * 输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6] + * 输出:[2,2,2,1,4,3,3,9,6,7,19] + * + * + * + * + * 提示: + * + * + * 1 + * 0 + * arr2 中的元素 arr2[i] 各不相同 + * arr2 中的每个元素 arr2[i] 都出现在 arr1 中 + * + * + */ + +// @lc code=start +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function (arr1, arr2) { + let map = new Map(); + let result = []; + + for (let i = 0; i < arr1.length; i++) { + map.set(arr1[i], (map.get(arr1[i]) || 0) + 1); + } + + for (let i = 0; i < arr2.length; i++) { + result.push(...new Array(map.get(arr2[i])).fill(arr2[i])); + map.delete(arr2[i]); + } + + Array.from(map.keys()) + .sort((a, b) => a - b) + .forEach((value) => { + result.push(...new Array(map.get(value)).fill(value)); + }); + + return result; +}; +// @lc code=end diff --git "a/Week_08/146.lru-\347\274\223\345\255\230\346\234\272\345\210\266.js" "b/Week_08/146.lru-\347\274\223\345\255\230\346\234\272\345\210\266.js" new file mode 100644 index 00000000..a09d3346 --- /dev/null +++ "b/Week_08/146.lru-\347\274\223\345\255\230\346\234\272\345\210\266.js" @@ -0,0 +1,102 @@ +/* + * @lc app=leetcode.cn id=146 lang=javascript + * + * [146] LRU 缓存机制 + */ + +// @lc code=start +/** + * @param {number} capacity + */ +var LRUCache = function (capacity) { + this.capacity = capacity; + this.head = new ListNode(); + this.tail = new ListNode(); + this.head.next = this.tail; + this.tail.prev = this.head; + this.map = new Map(); +}; + +/** + * @param {number} key + * @return {number} + */ +LRUCache.prototype.get = function (key) { + if (this.map.has(key)) { + const node = this.map.get(key); + + if (node.value !== null) { + this.moveToHead(node); + + return node.value; + } + } + + return -1; +}; + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +LRUCache.prototype.put = function (key, value) { + if (this.map.has(key)) { + const node = this.map.get(key); + + node.value = value; + this.moveToHead(node); + } else { + const node = new ListNode(key, value); + + this.map.set(key, node); + this.addToHead(node); + + if (this.map.size > this.capacity) { + const tail = this.removeTail(); + + this.map.delete(tail.key); + } + } +}; + +LRUCache.prototype.addToHead = function (node) { + node.next = this.head.next; + this.head.next.prev = node; + this.head.next = node; + node.prev = this.head; +}; + +LRUCache.prototype.removeNode = function (node) { + node.prev.next = node.next; + node.next.prev = node.prev; +}; + +LRUCache.prototype.moveToHead = function (node) { + this.removeNode(node); + this.addToHead(node); +}; + +LRUCache.prototype.removeTail = function () { + const tail = this.tail.prev; + this.removeNode(tail); + + return tail; +}; + +class ListNode { + constructor(key, value = null) { + this.key = key; + this.value = value; + this.prev = null; + this.next - null; + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * var obj = new LRUCache(capacity) + * var param_1 = obj.get(key) + * obj.put(key,value) + */ +// @lc code=end diff --git "a/Week_08/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" "b/Week_08/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" new file mode 100644 index 00000000..843fd217 --- /dev/null +++ "b/Week_08/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" @@ -0,0 +1,31 @@ +/* + * @lc app=leetcode.cn id=190 lang=javascript + * + * [190] 颠倒二进制位 + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +var reverseBits = function (n) { + // https://leetcode-cn.com/problems/reverse-bits/solution/190-dian-dao-er-jin-zhi-wei-by-alexer-660/ + let result = 0; // 存储结果 + + // 32位二进制数,因此需要移动32次 + // 每次将n的左后一位移动到result的第一位 + for (let i = 0; i < 32; i++) { + result = + // result向左移动 + (result << 1) + + // 填入n的最后一位 + (n & 1); + n = n >> 1; // n向右移动 + } + + // 11111111111111111111111111111101,这个Case反转后为负数,需要转换为正数 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift + return result >>> 0; +}; +// @lc code=end diff --git "a/Week_08/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..c90d72af --- /dev/null +++ "b/Week_08/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,67 @@ +/* + * @lc app=leetcode.cn id=190 lang=javascript + * + * [190] 颠倒二进制位 + * + * https://leetcode-cn.com/problems/reverse-bits/description/ + * + * algorithms + * Easy (63.18%) + * Likes: 253 + * Dislikes: 0 + * Total Accepted: 66.2K + * Total Submissions: 104.7K + * Testcase Example: '00000010100101000001111010011100' + * + * 颠倒给定的 32 位无符号整数的二进制位。 + * + * + * + * 示例 1: + * + * 输入: 00000010100101000001111010011100 + * 输出: 00111001011110000010100101000000 + * 解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, + * ⁠ 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 + * + * 示例 2: + * + * 输入:11111111111111111111111111111101 + * 输出:10111111111111111111111111111111 + * 解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, + * 因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。 + * + * + * + * 提示: + * + * + * 请注意,在某些语言(如 + * Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 + * -1073741825。 + * + * + * + * + * 进阶: + * 如果多次调用这个函数,你将如何优化你的算法? + * + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +var reverseBits = function (n) { + let result = 0; + + for (let i = 0; i < 32; i++) { + result = (result << 1) + (n & 1); + n >>= 1; + } + + return result >>> 0; +}; +// @lc code=end diff --git "a/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\345\276\252\347\216\257\345\222\214\344\275\215\347\247\273\345\212\250.js" "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\345\276\252\347\216\257\345\222\214\344\275\215\347\247\273\345\212\250.js" new file mode 100644 index 00000000..e215c976 --- /dev/null +++ "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\345\276\252\347\216\257\345\222\214\344\275\215\347\247\273\345\212\250.js" @@ -0,0 +1,98 @@ +/* + * @lc app=leetcode.cn id=191 lang=javascript + * + * [191] 位1的个数 + * + * https://leetcode-cn.com/problems/number-of-1-bits/description/ + * + * algorithms + * Easy (70.93%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 101.6K + * Total Submissions: 143.3K + * Testcase Example: '00000000000000000000000000001011' + * + * 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。 + * + * + * + * 提示: + * + * + * 请注意,在某些语言(如 + * Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 + * + * + * + * + * 进阶: + * + * + * 如果多次调用这个函数,你将如何优化你的算法? + * + * + * + * + * 示例 1: + * + * + * 输入:00000000000000000000000000001011 + * 输出:3 + * 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 + * + * + * 示例 2: + * + * + * 输入:00000000000000000000000010000000 + * 输出:1 + * 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 + * + * + * 示例 3: + * + * + * 输入:11111111111111111111111111111101 + * 输出:31 + * 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 + * + * + * + * 提示: + * + * + * 输入必须是长度为 32 的 二进制串 。 + * + * + * + * + * + * + * + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function (n) { + let count = 0; // 统计1的数量 + let mask = 1; // 用于和n的每个位置进行与运算,判断1是否存在 + + // 遍历n,判断每个位置是否有1 + for (let i = 0; i < 32; i++) { + // 如果与运算的结果不为0,表示当前位置是1 + if ((n & mask) !== 0) { + count++; + } + // 将mask的1向左移动一位,用于判断下一位 + mask <<= 1; + } + + // 返回1的数量 + return count; +}; +// @lc code=end diff --git "a/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\345\276\252\347\216\257\345\222\214\344\275\215\347\247\273\345\212\250\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\345\276\252\347\216\257\345\222\214\344\275\215\347\247\273\345\212\250\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..e55ac477 --- /dev/null +++ "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\345\276\252\347\216\257\345\222\214\344\275\215\347\247\273\345\212\250\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,94 @@ +/* + * @lc app=leetcode.cn id=191 lang=javascript + * + * [191] 位1的个数 + * + * https://leetcode-cn.com/problems/number-of-1-bits/description/ + * + * algorithms + * Easy (70.93%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 101.6K + * Total Submissions: 143.3K + * Testcase Example: '00000000000000000000000000001011' + * + * 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。 + * + * + * + * 提示: + * + * + * 请注意,在某些语言(如 + * Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 + * + * + * + * + * 进阶: + * + * + * 如果多次调用这个函数,你将如何优化你的算法? + * + * + * + * + * 示例 1: + * + * + * 输入:00000000000000000000000000001011 + * 输出:3 + * 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 + * + * + * 示例 2: + * + * + * 输入:00000000000000000000000010000000 + * 输出:1 + * 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 + * + * + * 示例 3: + * + * + * 输入:11111111111111111111111111111101 + * 输出:31 + * 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 + * + * + * + * 提示: + * + * + * 输入必须是长度为 32 的 二进制串 。 + * + * + * + * + * + * + * + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function (n) { + let mask = 1; + let count = 0; + + for (let i = 0; i < 32; i++) { + if ((n & mask) !== 0) { + count++; + } + mask <<= 1; + } + + return count; +}; +// @lc code=end diff --git "a/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\346\257\217\346\254\241\346\270\205\351\231\244\344\270\200\344\270\2521.js" "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\346\257\217\346\254\241\346\270\205\351\231\244\344\270\200\344\270\2521.js" new file mode 100644 index 00000000..c81953ce --- /dev/null +++ "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\346\257\217\346\254\241\346\270\205\351\231\244\344\270\200\344\270\2521.js" @@ -0,0 +1,92 @@ +/* + * @lc app=leetcode.cn id=191 lang=javascript + * + * [191] 位1的个数 + * + * https://leetcode-cn.com/problems/number-of-1-bits/description/ + * + * algorithms + * Easy (70.93%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 101.6K + * Total Submissions: 143.3K + * Testcase Example: '00000000000000000000000000001011' + * + * 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。 + * + * + *231. 2的幂,递归,JavaScript,详细注释 + * 提示: + * + * + * 请注意,在某些语言(如 + * Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 + * + * + * + * + * 进阶: + * + * + * 如果多次调用这个函数,你将如何优化你的算法? + * + * + * + * + * 示例 1: + * + * + * 输入:00000000000000000000000000001011 + * 输出:3 + * 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 + * + * + * 示例 2: + * + * + * 输入:00000000000000000000000010000000 + * 输出:1 + * 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 + * + * + * 示例 3: + * + * + * 输入:11111111111111111111111111111101 + * 输出:31 + * 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 + * + * + * + * 提示: + * + * + * 输入必须是长度为 32 的 二进制串 。 + * + * + * + * + * + * + * + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function (n) { + let count = 0; // 统计1的数量 + + // 不断循环直到1被清空 + while (n !== 0) { + count++; // 每清除一个1就计数一次 + n = n & (n - 1); // 每次清除最后一位的1 + } + + return count; +}; +// @lc code=end diff --git "a/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\346\257\217\346\254\241\346\270\205\351\231\244\344\270\200\344\270\2521\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\346\257\217\346\254\241\346\270\205\351\231\244\344\270\200\344\270\2521\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..da5a44a2 --- /dev/null +++ "b/Week_08/191. \344\275\2151\347\232\204\344\270\252\346\225\260\357\274\214\346\257\217\346\254\241\346\270\205\351\231\244\344\270\200\344\270\2521\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,91 @@ +/* + * @lc app=leetcode.cn id=191 lang=javascript + * + * [191] 位1的个数 + * + * https://leetcode-cn.com/problems/number-of-1-bits/description/ + * + * algorithms + * Easy (70.93%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 101.6K + * Total Submissions: 143.3K + * Testcase Example: '00000000000000000000000000001011' + * + * 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量)。 + * + * + * + * 提示: + * + * + * 请注意,在某些语言(如 + * Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + * 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 + * + * + * + * + * 进阶: + * + * + * 如果多次调用这个函数,你将如何优化你的算法? + * + * + * + * + * 示例 1: + * + * + * 输入:00000000000000000000000000001011 + * 输出:3 + * 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 + * + * + * 示例 2: + * + * + * 输入:00000000000000000000000010000000 + * 输出:1 + * 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 + * + * + * 示例 3: + * + * + * 输入:11111111111111111111111111111101 + * 输出:31 + * 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 + * + * + * + * 提示: + * + * + * 输入必须是长度为 32 的 二进制串 。 + * + * + * + * + * + * + * + */ + +// @lc code=start +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function (n) { + let count = 0; + + while (n !== 0) { + count++; + n = n & (n - 1); + } + + return count; +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\345\217\226\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\2041.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\345\217\226\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\2041.js" new file mode 100644 index 00000000..daed1c2b --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\345\217\226\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\2041.js" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + return n > 0 && (n & -n) === n; +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\345\217\226\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\2041\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\345\217\226\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\2041\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..daed1c2b --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\345\217\226\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\2041\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + return n > 0 && (n & -n) === n; +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\357\274\232\345\216\273\351\231\244\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\204 1.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\357\274\232\345\216\273\351\231\244\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\204 1.js" new file mode 100644 index 00000000..90ad5284 --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\357\274\232\345\216\273\351\231\244\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\204 1.js" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + return n > 0 && (n & (n - 1)) === 0; +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\357\274\232\345\216\273\351\231\244\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\204 1\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\357\274\232\345\216\273\351\231\244\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\204 1\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..90ad5284 --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\344\275\215\350\277\220\347\256\227\357\274\232\345\216\273\351\231\244\344\272\214\350\277\233\345\210\266\344\270\255\346\234\200\345\217\263\350\276\271\347\232\204 1\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,45 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + return n > 0 && (n & (n - 1)) === 0; +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\350\277\255\344\273\243.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\350\277\255\344\273\243.js" new file mode 100644 index 00000000..672e4af6 --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\350\277\255\344\273\243.js" @@ -0,0 +1,53 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + if (n <= 0) { + return false; + } + + while ((n & 1) === 0) { + n /= 2; + } + + return n === 1; +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\350\277\255\344\273\243\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\350\277\255\344\273\243\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..2e415a88 --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\350\277\255\344\273\243\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,53 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + if (n <= 0) { + return false; + } + + while ((n & 1) === 0) { + n = n >> 1; + } + + return n === 1; +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\351\200\222\345\275\222.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\351\200\222\345\275\222.js" new file mode 100644 index 00000000..530fc2a1 --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\351\200\222\345\275\222.js" @@ -0,0 +1,57 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + if (n <= 0) { + return false; + } + + if (n === 1) { + return true; + } + + if ((n & 1) === 1) { + return false; + } + + return isPowerOfTwo(n / 2); +}; +// @lc code=end diff --git "a/Week_08/231. 2\347\232\204\345\271\202\357\274\214\351\200\222\345\275\222\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\351\200\222\345\275\222\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..e15e12ee --- /dev/null +++ "b/Week_08/231. 2\347\232\204\345\271\202\357\274\214\351\200\222\345\275\222\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,53 @@ +/* + * @lc app=leetcode.cn id=231 lang=javascript + * + * [231] 2的幂 + * + * https://leetcode-cn.com/problems/power-of-two/description/ + * + * algorithms + * Easy (48.62%) + * Likes: 248 + * Dislikes: 0 + * Total Accepted: 80.9K + * Total Submissions: 166.3K + * Testcase Example: '1' + * + * 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + * + * 示例 1: + * + * 输入: 1 + * 输出: true + * 解释: 2^0 = 1 + * + * 示例 2: + * + * 输入: 16 + * 输出: true + * 解释: 2^4 = 16 + * + * 示例 3: + * + * 输入: 218 + * 输出: false + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + if (n <= 0) { + return false; + } + + if ((n & 1) === 0) { + return isPowerOfTwo(n >> 1); + } else { + return n === 1; + } +}; +// @lc code=end diff --git "a/Week_08/26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\214\345\217\214\346\214\207\351\222\210.js" "b/Week_08/26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\214\345\217\214\346\214\207\351\222\210.js" new file mode 100644 index 00000000..354ca6d5 --- /dev/null +++ "b/Week_08/26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\214\345\217\214\346\214\207\351\222\210.js" @@ -0,0 +1,77 @@ +/* + * @lc app=leetcode.cn id=26 lang=javascript + * + * [26] 删除排序数组中的重复项 + * + * https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/description/ + * + * algorithms + * Easy (51.37%) + * Likes: 1597 + * Dislikes: 0 + * Total Accepted: 403.3K + * Total Submissions: 784.1K + * Testcase Example: '[1,1,2]' + * + * 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 + * + * 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 + * + * + * + * 示例 1: + * + * 给定数组 nums = [1,1,2], + * + * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 + * + * 你不需要考虑数组中超出新长度后面的元素。 + * + * 示例 2: + * + * 给定 nums = [0,0,1,1,1,2,2,3,3,4], + * + * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 + * + * 你不需要考虑数组中超出新长度后面的元素。 + * + * + * + * + * 说明: + * + * 为什么返回数值是整数,但输出的答案是数组呢? + * + * 请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 + * + * 你可以想象内部操作如下: + * + * // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 + * int len = removeDuplicates(nums); + * + * // 在函数里修改输入数组对于调用者是可见的。 + * // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 + * for (int i = 0; i < len; i++) { + * print(nums[i]); + * } + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function (nums) { + let curr = 0; + + for (let i = 1; i < nums.length; i++) { + if (nums[i] !== nums[curr]) { + nums[++curr] = nums[i]; + } + } + + return curr + 1; +}; +// @lc code=end diff --git "a/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..44be8897 --- /dev/null +++ "b/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,59 @@ +/* + * @lc app=leetcode.cn id=338 lang=javascript + * + * [338] 比特位计数 + * + * https://leetcode-cn.com/problems/counting-bits/description/ + * + * algorithms + * Medium (76.45%) + * Likes: 484 + * Dislikes: 0 + * Total Accepted: 67.7K + * Total Submissions: 88.5K + * Testcase Example: '2' + * + * 给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。 + * + * 示例 1: + * + * 输入: 2 + * 输出: [0,1,1] + * + * 示例 2: + * + * 输入: 5 + * 输出: [0,1,1,2,1,2] + * + * 进阶: + * + * + * 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗? + * 要求算法的空间复杂度为O(n)。 + * 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。 + * + * + */ + +// @lc code=start +/** + * @param {number} num + * @return {number[]} + */ +var countBits = function (num) { + let dp = [0]; // 用于递推,0的1数量为0 + + // 遍历所有数字,分别计算1的个数 + for (let i = 1; i <= num; i++) { + if (i & 1) { + // 奇数的1数量为上一个偶数加1 + dp[i] = dp[i - 1] + 1; + } else { + // 偶数的1数量,为dp[i/2]的数量 + dp[i] = dp[i >> 1]; + } + } + + return dp; +}; +// @lc code=end diff --git "a/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\344\274\230\345\214\226.js" "b/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\344\274\230\345\214\226.js" new file mode 100644 index 00000000..20872398 --- /dev/null +++ "b/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\344\274\230\345\214\226.js" @@ -0,0 +1,58 @@ +/* + * @lc app=leetcode.cn id=338 lang=javascript + * + * [338] 比特位计数 + * + * https://leetcode-cn.com/problems/counting-bits/description/ + * + * algorithms + * Medium (76.45%) + * Likes: 484 + * Dislikes: 0 + * Total Accepted: 67.7K + * Total Submissions: 88.5K + * Testcase Example: '2' + * + * 给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。 + * + * 示例 1: + * + * 输入: 2 + * 输出: [0,1,1] + * + * 示例 2: + * + * 输入: 5 + * 输出: [0,1,1,2,1,2] + * + * 进阶: + * + * + * 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗? + * 要求算法的空间复杂度为O(n)。 + * 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。 + * + * + */ + +// @lc code=start +/** + * @param {number} num + * @return {number[]} + */ +var countBits = function (num) { + let dp = [0]; // 用于递推,0的1数量为0 + + // 遍历所有数字,分别计算1的个数 + for (let i = 1; i <= num; i++) { + // 奇数的1数量,等于上一个偶数的1数量加1,即 dp[i] = dp[i - 1] + 1 + // 偶数的1数量,等于dp[i/2]的数量,即 dp[i] = dp[i >> 2] + // 如果当前是奇数,dp[i - 1] === dp[i >> 2],即 dp[i] = dp[i >> 2] + 1 + // 如果当前是偶数,dp[i] = dp[i >> 2] + // 对于每一位来说,dp[i] = dp[i >> 2]是固定的,只需要增加判断i是否奇数,是就加1 + dp[i] = dp[i >> 1] + (i & 1); + } + + return dp; +}; +// @lc code=end diff --git "a/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\350\256\241\346\225\260.js" "b/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\350\256\241\346\225\260.js" new file mode 100644 index 00000000..a3ccb69a --- /dev/null +++ "b/Week_08/338. \346\257\224\347\211\271\344\275\215\350\256\241\346\225\260\357\274\214\350\256\241\346\225\260.js" @@ -0,0 +1,63 @@ +/* + * @lc app=leetcode.cn id=338 lang=javascript + * + * [338] 比特位计数 + * + * https://leetcode-cn.com/problems/counting-bits/description/ + * + * algorithms + * Medium (76.45%) + * Likes: 484 + * Dislikes: 0 + * Total Accepted: 67.7K + * Total Submissions: 88.5K + * Testcase Example: '2' + * + * 给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。 + * + * 示例 1: + * + * 输入: 2 + * 输出: [0,1,1] + * + * 示例 2: + * + * 输入: 5 + * 输出: [0,1,1,2,1,2] + * + * 进阶: + * + * + * 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗? + * 要求算法的空间复杂度为O(n)。 + * 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。 + * + * + */ + +// @lc code=start +/** + * @param {number} num + * @return {number[]} + */ +var countBits = function (num) { + let result = []; // 存出结果 + + // 遍历所有数字,分别计算1的个数 + for (let i = 0; i <= num; i++) { + let count = 0; // 统计当前数字的1数量 + let n = i; // 缓存当前数字 + + // 只要n不为0,就继续删除1 + while (n !== 0) { + n &= n - 1; // 每次清除最后一位1 + count++; // 统计清除数量 + } + + // 将1的数量存入结果 + result.push(count); + } + + return result; +}; +// @lc code=end diff --git "a/Week_08/493. \347\277\273\350\275\254\345\257\271\357\274\214\345\275\222\345\271\266\346\216\222\345\272\217\357\274\214\347\254\254\344\272\214\351\201\215\357\274\214\344\274\230\345\214\226\347\211\210.js" "b/Week_08/493. \347\277\273\350\275\254\345\257\271\357\274\214\345\275\222\345\271\266\346\216\222\345\272\217\357\274\214\347\254\254\344\272\214\351\201\215\357\274\214\344\274\230\345\214\226\347\211\210.js" new file mode 100644 index 00000000..ce1e666a --- /dev/null +++ "b/Week_08/493. \347\277\273\350\275\254\345\257\271\357\274\214\345\275\222\345\271\266\346\216\222\345\272\217\357\274\214\347\254\254\344\272\214\351\201\215\357\274\214\344\274\230\345\214\226\347\211\210.js" @@ -0,0 +1,110 @@ +/* + * @lc app=leetcode.cn id=493 lang=javascript + * + * [493] 翻转对 + * + * https://leetcode-cn.com/problems/reverse-pairs/description/ + * + * algorithms + * Hard (33.67%) + * Likes: 253 + * Dislikes: 0 + * Total Accepted: 22.4K + * Total Submissions: 66.5K + * Testcase Example: '[1,3,2,3,1]' + * + * 给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。 + * + * 你需要返回给定数组中的重要翻转对的数量。 + * + * 示例 1: + * + * + * 输入: [1,3,2,3,1] + * 输出: 2 + * + * + * 示例 2: + * + * + * 输入: [2,4,3,5,1] + * 输出: 3 + * + * + * 注意: + * + * + * 给定数组的长度不会超过50000。 + * 输入数组中的所有数字都在32位整数的表示范围内。 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var reversePairs = function (nums) { + return mergeSort(nums, 0, nums.length - 1); +}; + +const mergeSort = (nums, left, right) => { + if (right <= left) { + return 0; + } + + const mid = (left + right) >> 1; + + return ( + mergeSort(nums, left, mid) + + mergeSort(nums, mid + 1, right) + + merge(nums, left, mid, right) + ); +}; + +const merge = (nums, left, mid, right) => { + let i = left; + let j = mid + 1; + let k = 0; + let temp = []; + let m = left; + let n = j; + let count = 0; + + /* while (m <= mid) { + while (n <= right && nums[m] > 2 * nums[n]) { + n++; + } + count += n - mid - 1; + m++; + } */ + + while (m <= mid && n <= right) { + if (nums[m] > nums[n] * 2) { + count += mid - m + 1; + n++; + } else { + m++; + } + } + + while (i <= mid && j <= right) { + temp[k++] = nums[i] < nums[j] ? nums[i++] : nums[j++]; + } + + while (i <= mid) { + temp[k++] = nums[i++]; + } + + while (j <= right) { + temp[k++] = nums[j++]; + } + + for (let p = 0; p < temp.length; p++) { + nums[left + p] = temp[p]; + } + + return count; +}; +// @lc code=end diff --git "a/Week_08/51. N \347\232\207\345\220\216\357\274\214DFS.js" "b/Week_08/51. N \347\232\207\345\220\216\357\274\214DFS.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_08/51. N \347\232\207\345\220\216\357\274\214\344\275\215\350\277\220\347\256\227.js" "b/Week_08/51. N \347\232\207\345\220\216\357\274\214\344\275\215\350\277\220\347\256\227.js" new file mode 100644 index 00000000..e69de29b diff --git "a/Week_08/52. N\347\232\207\345\220\216 II\357\274\214DFS.js" "b/Week_08/52. N\347\232\207\345\220\216 II\357\274\214DFS.js" new file mode 100644 index 00000000..be8c1ad9 --- /dev/null +++ "b/Week_08/52. N\347\232\207\345\220\216 II\357\274\214DFS.js" @@ -0,0 +1,47 @@ +/* + * @lc app=leetcode.cn id=52 lang=javascript + * + * [52] N皇后 II + */ + +// @lc code=start +/** + * @param {number} n + * @return {number} + */ +var totalNQueens = function (n) { + let colSet = new Set(); + let pieSet = new Set(); + let naSet = new Set(); + let count = 0; + + function dfs(row) { + if (row >= n) { + count++; + return; + } + + for (let col = 0; col < n; col++) { + const pie = row + col; + const na = row - col; + + if (colSet.has(col) || pieSet.has(pie) || naSet.has(na)) { + continue; + } + + colSet.add(col); + pieSet.add(pie); + naSet.add(na); + + dfs(row + 1); + + colSet.delete(col); + pieSet.delete(pie); + naSet.delete(na); + } + } + dfs(0); + + return count; +}; +// @lc code=end diff --git "a/Week_08/52.n\347\232\207\345\220\216-ii\357\274\214\344\275\215\350\277\220\347\256\227.js" "b/Week_08/52.n\347\232\207\345\220\216-ii\357\274\214\344\275\215\350\277\220\347\256\227.js" new file mode 100644 index 00000000..7f40203c --- /dev/null +++ "b/Week_08/52.n\347\232\207\345\220\216-ii\357\274\214\344\275\215\350\277\220\347\256\227.js" @@ -0,0 +1,63 @@ +/* + * @lc app=leetcode.cn id=52 lang=javascript + * + * [52] N皇后 II + */ + +// @lc code=start +/** + * @param {number} n + * @return {number} + */ +var totalNQueens = function (n) { + let count = 0; // 统计解法数量 + + function dfs( + row, // 当前搜索的行 + colBit, // 列的位置 + pieBit, // 撇的位置 + naBit, // 捺的位置 + ) { + // 如果搜索到n,表示已经找到棋盘中所有皇后的位置 + if (row === n) { + // 统计当前解法数量 + count++; + return; + } + + // 计算当前可填入皇后的位置 + // 如果当前行的所有都会被攻击到,pos为0,无法进入循环 + let pos = + // 将列、撇、捺已放入皇后的位置取反,就得到可使用的位置 + ~(colBit | pieBit | naBit) & + // 将取反的结果和n个1进行与运算 + ((1 << n) - 1); + + // 将每个可放皇后的位置都放入皇后 + while (pos !== 0) { + const currPos = pos & -pos; // 取得最低位的1,即为当前放入皇后的位置 + // 删除最后一位的1,表示在被删除的位置放入了皇后 + // 下次循环时就会从第二位1开始放皇后,直到所有位置都放入皇后 + pos = pos & (pos - 1); + + dfs( + row + 1, // 搜索下一行 + // 用或运算,将当前皇后的位置存储,下一行搜索时,能被当前皇后攻击的位置就不能放皇后 + colBit | currPos, // 下一行的同一列不能放皇后 + (pieBit | currPos) << 1, // 下一行的撇位置不能放皇后 + (naBit | currPos) >> 1, // 下一行的捺位置不能放皇后 + ); + } + } + + dfs( + 0, // 初始时搜索第一行 + // 列、撇、捺位置初始时都可以放皇后 + 0, // 列的位置 + 0, // 撇的位置 + 0, // 捺的位置 + ); + + return count; +}; +// @lc code=end diff --git "a/Week_08/56. \345\220\210\345\271\266\345\214\272\351\227\264\357\274\214\347\254\254\344\272\214\351\201\215.js" "b/Week_08/56. \345\220\210\345\271\266\345\214\272\351\227\264\357\274\214\347\254\254\344\272\214\351\201\215.js" new file mode 100644 index 00000000..d5d4b76a --- /dev/null +++ "b/Week_08/56. \345\220\210\345\271\266\345\214\272\351\227\264\357\274\214\347\254\254\344\272\214\351\201\215.js" @@ -0,0 +1,66 @@ +/* + * @lc app=leetcode.cn id=56 lang=javascript + * + * [56] 合并区间 + * + * https://leetcode-cn.com/problems/merge-intervals/description/ + * + * algorithms + * Medium (44.04%) + * Likes: 771 + * Dislikes: 0 + * Total Accepted: 182.3K + * Total Submissions: 413.8K + * Testcase Example: '[[1,3],[2,6],[8,10],[15,18]]' + * + * 给出一个区间的集合,请合并所有重叠的区间。 + * + * + * + * 示例 1: + * + * 输入: intervals = [[1,3],[2,6],[8,10],[15,18]] + * 输出: [[1,6],[8,10],[15,18]] + * 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. + * + * + * 示例 2: + * + * 输入: intervals = [[1,4],[4,5]] + * 输出: [[1,5]] + * 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。 + * + * 注意:输入类型已于2019年4月15日更改。 请重置默认代码定义以获取新方法签名。 + * + * + * + * 提示: + * + * + * intervals[i][0] <= intervals[i][1] + * + * + */ + +// @lc code=start +/** + * @param {number[][]} intervals + * @return {number[][]} + */ +var merge = function (intervals) { + intervals.sort((a, b) => a[0] - b[0]); + let result = [intervals[0]]; + + for (let i = 1; i < intervals.length; i++) { + const last = result.length - 1; + + if (result[last][1] < intervals[i][0]) { + result.push(intervals[i]); + } else if (result[last][1] < intervals[i][1]) { + result[last][1] = intervals[i][1]; + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_08/56.\345\220\210\345\271\266\345\214\272\351\227\264.js" "b/Week_08/56.\345\220\210\345\271\266\345\214\272\351\227\264.js" new file mode 100644 index 00000000..d28f1f32 --- /dev/null +++ "b/Week_08/56.\345\220\210\345\271\266\345\214\272\351\227\264.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=56 lang=javascript + * + * [56] 合并区间 + */ + +// @lc code=start +/** + * @param {number[][]} intervals + * @return {number[][]} + */ +var merge = function (intervals) { + let result = []; // 存出结果 + + // 先将intervals按照第一个索引排序,这样相近的去见就排列在一起 + // 区间只要依次对比,就可以判断是否需要合并,并且不会出现遗漏 + intervals.sort((a, b) => a[0] - b[0]); + + // 先将第一个存入结果,以便与之后的区间对比 + result.push(intervals[0]); + + // 从1开始,逐个判断区间是否需要合并 + for (let i = 1; i < intervals.length; i++) { + // 如果当前区间的左边界大于上一个的右边界,两个区间必然无法合并 + if (intervals[i][0] > result[result.length - 1][1]) { + result.push(intervals[i]); + } else if (result[result.length - 1][1] <= intervals[i][1]) { + // 如果当前区间的左边界小等于上一个区间的右边界 + // 并且上一个区间的右边界小等于当前区间的右边界 + // 满足这两个条件的话,区间可以黑榜 + // 由于当前是else判断,intervals[i][0] <= result[i - 1][1]可以省略 + result[result.length - 1][1] = intervals[i][1]; + } + } + + return result; +}; +// @lc code=end diff --git a/Week_08/README.md b/Week_08/README.md index 50de3041..0872dea2 100644 --- a/Week_08/README.md +++ b/Week_08/README.md @@ -1 +1,90 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +重写了归并排序的两个版本模板: + +1. + +``` javascript +const mergeSort = (nums, compare = (a, b) => a - b) => { + // 如果数组长度为1,无需排序,直接返回即可 + if (nums.length === 1) { + return nums; + } + + const mid = nums.length >> 1; // 计算中间索引 + // 将数组拆分成两段,分别进行排序 + const left = nums.slice(0, mid); + const right = nums.slice(mid); + + // 将子数组不断拆分,并将每个子数组都进行排序好,之后合并返回 + return merge(mergeSort(left, compare), mergeSort(right, compare), compare); +}; + +// 将两个子数组排序、合并 +const merge = (left, right, compare) => { + let result = []; // 存储排序后的结果 + + // 将两个子数组按照compare的结果排序,直到其中一个被清空 + while (left.length && right.length) { + result.push(compare(left[0], right[0]) < 0 ? left.shift() : right.shift()); + } + + // 将数组剩余元素存入result,由于left和right已经被排序,因此无需再进行排序 + result.splice(result.length, 0, ...left, ...right); + + return result; +}; +``` + +2. + +``` javascript +const mergeSort = (nums, left, right, compare = (a, b) => a - b) => { + if (right <= left) { + return; + } + + const mid = (left + right) >> 1; + mergeSort(nums, left, mid, compare); + mergeSort(nums, mid + 1, right, compare); + + merge(nums, left, right, mid, compare); +}; + +const merge = (nums, left, right, mid, compare) => { + let temp = []; + let i = left; + let j = mid + 1; + let k = 0; + + while (i <= mid && j <= right) { + temp[k++] = compare(nums[i], nums[j]) < 0 ? nums[i++] : nums[j++]; + } + + while (i <= mid) { + temp[k++] = nums[i++]; + } + + while (j <= right) { + temp[k++] = nums[j++]; + } + + nums.splice(left, right - left + 1, ...temp); +}; +``` + +我为部分题目写了题解,如下: + +其余没写题解的题目,都在作业中写了详细注释,课程结束后会补上。 + +1. [LeetCode题解:231. 2的幂,位运算:去除二进制中最右边的 1,JavaScript,详细注释](https://leetcode-cn.com/problems/power-of-two/solution/leetcodeti-jie-wei-yun-suan-qu-chu-er-jin-zhi-zh-2) +2. [LeetCode题解:231. 2的幂,位运算取二进制中最右边的1,JavaScript,详细注释](https://leetcode-cn.com/problems/power-of-two/solution/leetcodeti-jie-231-2de-mi-wei-yun-suan-qu-er-jin-z) +3. [LeetCode题解:231. 2的幂,递归,JavaScript,详细注释](https://leetcode-cn.com/problems/power-of-two/solution/leetcodeti-jie-231-2de-mi-di-gui-javascriptxiang-x) +4. [LeetCode题解:231. 2的幂,迭代,JavaScript,详细注释](https://leetcode-cn.com/problems/power-of-two/solution/leetcodeti-jie-231-2de-mi-die-dai-javascriptxiang-) +5. [LeetCode题解:52. N皇后 II,回溯+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/n-queens-ii/solution/leetcodeti-jie-52-nhuang-hou-iihui-su-ha-xi-biao-j) +6. [LeetCode题解:51. N 皇后,回溯+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/n-queens/solution/leetcodeti-jie-51-n-huang-hou-hui-su-ha-xi-biao-ja) +7. [LeetCode题解:52. N皇后 II,回溯+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/n-queens-ii/solution/leetcodeti-jie-52-nhuang-hou-iihui-su-ha-xi-biao-j) +8. [LeetCode题解:242. 有效的字母异位词,数组计数,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-s-2) +9. [LeetCode题解:242. 有效的字母异位词,哈希表两次循环,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-h-2) +10. [LeetCode题解:242. 有效的字母异位词,哈希表一次循环,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-ha-) +11. [LeetCode题解:242. 有效的字母异位词,数组排序,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-shu) diff --git "a/Week_08/\345\211\221\346\214\207 Offer 51. \346\225\260\347\273\204\344\270\255\347\232\204\351\200\206\345\272\217\345\257\271\357\274\214\345\275\222\345\271\266\346\216\222\345\272\217.js" "b/Week_08/\345\211\221\346\214\207 Offer 51. \346\225\260\347\273\204\344\270\255\347\232\204\351\200\206\345\272\217\345\257\271\357\274\214\345\275\222\345\271\266\346\216\222\345\272\217.js" new file mode 100644 index 00000000..89bc1070 --- /dev/null +++ "b/Week_08/\345\211\221\346\214\207 Offer 51. \346\225\260\347\273\204\344\270\255\347\232\204\351\200\206\345\272\217\345\257\271\357\274\214\345\275\222\345\271\266\346\216\222\345\272\217.js" @@ -0,0 +1,59 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var reversePairs = function (nums) { + return mergeSort(nums, 0, nums.length - 1); +}; + +const mergeSort = (nums, left, right) => { + if (right <= left) { + return 0; + } + + const mid = (left + right) >> 1; + + return ( + mergeSort(nums, left, mid) + + mergeSort(nums, mid + 1, right) + + merge(nums, left, mid, right) + ); +}; + +const merge = (nums, left, mid, right) => { + let m = left; + let n = mid + 1; + let count = 0; + + while (m <= mid && n <= right) { + if (nums[m] > nums[n]) { + count += mid - m + 1; + n++; + } else { + m++; + } + } + + let i = left; + let j = mid + 1; + let k = 0; + let temp = []; + + while (i <= mid && j <= right) { + temp[k++] = nums[i] < nums[j] ? nums[i++] : nums[j++]; + } + + while (i <= mid) { + temp[k++] = nums[i++]; + } + + while (j <= right) { + temp[k++] = nums[j++]; + } + + for (let p = 0; p < temp.length; p++) { + nums[left + p] = temp[p]; + } + + return count; +}; diff --git "a/Week_09/1091. \344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS.js" "b/Week_09/1091. \344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS.js" new file mode 100644 index 00000000..93bd38d1 --- /dev/null +++ "b/Week_09/1091. \344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214BFS.js" @@ -0,0 +1,133 @@ +/* + * @lc app=leetcode.cn id=1091 lang=javascript + * + * [1091] 二进制矩阵中的最短路径 + * + * https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/description/ + * + * algorithms + * Medium (35.98%) + * Likes: 81 + * Dislikes: 0 + * Total Accepted: 15K + * Total Submissions: 41.6K + * Testcase Example: '[[0,1],[1,0]]' + * + * 在一个 N × N 的方形网格中,每个单元格有两种状态:空(0)或者阻塞(1)。 + * + * 一条从左上角到右下角、长度为 k 的畅通路径,由满足下述条件的单元格 C_1, C_2, ..., C_k 组成: + * + * + * 相邻单元格 C_i 和 C_{i+1} 在八个方向之一上连通(此时,C_i 和 C_{i+1} 不同且共享边或角) + * C_1 位于 (0, 0)(即,值为 grid[0][0]) + * C_k 位于 (N-1, N-1)(即,值为 grid[N-1][N-1]) + * 如果 C_i 位于 (r, c),则 grid[r][c] 为空(即,grid[r][c] == 0) + * + * + * 返回这条从左上角到右下角的最短畅通路径的长度。如果不存在这样的路径,返回 -1 。 + * + * + * + * 示例 1: + * + * 输入:[[0,1],[1,0]] + * + * 输出:2 + * + * + * + * 示例 2: + * + * 输入:[[0,0,0],[1,1,0],[1,1,0]] + * + * 输出:4 + * + * + * + * + * + * 提示: + * + * + * 1 <= grid.length == grid[0].length <= 100 + * grid[i][j] 为 0 或 1 + * + * + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + // 调试Case + // [[0,0,0],[0,0,0],[0,0,0]] + // [[0,0,0],[1,1,0],[1,1,0]] + // [[1,0,0],[1,1,0],[1,1,0]] + // [[1,0,0],[1,1,0],[1,1,0]] + // [[0,1,0,0],[0,0,0,1],[0,0,0,0],[0,0,0,0]] + // [[0,0,0,0,1],[1,0,0,0,0],[0,1,0,1,0],[0,0,0,1,1],[0,0,0,1,0]] + // [[0,0,1,0,1,1],[1,0,0,1,0,0],[0,1,0,1,0,0],[1,0,1,0,0,0],[0,1,0,1,0,0],[0,0,0,0,0,0]] + // [[0,0,1,0,0,0,0],[0,1,0,0,0,0,1],[0,0,1,0,1,0,0],[0,0,0,1,1,1,0],[1,0,0,1,1,0,0],[1,1,1,1,1,0,1],[0,0,1,0,0,0,0]] + // [[0,0,0,0,1,1,1,1,0],[0,1,1,0,0,0,0,1,0],[0,0,1,0,0,0,0,0,0],[1,1,0,0,1,0,0,1,1],[0,0,1,1,1,0,1,0,1],[0,1,0,1,0,0,0,0,0],[0,0,0,1,0,1,0,0,0],[0,1,0,1,1,0,0,0,0],[0,0,0,0,0,1,0,1,0]] + if (grid[0][0] === 1) { + return -1; + } + + const m = grid.length - 1; + + if (grid[m][m] === 1) { + return -1; + } + + if (grid.length === 1) { + return 1; + } + + let queue = [[0, 0]]; + grid[0][0] = 1; + let count = 1; + const direction = [ + [-1, 1], + [0, 1], + [1, 1], + [1, 0], + [1, -1], + ]; + + while (queue.length) { + let queueLength = queue.length; + + while (--queueLength >= 0) { + const [x, y] = queue.shift(); + + for (const [dx, dy] of direction) { + const newX = x + dx; + const newY = y + dy; + + if ( + newX < 0 || + newX > m || + newY < 0 || + newY > m || + grid[newX][newY] === 1 + ) { + continue; + } + + if (newX === m && newY === m) { + return count + 1; + } + + queue.push([newX, newY]); + grid[newX][newY] = 1; + } + } + + count++; + } + + return -1; +}; +// @lc code=end diff --git "a/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap String.js" "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap String.js" new file mode 100644 index 00000000..fa9d3e6f --- /dev/null +++ "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap String.js" @@ -0,0 +1,286 @@ +/* + * @lc app=leetcode.cn id=1091 lang=javascript + * + * [1091] 二进制矩阵中的最短路径 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + // 调试Case + // [[0,0,0],[0,0,0],[0,0,0]] + // [[0,0,0],[1,1,0],[1,1,0]] + // [[0,1,0,0],[0,0,0,1],[0,0,0,0],[0,0,0,0]] + // [[0,0,0,0,1],[1,0,0,0,0],[0,1,0,1,0],[0,0,0,1,1],[0,0,0,1,0]] + // [[0,0,1,0,1,1],[1,0,0,1,0,0],[0,1,0,1,0,0],[1,0,1,0,0,0],[0,1,0,1,0,0],[0,0,0,0,0,0]] + // [[0,0,1,0,0,0,0],[0,1,0,0,0,0,1],[0,0,1,0,1,0,0],[0,0,0,1,1,1,0],[1,0,0,1,1,0,0],[1,1,1,1,1,0,1],[0,0,1,0,0,0,0]] + // [[0,0,0,0,1,1,1,1,0],[0,1,1,0,0,0,0,1,0],[0,0,1,0,0,0,0,0,0],[1,1,0,0,1,0,0,1,1],[0,0,1,1,1,0,1,0,1],[0,1,0,1,0,0,0,0,0],[0,0,0,1,0,1,0,0,0],[0,1,0,1,1,0,0,0,0],[0,0,0,0,0,1,0,1,0]] + // 缓存矩阵的终点位置 + const m = grid.length - 1; + const n = grid[0].length - 1; + const row = grid[0].length; + + // 当起点和终点为1时,必然无法到达终点 + if (grid[0][0] === 1 || grid[m][n] === 1) { + return -1; + } + + // 如果矩阵只有1个点,且为0,路径为1 + if (m === 0 && n === 0 && grid[0][0] === 0) { + return 1; + } + + const start = [JSON.stringify([0, 0]), 0]; + let visited = new Set(); + let routeMap = new Map(); + let distanceMap = new Map([start]); + const heuristic = (a, b) => { + return Math.max(Math.abs(m - a), Math.abs(n - b)); + }; + const compare = (a, b) => { + return a[1] - b[1]; + }; + let binaryHeap = new BinaryHeap(compare); + binaryHeap.insert(start); + // 可以向四周所有方向行走,缓存8个方向 + const direction = [ + [1, 0], // 下 + [0, 1], // 右 + [1, 1], // 右下 + [-1, 1], // 右上 + [1, -1], // 左下 + // 一下3种都是往回走,无需判断 + // [-1, 0], // 上 + // [0, -1], // 左 + // [-1, -1], // 左上 + ]; + // 如果队列中有值,则继续搜索 + while (binaryHeap.size()) { + // 出队一个坐标,计算它可以行走的下一步位置 + const head = binaryHeap.deleteHead(); + let node = head[0]; + const [x, y] = JSON.parse(node); + + if (visited.has(node)) { + continue; + } + if (x === m && y === n) { + let route = [node]; + // grid[x][y] = 8; + + while (routeMap.has(node)) { + node = routeMap.get(node); + route.unshift(node); + // grid[x][y] = 8; + } + // console.log(route); + // console.log(grid); + + return route.length; + } + // grid[x][y] = 8; + // console.log(x, y, visited.has(node)); + visited.add(node); + + for (let i = 0; i < direction.length; i++) { + // 下一步可以向四周行走,计算出相应新坐标 + const newX = x + direction[i][0]; + const newY = y + direction[i][1]; + const newNode = JSON.stringify([newX, newY]); + + if ( + !visited.has(newNode) && + // 判断新坐标不可超出矩阵 + newX >= 0 && + newY >= 0 && + newX <= m && + newY <= n && + // 下一步可以行走,才进行记录 + grid[newX][newY] !== 1 + ) { + // 将下一步的坐标存入队列,用于下一层循环 + const nextStep = distanceMap.get(node) + 1; + binaryHeap.insert([newNode, nextStep + heuristic(newX, newY)]); + + if (!distanceMap.has(newNode) || nextStep < distanceMap.get(newNode)) { + distanceMap.set(newNode, nextStep); + routeMap.set(newNode, node); + } + } + } + } + + return -1; +}; + +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆中插入多个元素 + insertMultiple(arr) { + for (let i = 0; i < arr.length; i++) { + this.insert(arr[i]); + } + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } + + // 清除所有元素 + clear() { + this.data = []; + } +} +// @lc code=end + +[ + [0, 0, 1, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 1], + [0, 0, 1, 0, 1, 0, 0], + [0, 0, 0, 1, 1, 1, 0], + [1, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 0, 1], + [0, 0, 1, 0, 0, 0, 0], +]; +[ + [7, 7, 1, 7, 0, 0, 0], + [7, 1, 7, 7, 7, 0, 1], + [7, 7, 1, 7, 1, 7, 0], + [7, 7, 7, 1, 1, 1, 7], + [1, 7, 7, 1, 1, 7, 7], + [1, 1, 1, 1, 1, 7, 1], + [0, 0, 1, 0, 0, 0, 8], +]; +[ + [7, 7, 7, 0, 1, 1, 1, 1, 0], + [7, 1, 1, 7, 0, 0, 0, 1, 0], + [7, 7, 1, 7, 7, 0, 0, 0, 0], + [1, 1, 7, 7, 1, 7, 0, 1, 1], + [0, 0, 1, 1, 1, 7, 1, 0, 1], + [0, 1, 0, 1, 0, 7, 7, 0, 0], + [0, 0, 0, 1, 0, 1, 7, 7, 0], + [0, 1, 0, 1, 1, 0, 0, 7, 7], + [0, 0, 0, 0, 0, 1, 0, 1, 7], +]; diff --git "a/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap no Set.js" "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap no Set.js" new file mode 100644 index 00000000..7928f525 --- /dev/null +++ "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap no Set.js" @@ -0,0 +1,309 @@ +/* + * @lc app=leetcode.cn id=1091 lang=javascript + * + * [1091] 二进制矩阵中的最短路径 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + // 调试Case + // [[0,0,0],[0,0,0],[0,0,0]] + // [[0,0,0],[1,1,0],[1,1,0]] + // [[0,1,0,0],[0,0,0,1],[0,0,0,0],[0,0,0,0]] + // [[0,0,0,0,1],[1,0,0,0,0],[0,1,0,1,0],[0,0,0,1,1],[0,0,0,1,0]] + // [[0,0,1,0,1,1],[1,0,0,1,0,0],[0,1,0,1,0,0],[1,0,1,0,0,0],[0,1,0,1,0,0],[0,0,0,0,0,0]] + // [[0,0,1,0,0,0,0],[0,1,0,0,0,0,1],[0,0,1,0,1,0,0],[0,0,0,1,1,1,0],[1,0,0,1,1,0,0],[1,1,1,1,1,0,1],[0,0,1,0,0,0,0]] + // [[0,0,0,0,1,1,1,1,0],[0,1,1,0,0,0,0,1,0],[0,0,1,0,0,0,0,0,0],[1,1,0,0,1,0,0,1,1],[0,0,1,1,1,0,1,0,1],[0,1,0,1,0,0,0,0,0],[0,0,0,1,0,1,0,0,0],[0,1,0,1,1,0,0,0,0],[0,0,0,0,0,1,0,1,0]] + // 缓存矩阵的终点位置 + const m = grid.length - 1; + const n = grid[0].length - 1; + const row = grid[0].length; + + // 当起点和终点为1时,必然无法到达终点 + if (grid[0][0] === 1 || grid[m][n] === 1) { + return -1; + } + + // 如果矩阵只有1个点,且为0,路径为1 + if (m === 0 && n === 0 && grid[0][0] === 0) { + return 1; + } + + const start = [0, 0]; + let routeMap = new Map(); + let distanceMap = new Map([start]); + const heuristic = (a, b) => { + return Math.max(Math.abs(m - a), Math.abs(n - b)); + }; + const compare = (a, b) => { + return a[1] - b[1]; + }; + let binaryHeap = new BinaryHeap(compare); + binaryHeap.insert(start); + // 可以向四周所有方向行走,缓存8个方向 + const direction = [ + [1, 0], // 下 + [0, 1], // 右 + [1, 1], // 右下 + [-1, 1], // 右上 + [1, -1], // 左下 + // 一下3种都是往回走,无需判断 + // [-1, 0], // 上 + // [0, -1], // 左 + // [-1, -1], // 左上 + ]; + // 如果队列中有值,则继续搜索 + while (binaryHeap.size()) { + // 出队一个坐标,计算它可以行走的下一步位置 + let node = binaryHeap.deleteHead()[0]; + const x = Math.floor(node / row); + const y = node % row; + + if (grid[x][y] !== 0) { + continue; + } + if (x === m && y === n) { + let route = [node]; + // grid[x][y] = 8; + + while (routeMap.has(node)) { + node = routeMap.get(node); + // const x = Math.floor(node / row); + // const y = node % row; + route.unshift(node); + // grid[x][y] = 8; + } + // console.log(route); + // console.log(grid); + + return route.length; + } + // grid[x][y] = 8; + grid[x][y] = 2; + + for (let i = 0; i < direction.length; i++) { + // 下一步可以向四周行走,计算出相应新坐标 + const newX = x + direction[i][0]; + const newY = y + direction[i][1]; + const newNode = newX * row + newY; + + if ( + // 判断新坐标不可超出矩阵 + newX >= 0 && + newY >= 0 && + newX <= m && + newY <= n && + // 下一步可以行走,才进行记录 + grid[newX][newY] === 0 + ) { + // 将下一步的坐标存入队列,用于下一层循环 + const nextStep = distanceMap.get(node) + 1; + binaryHeap.insert([newNode, nextStep + heuristic(newX, newY)]); + + if (!distanceMap.has(newNode) || nextStep < distanceMap.get(newNode)) { + distanceMap.set(newNode, nextStep); + routeMap.set(newNode, node); + } + } + } + } + + return -1; +}; + +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆中插入多个元素 + insertMultiple(arr) { + for (let i = 0; i < arr.length; i++) { + this.insert(arr[i]); + } + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } + + // 清除所有元素 + clear() { + this.data = []; + } +} +// @lc code=end + +[ + [0, 0, 1, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 1], + [0, 0, 1, 0, 1, 0, 0], + [0, 0, 0, 1, 1, 1, 0], + [1, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 0, 1], + [0, 0, 1, 0, 0, 0, 0], +]; +[ + [7, 7, 1, 7, 0, 0, 0], + [7, 1, 7, 7, 7, 0, 1], + [7, 7, 1, 7, 1, 7, 0], + [7, 7, 7, 1, 1, 1, 7], + [1, 7, 7, 1, 1, 7, 7], + [1, 1, 1, 1, 1, 7, 1], + [0, 0, 1, 0, 0, 0, 8], +]; +[ + [7, 7, 7, 0, 1, 1, 1, 1, 0], + [7, 1, 1, 7, 0, 0, 0, 1, 0], + [7, 7, 1, 7, 7, 0, 0, 0, 0], + [1, 1, 7, 7, 1, 7, 0, 1, 1], + [0, 0, 1, 1, 1, 7, 1, 0, 1], + [0, 1, 0, 1, 0, 7, 7, 0, 0], + [0, 0, 0, 1, 0, 1, 7, 7, 0], + [0, 1, 0, 1, 1, 0, 0, 7, 7], + [0, 0, 0, 0, 0, 1, 0, 1, 7], +]; + +[ + [0, 0, 0, 0, 1, 1, 1, 1, 0], + [0, 1, 1, 0, 0, 0, 0, 1, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 1, 0, 0, 1, 1], + [0, 0, 1, 1, 1, 0, 1, 0, 1], + [0, 1, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 1, 0], +]; + +[ + [8, 8, 8, 0, 1, 1, 1, 1, 0], + [0, 1, 1, 8, 0, 0, 0, 1, 0], + [0, 0, 1, 0, 8, 0, 0, 0, 0], + [1, 1, 0, 0, 1, 8, 0, 1, 1], + [0, 0, 1, 1, 1, 8, 1, 0, 1], + [0, 1, 0, 1, 0, 8, 0, 0, 0], + [0, 0, 0, 1, 0, 1, 8, 0, 0], + [0, 1, 0, 1, 1, 0, 0, 8, 0], + [0, 0, 0, 0, 0, 1, 0, 1, 8], +]; diff --git "a/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap \344\274\230\345\214\226.js" "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap \344\274\230\345\214\226.js" new file mode 100644 index 00000000..cf7526e6 --- /dev/null +++ "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap \344\274\230\345\214\226.js" @@ -0,0 +1,320 @@ +/* + * @lc app=leetcode.cn id=1091 lang=javascript + * + * [1091] 二进制矩阵中的最短路径 + * + * https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/description/ + * + * algorithms + * Medium (35.98%) + * Likes: 81 + * Dislikes: 0 + * Total Accepted: 15K + * Total Submissions: 41.6K + * Testcase Example: '[[0,1],[1,0]]' + * + * 在一个 N × N 的方形网格中,每个单元格有两种状态:空(0)或者阻塞(1)。 + * + * 一条从左上角到右下角、长度为 k 的畅通路径,由满足下述条件的单元格 C_1, C_2, ..., C_k 组成: + * + * + * 相邻单元格 C_i 和 C_{i+1} 在八个方向之一上连通(此时,C_i 和 C_{i+1} 不同且共享边或角) + * C_1 位于 (0, 0)(即,值为 grid[0][0]) + * C_k 位于 (N-1, N-1)(即,值为 grid[N-1][N-1]) + * 如果 C_i 位于 (r, c),则 grid[r][c] 为空(即,grid[r][c] == 0) + * + * + * 返回这条从左上角到右下角的最短畅通路径的长度。如果不存在这样的路径,返回 -1 。 + * + * + * + * 示例 1: + * + * 输入:[[0,1],[1,0]] + * + * 输出:2 + * + * + * + * 示例 2: + * + * 输入:[[0,0,0],[1,1,0],[1,1,0]] + * + * 输出:4 + * + * + * + * + * + * 提示: + * + * + * 1 <= grid.length == grid[0].length <= 100 + * grid[i][j] 为 0 或 1 + * + * + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + // 调试Case + // [[0,0,0],[0,0,0],[0,0,0]] + // [[0,0,0],[1,1,0],[1,1,0]] + // [[1,0,0],[1,1,0],[1,1,0]] + // [[1,0,0],[1,1,0],[1,1,0]] + // [[0,1,0,0],[0,0,0,1],[0,0,0,0],[0,0,0,0]] + // [[0,0,0,0,1],[1,0,0,0,0],[0,1,0,1,0],[0,0,0,1,1],[0,0,0,1,0]] + // [[0,0,1,0,1,1],[1,0,0,1,0,0],[0,1,0,1,0,0],[1,0,1,0,0,0],[0,1,0,1,0,0],[0,0,0,0,0,0]] + // [[0,0,1,0,0,0,0],[0,1,0,0,0,0,1],[0,0,1,0,1,0,0],[0,0,0,1,1,1,0],[1,0,0,1,1,0,0],[1,1,1,1,1,0,1],[0,0,1,0,0,0,0]] + // [[0,0,0,0,1,1,1,1,0],[0,1,1,0,0,0,0,1,0],[0,0,1,0,0,0,0,0,0],[1,1,0,0,1,0,0,1,1],[0,0,1,1,1,0,1,0,1],[0,1,0,1,0,0,0,0,0],[0,0,0,1,0,1,0,0,0],[0,1,0,1,1,0,0,0,0],[0,0,0,0,0,1,0,1,0]] + // 这个Case如果heuristic使用欧氏距离计算无法通过 + // [[0,0,1,0,0,1,0,1,0],[0,0,0,0,0,0,0,0,0],[0,1,1,0,1,1,1,1,1],[0,0,0,1,0,0,0,0,0],[1,1,0,0,0,1,0,0,0],[1,0,1,0,0,1,0,0,1],[1,1,1,1,0,0,1,0,0],[1,0,0,1,0,0,1,1,1],[0,0,0,0,0,0,0,0,0]] + const m = grid.length - 1; // 行和列的的最后元素索引 + const colNum = grid.length; // 列的数量,即每行的元素数量 + const destination = colNum ** 2 - 1; // 终点的编号 + + // 当起点和终点为1时,一定无法到达,返回-1 + if (grid[0][0] === 1 || grid[m][m] === 1) { + return -1; + } + + // 要传入堆中的排序函数 + const compare = (a, b) => { + // 按照每个节点的优先级从小到大排序 + // 优先级表示估算出的从起点到终点的总长度 + return a.priority - b.priority; + }; + // 估价函数,用于估算当前坐标到终点的距离,也可以理解为走到终点需要的步数 + const heuristic = (x, y) => { + // 这里使用的是“曼哈顿距离”进行估算,由于m必然大于等于x和y,因此不需要取绝对值 + return Math.max(m - x, m - y); + // 此处若使用欧氏距离计算,无法提交成功 + // return Math.sqrt((m - x) ** 2 + (m - y) ** 2); + }; + + // 缓存从起点走到每个点的距离,也可以理解为步数 + const distanceMap = new Map([[0, 1]]); + // 可以使用Map缓存经过的所有位置,最终绘制出最短路径,但这题并不需要 + // let routeMap = new Map(); + // 创建一个小顶堆,用于给节点排序,每次循环只需要取出堆顶元素,也就是每次都走预估的路径总长最小的点 + let binaryHeap = new BinaryHeap(compare); + // 向堆中插入起点[0,0],编号为0,优先级可以是任意数值 + binaryHeap.insert({node: 0, priority: 0}); + // 每次可以走的方向,根据题意可以走8个方向,但实际上都是从左上走向右下,不需要往回走 + const direction = [ + [-1, 1], // 右上 + [0, 1], // 右 + [1, 1], // 右下 + [1, 0], // 下 + [1, -1], // 左下 + ]; + + // 不断取出堆顶元素,如果堆被清空,表示没有找到路径 + while (binaryHeap.size()) { + // 取出堆顶元素,即为当前预估能够得到最短路径的节点编号 + let {node} = binaryHeap.deleteHead(); + // 计算当前编号对应的横纵坐标 + const x = Math.floor(node / colNum); + const y = node % colNum; + + // 如果当前坐标的值为1,表示无法行走,则跳过 + if (grid[x][y] === 1) { + continue; + } + + // 如果当前节点是终点,表示找到了最短路径 + if (node === destination) { + // 可以用这段代码生成走过的路径 + /* const target = node; + grid[x][y] = 8; + while (routeMap.has(node)) { + node = routeMap.get(node); + const x = Math.floor(node / colNum); + const y = node % colNum; + grid[x][y] = 8; + } + console.log(grid); + return distanceMap.get(target); */ + // distanceMap中存储的是终点的路径长度 + return distanceMap.get(node); + } + + // 将当前坐标标记为1,表示已经走过,避免重复行走 + grid[x][y] = 1; + + // 计算出当前坐标所有可行走的位置 + for (let i = 0; i < direction.length; i++) { + // 计算出下一步的横纵坐标 + const newX = x + direction[i][0]; + const newY = y + direction[i][1]; + + // 如果新坐标超出网格,或者被标记为1,表示无法行走,则跳过 + if ( + newX < 0 || + newY < 0 || + newX > m || + newY > m || + grid[newX][newY] === 1 + ) { + continue; + } + + // 计算出可行走坐标的编号 + const newNode = newX * colNum + newY; + // 从上一个节点,再走一步就到当前节点,走过的距离加1。这个距离就是从起点走到当前节点经过的距离 + const distance = distanceMap.get(node) + 1; + // 将当前节点及其优先级插入堆中,进行排序 + binaryHeap.insert({ + // 缓存当前节点,即为其在网格中的编号 + node: newNode, + // 从起点走到当前位置的距离,加上估算出的从当前位置走到终点的距离。就是如果走这个位置,整条路径的长度 + priority: distance + heuristic(newX, newY), + }); + + // 如果当前节点未在distanceMap中储存过,需要进行缓存 + // 如果已缓存的距离比此次计算的距离大,表示之前缓存的距离不是最优的,需要更新 + // 由于每次都会进行一次更新,因此distanceMap和routeMap中始终保存了最优解 + if (!distanceMap.has(newNode) || distanceMap.get(newNode) > distance) { + // 缓存从起点走到当前节点的距离 + distanceMap.set(newNode, distance); + // 在routeMap中存储当前节点的前一个节点,走到终点后,可以从中循环生成经过路径 + // routeMap.set(newNode, node); + } + } + } + + // 如果堆被清空,表示没有找到路径,返回-1 + return -1; +}; + +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆中插入多个元素 + insertMultiple(arr) { + for (let i = 0; i < arr.length; i++) { + this.insert(arr[i]); + } + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } + + // 清除所有元素 + clear() { + this.data = []; + } +} +// @lc code=end diff --git "a/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap.js" "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap.js" new file mode 100644 index 00000000..0e57a84e --- /dev/null +++ "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star Heap.js" @@ -0,0 +1,286 @@ +/* + * @lc app=leetcode.cn id=1091 lang=javascript + * + * [1091] 二进制矩阵中的最短路径 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + // 调试Case + // [[0,0,0],[0,0,0],[0,0,0]] + // [[0,0,0],[1,1,0],[1,1,0]] + // [[0,1,0,0],[0,0,0,1],[0,0,0,0],[0,0,0,0]] + // [[0,0,0,0,1],[1,0,0,0,0],[0,1,0,1,0],[0,0,0,1,1],[0,0,0,1,0]] + // [[0,0,1,0,1,1],[1,0,0,1,0,0],[0,1,0,1,0,0],[1,0,1,0,0,0],[0,1,0,1,0,0],[0,0,0,0,0,0]] + // [[0,0,1,0,0,0,0],[0,1,0,0,0,0,1],[0,0,1,0,1,0,0],[0,0,0,1,1,1,0],[1,0,0,1,1,0,0],[1,1,1,1,1,0,1],[0,0,1,0,0,0,0]] + // [[0,0,0,0,1,1,1,1,0],[0,1,1,0,0,0,0,1,0],[0,0,1,0,0,0,0,0,0],[1,1,0,0,1,0,0,1,1],[0,0,1,1,1,0,1,0,1],[0,1,0,1,0,0,0,0,0],[0,0,0,1,0,1,0,0,0],[0,1,0,1,1,0,0,0,0],[0,0,0,0,0,1,0,1,0]] + // 缓存矩阵的终点位置 + const m = grid.length - 1; + const n = grid[0].length - 1; + const row = grid[0].length; + + // 当起点和终点为1时,必然无法到达终点 + if (grid[0][0] === 1 || grid[m][n] === 1) { + return -1; + } + + // 如果矩阵只有1个点,且为0,路径为1 + if (m === 0 && n === 0 && grid[0][0] === 0) { + return 1; + } + + const start = [0, 0]; + let visited = new Set(); + let routeMap = new Map(); + let distanceMap = new Map([start]); + const heuristic = (a, b) => { + return Math.max(Math.abs(m - a), Math.abs(n - b)); + }; + const compare = (a, b) => { + return a[1] - b[1]; + }; + let binaryHeap = new BinaryHeap(compare); + binaryHeap.insert(start); + // 可以向四周所有方向行走,缓存8个方向 + const direction = [ + [1, 0], // 下 + [0, 1], // 右 + [1, 1], // 右下 + [-1, 1], // 右上 + [1, -1], // 左下 + // 一下3种都是往回走,无需判断 + // [-1, 0], // 上 + // [0, -1], // 左 + // [-1, -1], // 左上 + ]; + // 如果队列中有值,则继续搜索 + while (binaryHeap.size()) { + // 出队一个坐标,计算它可以行走的下一步位置 + let node = binaryHeap.deleteHead()[0]; + const x = Math.floor(node / row); + const y = node % row; + + if (visited.has(node)) { + continue; + } + if (x === m && y === n) { + let route = [node]; + // grid[x][y] = 8; + + while (routeMap.has(node)) { + node = routeMap.get(node); + route.unshift(node); + // grid[x][y] = 8; + } + // console.log(route); + // console.log(grid); + + return route.length; + } + // grid[x][y] = 8; + // console.log(x, y, visited.has(node)); + visited.add(node); + + for (let i = 0; i < direction.length; i++) { + // 下一步可以向四周行走,计算出相应新坐标 + const newX = x + direction[i][0]; + const newY = y + direction[i][1]; + const newNode = newX * row + newY; + + if ( + !visited.has(newNode) && + // 判断新坐标不可超出矩阵 + newX >= 0 && + newY >= 0 && + newX <= m && + newY <= n && + // 下一步可以行走,才进行记录 + grid[newX][newY] === 0 + ) { + // 将下一步的坐标存入队列,用于下一层循环 + const nextStep = distanceMap.get(node) + 1; + binaryHeap.insert([newNode, nextStep + heuristic(newX, newY)]); + + if (!distanceMap.has(newNode) || nextStep < distanceMap.get(newNode)) { + distanceMap.set(newNode, nextStep); + routeMap.set(newNode, node); + } + } + } + } + + return -1; +}; + +class BinaryHeap { + constructor(compare) { + this.data = []; // 使用数组存储堆 + this.compare = compare; // 堆元素的排序函数 + } + + // 获取堆的元素数量 + size() { + return this.data.length; + } + + // 向堆中插入多个元素 + insertMultiple(arr) { + for (let i = 0; i < arr.length; i++) { + this.insert(arr[i]); + } + } + + // 向堆插入元素 + insert(value) { + this.insertAt(this.data.length, value); + } + + // 将元素插入到index位置 + insertAt(index, value) { + // 先将元素插入到指定的位置 + this.data[index] = value; + let fatherIndex = index; + // 对比当前节点与其父节点,如果当前节点更小就交换它们 + // Math.floor((index - 1) / 2)是父节点在数组中的索引 + while ( + index > 0 && + // 使用compare比较大小 + this.compare( + value, + this.data[(fatherIndex = Math.floor((index - 1) / 2))], + ) < 0 + ) { + // 将父节点移动到当前位置 + this.data[index] = this.data[fatherIndex]; + // 将插入的值移动到父节点位置 + this.data[fatherIndex] = value; + // 更新索引为父节点索引,继续下一次循环 + index = fatherIndex; + } + } + + // 删除最大节点 + deleteHead() { + return this.delete(0); + } + + // 将指定位置的元素删除 + delete(index) { + // 如果堆为空,则不进行删除操作 + if (this.data.length === 0) { + return; + } + + let value = this.data[index]; // 将要删除的元素缓存 + let parent = index; // 以当前元素为起始,向下整理堆 + + // 不断向子节点整理堆,每次循环将子节点中经过compare方法对比后较大者与父节点调换 + while (parent < this.data.length) { + let left = parent * 2 + 1; // 左子节点索引 + let right = parent * 2 + 2; // 右子节点索引 + + // 没有左子节点,表示当前节点已经是最后一个节点 + if (left >= this.data.length) { + break; + } + + // 没有右子节点,则直接将左子节点提前到父节点即可 + // 该左子节点即为最后一个节点 + if (right >= this.data.length) { + this.data[parent] = this.data[left]; + parent = left; + break; + } + + // 使用compare方法比较左右子节点的大小,更大的补到父节点 + if (this.compare(this.data[left], this.data[right]) < 0) { + // 由于被删除的节点已保存,此处只需要将子节点复制到当前父节点即可 + this.data[parent] = this.data[left]; + // 完成移动后将父节点指针移动到子节点,供下一次整理使用 + parent = left; + } else { + this.data[parent] = this.data[right]; + parent = right; + } + } + + // 查看最后的空位是不是最后的叶子节点 + if (parent < this.data.length - 1) { + // 如果还未整理到叶子节点,则继续向下整理 + this.insertAt(parent, this.data.pop()); + } else { + // 当完成整理时,最后一个节点即为多于元素,直接弹出数组即可 + this.data.pop(); + } + + // 返回被删除的元素 + return value; + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 删除指定元素 + deleteItem(value) { + // 查找元素在堆中对应的索引 + const index = this.data.findIndex((item) => item === value); + + // 根据索引删除相应元素 + if (typeof index === 'number') { + this.delete(index); + } + } + + // 读取堆顶元素 + peek() { + return this.data[0]; + } + + // 清除所有元素 + clear() { + this.data = []; + } +} +// @lc code=end + +[ + [0, 0, 1, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 1], + [0, 0, 1, 0, 1, 0, 0], + [0, 0, 0, 1, 1, 1, 0], + [1, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 0, 1], + [0, 0, 1, 0, 0, 0, 0], +]; +[ + [7, 7, 1, 7, 0, 0, 0], + [7, 1, 7, 7, 7, 0, 1], + [7, 7, 1, 7, 1, 7, 0], + [7, 7, 7, 1, 1, 1, 7], + [1, 7, 7, 1, 1, 7, 7], + [1, 1, 1, 1, 1, 7, 1], + [0, 0, 1, 0, 0, 0, 8], +]; +[ + [7, 7, 7, 0, 1, 1, 1, 1, 0], + [7, 1, 1, 7, 0, 0, 0, 1, 0], + [7, 7, 1, 7, 7, 0, 0, 0, 0], + [1, 1, 7, 7, 1, 7, 0, 1, 1], + [0, 0, 1, 1, 1, 7, 1, 0, 1], + [0, 1, 0, 1, 0, 7, 7, 0, 0], + [0, 0, 0, 1, 0, 1, 7, 7, 0], + [0, 1, 0, 1, 1, 0, 0, 7, 7], + [0, 0, 0, 0, 0, 1, 0, 1, 7], +]; diff --git "a/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star PriorityQueue.js" "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star PriorityQueue.js" new file mode 100644 index 00000000..adaccc9d --- /dev/null +++ "b/Week_09/1091.\344\272\214\350\277\233\345\210\266\347\237\251\351\230\265\344\270\255\347\232\204\346\234\200\347\237\255\350\267\257\345\276\204\357\274\214A star PriorityQueue.js" @@ -0,0 +1,144 @@ +/* + * @lc app=leetcode.cn id=1091 lang=javascript + * + * [1091] 二进制矩阵中的最短路径 + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var shortestPathBinaryMatrix = function (grid) { + // 调试Case + // [[0,0,0],[0,0,0],[0,0,0]] + // [[0,0,0],[1,1,0],[1,1,0]] + // [[0,1,0,0],[0,0,0,1],[0,0,0,0],[0,0,0,0]] + // [[0,0,0,0,1],[1,0,0,0,0],[0,1,0,1,0],[0,0,0,1,1],[0,0,0,1,0]] + // [[0,0,1,0,1,1],[1,0,0,1,0,0],[0,1,0,1,0,0],[1,0,1,0,0,0],[0,1,0,1,0,0],[0,0,0,0,0,0]] + // [[0,0,1,0,0,0,0],[0,1,0,0,0,0,1],[0,0,1,0,1,0,0],[0,0,0,1,1,1,0],[1,0,0,1,1,0,0],[1,1,1,1,1,0,1],[0,0,1,0,0,0,0]] + // [[0,0,0,0,1,1,1,1,0],[0,1,1,0,0,0,0,1,0],[0,0,1,0,0,0,0,0,0],[1,1,0,0,1,0,0,1,1],[0,0,1,1,1,0,1,0,1],[0,1,0,1,0,0,0,0,0],[0,0,0,1,0,1,0,0,0],[0,1,0,1,1,0,0,0,0],[0,0,0,0,0,1,0,1,0]] + // 缓存矩阵的终点位置 + const m = grid.length - 1; + const n = grid[0].length - 1; + const row = grid[0].length; + + // 当起点和终点为1时,必然无法到达终点 + if (grid[0][0] === 1 || grid[m][n] === 1) { + return -1; + } + + // 如果矩阵只有1个点,且为0,路径为1 + if (m === 0 && n === 0 && grid[0][0] === 0) { + return 1; + } + + const start = [0, 0]; + let visited = new Set(); + let routeMap = new Map(); + let distanceMap = new Map([start]); + const heuristic = (a, b) => { + return Math.max(Math.abs(m - a), Math.abs(n - b)); + }; + let minPriorityQueue = new MinPriorityQueue(); + minPriorityQueue.enqueue(0, Infinity); + // 可以向四周所有方向行走,缓存8个方向 + const direction = [ + [1, 0], // 下 + [0, 1], // 右 + [1, 1], // 右下 + [-1, 1], // 右上 + [1, -1], // 左下 + // 一下3种都是往回走,无需判断 + // [-1, 0], // 上 + // [0, -1], // 左 + // [-1, -1], // 左上 + ]; + // 如果队列中有值,则继续搜索 + while (minPriorityQueue.size()) { + // 出队一个坐标,计算它可以行走的下一步位置 + let node = minPriorityQueue.dequeue().element; + const x = Math.floor(node / row); + const y = node % row; + + if (visited.has(node)) { + continue; + } + if (x === m && y === n) { + let route = [node]; + // grid[x][y] = 8; + + while (routeMap.has(node)) { + node = routeMap.get(node); + route.unshift(node); + // grid[x][y] = 8; + } + // console.log(route); + // console.log(grid); + + return route.length; + } + // grid[x][y] = 8; + visited.add(node); + + for (let i = 0; i < direction.length; i++) { + // 下一步可以向四周行走,计算出相应新坐标 + const newX = x + direction[i][0]; + const newY = y + direction[i][1]; + const newNode = newX * row + newY; + + if ( + !visited.has(newNode) && + // 判断新坐标不可超出矩阵 + newX >= 0 && + newY >= 0 && + newX <= m && + newY <= n && + // 下一步可以行走,才进行记录 + grid[newX][newY] !== 1 + ) { + // 将下一步的坐标存入队列,用于下一层循环 + const nextStep = distanceMap.get(node) + 1; + minPriorityQueue.enqueue(newNode, nextStep + heuristic(newX, newY)); + + if (!distanceMap.has(newNode) || nextStep < distanceMap.get(newNode)) { + distanceMap.set(newNode, nextStep); + routeMap.set(newNode, node); + } + } + } + } + + return -1; +}; +// @lc code=end + +[ + [0, 0, 1, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 1], + [0, 0, 1, 0, 1, 0, 0], + [0, 0, 0, 1, 1, 1, 0], + [1, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 0, 1], + [0, 0, 1, 0, 0, 0, 0], +]; +[ + [7, 7, 1, 7, 0, 0, 0], + [7, 1, 7, 7, 7, 0, 1], + [7, 7, 1, 7, 1, 7, 0], + [7, 7, 7, 1, 1, 1, 7], + [1, 7, 7, 1, 1, 7, 7], + [1, 1, 1, 1, 1, 7, 1], + [0, 0, 1, 0, 0, 0, 8], +]; +[ + [7, 7, 7, 0, 1, 1, 1, 1, 0], + [7, 1, 1, 7, 0, 0, 0, 1, 0], + [7, 7, 1, 7, 7, 0, 0, 0, 0], + [1, 1, 7, 7, 1, 7, 0, 1, 1], + [0, 0, 1, 1, 1, 7, 1, 0, 1], + [0, 1, 0, 1, 0, 7, 7, 0, 0], + [0, 0, 0, 1, 0, 1, 7, 7, 0], + [0, 1, 0, 1, 1, 0, 0, 7, 7], + [0, 0, 0, 0, 0, 1, 0, 1, 7], +]; diff --git "a/Week_09/14.\346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200.js" "b/Week_09/14.\346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200.js" new file mode 100644 index 00000000..c9a18bc5 --- /dev/null +++ "b/Week_09/14.\346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200.js" @@ -0,0 +1,34 @@ +/* + * @lc app=leetcode.cn id=14 lang=javascript + * + * [14] 最长公共前缀 + */ + +// @lc code=start +/** + * @param {string[]} strs + * @return {string} + */ +var longestCommonPrefix = function (strs) { + if (strs.length === 0) { + return ''; + } + + let len = Math.min(...strs.map((str) => str.length)); + let result = ''; + + outer: for (let i = 0; i < len; i++) { + let str = strs[0][i]; + + for (let j = 1; j < strs.length; j++) { + if (strs[j][i] !== str) { + break outer; + } + } + + result += str; + } + + return result; +}; +// @lc code=end diff --git "a/Week_09/151. \347\277\273\350\275\254\345\255\227\347\254\246\344\270\262\351\207\214\347\232\204\345\215\225\350\257\215.js" "b/Week_09/151. \347\277\273\350\275\254\345\255\227\347\254\246\344\270\262\351\207\214\347\232\204\345\215\225\350\257\215.js" new file mode 100644 index 00000000..86b3e968 --- /dev/null +++ "b/Week_09/151. \347\277\273\350\275\254\345\255\227\347\254\246\344\270\262\351\207\214\347\232\204\345\215\225\350\257\215.js" @@ -0,0 +1,91 @@ +/* + * @lc app=leetcode.cn id=151 lang=javascript + * + * [151] 翻转字符串里的单词 + * + * https://leetcode-cn.com/problems/reverse-words-in-a-string/description/ + * + * algorithms + * Medium (45.05%) + * Likes: 263 + * Dislikes: 0 + * Total Accepted: 113.3K + * Total Submissions: 251.5K + * Testcase Example: '"the sky is blue"' + * + * 给定一个字符串,逐个翻转字符串中的每个单词。 + * + * 说明: + * + * + * 无空格字符构成一个 单词 。 + * 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + * 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + * + * + * + * + * 示例 1: + * + * 输入:"the sky is blue" + * 输出:"blue is sky the" + * + * + * 示例 2: + * + * 输入:"  hello world!  " + * 输出:"world! hello" + * 解释:输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + * + * + * 示例 3: + * + * 输入:"a good   example" + * 输出:"example good a" + * 解释:如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + * + * + * 示例 4: + * + * 输入:s = " Bob Loves Alice " + * 输出:"Alice Loves Bob" + * + * + * 示例 5: + * + * 输入:s = "Alice does not even like bob" + * 输出:"bob like even not does Alice" + * + * + * + * + * 提示: + * + * + * 1 <= s.length <= 10^4 + * s 包含英文大小写字母、数字和空格 ' ' + * s 中 至少存在一个 单词 + * + * + * + * + * + * + * + * 进阶: + * + * + * 请尝试使用 O(1) 额外空间复杂度的原地解法。 + * + * + */ + +// @lc code=start +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function (s) { + return s.split(/\s+/g).reverse().join(' ').trim(); +}; +// @lc code=end diff --git "a/Week_09/198.\346\211\223\345\256\266\345\212\253\350\210\215.js" "b/Week_09/198.\346\211\223\345\256\266\345\212\253\350\210\215.js" new file mode 100644 index 00000000..7cd4b6c9 --- /dev/null +++ "b/Week_09/198.\346\211\223\345\256\266\345\212\253\350\210\215.js" @@ -0,0 +1,23 @@ +/* + * @lc app=leetcode.cn id=198 lang=javascript + * + * [198] 打家劫舍 + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + let dp = [0, 0]; + + for (let i = 0; i < nums.length; i++) { + const temp = dp[0]; + dp[0] = nums[i] + dp[1]; + dp[1] = Math.max(temp, dp[1]); + } + + return Math.max(dp[0], dp[1]); +}; +// @lc code=end diff --git "a/Week_09/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" "b/Week_09/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" new file mode 100644 index 00000000..0b4f8ab3 --- /dev/null +++ "b/Week_09/213.\346\211\223\345\256\266\345\212\253\350\210\215-ii\357\274\214\347\274\223\345\255\230\345\201\267\347\233\227\347\212\266\346\200\201.js" @@ -0,0 +1,93 @@ +/* + * @lc app=leetcode.cn id=213 lang=javascript + * + * [213] 打家劫舍 II + * + * https://leetcode-cn.com/problems/house-robber-ii/description/ + * + * algorithms + * Medium (40.74%) + * Likes: 465 + * Dislikes: 0 + * Total Accepted: 75.1K + * Total Submissions: 184.2K + * Testcase Example: '[2,3,2]' + * + * 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 + * ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。 + * + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。 + * + * + * + * 示例 1: + * + * + * 输入:nums = [2,3,2] + * 输出:3 + * 解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。 + * + * + * 示例 2: + * + * + * 输入:nums = [1,2,3,1] + * 输出:4 + * 解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。 + * 偷窃到的最高金额 = 1 + 3 = 4 。 + * + * 示例 3: + * + * + * 输入:nums = [0] + * 输出:0 + * + * + * + * + * 提示: + * + * + * 1 + * 0 + * + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + // 用dp1递推从0偷到nums.length-2,初始值是第0户偷与不偷的状态 + let dp1 = [[nums[0] || 0, 0]]; + + // 从1开始递推到nums.length-2 + for (let i = 1; i < nums.length - 1; i++) { + dp1[i] = [ + // 如果偷当前人家,那么上一户人家就不能偷,只能取不偷的值 + dp1[i - 1][1] + nums[i], + // 如果不偷当前人家,上一户人家偷不偷都可以,只需要取一个最大值 + Math.max(dp1[i - 1][0], dp1[i - 1][1]), + ]; + } + + // 用dp2递推从1偷到nums.length-1,初始值是第1户偷与不偷的状态 + // 为保证dp2索引和nums能一一对应,dp2[0]存储一个空数组占位 + let dp2 = [[], [nums[1] || 0, 0]]; + + // 从0开始递推到nums.length-1 + for (let i = 2; i < nums.length; i++) { + dp2[i] = [ + // 如果偷当前人家,那么上一户人家就不能偷,只能取不偷的值 + dp2[i - 1][1] + nums[i], + // 如果不偷当前人家,上一户人家偷不偷都可以,只需要取一个最大值 + Math.max(dp2[i - 1][0], dp2[i - 1][1]), + ]; + } + + // 两次递推结果的最大值,就是最终结果 + return Math.max(...dp1[dp1.length - 1], ...dp2[dp2.length - 1]); +}; +// @lc code=end diff --git "a/Week_09/242.\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" "b/Week_09/242.\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" new file mode 100644 index 00000000..f3941bd0 --- /dev/null +++ "b/Week_09/242.\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + if (s.length !== t.length) { + return false; + } + + let map = new Map(); + + for (const char of s) { + if (map.has(char)) { + map.set(char, map.get(char) + 1); + } else { + map.set(char, 1); + } + } + + for (const char of t) { + if (!map.get(char)) { + return false; + } + + map.set(char, map.get(char) - 1); + } + + return true; +}; +// @lc code=end diff --git "a/Week_09/344.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.js" "b/Week_09/344.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.js" new file mode 100644 index 00000000..2adea00c --- /dev/null +++ "b/Week_09/344.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262.js" @@ -0,0 +1,22 @@ +/* + * @lc app=leetcode.cn id=344 lang=javascript + * + * [344] 反转字符串 + */ + +// @lc code=start +/** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ +var reverseString = function (s) { + let i = 0; + let j = s.length - 1; + + while (i < j) { + [s[i++], s[j--]] = [s[j], s[i]]; + } + + return s; +}; +// @lc code=end diff --git "a/Week_09/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246\357\274\214\345\223\210\345\270\214\350\241\250.js" "b/Week_09/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246\357\274\214\345\223\210\345\270\214\350\241\250.js" new file mode 100644 index 00000000..e85e953e --- /dev/null +++ "b/Week_09/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246\357\274\214\345\223\210\345\270\214\350\241\250.js" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=387 lang=javascript + * + * [387] 字符串中的第一个唯一字符 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function (s) { + let map = new Map(); + + for (const char of s) { + map.has(char) ? map.set(char, map.get(char) + 1) : map.set(char, 1); + } + + for (let i = 0; i < s.length; i++) { + if (map.get(s[i]) === 1) { + return i; + } + } + + return -1; +}; +// @lc code=end diff --git "a/Week_09/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246\357\274\214\351\230\237\345\210\227.js" "b/Week_09/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246\357\274\214\351\230\237\345\210\227.js" new file mode 100644 index 00000000..22cdc005 --- /dev/null +++ "b/Week_09/387.\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246\357\274\214\351\230\237\345\210\227.js" @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=387 lang=javascript + * + * [387] 字符串中的第一个唯一字符 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var firstUniqChar = function (s) { + let queue = []; + let map = new Map(); + + for (let i = 0; i < s.length; i++) { + const char = s[i]; + + if (!map.has(char) || map.get(char) === 1) { + queue.push([char, i]); + } + map.has(char) ? map.set(char, map.get(char) + 1) : map.set(char, 1); + while (queue.length && map.get(queue[0][0]) > 1) { + queue.shift(); + } + } + + return queue[0] ? queue[0][1] : -1; +}; +// @lc code=end diff --git "a/Week_09/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" "b/Week_09/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" new file mode 100644 index 00000000..ed2c75e7 --- /dev/null +++ "b/Week_09/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\345\217\230\351\207\217.js" @@ -0,0 +1,59 @@ +/* + * @lc app=leetcode.cn id=53 lang=javascript + * + * [53] 最大子序和 + * + * https://leetcode-cn.com/problems/maximum-subarray/description/ + * + * algorithms + * Easy (53.00%) + * Likes: 2778 + * Dislikes: 0 + * Total Accepted: 391.6K + * Total Submissions: 738.8K + * Testcase Example: '[-2,1,-3,4,-1,2,1,-5,4]' + * + * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + * + * 示例: + * + * 输入: [-2,1,-3,4,-1,2,1,-5,4] + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * + * + * 进阶: + * + * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function (nums) { + let max = nums[0]; // 缓存最大子序和,nums[0]的最大子序和是它自身 + let currMax = nums[0]; // 计算当前位置的子序和,初始值即为nums[0] + + // 从1开始递推 + for (let i = 1; i < nums.length; i++) { + // 计算当前位置的子序和 + currMax = + Math.max( + // nums[i]包含于之前的子数组中 + currMax, + // nums[i]不包含于之前的子数组,上一次的子序和为0 + 0, + ) + + // nums[i]必然包含于当前子数组 + nums[i]; + // 每次对比已知的最大子序和 + max = Math.max(max, currMax); + } + + // 最大子序和已在循环中生成,直接返回即可 + return max; +}; +// @lc code=end diff --git "a/Week_09/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" "b/Week_09/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" new file mode 100644 index 00000000..3be02655 --- /dev/null +++ "b/Week_09/53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\275\277\347\224\250\346\225\260\347\273\204.js" @@ -0,0 +1,55 @@ +/* + * @lc app=leetcode.cn id=53 lang=javascript + * + * [53] 最大子序和 + * + * https://leetcode-cn.com/problems/maximum-subarray/description/ + * + * algorithms + * Easy (53.00%) + * Likes: 2778 + * Dislikes: 0 + * Total Accepted: 391.6K + * Total Submissions: 738.8K + * Testcase Example: '[-2,1,-3,4,-1,2,1,-5,4]' + * + * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + * + * 示例: + * + * 输入: [-2,1,-3,4,-1,2,1,-5,4] + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * + * + * 进阶: + * + * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function (nums) { + let max = [nums[0]]; // 递推nums每个值能产生的最大子序和,nums[0]的最大子序和是它自身 + + // 从1开始递推 + for (let i = 1; i < nums.length; i++) { + // 子数组至少包含一个元素,因此只需要考虑nums[i]在子数组中的情况 + // nums[i]所在的子数组,可能包含它之前和之后的元素。而之后的元素会在后续的遍历中计算,此处无需判断 + // 对于nums[i]来说,存在两种情况,它可能包含于之前的子数组,或者从自己开始重新生成一个子数组 + max[i] = Math.max( + // nums[i]包含于之前的子数组中,上一次的子序和与nums[i]之和为新的子序和 + max[i - 1] + nums[i], + // nums[i]不包含于之前的子数组,上一次的子序和为0,nums[i]自己就是新的子序和 + 0 + nums[i], + ); + } + + // 每个位置都会产生一个子序和,最终结果是其中最大者 + return Math.max(...max); +}; +// @lc code=end diff --git "a/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\345\205\250\351\203\250\345\217\215\350\275\254\345\220\216\357\274\214\345\206\215\345\217\215\350\275\254\345\215\225\350\257\215.js" "b/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\345\205\250\351\203\250\345\217\215\350\275\254\345\220\216\357\274\214\345\206\215\345\217\215\350\275\254\345\215\225\350\257\215.js" new file mode 100644 index 00000000..4e2b643a --- /dev/null +++ "b/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\345\205\250\351\203\250\345\217\215\350\275\254\345\220\216\357\274\214\345\206\215\345\217\215\350\275\254\345\215\225\350\257\215.js" @@ -0,0 +1,44 @@ +/* + * @lc app=leetcode.cn id=557 lang=javascript + * + * [557] 反转字符串中的单词 III + * + * https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/description/ + * + * algorithms + * Easy (73.75%) + * Likes: 269 + * Dislikes: 0 + * Total Accepted: 109.5K + * Total Submissions: 148.5K + * Testcase Example: `"Let's take LeetCode contest"` + * + * 给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 + * + * + * + * 示例: + * + * 输入:"Let's take LeetCode contest" + * 输出:"s'teL ekat edoCteeL tsetnoc" + * + * + * + * + * 提示: + * + * + * 在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。 + * + * + */ + +// @lc code=start +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function (s) { + return s.split('').reverse().join('').split(' ').reverse().join(' '); +}; +// @lc code=end diff --git "a/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\345\217\214\346\214\207\351\222\210\357\274\214\345\255\227\347\254\246\344\270\262.js" "b/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\345\217\214\346\214\207\351\222\210\357\274\214\345\255\227\347\254\246\344\270\262.js" new file mode 100644 index 00000000..6aedb8d2 --- /dev/null +++ "b/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\345\217\214\346\214\207\351\222\210\357\274\214\345\255\227\347\254\246\344\270\262.js" @@ -0,0 +1,67 @@ +/* + * @lc app=leetcode.cn id=557 lang=javascript + * + * [557] 反转字符串中的单词 III + * + * https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/description/ + * + * algorithms + * Easy (73.75%) + * Likes: 269 + * Dislikes: 0 + * Total Accepted: 109.5K + * Total Submissions: 148.5K + * Testcase Example: `"Let's take LeetCode contest"` + * + * 给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 + * + * + * + * 示例: + * + * 输入:"Let's take LeetCode contest" + * 输出:"s'teL ekat edoCteeL tsetnoc" + * + * + * + * + * 提示: + * + * + * 在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。 + * + * + */ + +// @lc code=start +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function (s) { + let result = ''; + + for (let i = 0, j = 0; j < s.length; ) { + while (s[j] !== ' ' && j < s.length) { + j++; + } + + if (s[j] === ' ' || s[j] === undefined) { + let left = i; + let right = j - 1; + + while (left <= right) { + result += s[right--]; + } + } + + while (s[j] === ' ') { + result += s[j++]; + } + + i = j; + } + + return result; +}; +// @lc code=end diff --git "a/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\346\240\210.js" "b/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\346\240\210.js" new file mode 100644 index 00000000..682e1b32 --- /dev/null +++ "b/Week_09/557. \345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III\357\274\214\346\240\210.js" @@ -0,0 +1,56 @@ +/* + * @lc app=leetcode.cn id=557 lang=javascript + * + * [557] 反转字符串中的单词 III + * + * https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/description/ + * + * algorithms + * Easy (73.75%) + * Likes: 269 + * Dislikes: 0 + * Total Accepted: 109.5K + * Total Submissions: 148.5K + * Testcase Example: `"Let's take LeetCode contest"` + * + * 给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。 + * + * + * + * 示例: + * + * 输入:"Let's take LeetCode contest" + * 输出:"s'teL ekat edoCteeL tsetnoc" + * + * + * + * + * 提示: + * + * + * 在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。 + * + * + */ + +// @lc code=start +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function (s) { + let result = ''; + let word = ''; + + for (const char of s) { + if (char !== ' ') { + word = char + word; + } else { + result = result + word + char; + word = ''; + } + } + + return result + word; +}; +// @lc code=end diff --git "a/Week_09/58.\346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246.js" "b/Week_09/58.\346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246.js" new file mode 100644 index 00000000..069140a6 --- /dev/null +++ "b/Week_09/58.\346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246.js" @@ -0,0 +1,30 @@ +/* + * @lc app=leetcode.cn id=58 lang=javascript + * + * [58] 最后一个单词的长度 + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var lengthOfLastWord = function (s) { + let count = 0; + let lastSpace = true; + + for (let i = s.length - 1; i >= 0; i--) { + if (s[i] === ' ' && lastSpace) { + continue; + } + if (s[i] !== ' ') { + count++; + lastSpace = false; + } else { + break; + } + } + + return count; +}; +// @lc code=end diff --git "a/Week_09/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_09/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..4cdcface --- /dev/null +++ "b/Week_09/62. \344\270\215\345\220\214\350\267\257\345\276\204\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,83 @@ +/* + * @lc app=leetcode.cn id=62 lang=javascript + * + * [62] 不同路径 + * + * https://leetcode-cn.com/problems/unique-paths/description/ + * + * algorithms + * Medium (63.81%) + * Likes: 843 + * Dislikes: 0 + * Total Accepted: 197.7K + * Total Submissions: 309.9K + * Testcase Example: '3\n7' + * + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 + * + * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 + * + * 问总共有多少条不同的路径? + * + * + * + * 示例 1: + * + * + * 输入:m = 3, n = 7 + * 输出:28 + * + * 示例 2: + * + * + * 输入:m = 3, n = 2 + * 输出:3 + * 解释: + * 从左上角开始,总共有 3 条路径可以到达右下角。 + * 1. 向右 -> 向右 -> 向下 + * 2. 向右 -> 向下 -> 向右 + * 3. 向下 -> 向右 -> 向右 + * + * + * 示例 3: + * + * + * 输入:m = 7, n = 3 + * 输出:28 + * + * + * 示例 4: + * + * + * 输入:m = 3, n = 3 + * 输出:6 + * + * + * + * 提示: + * + * + * 1 + * 题目数据保证答案小于等于 2 * 10^9 + * + * + */ + +// @lc code=start +/** + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = function (m, n) { + let dp = new Array(n).fill(1); + + for (let i = 1; i < m; i++) { + for (let j = 1; j < n; j++) { + dp[j] = dp[j - 1] + dp[j]; + } + } + + return dp[n - 1]; +}; +// @lc code=end diff --git "a/Week_09/64. \346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\274\230\345\214\226\347\251\272\351\227\264.js" "b/Week_09/64. \346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\274\230\345\214\226\347\251\272\351\227\264.js" new file mode 100644 index 00000000..98bcefe7 --- /dev/null +++ "b/Week_09/64. \346\234\200\345\260\217\350\267\257\345\276\204\345\222\214\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222\357\274\214\344\274\230\345\214\226\347\251\272\351\227\264.js" @@ -0,0 +1,81 @@ +/* + * @lc app=leetcode.cn id=64 lang=javascript + * + * [64] 最小路径和 + * + * https://leetcode-cn.com/problems/minimum-path-sum/description/ + * + * algorithms + * Medium (68.04%) + * Likes: 769 + * Dislikes: 0 + * Total Accepted: 175.4K + * Total Submissions: 257.8K + * Testcase Example: '[[1,3,1],[1,5,1],[4,2,1]]' + * + * 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + * + * 说明:每次只能向下或者向右移动一步。 + * + * + * + * 示例 1: + * + * + * 输入:grid = [[1,3,1],[1,5,1],[4,2,1]] + * 输出:7 + * 解释:因为路径 1→3→1→1→1 的总和最小。 + * + * + * 示例 2: + * + * + * 输入:grid = [[1,2,3],[4,5,6]] + * 输出:12 + * + * + * + * + * 提示: + * + * + * m == grid.length + * n == grid[i].length + * 1 + * 0 + * + * + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function (grid) { + const m = grid.length; + const n = grid[0].length; + const dpy = new Array(m).fill(0); + const dpx = new Array(n).fill(0); + dpy[0] = grid[0][0]; + dpx[0] = grid[0][0]; + + for (let i = 1; i < m; i++) { + dpy[i] = dpy[i - 1] + grid[i][0]; + } + + for (let i = 1; i < n; i++) { + dpx[i] = dpx[i - 1] + grid[0][i]; + } + + for (let i = 1; i < m; i++) { + dpx[0] = dpy[i]; + + for (let j = 1; j < n; j++) { + dpx[j] = Math.min(dpx[j - 1], dpx[j]) + grid[i][j]; + } + } + + return dpx[n - 1]; +}; +// @lc code=end diff --git "a/Week_09/70. \347\210\254\346\245\274\346\242\257\357\274\214\351\200\232\347\224\250\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_09/70. \347\210\254\346\245\274\346\242\257\357\274\214\351\200\232\347\224\250\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..f67ee47b --- /dev/null +++ "b/Week_09/70. \347\210\254\346\245\274\346\242\257\357\274\214\351\200\232\347\224\250\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,61 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + * + * https://leetcode-cn.com/problems/climbing-stairs/description/ + * + * algorithms + * Easy (50.33%) + * Likes: 1198 + * Dislikes: 0 + * Total Accepted: 269.8K + * Total Submissions: 535.5K + * Testcase Example: '2' + * + * 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 + * + * 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? + * + * 注意:给定 n 是一个正整数。 + * + * 示例 1: + * + * 输入: 2 + * 输出: 2 + * 解释: 有两种方法可以爬到楼顶。 + * 1. 1 阶 + 1 阶 + * 2. 2 阶 + * + * 示例 2: + * + * 输入: 3 + * 输出: 3 + * 解释: 有三种方法可以爬到楼顶。 + * 1. 1 阶 + 1 阶 + 1 阶 + * 2. 1 阶 + 2 阶 + * 3. 2 阶 + 1 阶 + * + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function (n) { + const step = [1, 2]; + let dp = new Array(n + 1).fill(0); + dp[0] = 1; + dp[1] = 1; + + for (let i = 2; i <= n; i++) { + for (let j = 0; j < step.length; j++) { + dp[i] += dp[i - step[j]]; + } + } + + return dp[n]; +}; +// @lc code=end diff --git "a/Week_09/709.\350\275\254\346\215\242\346\210\220\345\260\217\345\206\231\345\255\227\346\257\215.js" "b/Week_09/709.\350\275\254\346\215\242\346\210\220\345\260\217\345\206\231\345\255\227\346\257\215.js" new file mode 100644 index 00000000..0c53a639 --- /dev/null +++ "b/Week_09/709.\350\275\254\346\215\242\346\210\220\345\260\217\345\206\231\345\255\227\346\257\215.js" @@ -0,0 +1,27 @@ +/* + * @lc app=leetcode.cn id=709 lang=javascript + * + * [709] 转换成小写字母 + */ + +// @lc code=start +/** + * @param {string} str + * @return {string} + */ +var toLowerCase = function (str) { + let result = ''; + + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i); + + if (code >= 65 && code <= 90) { + result += String.fromCharCode(code + 32); + } else { + result += str[i]; + } + } + + return result; +}; +// @lc code=end diff --git "a/Week_09/72. \347\274\226\350\276\221\350\267\235\347\246\273.js" "b/Week_09/72. \347\274\226\350\276\221\350\267\235\347\246\273.js" new file mode 100644 index 00000000..929b9122 --- /dev/null +++ "b/Week_09/72. \347\274\226\350\276\221\350\267\235\347\246\273.js" @@ -0,0 +1,104 @@ +/* + * @lc app=leetcode.cn id=72 lang=javascript + * + * [72] 编辑距离 + * + * https://leetcode-cn.com/problems/edit-distance/description/ + * + * algorithms + * Hard (60.28%) + * Likes: 1366 + * Dislikes: 0 + * Total Accepted: 103.9K + * Total Submissions: 172.4K + * Testcase Example: '"horse"\n"ros"' + * + * 给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。 + * + * 你可以对一个单词进行如下三种操作: + * + * + * 插入一个字符 + * 删除一个字符 + * 替换一个字符 + * + * + * + * + * 示例 1: + * + * + * 输入:word1 = "horse", word2 = "ros" + * 输出:3 + * 解释: + * horse -> rorse (将 'h' 替换为 'r') + * rorse -> rose (删除 'r') + * rose -> ros (删除 'e') + * + * + * 示例 2: + * + * + * 输入:word1 = "intention", word2 = "execution" + * 输出:5 + * 解释: + * intention -> inention (删除 't') + * inention -> enention (将 'i' 替换为 'e') + * enention -> exention (将 'n' 替换为 'x') + * exention -> exection (将 'n' 替换为 'c') + * exection -> execution (插入 'u') + * + * + * + * + * 提示: + * + * + * 0 + * word1 和 word2 由小写英文字母组成 + * + * + */ + +// @lc code=start +/** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ +var minDistance = function (word1, word2) { + // 缓存两个单词的长度,用于创建二维数组递推 + const m = word1.length; + const n = word2.length; + // 初始化m+1行 + let dp = new Array(m + 1); + + // 初始化第一行和第一列的值,每个单词自身都是从空字符串,一直变化到当前长度 + // 因此初始值都是0~m或0~n + for (let i = 0; i <= m; i++) { + dp[i] = [i]; + } + for (let i = 0; i <= n; i++) { + dp[0][i] = i; + } + + // 递推每个字母操作数 + for (let i = 1; i <= m; i++) { + for (let j = 1; j <= n; j++) { + // 缓存当前对比的字母 + const char1 = word1[i - 1]; + const char2 = word2[j - 1]; + + if (char1 === char2) { + // 当前两个字母相等,表示当前无需进行变化,可以直接从上一个字母的操作数递推而来 + dp[i][j] = dp[i - 1][j - 1]; + } else { + // + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1; + } + } + } + + return dp[m][n]; +}; +// @lc code=end diff --git "a/Week_09/72.\347\274\226\350\276\221\350\267\235\347\246\273.js" "b/Week_09/72.\347\274\226\350\276\221\350\267\235\347\246\273.js" new file mode 100644 index 00000000..0bbaa3c4 --- /dev/null +++ "b/Week_09/72.\347\274\226\350\276\221\350\267\235\347\246\273.js" @@ -0,0 +1,38 @@ +/* + * @lc app=leetcode.cn id=72 lang=javascript + * + * [72] 编辑距离 + */ + +// @lc code=start +/** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ +var minDistance = function (word1, word2) { + let m = word1.length; + let n = word2.length; + let dp = []; + + for (let i = 0; i <= m; i++) { + dp[i] = [i]; + } + + for (let i = 0; i <= n; i++) { + dp[0][i] = i; + } + + for (let i = 1; i <= m; i++) { + for (let j = 1; j <= n; j++) { + if (word1[i - 1] === word2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1; + } + } + } + + return dp[m][n]; +}; +// @lc code=end diff --git "a/Week_09/746. \344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_09/746. \344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..16437921 --- /dev/null +++ "b/Week_09/746. \344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,74 @@ +/* + * @lc app=leetcode.cn id=746 lang=javascript + * + * [746] 使用最小花费爬楼梯 + * + * https://leetcode-cn.com/problems/min-cost-climbing-stairs/description/ + * + * algorithms + * Easy (54.65%) + * Likes: 504 + * Dislikes: 0 + * Total Accepted: 82.7K + * Total Submissions: 151.3K + * Testcase Example: '[0,0,0,0]' + * + * 数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。 + * + * 每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。 + * + * 请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。 + * + * + * + * 示例 1: + * + * + * 输入:cost = [10, 15, 20] + * 输出:15 + * 解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。 + * + * + * 示例 2: + * + * + * 输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] + * 输出:6 + * 解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。 + * + * + * + * + * 提示: + * + * + * cost 的长度范围是 [2, 1000]。 + * cost[i] 将会是一个整型数据,范围为 [0, 999] 。 + * + * + */ + +// @lc code=start +/** + * @param {number[]} cost + * @return {number} + */ +var minCostClimbingStairs = function (cost) { + const target = cost.length; // 数组长度即为到达目标位置 + // 每次可以走两步,因此只需要保存两个结果 + // 起点可以是0或1,此时还没有开始爬楼梯,因此cost都为0 + let prev = 0; + let curr = 0; + + // 从2开始递推到target + for (let i = 2; i <= target; i++) { + const temp = curr; // curr是下一次递推的prev,先缓存 + // 当前位置,可以从前两个位置支付一个cost走过来,并且取最小值 + curr = Math.min(prev + cost[i - 2], curr + cost[i - 1]); + prev = temp; // 将缓存的curr作为prev + } + + // 递推到target,curr的值就是走到目标的花费 + return curr; +}; +// @lc code=end diff --git "a/Week_09/771.\345\256\235\347\237\263\344\270\216\347\237\263\345\244\264.js" "b/Week_09/771.\345\256\235\347\237\263\344\270\216\347\237\263\345\244\264.js" new file mode 100644 index 00000000..666fb211 --- /dev/null +++ "b/Week_09/771.\345\256\235\347\237\263\344\270\216\347\237\263\345\244\264.js" @@ -0,0 +1,25 @@ +/* + * @lc app=leetcode.cn id=771 lang=javascript + * + * [771] 宝石与石头 + */ + +// @lc code=start +/** + * @param {string} jewels + * @param {string} stones + * @return {number} + */ +var numJewelsInStones = function (jewels, stones) { + let set = new Set(jewels.split('')); + let count = 0; + + for (let i = 0; i < stones.length; i++) { + if (set.has(stones[i])) { + count++; + } + } + + return count; +}; +// @lc code=end diff --git a/Week_09/README.md b/Week_09/README.md index 50de3041..3c04b836 100644 --- a/Week_09/README.md +++ b/Week_09/README.md @@ -1 +1,25 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +这种家里人接连生病,都在不断地照顾、看病中度过。导致刷题量严重不足。下周会为考试做一些复习准备。 + +字符串类型的题目,简单的简单,难的很难,两极分化有点严重。 + +字符串匹配算法让人眼前一亮,以前对字符串匹配的理解仅仅局限于判断相等和正则,没想到细节的处理如此重要。 + +我为部分题目写了题解,如下: + +其余没写题解的题目,课程结束后会补上。 + +1. [LeetCode题解:70. 爬楼梯,DP遍历,变量缓存结果,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-bian-liang-huan-cun-jie-guo) +2. [LeetCode题解:70. 爬楼梯,DP遍历数组,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-dpbian-li-shu-zu-javascriptxiang-xi-z) +3. [LeetCode题解:70. 爬楼梯,递归+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-di-gui-ha-xi-biao-javascriptxiang-xi-) +4. [LeetCode题解:62. 不同路径,动态规划,JavaScript,详细注释](https://leetcode-cn.com/problems/unique-paths/solution/leetcodeti-jie-62-bu-tong-lu-jing-dong-t-5p7s) +5. [LeetCode题解:121. 买卖股票的最佳时机,暴力法,JavaScript,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/leetcodeti-jie-121-mai-mai-gu-piao-de-zui-jia-sh-2) +6. [LeetCode题解:121. 买卖股票的最佳时机,一次遍历,JavaScript,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/leetcodeti-jie-121-mai-mai-gu-piao-de-zui-jia-shi-) +7. [LeetCode题解:122. 买卖股票的最佳时机 II,JavaScript,一遍循环,详细注释](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/122-mai-mai-gu-piao-de-zui-jia-shi-ji-iijavascript) +8. [LeetCode题解:242. 有效的字母异位词,数组计数,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-s-2) +9. [LeetCode题解:242. 有效的字母异位词,哈希表两次循环,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-h-2) +10. [LeetCode题解:242. 有效的字母异位词,哈希表一次循环,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-ha-) +11. [LeetCode题解:242. 有效的字母异位词,数组排序,JavaScript,详细注释](https://leetcode-cn.com/problems/valid-anagram/solution/leetcodeti-jie-242-you-xiao-de-zi-mu-yi-wei-ci-shu) +12. [LeetCode题解:49. 字母异位词分组,数组计数+哈希表,JavaScript,详细注释](https://leetcode-cn.com/problems/group-anagrams/solution/leetcodeti-jie-49-zi-mu-yi-wei-ci-fen-zu-shu-zu-ji) +13. [LeetCode题解:49. 字母异位词分组,数组排序,JavaScript,详细注释](https://leetcode-cn.com/problems/group-anagrams/solution/leetcodeti-jie-49-zi-mu-yi-wei-ci-fen-zu-shu-zu-pa) diff --git "a/Week_09/\351\235\242\350\257\225\351\242\230 08.01. \344\270\211\346\255\245\351\227\256\351\242\230\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" "b/Week_09/\351\235\242\350\257\225\351\242\230 08.01. \344\270\211\346\255\245\351\227\256\351\242\230\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" new file mode 100644 index 00000000..00003526 --- /dev/null +++ "b/Week_09/\351\235\242\350\257\225\351\242\230 08.01. \344\270\211\346\255\245\351\227\256\351\242\230\357\274\214\345\212\250\346\200\201\350\247\204\345\210\222.js" @@ -0,0 +1,20 @@ +/** + * @param {number} n + * @return {number} + */ +var waysToStep = function (n) { + const step = [1, 2, 3]; + let dp = new Array(n + 1).fill(0); + dp[0] = 1; + dp[1] = 1; + dp[2] = 2; + + for (let i = 3; i <= n; i++) { + for (let j = 0; j < step.length; j++) { + dp[i] += dp[i - step[j]]; + } + dp[i] %= 1000000007; + } + + return dp[n]; +}; diff --git "a/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" "b/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" index 50de3041..f82676ff 100644 --- "a/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" +++ "b/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" @@ -1 +1,39 @@ -学习笔记 \ No newline at end of file +# 毕业总结 + +## 回顾过往 + +我大学读的是电子信息工程专业,期间也学过 C 语言、C++、数据结构等相关课程。上学的时候对编程留下的印象就是:“在一个黑漆漆的屏幕上,敲了半天,最后输出一个结果,完全不知道自己在做什么”。 + +毕业后,我就立志不再从事计算机相关工作,转投其他行业。多年之后,在机缘巧合下,竟然又重新回到了互联网行业,做起了前端开发。 + +虽然工作中已经离不开代码,但对于数据结构和算法,一直保持着敬而远之的态度。一来编程知识让人觉得很枯燥,二来算法题经常让人一头雾水,对自信心是一个沉重打击。因此对这些知识从内心感到恐惧和排斥。 + +## 训练营带来的改变 + +1. 改变心态 + +训练营给我带来最重要的改变,就是老师所说的:这些算法不是正常人靠智力能想出来的,我们只要理解并熟练运用即可。 + +这一点让我放下了一直以来沉重的心理负担。让我开始不再为自己的智力感到悲伤,在开始刷题的路上少了很多顾虑。 + +2. “五毒神掌”刷题法 + +虽然在心理上不再排斥刷题,但看到 LeetCode 上越来越多的题目,实在是有些无所适从。 + +而且由于一直没有养成刷题习惯,以前刷过的题目,过几个月再刷时,竟然完全没有任何印象。刷的时候总是觉得自己会了,实际上水平没有任何长进,不得不说是一个遗憾。 + +自从在训练营学习到了“五毒神掌”刷题法,通过有节奏的刷题训练,开始掌握一些编程技巧和解题思路,逐渐脱离了边刷边忘的怪圈。 + +3. 小有所成 + +截至目前,在 LeetCode 解决问题 148 个,发布题解 191 个,刷题遍数已经不计其数。我觉得初期最重要的是锻炼熟练度,因此主要精力放在过遍数和写题解上,没有太过追求解题数量。 + +除此之外,训练营给我带来最大的改变,是对待刷题的态度从害怕变成了热爱。 + +我在一遍又一遍的刷题过程中,明显感到自己的编程能力有了大幅提高。而且体会到算法有一种“四两拨千斤”的巨大威力,仅用很少的资源,就能做到很多的事情。同时也有一种不断超越自己智力的喜悦,越是练习,就越有一种成为武侠高手的感觉。 + +## 展望未来 + +我现在刷题的动力不仅仅是跳槽或者升职加薪,更多的是因为兴趣,刷题已经成为我重要的娱乐活动。 + +在可见的将来,除非找到了更有挑战性的事情,我会将刷题事业继续下去,直至地老天荒。 diff --git a/getSolve.js b/getSolve.js new file mode 100644 index 00000000..724efd75 --- /dev/null +++ b/getSolve.js @@ -0,0 +1,66 @@ +let solutions = document.querySelectorAll( + '.css-7dwjih-Content .css-1e9rbo3-Link:last-child', +); +let exercises = [ + 'https://leetcode-cn.com/problems/number-of-1-bits/', + 'https://leetcode-cn.com/problems/power-of-two/', + 'https://leetcode-cn.com/problems/reverse-bits/', + 'https://leetcode-cn.com/problems/n-queens/', + 'https://leetcode-cn.com/problems/n-queens-ii/', + 'https://leetcode-cn.com/problems/counting-bits/', + 'https://leetcode-cn.com/problems/lru-cache/', + 'https://leetcode-cn.com/problems/relative-sort-array/', + 'https://leetcode-cn.com/problems/valid-anagram/', + 'https://leetcode-cn.com/problems/design-a-leaderboard/', + 'https://leetcode-cn.com/problems/merge-intervals/', + 'https://leetcode-cn.com/problems/reverse-pairs/', + 'https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/', +]; +let exerciseMap = new Map( + exercises.map((exercise, index) => { + const arr = exercise.split('/'); + console.log(arr); + return [arr[arr.length - 2], index]; + }), +); +let resultArr = new Array(exerciseMap.size); +let result = ''; +let count = 1; +let test = 0; + +console.log(solutions); +console.log(exerciseMap); +console.log(resultArr); +Array.from(solutions).forEach((solution) => { + for (const [exercise, index] of exerciseMap) { + if (solution.getAttribute('href').match(exercise)) { + /* console.log( + solution.innerHTML, + exercise, + solution.innerHTML.match(exercise), + index, + ); */ + const str = `[${solution.innerHTML}](${ + window.origin + solution.getAttribute('href') + })\n`; + if (resultArr[index]) { + resultArr[index].push(str); + } else { + resultArr[index] = [str]; + } + } + } +}); + +console.log(test); +console.log(resultArr); +for (let i = 0; i < resultArr.length; i++) { + if (resultArr[i]) { + for (let j = 0; j < resultArr[i].length; j++) { + result += `${count++}. ${resultArr[i][j]}`; + } + } +} + +copy(result); +console.log(result); diff --git a/mergeSort.js b/mergeSort.js new file mode 100644 index 00000000..7d52289b --- /dev/null +++ b/mergeSort.js @@ -0,0 +1,29 @@ +const mergeSort = (nums, compare = (a, b) => a - b) => { + // 如果数组长度为1,无需排序,直接返回即可 + if (nums.length === 1) { + return nums; + } + + const mid = nums.length >> 1; // 计算中间索引 + // 将数组拆分成两段,分别进行排序 + const left = nums.slice(0, mid); + const right = nums.slice(mid); + + // 将子数组不断拆分,并将每个子数组都进行排序好,之后合并返回 + return merge(mergeSort(left, compare), mergeSort(right, compare), compare); +}; + +// 将两个子数组排序、合并 +const merge = (left, right, compare) => { + let result = []; // 存储排序后的结果 + + // 将两个子数组按照compare的结果排序,直到其中一个被清空 + while (left.length && right.length) { + result.push(compare(left[0], right[0]) < 0 ? left.shift() : right.shift()); + } + + // 将数组剩余元素存入result,由于left和right已经被排序,因此无需再进行排序 + result.splice(result.length, 0, ...left, ...right); + + return result; +}; diff --git a/mergeSort2.js b/mergeSort2.js new file mode 100644 index 00000000..71a9a68c --- /dev/null +++ b/mergeSort2.js @@ -0,0 +1,32 @@ +const mergeSort = (nums, left, right, compare = (a, b) => a - b) => { + if (right <= left) { + return; + } + + const mid = (left + right) >> 1; + mergeSort(nums, left, mid, compare); + mergeSort(nums, mid + 1, right, compare); + + merge(nums, left, right, mid, compare); +}; + +const merge = (nums, left, right, mid, compare) => { + let temp = []; + let i = left; + let j = mid + 1; + let k = 0; + + while (i <= mid && j <= right) { + temp[k++] = compare(nums[i], nums[j]) < 0 ? nums[i++] : nums[j++]; + } + + while (i <= mid) { + temp[k++] = nums[i++]; + } + + while (j <= right) { + temp[k++] = nums[j++]; + } + + nums.splice(left, right - left + 1, ...temp); +}; diff --git a/quickSort.js b/quickSort.js new file mode 100644 index 00000000..66d0613a --- /dev/null +++ b/quickSort.js @@ -0,0 +1,46 @@ +const quickSort = (nums, left, right, k) => { + // 如果nums的长度小等于1,无需排序,直接作为结果返回 + if (nums.length <= 1) return nums; + + // 如果nums有长度,就需要排序 + if (left < right) { + // 将nums从left到right的部分分为两段,其中间值是pivot + // pivot左侧的值都小于nums[pivot],pivot右侧的值都大于nums[pivot] + const pivot = partition(nums, left, right); + + // 将拆分后的nums分别继续排序 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } + + // 完成所有递归后,nums已经从小到大排序 + return nums; +}; + +const partition = (nums, left, right) => { + let pivot = left; // 以left为中值,比它小的都放在其左侧,比它大的都放在其右侧 + let index = left + 1; // 从left+1开始遍历数组,同时index表示了比nums[pivot]小的值存放的位置 + + // 遍历从left+1到right的元素,将其以nums[pivot]为中值拆分成两半 + for (let i = index; i <= right; i++) { + // 如果当前值比nums[pivot]小,则将其移动到index的位置 + if (nums[i] < nums[pivot]) { + // 将nums[i]与nums[index]对调 + [nums[i], nums[index]] = [nums[index], nums[i]]; + // 将index移动一位,用于存储下一个值 + index++; + } + } + + // 完成循环时,index多移动了一位,要将其回退 + index--; + // 循环停止时,index位置存储的是小于nums[pivot]的值,而nums[pivot]还在原地 + // 将两者对调,中值会被移动到index的位置,在其左侧的值都比它小 + [nums[pivot], nums[index]] = [nums[index], nums[pivot]]; + + // 将新的中值返回,下层递归根据它将数组拆分成两段继续排序 + return index; +}; + +let arr = [0, 1, 2, 1]; +console.log(quickSort(arr, 0, arr.length - 1)); diff --git "a/\350\256\262\347\250\277.md" "b/\350\256\262\347\250\277.md" new file mode 100644 index 00000000..1c39000e --- /dev/null +++ "b/\350\256\262\347\250\277.md" @@ -0,0 +1,79 @@ +大家好,我从 2020 年 6 月下旬开始养成刷题习惯。截至目前,在 LeetCode 解决问题 138 个,发布题解 191 个,每周可以保持 30 遍左右的刷题量。 + +今天,我将这段时间的刷题经验和体会分享给大家,希望对大家有帮助。现在正式开始我的分享。 + +首先,为何要刷题。 + +除了能够升职加薪这样显而易见的好处之外,我还想补充一个,就是战胜年龄。 + +人的智力分为流体智力和晶体智力。以下摘自百度百科: + +流体智力(Fluid Intelligence)是一种以生理为基础的认知能力,如知觉、记忆、运算速度、推理能力等。流体智力是与晶体智力相对应的概念,流体智力随年龄的老化而减退。而晶体智力则并不随年龄的老化而减退,晶体智力主要指学会的技能、语言文字能力、判断力、联想力等。 + +流体智力属于人类的基本能力,受先天遗传因素影响较大,受教育文化影响较少。流体智力的发展与年龄有密切的关系:一般人在 20 岁以后,流体智力的发展达到顶峰,30 岁以后随着年龄的增长而降低。 + +也就是说,只要你是人类,不可避免地会在 30 岁之后面临流体智力下降这个困扰。但没关系,你还是可以战胜它,方法就是终身学习。 + +通过学习算法,我们可以掌握许多前人的智慧的结晶,只要把这些套路练熟,就可以轻松战胜不掌握套路,只会用流体智力解题的年轻人。 + +第二,如何养成刷题习惯。 + +1. 做好心理建设。 + +抛弃对自己智力的执着,就如老师所说,大多数题目都不是靠正常人的智力能够解决的。因此要学会转换心态,看到一个完全不会的题目时,要能明白这是正常现象,最好能找到一种“我又可以学习一种新套路”的感觉。 + +当发现做了 N 遍的题目,自己竟然还是做错时,要能够意识到遗忘是正常的,这时候需要再刷 N 遍。 + +2. 给自己一个开始的理由 + +我自己能够养成刷题习惯,是因为加了一个刷题群。群里有个规定:“每周刷 3 题,没完成的一律踢出”。我为了不陷入“被移出群聊”这种尴尬的局面,勉强开始刷题。 + +如果你是加入刷题群的话,切记要找一个能够严格遵守规定的群主。因为没有严格的纪律,一切 Flag 最终都会不了了之。 + +如果你现在报了训练营,依然没能坚持下来,也没关系。这可能说明你还没有到开始的时机,你需要先完成第一步心理建设,然后再寻找开始刷题的契机。 + +3. 找到反馈 + +传统学习的最大问题就是缺少反馈。而很多人会热爱游戏,其实是因为游戏非常善于提供及时反馈。当你每打一次怪,就会蹦出一个伤害数字,每杀一头怪,就会得到奖励。这些都是非常好的反馈机制。 + +而刷题的优势是,同样有相对及时的反馈。你每提交一次代码,都会有成功或失败的提示。只要你能坚持刷题一段时间,自然能找到反馈,之后不用毅力就能把题一直刷下去。 + +刷题,或者说学习,是一件很辛苦的事情。但我们不要让它变得痛苦,任何会让自己痛苦的事情,人们都会不自觉的去回避。只有反馈,才能提供源源不断的刷题动力。 + +第三,如何有效刷题 + +1. 提高刷题质量 + +我个人的意见是,不要追求解决问题的数量,重要的是严格执行“五毒神掌”。 + +初期最重要的是锻炼熟练度,你可以用多种解法刷同一道题。特别是基础薄弱的同学,甚至可以把暴力法也多刷几遍。我自己有的题目已经刷过几十遍。 + +2. 变量命名规范,代码书写规范,好好写注释 + +很多题解的变量名都喜欢用“a、b、c、d”。无视代码可读性,把代码压缩成一行。注释很少,甚至不写。 + +我们要明确一点,代码更多时候是给人读的,重要的是让人看懂。好的命名和格式,可以增加读者的阅读效率。有些细节的小操作,例如一些边界条件的处理,如果不写好注释的话,会让读者摸不着头脑。 + +我自己写的题解,会尽量为每一行都写注释,为的就是让自己充分理解每一句代码都是为何而写,真正做到有理有据。 + +3. 写高质量题解 + +写题解是个非常费时费力的事情。根据我的估算,写一个题解至少要花掉做题 3 倍的时间。 + +但写题解的好处也很大,当出现遗忘的时候,回去翻自己写的题解,要比看别人的题解,或者是自己写的代码要高效的多。 + +以上就是我今天的分享。现在我简单做个总结。 + +今天的分享,我先是探讨了刷题的理由是为了保持自己有持久而高效的竞争力,有能够在职场长期战斗的能力。 + +我还分享了如何养成刷题习惯的经验。分别是,放弃对自己智力的执着,为了“不被提出群聊”而开始刷题,为了得到反馈而持续刷题。 + +最后,我总结了有效刷题的心法,其中最重要的是严格执行“五毒神掌”。另外,规范的代码和注释非常重要,如果能写题解是再好不过。 + +如果你问我想刷题多长时间,我的回答是天长地久。 + +最后,附上一个我写的题解供大家参考。 + +https://leetcode-cn.com/problems/jump-game/solution/leetcodeti-jie-55-tiao-yue-you-xi-tan-xin-javascri/ + +我的分享到此结束,谢谢大家。 diff --git "a/\350\256\262\347\250\277\345\233\276\347\211\207.png" "b/\350\256\262\347\250\277\345\233\276\347\211\207.png" new file mode 100644 index 00000000..41aafaea Binary files /dev/null and "b/\350\256\262\347\250\277\345\233\276\347\211\207.png" differ