diff --git "a/Week01/1.\344\270\244\346\225\260\344\271\213\345\222\214.js" "b/Week01/1.\344\270\244\346\225\260\344\271\213\345\222\214.js" new file mode 100644 index 000000000..e333ce73c --- /dev/null +++ "b/Week01/1.\344\270\244\346\225\260\344\271\213\345\222\214.js" @@ -0,0 +1,48 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + * + * https://leetcode-cn.com/problems/two-sum/description/ + * + * algorithms + * Easy (48.69%) + * Likes: 8426 + * Dislikes: 0 + * Total Accepted: 1.1M + * Total Submissions: 2.3M + * Testcase Example: '[2,7,11,15]\n9' + * + * 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + * + * 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 + * + * + * + * 示例: + * + * 给定 nums = [2, 7, 11, 15], target = 9 + * + * 因为 nums[0] + nums[1] = 2 + 7 = 9 + * 所以返回 [0, 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, len = nums.length; i < len; i++) { + if (map.has(nums[i])) { + return [map.get(nums[i]), i]; + } else { + map.set(target - nums[i], i); + } + } +}; +// @lc code=end diff --git "a/Week01/189.\346\227\213\350\275\254\346\225\260\347\273\204.js" "b/Week01/189.\346\227\213\350\275\254\346\225\260\347\273\204.js" new file mode 100644 index 000000000..ca8309958 --- /dev/null +++ "b/Week01/189.\346\227\213\350\275\254\346\225\260\347\273\204.js" @@ -0,0 +1,60 @@ +/* + * @lc app=leetcode.cn id=189 lang=javascript + * + * [189] 旋转数组 + * + * https://leetcode-cn.com/problems/rotate-array/description/ + * + * algorithms + * Easy (41.75%) + * Likes: 593 + * Dislikes: 0 + * Total Accepted: 132.1K + * Total Submissions: 316.4K + * Testcase Example: '[1,2,3,4,5,6,7]\n3' + * + * 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 + * + * 示例 1: + * + * 输入: [1,2,3,4,5,6,7] 和 k = 3 + * 输出: [5,6,7,1,2,3,4] + * 解释: + * 向右旋转 1 步: [7,1,2,3,4,5,6] + * 向右旋转 2 步: [6,7,1,2,3,4,5] + * 向右旋转 3 步: [5,6,7,1,2,3,4] + * + * + * 示例 2: + * + * 输入: [-1,-100,3,99] 和 k = 2 + * 输出: [3,99,-1,-100] + * 解释: + * 向右旋转 1 步: [99,-1,-100,3] + * 向右旋转 2 步: [3,99,-1,-100] + * + * 说明: + * + * + * 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 + * 要求使用空间复杂度为 O(1) 的 原地 算法。 + * + * + */ + +// @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) { + // 法1:pop的元素unshift到数组中k次 + /* for (let i = 0; i < k; i++) { + nums.unshift(nums.pop()); + } */ + + // 法2:最后k个元素截取出来放到前面 + nums.splice(0, 0, ...nums.splice(nums.length - k)); +}; +// @lc code=end diff --git "a/Week01/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.js" "b/Week01/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.js" new file mode 100644 index 000000000..8a735d3b3 --- /dev/null +++ "b/Week01/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.js" @@ -0,0 +1,63 @@ +/* + * @lc app=leetcode.cn id=21 lang=javascript + * + * [21] 合并两个有序链表 + * + * https://leetcode-cn.com/problems/merge-two-sorted-lists/description/ + * + * algorithms + * Easy (62.81%) + * Likes: 1105 + * Dislikes: 0 + * Total Accepted: 288.2K + * Total Submissions: 458.9K + * Testcase Example: '[1,2,4]\n[1,3,4]' + * + * 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 + * + * + * + * 示例: + * + * 输入:1->2->4, 1->3->4 + * 输出:1->1->2->3->4->4 + * + * + */ + +// @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) { + // 哨兵节点——1. 便于最后返回用;2. 记录结果链表 + const headNode = new ListNode(-1); + + let head = headNode; // 结果链表指针 + while (l1 !== null && l2 !== null) { + if (l1.val <= l2.val) { + head.next = l1; + // l1往后挪,用于下一次循环 + l1 = l1.next; + } else { + head.next = l2; + // 同样l2往后挪,用于下一次循环 + l2 = l2.next; + } + // 每次循环 head都要往后一步,这样才能形成链表 + head = head.next; + } + // 执行到这里,要么l1先空了,要么l2先空了,但其中之一肯定是较大的部分了 + head.next = l1 === null ? l2 : l1; + return headNode.next; +}; +// @lc code=end diff --git "a/Week01/66.\345\212\240\344\270\200.js" "b/Week01/66.\345\212\240\344\270\200.js" new file mode 100644 index 000000000..b3a907694 --- /dev/null +++ "b/Week01/66.\345\212\240\344\270\200.js" @@ -0,0 +1,63 @@ +/* + * @lc app=leetcode.cn id=66 lang=javascript + * + * [66] 加一 + * + * https://leetcode-cn.com/problems/plus-one/description/ + * + * algorithms + * Easy (44.32%) + * Likes: 483 + * Dislikes: 0 + * Total Accepted: 163.5K + * Total Submissions: 368.8K + * Testcase Example: '[1,2,3]' + * + * 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 + * + * 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 + * + * 你可以假设除了整数 0 之外,这个整数不会以零开头。 + * + * 示例 1: + * + * 输入: [1,2,3] + * 输出: [1,2,4] + * 解释: 输入数组表示数字 123。 + * + * + * 示例 2: + * + * 输入: [4,3,2,1] + * 输出: [4,3,2,2] + * 解释: 输入数组表示数字 4321。 + * + * + */ + +// @lc code=start +/** + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function (digits) { + // 法1:转为数字 + 1 后再转为数组 + // NOTE: digits可能很长,转为的数字要使用bigint新特性保存才不容易丢失精度 + /* return String(BigInt(digits.join("")) + 1n).split(""); */ + + // 法2:常规末位 + 1,超10,往前进位,直到第一位,若还是超过10,则额外处理下 + const len = digits.length; + for (let i = len - 1; i > -1; i--) { + digits[i]++; + // 取余 + digits[i] %= 10; + if (digits[i] !== 0) { + // 直到不需要进位,终止 + return digits; + } + } + // 上面的循环跑完都还没有return,证明需要加一位,其余位也都变0了 + digits = [1].concat(Array(len).fill(0)); + return digits; +}; +// @lc code=end diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 50de30414..363ba6181 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -1 +1,36 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## 刷题五+遍,其义自见 + +1. 5-10分钟思考,没有很明确的思路直接看题解,首先记住,其次理解 + +2. 立即:自己将刚才记住的解题方法在leetcode或ide中完成,并通过测试用例 +3. 第二天:重新完成第二遍,并追求更优、更多解法 +4. 一周后:再次练习已经完成三遍的题目,至少掌握一种解法,如有可能,掌握2-3种解法 +5. 面试前(平常有时间时):重刷即使已经做了四遍的题目,不限制次数,对于薄弱题目,如第四遍还磕磕碰碰的,重点刷 + +## 自古套路得人心 + +首先,需要确定的是,计算机是“死”的,擅长并且只会重复和计算;那么那些所谓的题目,就是能通过这样或那样的重复计算得出来,因此,一切算法实现的最小单元肯定是 + +1. 分支语句 +2. 循环语句 +3. 递归 + +(PS:可能有更高级的,后面再补充,目前来看,这几个用得最多) + +为此,需要建立良好的观念: + +1. 没那么难,先刻意练习,把经典题解烂熟于心 +2. 从易到难,找到解题的核心实现,某些情况下,可以先用暴力破解法尝试下,再进一步看有没有抽象及优化的可能 +3. 一切程序的基础都是算法和数据结构,只有掌握和理解了这些,才能通向更高级的台阶 + +## 本周反思 + +投入时间过少,对于基础的知识没来得及记录总结和归纳,同时,完成的题目不多;下周亟需调整,计划如下 + +## 下周计划 + +1. 整理第一周及第二周知识点,完成知识图谱 + +2. 完成第二周所有简单、中等题目,并补充完成第一周所有简单、中等题目,最后尝试困难题目 \ No newline at end of file diff --git "a/Week02/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/Week02/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 000000000..c387e1ea8 --- /dev/null +++ "b/Week02/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,73 @@ +/* + * @lc app=leetcode.cn id=242 lang=javascript + * + * [242] 有效的字母异位词 + * + * https://leetcode-cn.com/problems/valid-anagram/description/ + * + * algorithms + * Easy (60.15%) + * Likes: 205 + * Dislikes: 0 + * Total Accepted: 108.9K + * Total Submissions: 180.7K + * Testcase Example: '"anagram"\n"nagaram"' + * + * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + * + * 示例 1: + * + * 输入: s = "anagram", t = "nagaram" + * 输出: true + * + * + * 示例 2: + * + * 输入: s = "rat", t = "car" + * 输出: false + * + * 说明: + * 你可以假设字符串只包含小写字母。 + * + * 进阶: + * 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? + * + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + if (s.length !== t.length) { + // 对于length不等的s/t,先处理掉 + return false; + } + + // 解法一:排序后比较 + // const sorted_s = s.split("").sort().join(""); + // const sorted_t = t.split("").sort().join(""); + // return sorted_s === sorted_t; + + // 解法二:使用hash表,对于s就存,对于t就取 + let obj = {}; + for (var i = 0; i < s.length; i++) { + if (!obj[s[i]]) { + obj[s[i]] = 0; + } + obj[s[i]]++; + } + for (var j = 0; j < t.length; j++) { + obj[t[j]]--; + if (obj[t[j]] === 0) { + // 删除这个k-v + delete obj[t[j]]; + } + } + return !Object.keys(obj).length; +}; + +isAnagram("anagram", "nagaram"); +// @lc code=end diff --git "a/Week02/589.n\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" "b/Week02/589.n\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" new file mode 100644 index 000000000..e49885c2d --- /dev/null +++ "b/Week02/589.n\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.js" @@ -0,0 +1,68 @@ +/* + * @lc app=leetcode.cn id=589 lang=javascript + * + * [589] N叉树的前序遍历 + * + * https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/description/ + * + * algorithms + * Easy (73.24%) + * Likes: 82 + * Dislikes: 0 + * Total Accepted: 30.3K + * Total Submissions: 41.3K + * Testcase Example: '[1,null,3,2,4,null,5,6]' + * + * 给定一个 N 叉树,返回其节点值的前序遍历。 + * + * 例如,给定一个 3叉树 : + * + * + * + * + * + * + * + * 返回其前序遍历: [1,3,5,6,2,4]。 + * + * + * + * 说明: 递归法很简单,你可以使用迭代法完成此题吗? + */ + +// @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) { + if (!root) { + return []; + } + // 解法一:递归 + // return Array.prototype.concat.apply( + // [root.val], + // root.children.map((item) => preorder(item)) + // ); + + // 解法二:迭代,利用栈,也有点手动递归的意思 + const res = []; + const arr = [root]; + while (arr.length) { + const cur = arr.pop(); + res.push(cur.val); + for (let i = cur.children.length - 1; i >= 0; i--) { + arr.push(cur.children[i]); + } + } + return res; +}; +// @lc code=end diff --git a/Week02/NOTE.md b/Week02/NOTE.md index 50de30414..c7bc1948f 100644 --- a/Week02/NOTE.md +++ b/Week02/NOTE.md @@ -1 +1,5 @@ -学习笔记 \ No newline at end of file +# 学习总结 + +本周主要学习内容有hash表、数、堆、图,这些数据结构在js中大部分都没有原生实现,加上自己没有科班出身的底子,在学习过程中还需要去查阅一些必要的资料,学习开始变得稍微有点费劲了。不过一个事物从零到一能学到的东西真的很多,并且课程中还有一些对于为何会出现这样的数据结构的实践解释,能让自己建立一定的印象,在日后的工作学习中如有遇到类似的问题,可以尝试使用数据结构与算法去实现。 + +此外,上周计划的学习笔记和做题计划由于本周繁忙的工作未能完成,还在整理中,需要重新安排下自己的工作、学习、生活时间配比了,不然一拖再拖,更完不成了,毕竟这些知识不是简单的听一听课,做一做题目就能融会贯通的,学习笔记和更多次数的刷题迫在眉睫。 \ No newline at end of file diff --git "a/Week03/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.js" "b/Week03/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.js" new file mode 100644 index 000000000..64e0c71c0 --- /dev/null +++ "b/Week03/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.js" @@ -0,0 +1,93 @@ +/* + * @lc app=leetcode.cn id=236 lang=javascript + * + * [236] 二叉树的最近公共祖先 + * + * https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/description/ + * + * algorithms + * Medium (63.95%) + * Likes: 616 + * Dislikes: 0 + * Total Accepted: 92.1K + * Total Submissions: 143.7K + * Testcase Example: '[3,5,1,6,2,0,8,null,null,7,4]\n5\n1' + * + * 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + * + * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x + * 的深度尽可能大(一个节点也可以是它自己的祖先)。” + * + * 例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4] + * + * + * + * + * + * 示例 1: + * + * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 + * 输出: 3 + * 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 + * + * + * 示例 2: + * + * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 + * 输出: 5 + * 解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 + * + * + * + * + * 说明: + * + * + * 所有节点的值都是唯一的。 + * p、q 为不同节点且均存在于给定的二叉树中。 + * + * + */ + +// @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} + */ +/** + * 遍历整颗二叉树,对于节点x,定义f(x)确定x节点的子树是否包含p或者q,若包含,则返回true,否则false + * 那么符合最近公共祖先的节点x一定满足如下条件—— + * f(x.left) && f(x.right) || ((x = p || x = q) && f(x.left) || f(x.right)) + * 对于条件一:f(x.left) && f(x.right),表示左子树包含p或者q,那么右子树肯定包含反之的那一个,此时x就是要找的最近公共祖先 + * 对于条件二:(x = p || x = q) && f(x.left) || f(x.right),主要是考虑x恰好是p或q,并且其子树包含了反之的那一个,此时x也算是最近公共祖先 + */ +var lowestCommonAncestor = function (root, p, q) { + let res; + function dfs(root, p, q) { + if (!root) { + return false; + } + const lson = dfs(root.left, p, q); + const rson = dfs(root.right, p, q); + if ( + (lson && rson) || + ((root.val === p.val || root.val === q.val) && (lson || rson)) + ) { + res = root; + return; + } + return lson || rson || root.val === p.val || root.val === q.val; + } + dfs(root, p, q); + return res; +}; +// @lc code=end diff --git "a/Week03/77.\347\273\204\345\220\210.js" "b/Week03/77.\347\273\204\345\220\210.js" new file mode 100644 index 000000000..df96adc74 --- /dev/null +++ "b/Week03/77.\347\273\204\345\220\210.js" @@ -0,0 +1,63 @@ +/* + * @lc app=leetcode.cn id=77 lang=javascript + * + * [77] 组合 + * + * https://leetcode-cn.com/problems/combinations/description/ + * + * algorithms + * Medium (73.83%) + * Likes: 293 + * Dislikes: 0 + * Total Accepted: 56.9K + * Total Submissions: 77K + * Testcase Example: '4\n2' + * + * 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 + * + * 示例: + * + * 输入: n = 4, k = 2 + * 输出: + * [ + * ⁠ [2,4], + * ⁠ [3,4], + * ⁠ [2,3], + * ⁠ [1,2], + * ⁠ [1,3], + * ⁠ [1,4], + * ] + * + */ + +// @lc code=start +/** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ +var combine = function (n, k) { + let res = []; + let subRes = []; + if (k === 0) { + return [[]]; + } + function dfs(start, n, k, res, subRes) { + if (subRes.length === k) { + console.log(1, subRes); + res.push(subRes.slice(0)); + return; + } + for (let i = start; i < n + 1; i++) { + subRes.push(i); + console.log(2, subRes); + dfs(i + 1, n, k, res, subRes); + subRes.pop(); + } + return res; + } + return dfs(1, n, k, res, subRes); +}; + +combine(4, 3); +// @lc code=end diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 50de30414..0d2b26706 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -1 +1,9 @@ -学习笔记 \ No newline at end of file +# 学习总结 + +## 笔记 + +![image-20200629010459960](https://i.loli.net/2020/06/29/nZoM6OXdvfsPQtY.png) + +## 总结 + +未曾想到这么快就进入了一定的深水区,本周的知识点并不多,但习题难度却上升了一个等级,如不多多练习找到递归的那种感觉,这类题目属实难以下手,就个人而言,相关练习还是太少了,亟需进一步做相关练习,吃透递归,下周需要分摊一些时间到这类习题上面来。 \ No newline at end of file diff --git "a/Week04/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.js" "b/Week04/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.js" new file mode 100644 index 000000000..d9f2207d8 --- /dev/null +++ "b/Week04/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.js" @@ -0,0 +1,57 @@ +/* + * @lc app=leetcode.cn id=153 lang=javascript + * + * [153] 寻找旋转排序数组中的最小值 + * + * https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/description/ + * + * algorithms + * Medium (50.95%) + * Likes: 202 + * Dislikes: 0 + * Total Accepted: 57.6K + * Total Submissions: 112.8K + * Testcase Example: '[3,4,5,1,2]' + * + * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + * + * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + * + * 请找出其中最小的元素。 + * + * 你可以假设数组中不存在重复元素。 + * + * 示例 1: + * + * 输入: [3,4,5,1,2] + * 输出: 1 + * + * 示例 2: + * + * 输入: [4,5,6,7,0,1,2] + * 输出: 0 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number} + */ +var findMin = function (nums) { + if (nums.length === 1) { + return nums[0]; + } + let left = 0, + right = nums.length - 1; + while (left < right) { + const mid = Math.floor(left + (right - left) / 2); + if (nums[mid] > nums[right]) { + left = mid + 1; + } else { + right = mid; + } + } + return nums[left]; +}; +// @lc code=end diff --git "a/Week04/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.js" "b/Week04/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.js" new file mode 100644 index 000000000..a325c9101 --- /dev/null +++ "b/Week04/860.\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.js" @@ -0,0 +1,106 @@ +/* + * @lc app=leetcode.cn id=860 lang=javascript + * + * [860] 柠檬水找零 + * + * https://leetcode-cn.com/problems/lemonade-change/description/ + * + * algorithms + * Easy (55.24%) + * Likes: 121 + * Dislikes: 0 + * Total Accepted: 25.1K + * Total Submissions: 45.2K + * Testcase Example: '[5,5,5,10,20]' + * + * 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 + * + * 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 + * + * 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 + * + * 注意,一开始你手头没有任何零钱。 + * + * 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 + * + * 示例 1: + * + * 输入:[5,5,5,10,20] + * 输出:true + * 解释: + * 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 + * 第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 + * 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 + * 由于所有客户都得到了正确的找零,所以我们输出 true。 + * + * + * 示例 2: + * + * 输入:[5,5,10] + * 输出:true + * + * + * 示例 3: + * + * 输入:[10,10] + * 输出:false + * + * + * 示例 4: + * + * 输入:[5,5,10,10,20] + * 输出:false + * 解释: + * 前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。 + * 对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。 + * 对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。 + * 由于不是每位顾客都得到了正确的找零,所以答案是 false。 + * + * + * + * + * 提示: + * + * + * 0 <= bills.length <= 10000 + * bills[i] 不是 5 就是 10 或是 20 + * + * + */ + +// @lc code=start +/** + * @param {number[]} bills + * @return {boolean} + */ +var lemonadeChange = function (bills) { + // 贪心,需要找零时,优先使用大面额 + let five = 0, + ten = 0; + for (let bill of bills) { + if (bill === 5) { + five++; + } else if (bill === 10) { + if (five === 0) { + // 没有5元了 + return false; + } + five--; + ten++; + } else { + if (five > 0 && ten > 0) { + // 使用10 + 5 + five--; + ten--; + } else if (five >= 3) { + // 使用5 * 3 + five -= 3; + } else { + return false; + } + } + } + // 一直没有false,证明能找零正常 + return true; +}; +// @lc code=end diff --git a/Week04/NOTE.md b/Week04/NOTE.md index 50de30414..214b18389 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -1 +1,3 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +![](https://raw.githubusercontent.com/CyfforPro/just_a_pics_store/master/img/test.jpg) \ No newline at end of file diff --git "a/Week06/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.js" "b/Week06/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.js" new file mode 100644 index 000000000..78046433e --- /dev/null +++ "b/Week06/64.\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.js" @@ -0,0 +1,67 @@ +/* + * @lc app=leetcode.cn id=64 lang=javascript + * + * [64] 最小路径和 + * + * https://leetcode-cn.com/problems/minimum-path-sum/description/ + * + * algorithms + * Medium (65.68%) + * Likes: 524 + * Dislikes: 0 + * Total Accepted: 101.6K + * Total Submissions: 153.8K + * Testcase Example: '[[1,3,1],[1,5,1],[4,2,1]]' + * + * 给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 + * + * 说明:每次只能向下或者向右移动一步。 + * + * 示例: + * + * 输入: + * [ + * [1,3,1], + * ⁠ [1,5,1], + * ⁠ [4,2,1] + * ] + * 输出: 7 + * 解释: 因为路径 1→3→1→1→1 的总和最小。 + * + * + */ + +// @lc code=start +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function (grid) { + // 1. dp状态,dp(i, j)表示从坐标(i, j)到右下角的最小路径之和 + // 2. dp方程,dp(i, j) = grid(i, j) + min(dp(i + 1, j), dp(i, j + 1)) + + let row = grid.length - 1; // 行(i)的最大值 + let col = grid[0].length - 1; // 列(j)的最大值 + let dp = Array(row + 1).fill(Array(col + 1)); // 一个同grid大小的二维数组 + + for (let i = row; i >= 0; i--) { + for (let j = col; j >= 0; j--) { + if (i === row && j !== col) { + // 最后一行的非最后一列的值 + dp[i][j] = grid[i][j] + dp[i][j + 1]; + } else if (i !== row && j === col) { + // 最后一列的非最后一行的值 + dp[i][j] = grid[i][j] + dp[i + 1][j]; + } else if (i !== row && j !== col) { + // 其他值 + dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); + } else { + // 最后一行的最后一列 + dp[i][j] = grid[i][j]; + } + } + } + + return dp[0][0]; +}; +// @lc code=end diff --git "a/Week06/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" "b/Week06/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" new file mode 100644 index 000000000..d1cb75ec6 --- /dev/null +++ "b/Week06/91.\350\247\243\347\240\201\346\226\271\346\263\225.js" @@ -0,0 +1,75 @@ +/* + * @lc app=leetcode.cn id=91 lang=javascript + * + * [91] 解码方法 + * + * https://leetcode-cn.com/problems/decode-ways/description/ + * + * algorithms + * Medium (23.72%) + * Likes: 441 + * Dislikes: 0 + * Total Accepted: 56.7K + * Total Submissions: 236K + * Testcase Example: '"12"' + * + * 一条包含字母 A-Z 的消息通过以下方式进行了编码: + * + * 'A' -> 1 + * 'B' -> 2 + * ... + * 'Z' -> 26 + * + * + * 给定一个只包含数字的非空字符串,请计算解码方法的总数。 + * + * 示例 1: + * + * 输入: "12" + * 输出: 2 + * 解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。 + * + * + * 示例 2: + * + * 输入: "226" + * 输出: 3 + * 解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。 + * + * + */ + +// @lc code=start +/** + * @param {string} s + * @return {number} + */ +var numDecodings = function (s) { + // 1. dp状态 dp[i]表示str[0...i]的解码方法总数 + // 2. dp方程 + + let len = s.length; + + if (!len || s[0] === "0") { + return 0; + } + + let dp = Array(len + 1).fill(0); + + dp[0] = dp[1] = 1; + + for (let i = 2; i <= len; i++) { + if (s[i - 1] !== "0") { + dp[i] += dp[i - 1]; + } + if ( + s[i - 2] === "1" || + (s[i - 2] === "2" && s[i - 1] >= 0 && s[i - 1] <= 6) + ) { + dp[i] += dp[i - 2]; + } + } + + return dp[len]; +}; +// @lc code=end diff --git a/Week06/NOTE.md b/Week06/NOTE.md index 50de30414..5e35e6e9f 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -1 +1,3 @@ -学习笔记 \ No newline at end of file +## 学习笔记 + +![image-20200720001336161](https://raw.githubusercontent.com/CyfforPro/just_a_pics_store/master/img/image-20200720001336161.png) \ No newline at end of file diff --git "a/Week08/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" "b/Week08/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 000000000..318abd2e9 --- /dev/null +++ "b/Week08/190.\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.js" @@ -0,0 +1,69 @@ +/* + * @lc app=leetcode.cn id=190 lang=javascript + * + * [190] 颠倒二进制位 + * + * https://leetcode-cn.com/problems/reverse-bits/description/ + * + * algorithms + * Easy (58.93%) + * Likes: 188 + * Dislikes: 0 + * Total Accepted: 49K + * Total Submissions: 81.2K + * 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 res = 0; + for (let i = 0; i < 32; i++) { + // res左移一位,然后“拼接”(也就是加上)n的最后一位 + res = (res << 1) + (n & 1); + // 同时n要右移一位 + n >>= 1; + } + // NOTE: js的32位是有符号的,因此要使用>>>右移零位,来将符号位置零 + // >>>: 将 a 的二进制的符号位置0(表示无符号、正的),然后向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充 + return res >>> 0; +}; +// @lc code=end diff --git "a/Week08/56.\345\220\210\345\271\266\345\214\272\351\227\264.js" "b/Week08/56.\345\220\210\345\271\266\345\214\272\351\227\264.js" new file mode 100644 index 000000000..2dc23515c --- /dev/null +++ "b/Week08/56.\345\220\210\345\271\266\345\214\272\351\227\264.js" @@ -0,0 +1,56 @@ +/* + * @lc app=leetcode.cn id=56 lang=javascript + * + * [56] 合并区间 + * + * https://leetcode-cn.com/problems/merge-intervals/description/ + * + * algorithms + * Medium (42.68%) + * Likes: 530 + * Dislikes: 0 + * Total Accepted: 124K + * Total Submissions: 290.2K + * Testcase Example: '[[1,3],[2,6],[8,10],[15,18]]' + * + * 给出一个区间的集合,请合并所有重叠的区间。 + * + * 示例 1: + * + * 输入: [[1,3],[2,6],[8,10],[15,18]] + * 输出: [[1,6],[8,10],[15,18]] + * 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. + * + * + * 示例 2: + * + * 输入: [[1,4],[4,5]] + * 输出: [[1,5]] + * 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。 + * + */ + +// @lc code=start +/** + * @param {number[][]} intervals + * @return {number[][]} + */ +var merge = function (intervals) { + // 先按首个数字排序 + intervals.sort((a, b) => a[0] - b[0]); + + let merged = []; // 合并后数组 + + for (let interval of intervals) { + let len = merged.length; + if (!len || merged[len - 1][1] < interval[0]) { + // 若merged为空,或者已合并区间与当前区间不重合,直接添加 + merged.push(interval); + } else { + // 否则就进行合并——即将已合并的区间的最后一个数字更换为较大值 + merged[len - 1][1] = Math.max(merged[len - 1][1], interval[1]); + } + } + return merged; +}; +// @lc code=end diff --git a/Week08/NOTE.md b/Week08/NOTE.md index 50de30414..9f6db8d65 100644 --- a/Week08/NOTE.md +++ b/Week08/NOTE.md @@ -1 +1,7 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +![image-20200802232205084](https://raw.githubusercontent.com/CyfforPro/just_a_pics_store/master/img/image-20200802232205084.png) + +![image-20200802232238843](https://raw.githubusercontent.com/CyfforPro/just_a_pics_store/master/img/image-20200802232238843.png) + +![image-20200802232250820](https://raw.githubusercontent.com/CyfforPro/just_a_pics_store/master/img/image-20200802232250820.png) \ No newline at end of file diff --git "a/Week09/438.\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" "b/Week09/438.\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" new file mode 100644 index 000000000..bf5e181d3 --- /dev/null +++ "b/Week09/438.\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.js" @@ -0,0 +1,100 @@ +/* + * @lc app=leetcode.cn id=438 lang=javascript + * + * [438] 找到字符串中所有字母异位词 + * + * https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/description/ + * + * algorithms + * Medium (44.34%) + * Likes: 337 + * Dislikes: 0 + * Total Accepted: 34.6K + * Total Submissions: 75.6K + * Testcase Example: '"cbaebabacd"\n"abc"' + * + * 给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。 + * + * 字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。 + * + * 说明: + * + * + * 字母异位词指字母相同,但排列不同的字符串。 + * 不考虑答案输出的顺序。 + * + * + * 示例 1: + * + * + * 输入: + * s: "cbaebabacd" p: "abc" + * + * 输出: + * [0, 6] + * + * 解释: + * 起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。 + * 起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。 + * + * + * 示例 2: + * + * + * 输入: + * s: "abab" p: "ab" + * + * 输出: + * [0, 1, 2] + * + * 解释: + * 起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。 + * 起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。 + * 起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。 + * + * + */ + +// @lc code=start +/** + * @param {string} s + * @param {string} p + * @return {number[]} + */ +var findAnagrams = function (s, p) { + let res = []; + let left = 0, + right = 0; + let needs = {}, + windows = {}; + let match = 0; + for (let i = 0; i < p.length; i++) { + needs[p[i]] ? needs[p[i]]++ : (needs[p[i]] = 1); + } + let needsLen = Object.keys(needs).length; + while (right < s.length) { + let c1 = s[right]; + if (needs[c1]) { + windows[c1] ? windows[c1]++ : (windows[c1] = 1); + if (windows[c1] === needs[c1]) { + match++; + } + } + right++; + while (match === needsLen) { + if (right - left === p.length) { + res.push(left); + } + let c2 = s[left]; + if (needs[c2]) { + windows[c2]--; + if (windows[c2] < needs[c2]) { + match--; + } + } + left++; + } + } + return res; +}; +// @lc code=end diff --git "a/Week09/541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262-ii.js" "b/Week09/541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262-ii.js" new file mode 100644 index 000000000..434b4afe6 --- /dev/null +++ "b/Week09/541.\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262-ii.js" @@ -0,0 +1,64 @@ +/* + * @lc app=leetcode.cn id=541 lang=javascript + * + * [541] 反转字符串 II + * + * https://leetcode-cn.com/problems/reverse-string-ii/description/ + * + * algorithms + * Easy (53.50%) + * Likes: 84 + * Dislikes: 0 + * Total Accepted: 18.8K + * Total Submissions: 34.6K + * Testcase Example: '"abcdefg"\n2' + * + * 给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。 + * + * + * 如果剩余字符少于 k 个,则将剩余字符全部反转。 + * 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。 + * + * + * + * + * 示例: + * + * 输入: s = "abcdefg", k = 2 + * 输出: "bacdfeg" + * + * + * + * + * 提示: + * + * + * 该字符串只包含小写英文字母。 + * 给定字符串的长度和 k 在 [1, 10000] 范围内。 + * + * + */ + +// @lc code=start +/** + * @param {string} s + * @param {number} k + * @return {string} + */ +var reverseStr = function (s, k) { + let s_arr = s.split(""); + function reverse(start, end) { + while (start < end) { + const temp = s_arr[start]; + s_arr[start] = s_arr[end]; + s_arr[end] = temp; + start++; + end--; + } + } + for (let i = 0; i < s.length; i += 2 * k) { + reverse(i, i + k - 1); + } + return s_arr.join(""); +}; +// @lc code=end diff --git a/Week09/NOTE.md b/Week09/NOTE.md index 50de30414..0d8888451 100644 --- a/Week09/NOTE.md +++ b/Week09/NOTE.md @@ -1 +1,3 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +![image-20200809233356705](https://raw.githubusercontent.com/CyfforPro/just_a_pics_store/master/img/image-20200809233356705.png) \ No newline at end of file diff --git a/Week10/Summary.md b/Week10/Summary.md new file mode 100644 index 000000000..c5b8b0257 --- /dev/null +++ b/Week10/Summary.md @@ -0,0 +1,12 @@ +## 毕业总结 + +时光荏苒,没想到这么快10周的算法训练营就这么结束了,总有种意犹未尽的感觉。这期训练营带给我的提升主要如下: + +1. 重要认知——不要死磕题目,争取过遍数 +2. 对常见的数据结构和算法知识架构有了一定的认识,不至于每次遇到算法问题就摸不着北了 +3. 很多算法题都是有“套路”的,可以定期针对某一不熟悉专题进行刻意训练 +4. 解决了leetcode几十道题目 + +这个学习过程中,能感受到超哥对于算法的信手拈来,总能由浅入深的进行教学,很感谢他,同时也感谢各位辛苦的助教及班主任老师。 + +师傅领进门,修行靠个人,训练营结束了,但对我而言,算法的掌握这条路才刚刚开始,还有很多的遍数和题目没过,还需要持之以恒的坚持刷题并内化。再次感谢这个训练营给我开了个好头并教会了我解决算法这一薄弱项的解决之道。 \ No newline at end of file