diff --git a/.vscode/settings.json b/.vscode/settings.json index 38e1d7b..7d8629b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,18 @@ { "cSpell.words": [ + "abcdefg", + "cdefgab", + "chuan", "dvdf", "lcci", "lcof", + "lrloseumgh", + "mincost", "nums", "pwwkew", - "zhong" + "umghlrlose", + "xuan", + "zhong", + "zhuan" ] } \ No newline at end of file diff --git a/README.md b/README.md index 8ab20ce..f9d24c5 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ LeetCode 与 LintCode 解题记录。此为个人练习仓库,代码中对重 - 面试题参考思路,不严谨实现 廖雪峰 不要使用JavaScript内置的parseInt()函数,利用map和reduce操作实现一个string2int()函数。 https://www.liaoxuefeng.com/wiki/1022910821149312/1024322552460832 +- [左旋转字符串](src/string/zuo-xuan-zhuan-zi-fu-chuan-lcof.js) + + - LeetCode 面试题58 - II. 左旋转字符串 https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/ + ## 数组 - [电话号码的字母组合](src/array/letter-combinations-of-a-phone-number.js) @@ -253,7 +257,16 @@ LeetCode 与 LintCode 解题记录。此为个人练习仓库,代码中对重 - [最大数和最小数](src/array/maximum-and-minimum.js) - LintCode 770. 最大数和最小数 https://www.lintcode.com/problem/maximum-and-minimum/description + - LintCode 770. 最大数和最小数 https://www.lintcode.com/problem/maximum-and-minimum/description + +- [最低票价](src/array/minimum-cost-for-tickets.js) + + - LeetCode 983. 最低票价 https://leetcode-cn.com/problems/minimum-cost-for-tickets/ + +- [最大正方形](src/array/maximal-square.js) + + - LeetCode 221. 最大正方形 https://leetcode-cn.com/problems/maximal-square/ + - LintCode 436. 最大正方形 https://www.lintcode.com/problem/maximal-square/description ## 栈 @@ -310,6 +323,11 @@ LeetCode 与 LintCode 解题记录。此为个人练习仓库,代码中对重 - LeetCode 236. 二叉树的最近公共祖先 https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/ - LintCode 88. 最近公共祖先 https://www.lintcode.com/problem/lowest-common-ancestor-of-a-binary-tree/description +- [另一个树的子树](src/tree/subtree-of-another-tree.js) + + - LeetCode 572. 另一个树的子树 https://leetcode-cn.com/problems/subtree-of-another-tree/ + - LintCode 1165. 另一个树的子树 https://www.lintcode.com/problem/subtree-of-another-tree/description + ## 链表 - [合并K个排序链表](src/list/merge-k-sorted-lists.js) @@ -320,9 +338,15 @@ LeetCode 与 LintCode 解题记录。此为个人练习仓库,代码中对重 - [合并两个有序链表](src/list/merge-two-sorted-lists.js) - LeetCode 21. 合并两个有序链表 https://leetcode-cn.com/problems/merge-two-sorted-lists/ + - LeetCode 面试题25. 合并两个排序的链表 https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof - LintCode 165. 合并两个排序链表 https://www.lintcode.com/problem/merge-two-sorted-lists/description - [链表排序](src/list/sort-list.js) - LeetCode 148. 排序链表 https://leetcode-cn.com/problems/sort-list/ - LintCode 98. 链表排序 https://www.lintcode.com/problem/sort-list/description + +- [环形链表](src/list/linked-list-cycle.js) + + - LeetCode 141. 环形链表 https://leetcode-cn.com/problems/linked-list-cycle/ + - LintCode 102. 带环链表 https://www.lintcode.com/problem/linked-list-cycle/description diff --git a/src/array/maximal-square.js b/src/array/maximal-square.js new file mode 100644 index 0000000..233441f --- /dev/null +++ b/src/array/maximal-square.js @@ -0,0 +1,25 @@ +/** + * @param {character[][]} matrix + * @return {number} + */ +export const maximalSquare = function (matrix) { + if (!matrix || !matrix[0]) return 0 + + const rows = matrix.length + const cols = matrix[0].length + let max = 0 + + const dp = new Array(rows).fill().map(_ => new Array(cols).fill(0)) + + for (let n = 0; n < rows; n++) { + for (let i = 0; i < cols; i++) { + if (Number(matrix[n][i]) === 1) { + if (n === 0 || i === 0) dp[n][i] = 1 + else dp[n][i] = Math.min(dp[n - 1][i], dp[n][i - 1], dp[n - 1][i - 1]) + 1 // 找规律 + max = Math.max(max, dp[n][i]) + } + } + } + + return max * max +} diff --git a/src/array/minimum-cost-for-tickets.js b/src/array/minimum-cost-for-tickets.js new file mode 100644 index 0000000..ab2d320 --- /dev/null +++ b/src/array/minimum-cost-for-tickets.js @@ -0,0 +1,43 @@ +/** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ +export const mincostTickets = function (days, costs) { + // 动态规划: + // dp[i]: 从第i天开始,到最后一天所用的票价总和 + + // 思路: + // 1. 记录数组中的最早的一天和最后的一天,动态规划从最后一天向前到最早一天即可,因为 + // 其他时间不需要消耗通行证 + // 2. 用一个变量 k 指针从 days 的最后一个索引向前走,i 指针也从最后一天向前走,但是遇到了 + // 不需要花费通行证的某些天走 else 分支即可,即后一天的总花费就是这一天的总花费 + // 3. 如果遇到需要通行证的时候,分别对比买一天、七天、三十天、所需的总花费,选择总花费最少的策略即可 + // 4. 初始化的时候 dp 数组多了 30,是为了简化有的用例第 365 天需要出行,选择买 30 天 + // 通行证的时候的特判 + + // 作者:ignore_express + // 链接:https://leetcode-cn.com/problems/minimum-cost-for-tickets/solution/js-dong-tai-gui-hua-si-lu-jiang-jie-by-ignore_expr/ + // 来源:力扣(LeetCode) + // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 + const dp = new Array(366 + 30).fill(0) + const n = days.length + const maxDay = days[n - 1] + const minDay = days[0] + let k = n - 1 + + for (let i = maxDay; i >= minDay; i--) { + if (i === days[k]) { + dp[i] = Math.min( + dp[i + 1] + costs[0], + dp[i + 7] + costs[1], + dp[i + 30] + costs[2] + ) + k-- + } else { + dp[i] = dp[i + 1] + } + } + + return dp[minDay] +} diff --git a/src/list/linked-list-cycle.js b/src/list/linked-list-cycle.js new file mode 100644 index 0000000..3459839 --- /dev/null +++ b/src/list/linked-list-cycle.js @@ -0,0 +1,28 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {boolean} + */ +export const hasCycle = function (head) { + if (!head || !head.next) return false + + // 双指针解法 + let slow = head + let fast = head.next + + while (true) { + if (!fast || !fast.next) return false + else if (fast.next === slow || fast === slow) return true + else { + fast = fast.next.next + slow = slow.next + } + } +} diff --git a/src/string/zuo-xuan-zhuan-zi-fu-chuan-lcof.js b/src/string/zuo-xuan-zhuan-zi-fu-chuan-lcof.js new file mode 100644 index 0000000..de51a37 --- /dev/null +++ b/src/string/zuo-xuan-zhuan-zi-fu-chuan-lcof.js @@ -0,0 +1,8 @@ +/** + * @param {string} s + * @param {number} n + * @return {string} + */ +export const reverseLeftWords = function (s, n) { + return (s + s).substr(n, s.length) +} diff --git a/src/tree/subtree-of-another-tree.js b/src/tree/subtree-of-another-tree.js new file mode 100644 index 0000000..121578b --- /dev/null +++ b/src/tree/subtree-of-another-tree.js @@ -0,0 +1,27 @@ +/** + * 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} s + * @param {TreeNode} t + * @return {boolean} + */ +export const isSubtree = function (s, t) { + if (!s && t) { + return false + } + + const linkNode = function (node, target) { + if ((!node && target) || (node && !target)) return false + if (!node && !target) return true + + return node.val === target.val ? linkNode(node.left, target.left) && linkNode(node.right, target.right) : false + } + + return linkNode(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t) +} diff --git a/test/array/maximal-square.test.js b/test/array/maximal-square.test.js new file mode 100644 index 0000000..3e31906 --- /dev/null +++ b/test/array/maximal-square.test.js @@ -0,0 +1,15 @@ +import { maximalSquare } from '../../src/array/maximal-square' + +test('最大正方形', () => { + expect(maximalSquare([ + [1, 0, 1, 0, 0], + [1, 0, 1, 1, 1], + [1, 1, 1, 1, 1], + [1, 0, 0, 1, 0] + ])).toBe(4) + + expect(maximalSquare([ + [0, 0, 0], + [1, 1, 1] + ])).toBe(1) +}) diff --git a/test/array/minimum-cost-for-tickets.test.js b/test/array/minimum-cost-for-tickets.test.js new file mode 100644 index 0000000..97ed54a --- /dev/null +++ b/test/array/minimum-cost-for-tickets.test.js @@ -0,0 +1,6 @@ +import { mincostTickets } from '../../src/array/minimum-cost-for-tickets' + +test('最低票价', () => { + expect(mincostTickets([1, 4, 6, 7, 8, 20], [2, 7, 15])).toBe(11) + expect(mincostTickets([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [2, 7, 15])).toBe(17) +}) diff --git a/test/list/linked-list-cycle.test.js b/test/list/linked-list-cycle.test.js new file mode 100644 index 0000000..abcf7c1 --- /dev/null +++ b/test/list/linked-list-cycle.test.js @@ -0,0 +1,24 @@ +import { hasCycle } from '../../src/list/linked-list-cycle' + +function ListNode (val) { + this.val = val + this.next = null +} + +const arrToList = (arr) => { + const head = new ListNode(arr[0]) + let current = head + for (let n = 1, len = arr.length; n < len; n++) { + current.next = new ListNode(arr[n]) + current = current.next + } + + return head +} + +test('环形链表', () => { + const list = arrToList([1, 2, 3]) + list.next.next.next = list + expect(hasCycle(list)).toBe(true) + expect(hasCycle(arrToList([1, 2, 3]))).toBe(false) +}) diff --git a/test/string/zuo-xuan-zhuan-zi-fu-chuan-lcof.test.js b/test/string/zuo-xuan-zhuan-zi-fu-chuan-lcof.test.js new file mode 100644 index 0000000..43c52de --- /dev/null +++ b/test/string/zuo-xuan-zhuan-zi-fu-chuan-lcof.test.js @@ -0,0 +1,6 @@ +import { reverseLeftWords } from '../../src/string/zuo-xuan-zhuan-zi-fu-chuan-lcof' + +test('左旋转字符串', () => { + expect(reverseLeftWords('abcdefg', 2)).toBe('cdefgab') + expect(reverseLeftWords('lrloseumgh', 6)).toBe('umghlrlose') +}) diff --git a/test/tree/subtree-of-another-tree.test.js b/test/tree/subtree-of-another-tree.test.js new file mode 100644 index 0000000..bb54e19 --- /dev/null +++ b/test/tree/subtree-of-another-tree.test.js @@ -0,0 +1,7 @@ +import { isSubtree } from '../../src/tree/subtree-of-another-tree' +import Tree from './Tree' + +test('另一个树的子树', () => { + expect(isSubtree(Tree.arrToTree([3, 4, 5, 1, 2]), Tree.arrToTree([4, 1, 2]))).toBe(true) + expect(isSubtree(Tree.arrToTree([3, 4, 5, 1, 2, null, null, null, null, 0]), Tree.arrToTree([4, 1, 2]))).toBe(false) +})