From f75c7c0f7100fd6f0928872018a31c55dc0b3445 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Sun, 14 Jun 2020 22:54:45 +0800 Subject: [PATCH 01/12] week01 assignment-1 --- Week01/1.twoSum.js | 96 ++++++++++++++++++++++++++++++++++++++++++ Week01/283-moveZero.js | 93 ++++++++++++++++++++++++++++++++++++++++ Week01/NOTE.md | 59 +++++++++++++++++++++++++- 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 Week01/1.twoSum.js create mode 100644 Week01/283-moveZero.js diff --git a/Week01/1.twoSum.js b/Week01/1.twoSum.js new file mode 100644 index 000000000..6b6e6833d --- /dev/null +++ b/Week01/1.twoSum.js @@ -0,0 +1,96 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + * + * https://leetcode-cn.com/problems/two-sum/description/ + * + * algorithms + * Easy (48.57%) + * Likes: 8428 + * 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[]} + */ + +/* @sponge + 1.暴力求解,两次遍历 + 时间复杂度:O(n^2) + 空间复杂度:O(1) +*/ +var twoSum = function(nums, target) { + for(let i = 0 ;i< nums.length ;i++){ + for(let j = i+1 ;j< nums.length ;j++){ + if(nums[i]+nums[j]==target) + return [i,j] + } + } +}; + +/* + 2.一遍哈希表 + 时间复杂度:O(n) + 空间复杂度:O(n) + runtime:64ms beats 94.44% + memory usage:34.1MB beats 97.46% +*/ +var twoSum = function(nums, target) { + let hash = {}; + for(let i = 0;i= 0; i--) { + if (nums[i] === 0) { + nums.splice(i, 1); + nums.push(0); + } + } +}; + +/* + 2.两次遍历,利用双指针,指针j存储当前下标,遍历数组,非0的数就赋值给数组 + 的当前下标,直到非0的数遍历完,接着将当前下标后的所有位置置0 + 时间复杂度: O(n) + 空间复杂度: O(1) +*/ +var moveZeroes = function(nums) { + if(nums == null || nums.length==0) return; + + let j=0; + for(let i=0; i< nums.length ; i++){ + if(nums[i] != 0 ){ + if(i!=j) + nums[j] = nums[i]; + j++; + } + } + while(j j) { + nums[j] = nums[i]; + nums[i] = 0; + } + j++; + } + } +} +// @lc code=end \ No newline at end of file diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 50de30414..ff81c0090 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -1 +1,58 @@ -学习笔记 \ No newline at end of file +学习笔记 + +**时间、空间复杂度** + +------ + +主要用**Big O notation**来表示 + +- O(1): Constant Complexity 常数复杂度 +- O(log n): Logarithmic Complexity 对数复杂度 +- O(n): Linear Complexity 线性时间复杂度 +- O(n^2): N square Complexity 平方 +- O(n^3): N cubic Complexity 立方 +- O(2^n): Exponential Growth 指数 +- O(n!): Factorial 阶乘 + + + +### 一维基础数据结构 + +- 数组array +- 链表linked list +- 跳表skip list + +#### **数组array** + +| 操作 | 时间复杂度 | 备注 | +| :------ | :--------- | ------------------------------------------------------------ | +| prepend | O(1) | 注:正常情况下应该是O(n),但是可以进行特殊优化到O(1)。采用的方式是申请稍大一些的内存空,然后再数组最开始预留一部分空间,然后prepend时把第一个下标前移一个位置即可 | +| append | O(1) | | +| loopup | O(1) | | +| insert | O(n) | | +| delete | O(n) | | + +#### **链表linked list** + +| 操作 | 时间复杂度 | 备注 | +| :------ | :--------- | ---- | +| prepend | O(1) | | +| append | O(1) | | +| loopup | O(n) | | +| insert | O(1) | | +| delete | O(1) | | + +#### 跳表skip list + +跳表对标的是平衡树(AVL Tree)和二分查找,是一种插入、删除、搜索都是O(log n)的数据结构。它最大的优势是原理简单、容易实现、方便扩展、效率高。因此在一些热门的项目里用来代替平衡树,如Redis、LevelDB等。 + +**注:只能用于元素有序的情况。** + +**时间复杂度:**O(log n) + + + +#### **工程中的应用** + +- LRU Cache - Linked list +- Redis - Skip List \ No newline at end of file From 5631c6f02b6a39ee0ada334ea99d2b4b49ff44d9 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Mon, 15 Jun 2020 13:46:36 +0800 Subject: [PATCH 02/12] update week01 assignment-2 --- Week01/{1.twoSum.js => 1-twoSum.js} | 0 Week01/66-plusOne.js | 88 +++++++++++++++++++++++++++++ Week01/88-mergeTwoArray.js | 66 ++++++++++++++++++++++ 3 files changed, 154 insertions(+) rename Week01/{1.twoSum.js => 1-twoSum.js} (100%) create mode 100644 Week01/66-plusOne.js create mode 100644 Week01/88-mergeTwoArray.js diff --git a/Week01/1.twoSum.js b/Week01/1-twoSum.js similarity index 100% rename from Week01/1.twoSum.js rename to Week01/1-twoSum.js diff --git a/Week01/66-plusOne.js b/Week01/66-plusOne.js new file mode 100644 index 000000000..a462ce29b --- /dev/null +++ b/Week01/66-plusOne.js @@ -0,0 +1,88 @@ +/* + * @lc app=leetcode.cn id=66 lang=javascript + * + * [66] 加一 + */ + +// @lc code=start +/** + * @param {number[]} digits + * @return {number[]} + */ + +/**@sponge + * 解法一: + * 一开始的想法但是发现Number数据类型整数位超过16位,超过的部分会全部置0 + * 参考@gatsby-23,将Number数据类型改为ES10基本类型BigInt可以表示任意大的整数 + * + * 1.已知传入参数必定为数值型数组 + * 2.将参数转换为字符型(Array.prototype.join()) + * 3.由字符型变为BigInt型(BigInt构造函数)) + * 4.进行专属于BigInt类型的数学计算 + * 5.计算结果再次变为字符型(String构造函数) + * 6.将字符型数值变更为数组(String.prototype.split()) + * + * runtime: 72ms beats46.22% + * memory usage: 33.3MB beats 100% + */ +var plusOne = function(digits) { + let num = BigInt(digits.join('')); + // BigInt基本类型进行数学操作时,需要在数字字面量后加个n + let string = String(num + 1n); + let ary = string.split(''); + + return ary.map(str => Number(str)); +}; + +/**@sponge + * 解法二: + * 1.当末位为9的情况:末位为9需要进位所以当前值置0, + * 继续往前看有没有需要进位的数,没有则前一位+1并return + * 2.当末位不为9的情况:直接将末位+1并return + * 3.当数组里的数都为9的情况:由于遍历的每个数都为9,所以全部置0, + * 然后跳出循环,在数组开头插入1并return + * 时间复杂度:O(n) + * runtime: 72ms beats46.22% + * memory usage: 32.7MB beats 100% + */ +var plusOne = function (digits) { + let len = digits.length; + + for (let i = len - 1; i >= 0; i--) { + if(digits[i] == 9) + digits[i] = 0; + else { + digits[i]++; + return digits; + } + } + //数组都为9的情况 + digits.unshift(1) + return digits; +}; + +/**@other + * 解法三:余数法,类似的解法 + * 1.末位+1,将当前值置为余数,根据余数看是否需要进位: + * 1-余数为0的情况:余数为0说明当前值为10,所以当前值为余数0 + * 2-余数不为0的情况:直接return + * 2.当数组里的数都为9的情况:由于遍历的每个数都为9,所以遍历的每一位余数都为0 + * 最后会跳出循环往下执行,将数组长度+1,每个数置0,第一位置1并return + * 时间复杂度:O(n) + * runtime: 56ms beats98.1% + * memory usage: 32.4MB beats 100% + */ +var plusOne = function(digits) { + const len = digits.length; + for(let i = len - 1; i >= 0; i--) { + digits[i]++; + digits[i] %= 10; + if(digits[i]!=0) + return digits; + } + digits = [...Array(len + 1)].map(_=>0);; + digits[0] = 1; + return digits; +}; + +// @lc code=end \ No newline at end of file diff --git a/Week01/88-mergeTwoArray.js b/Week01/88-mergeTwoArray.js new file mode 100644 index 000000000..7ceb10acd --- /dev/null +++ b/Week01/88-mergeTwoArray.js @@ -0,0 +1,66 @@ +/* + * @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. + */ + + + /**@sponge + * 解法一:双指针 + * runtime:76ms,beats 34.25% + * memory usage:32.9MB,beats 100% + */ +var merge = function(nums1, m, nums2, n) { + let len = nums1.length; + nums1.splice(m,len-m); + console.log(nums1) + let j=0; + let lastIndex=0; + while(jnums1[i]&&i==nums1.length-1){ + nums1.push(cur); + lastIndex = nums1.length-1; + break; + } + + } + j++; + } + +}; + +/**@sponge + * 解法二:先concat->sort + * 注意:需要考虑如果sort参数缺省的话,排序会按照数据的第一位的unicode码来排序 + * runtime:72ms,beats 54.29% + * memory usage:33.2MB,beats 100% + */ +var merge = function(nums1, m, nums2, n) { + let len = nums1.length; + nums1.splice(m,len-m,...nums2); + nums1.sort((a,b)=>a-b); +}; + +// @lc code=end + From ce9034ec19e3a8971e85d8bf1674705105d8d329 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Sun, 21 Jun 2020 23:57:19 +0800 Subject: [PATCH 03/12] week02 assignment-1 --- Week02/1.twpSum.js | 96 +++++++++++++++++++++++++++++ Week02/242.validAnagram.js | 120 +++++++++++++++++++++++++++++++++++++ Week02/NOTE.md | 99 +++++++++++++++++++++++++++++- 3 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 Week02/1.twpSum.js create mode 100644 Week02/242.validAnagram.js diff --git a/Week02/1.twpSum.js b/Week02/1.twpSum.js new file mode 100644 index 000000000..46c39a85b --- /dev/null +++ b/Week02/1.twpSum.js @@ -0,0 +1,96 @@ +/* + * @lc app=leetcode.cn id=1 lang=javascript + * + * [1] 两数之和 + * + * https://leetcode-cn.com/problems/two-sum/description/ + * + * algorithms + * Easy (48.57%) + * Likes: 8428 + * 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[]} + */ + +/* @sponge + 1.暴力求解,两次遍历 + 时间复杂度:O(n^2) + 空间复杂度:O(1) +*/ +// var twoSum = function(nums, target) { +// for(let i = 0 ;i< nums.length ;i++){ +// for(let j = i+1 ;j< nums.length ;j++){ +// if(nums[i]+nums[j]==target) +// return [i,j] +// } +// } +// }; + +/* + 2.一遍哈希表 + 时间复杂度:O(n) + 空间复杂度:O(n) + runtime:64ms beats 94.44% + memory usage:34.1MB beats 97.46% +*/ +// var twoSum = function(nums, target) { +// let hash = {}; +// for(let i = 0;i0){ + sMap[tArr[i]]--; + }else{ + flag = !flag; + break; + } + } + return flag; +}; + +/**@other + * 解法三:哈希映射 + * 初始化 26 个字母数组,遍历字符串 s 和 t + * s 负责在对应位置增加,t 负责在对应位置减少 + * 如果哈希表的值都为 0,则二者是字母异位词 + * + * time:O(n) + * space:O(1) + * runtime:92ms 63.27% + * memory usage:36.4MB 85.71% + */ +var isAnagram = function(s, t) { + if(s.length != t.length) return false; + let alpha = []; + alpha.length = 26; + alpha.fill(0); + let a = 'a'.charCodeAt(0); + for(let i = 0;i=其子结点的值 \ No newline at end of file From 10a43c23a43a61416a02b0fc02ca0f321253e48a Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Sun, 28 Jun 2020 23:56:05 +0800 Subject: [PATCH 04/12] week03 assignment-1 --- Week03/22.generateParenthesis.js | 121 +++++++++++++++++++++++++++++++ Week03/77.combine.js | 95 ++++++++++++++++++++++++ Week03/78.subsets.js | 65 +++++++++++++++++ Week03/NOTE.md | 96 +++++++++++++++++++++++- 4 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 Week03/22.generateParenthesis.js create mode 100644 Week03/77.combine.js create mode 100644 Week03/78.subsets.js diff --git a/Week03/22.generateParenthesis.js b/Week03/22.generateParenthesis.js new file mode 100644 index 000000000..54962626a --- /dev/null +++ b/Week03/22.generateParenthesis.js @@ -0,0 +1,121 @@ +/* + * @lc app=leetcode.cn id=22 lang=javascript + * + * [22] 括号生成 + * + * https://leetcode-cn.com/problems/generate-parentheses/description/ + * + * algorithms + * Medium (75.46%) + * Likes: 1114 + * Dislikes: 0 + * Total Accepted: 139.7K + * Total Submissions: 184.9K + * Testcase Example: '3' + * + * 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 + * + * + * + * 示例: + * + * 输入:n = 3 + * 输出:[ + * ⁠ "((()))", + * ⁠ "(()())", + * ⁠ "(())()", + * ⁠ "()(())", + * ⁠ "()()()" + * ⁠ ] + * + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {string[]} + */ + + /**@other 解法一:递归、深度优先遍历 + * @param left 表示左括号还有多少个没用掉 + * @param right 表示右括号还有多少个没用掉 + * @param result 返回的结果数组 + * @param str 当前递归得到的字符串n + * @param n 需要用到多少个左括号、右括号 + * + * time:O(2^n) + * space:O(n) + * runtime:68ms 74.28% + * memory usage:36.1MB 12.5% + */ +var generateParenthesis = function(n) { + let result = []; + if(n==0) return result; + //teminator + var generate = (left, right, n, str)=>{ + if( left==n && right==n ){ + result.push(str); + return ; + } + + //process logic in current level + if(leftright ) //因为left{ + + } + generate(0,0,n,str); + return result; +}; + + +/**@other 解法三:动态规划 + * + * + */ +function Node(str,left,right){ + this.str = str; + this.left = left; + this.right = right; +} +function ListNode(str,left,right){ + this.str = str; + this.left = left; + this.right = right; +} +var generateParenthesis = function(n) { + let result = []; + let str = ''; + var generate = (left, right, n, str)=>{ + + } + generate(0,0,n,str); + return result; +}; +// @lc code=end + diff --git a/Week03/77.combine.js b/Week03/77.combine.js new file mode 100644 index 000000000..368c7713e --- /dev/null +++ b/Week03/77.combine.js @@ -0,0 +1,95 @@ +/* + * @lc app=leetcode.cn id=77 lang=javascript + * + * [77] 组合 + * + * https://leetcode-cn.com/problems/combinations/description/ + * + * algorithms + * Medium (73.67%) + * Likes: 293 + * Dislikes: 0 + * Total Accepted: 57.4K + * Total Submissions: 77.6K + * 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[][]} + */ + +/**@other + * 解法一:回溯法 + * + * runtime:144ms 45.56% + * memory usage:40.8MB 100% + */ +var combine = function(n, k) { + var result = []; + (function combineSub(start=1,subresult=[]){ + //terminator + if(subresult.length == k){ + result.push([...subresult]); + return; + } + for(var i= start;i<=n;i++){ + subresult.push(i); + combineSub(i+1,subresult); //drill down + subresult.pop(); + } + })(); + return result; +}; + +/**@other + * 解法二:递归合并 + * + * runtime:132ms 58.82% + * memory usage:42.6MB 50% + */ +var combine = function(n, k) { + var result = []; + var subresult = []; + // n==k/k==0 对于两种情况边界 + if( n==k || k == 0){ + var tmp = []; + for(var i = 1;i<=k;i++){ + subresult.push(i); + } + tmp.push(subresult); // 构造如[[x]]的返回形式 + return tmp; + } + // n-1 里选 k-1 个 + var result = combine(n-1,k-1); + // 组合[[x,y]]:第n个数被选择=>[[x,y,n]] + result.map((arr) => arr.push(n)); + // n-1 里选 k 个 + var tmp = combine(n-1,k); + // 第二种情况的结果打散装入返回数组里去 + // result:[[x,y]]、tmp[[x1,y1]] + // ==> result:[[x,y],[x1,y1]] + tmp.map((arr) => result.push(arr)); + // 返回每次成功组装的结果集,最后递归终点则返回所以可能组合 + return result; +}; +// @lc code=end + diff --git a/Week03/78.subsets.js b/Week03/78.subsets.js new file mode 100644 index 000000000..c194f9166 --- /dev/null +++ b/Week03/78.subsets.js @@ -0,0 +1,65 @@ +/* + * @lc app=leetcode.cn id=78 lang=javascript + * + * [78] 子集 + * + * https://leetcode-cn.com/problems/subsets/description/ + * + * algorithms + * Medium (77.33%) + * Likes: 619 + * Dislikes: 0 + * Total Accepted: 100.1K + * Total Submissions: 129.3K + * Testcase Example: '[1,2,3]' + * + * 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 + * + * 说明:解集不能包含重复的子集。 + * + * 示例: + * + * 输入: nums = [1,2,3] + * 输出: + * [ + * ⁠ [3], + * [1], + * [2], + * [1,2,3], + * [1,3], + * [2,3], + * [1,2], + * [] + * ] + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @return {number[][]} + */ + +/**@sponge + * 解法一:迭代 + * time:O(n*n^2) + * space:O(n*n^2) + * runtime:76ms 59.02% + * memory usage:37.3MB 25% + */ +var subsets = function(nums) { + let result = [[]]; + if(nums.length == 0) return result; + + for(num of nums){ + let subset = []; + for(subresult of result){ + let tempresult = [...subresult,num]; //把结果数组中的每个子集都和nums数组的每个数组合 + subset.push(tempresult); + } + result = [...result,...subset]; //将子集加入到结果数组中 + } + return result; +}; +// @lc code=end + diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 50de30414..2d48e228f 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -1 +1,95 @@ -学习笔记 \ No newline at end of file +## **递归-Recursion** + +本质是循环,通过函数体来不断调用自己进行的循环 + +```python +def recursion(level,param1,param2,...): + #recursion terminator-递归终结条件 + if level>MAX_LEVEL: + process_result + return + #process logic in current level-处理当前层逻辑 + process(level,data...) + #drill down-下探到下一层 + self.recursion(level+1,p1,...) + #reverse the current level status if needed-清理当前层 +``` + +**思维要点** + +1. 不要人肉进行递归,就是自己画递归过程(最大误区) +2. 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题) +3. 数学归纳法思维 + +**!!!递归的本质就是要找重复性** + +- 最近重复性 + +- - 分治 + - 回溯 + +- 最优重复性 + +- - 动态规划 + +- + +## **回溯算法-Backtracking** + +实际上是一种特殊的递归,本质就是要找重复性。 + +不断在每一层去试,每一层有不同的方法,典型问题:八皇后、数独 + +- 找到一个可能存在的正确的答案 +- 在尝试了所有可能的分步方法后宣告该问题没有答案,就取消上一步或者上几步的计算 +- 再通过其他可能的分步方式 + +worst:O(2^n) + +**题目:** + +50. Pow(x, n) + +78. ~~子集~~ + + + + + +## **分治算法-Divide&Conquer** + +实际上是一种特殊的递归,本质就是要找重复性以及分解问题和最后组合子问题的结果, + +与泛型递归的不同在于最后要将子结果再进行一次组合。 + +```javascript +function divide_conquer(problem,param1,...){ + //recursion teminator,即问题解决完了 + if(problem==null){ + print_result + return + } + + //prepare data,处理当前逻辑,即 把大问题拆分成子问题 + data = prepare_data(problem); + subproblems = split_problem(problem,data); + + //conquer subproblems,drill down,解决更细节的子问题 + subresult1 = divide_conquer(subproblems[0],p1,...); + subresult2 = divide_conquer(subproblems[1],p1,...); + subresult3 = divide_conquer(subproblems[2],p1,...); + + //process and generate the final result + result = process_result(subresult1,subresult2,subresult3,...); + + //revert the current level states +} +``` + +**题目:** + +169. 多数元素 + +17. 电话号码的字母组合 + +51. N皇后 \ No newline at end of file From 4abf199e381726df5979969e9c345af3c4f0a5ad Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Mon, 6 Jul 2020 00:00:19 +0800 Subject: [PATCH 05/12] week04 assignment-1 --- Week03/NOTE.md | 4 +- Week04/102.levelOrder.js | 112 +++++++++++++++++++ Week04/127.ladderLength.js | 186 +++++++++++++++++++++++++++++++ Week04/22.generateParenthesis.js | 121 ++++++++++++++++++++ Week04/33.search.js | 80 +++++++++++++ 5 files changed, 501 insertions(+), 2 deletions(-) create mode 100644 Week04/102.levelOrder.js create mode 100644 Week04/127.ladderLength.js create mode 100644 Week04/22.generateParenthesis.js create mode 100644 Week04/33.search.js diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 2d48e228f..fde9bbb48 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -38,10 +38,10 @@ def recursion(level,param1,param2,...): 实际上是一种特殊的递归,本质就是要找重复性。 -不断在每一层去试,每一层有不同的方法,典型问题:八皇后、数独 +回溯就是不断在每一层去试,每一层有不同的方法,典型问题:八皇后、数独 - 找到一个可能存在的正确的答案 -- 在尝试了所有可能的分步方法后宣告该问题没有答案,就取消上一步或者上几步的计算 +- 在尝试了所有可能的分步方法后如果该问题没有答案,就取消上一步或者上几步的计算 - 再通过其他可能的分步方式 worst:O(2^n) diff --git a/Week04/102.levelOrder.js b/Week04/102.levelOrder.js new file mode 100644 index 000000000..49d760160 --- /dev/null +++ b/Week04/102.levelOrder.js @@ -0,0 +1,112 @@ +/* + * @lc app=leetcode.cn id=102 lang=javascript + * + * [102] 二叉树的层序遍历 + * + * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/description/ + * + * algorithms + * Medium (62.88%) + * Likes: 555 + * Dislikes: 0 + * Total Accepted: 153.7K + * Total Submissions: 243.9K + * Testcase Example: '[3,9,20,null,null,15,7]' + * + * 给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。 + * + * + * + * 示例: + * 二叉树:[3,9,20,null,null,15,7], + * + * ⁠ 3 + * ⁠ / \ + * ⁠ 9 20 + * ⁠ / \ + * ⁠ 15 7 + * + * + * 返回其层次遍历结果: + * + * [ + * ⁠ [3], + * ⁠ [9,20], + * ⁠ [15,7] + * ] + * + * + */ + +// @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[][]} + */ +/**@other + * 解法一:BFS(层序遍历) + * time:O(n),每个点进队出队各一次 + * space:O(n),队列中元素的个数不超过n个 + * runtime:64ms 93.82% + * memory usage:37.2MB 8.33% + */ +var levelOrder = function (root) { + if (!root) return []; + + let queue = []; + let result = []; + queue.push(root); //将根节点放如队列中 + + while (queue.length) { + let size = queue.length; //获取当前队列的长度,即这一层的节点个数 + let level = []; //存放每一层的所有结点 + + //循环每一层的结点 + for (let i = 0; i < size; i++) { + let node = queue.shift(); + level.push(node.val); //将队列中的元素都取出来(即获取这一层的节点),放到当前层level中 + //如果节点的左右子树不为空,则放如队列中 + node.left && queue.push(node.left); + node.right && queue.push(node.right); + } + result.push(level); //将leve当前层的所有结点加入结果数组中 + } + return result; +}; + +/**@other + * 解法二:DFS(递归) + * 由于DFS不是按层访问,所以要加个level记录当前层数 + * 当递归到新节点要把该节点放如level对应的列表的末尾 + * 当遍历到新的一层level,而最终结果 result 中还没有创建 level 对应的列表时, + * 则在 result 中新建一个列表用来保存该 level 的所有节点. + * + * time:O(n),每个点进队出队各一次 + * space:O(h),h是树的高度 + * runtime:84ms 18.9% + * memory usage:37.4MB 8.33% + */ +var levelOrder = function(root) { + let result = []; + if(!root) return result; + + function dfs(level,root){ + if(result.length < level) result.push([]); + //将当前节点的值加入result中,level表示当前层 + result[level-1].push(root.val); + //递归的处理左子树,右子树,同时将层数level+1 + root.left && dfs(level+1,root.left); + root.right && dfs(level+1,root.right); + } + + dfs(1,root); + return result; +}; +// @lc code=end \ No newline at end of file diff --git a/Week04/127.ladderLength.js b/Week04/127.ladderLength.js new file mode 100644 index 000000000..17d9de0d8 --- /dev/null +++ b/Week04/127.ladderLength.js @@ -0,0 +1,186 @@ +/* + * @lc app=leetcode.cn id=127 lang=javascript + * + * [127] 单词接龙 + * + * https://leetcode-cn.com/problems/word-ladder/description/ + * + * algorithms + * Medium (41.81%) + * Likes: 364 + * Dislikes: 0 + * Total Accepted: 47.5K + * Total Submissions: 111.4K + * Testcase Example: '"hit"\n"cog"\n["hot","dot","dog","lot","log","cog"]' + * + * 给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord + * 的最短转换序列的长度。转换需遵循如下规则: + * + * + * 每次转换只能改变一个字母。 + * 转换过程中的中间单词必须是字典中的单词。 + * + * + * 说明: + * + * + * 如果不存在这样的转换序列,返回 0。 + * 所有单词具有相同的长度。 + * 所有单词只由小写字母组成。 + * 字典中不存在重复的单词。 + * 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 + * + * + * 示例 1: + * + * 输入: + * beginWord = "hit", + * endWord = "cog", + * wordList = ["hot","dot","dog","lot","log","cog"] + * + * 输出: 5 + * + * 解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", + * ⁠ 返回它的长度 5。 + * + * + * 示例 2: + * + * 输入: + * beginWord = "hit" + * endWord = "cog" + * wordList = ["hot","dot","dog","lot","log"] + * + * 输出: 0 + * + * 解释: endWord "cog" 不在字典中,所以无法进行转换。 + * + */ + +// @lc code=start +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + +/**@other + * 解法一:BFS + * time:O(n*m),n是wordList.length,m是beginword.length + * space:O(n*m),要在allComboMap记录每个单词的m个通用状态,访问数组的大小是n + * runtime:136ms 76.83% + * memory usage:50.9MB 50% + */ +var ladderLength = function(beginWord, endWord, wordList) { + if(!wordList.includes(endWord)) return 0; //如果wordList不存在endWord,返回0 + + let allComboMap = {}; //所有组合map + let len = beginWord.length; + + // 对 wordList 做预处理,找出所有的通用状态 + for (let i = 0;i-1) return ans; + //from End + ans = visitWordNode(queueEnd,visitedEnd,visitedBegin); + if (ans>-1) return ans; + } + + function visitWordNode(queue,visited,otherVisited){ + let [word,level] = queue.shift(); + for (let i = 0;i{ + if( left==n && right==n ){ + result.push(str); + return ; + } + + //process logic in current level + if(leftright ) //因为left{ + + } + generate(0,0,n,str); + return result; +}; + + +/**@other 解法三:动态规划 + * + * + */ +function Node(str,left,right){ + this.str = str; + this.left = left; + this.right = right; +} +function ListNode(str,left,right){ + this.str = str; + this.left = left; + this.right = right; +} +var generateParenthesis = function(n) { + let result = []; + let str = ''; + var generate = (left, right, n, str)=>{ + + } + generate(0,0,n,str); + return result; +}; +// @lc code=end + diff --git a/Week04/33.search.js b/Week04/33.search.js new file mode 100644 index 000000000..2af93c42c --- /dev/null +++ b/Week04/33.search.js @@ -0,0 +1,80 @@ +/* + * @lc app=leetcode.cn id=33 lang=javascript + * + * [33] 搜索旋转排序数组 + * + * https://leetcode-cn.com/problems/search-in-rotated-sorted-array/description/ + * + * algorithms + * Medium (37.97%) + * Likes: 811 + * Dislikes: 0 + * Total Accepted: 139.3K + * Total Submissions: 364.7K + * Testcase Example: '[4,5,6,7,0,1,2]\n0' + * + * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 + * + * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 + * + * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 + * + * 你可以假设数组中不存在重复的元素。 + * + * 你的算法时间复杂度必须是 O(log n) 级别。 + * + * 示例 1: + * + * 输入: nums = [4,5,6,7,0,1,2], target = 0 + * 输出: 4 + * + * + * 示例 2: + * + * 输入: nums = [4,5,6,7,0,1,2], target = 3 + * 输出: -1 + * + */ + +// @lc code=start +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + +/**@sponge + * 解法一:二分查找 + * 根据nums[mid]与nums[l]的关系判断mid在左段还是右段 + * 再判断target是在mid的左边还是右边 + * time:O(logn) + * space:O(1) + */ +var search = function(nums, target) { + let l = 0; + let r = nums.length-1; + + while (l<=r) { + let mid = Math.floor((l+r)/2); + if (target==nums[mid]) return mid; + // 先根据 nums[mid] 与 nums[lo] 的关系判断 mid 是在左段还是右段 + if (nums[mid] >= nums[l]) { + // 再判断 target 是在 mid 的左边还是右边,从而调整左右边界 lo 和 hi + if (target >= nums[l] && target < nums[mid]) { + r = mid - 1; + } else { + l = mid + 1; + } + } else { + if (target > nums[mid] && target <= nums[r]) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + } + return -1; +}; +// @lc code=end + From a2f0d5917bc57258ca97a2ff1aa842dacb058266 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Mon, 6 Jul 2020 12:38:50 +0800 Subject: [PATCH 06/12] week --- Week04/NOTE.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 1 deletion(-) diff --git a/Week04/NOTE.md b/Week04/NOTE.md index 50de30414..0a5dd0595 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -1 +1,170 @@ -学习笔记 \ No newline at end of file +## **广度优先搜索BFS** + +按照高度顺序一层一层的访问整棵树,高层次节点将会比低层次节点先被访问到。 + +用于找出两者之间的最短路径,最短路径问题,例如: + +- 编写国际跳棋AI,计算最少走多少步就可获胜 +- 编写拼写检查器,计算最少编辑多少个地方就可将拼错的单词改成正确的单词,如将READED改为READER需要编辑一个地方。 +- 根据人际关系网络找到关系最近的医生。 + +```python +#代码模板 +visited = set() +def BFS(graph, start, end): + queue = [] + queue.append([start]) + visited.add(start) + while queue: + node = queue.pop() + visited.add(node) + process(node) + nodes = generate_related_nodes(node) + queue.push(nodes) + #other processing work +``` + + + + + +## **深度优先搜索DFS** + +- 采用深度作为优先级,以便从根开始一直到达某个确定的叶子,然后再返回根到达另一个分支。 +- 深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为前序遍历,中序遍历和后序遍历。 + +![DFS Postorder Bottom Top Lett DFS Preorder Top Bottom Left Right DFS Inorder Left Right BFS Left Roht Top Bottom ](data:image/png;base64,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) + +二叉树的遍历代码: + +```python +#代码模板 +def dfs(node): + if node in visited: + # already visited + return + visited.add(node) + # process current node + # ... logic here + dfs(node.left) + dfs(node.right) +``` + +多叉树的遍历代码: + +```python +#递归写法 +visited = set() +def dfs(node, visited): + if node in visited: # terminator,先判断当前节点是否被访问过 + # already visited + return + visited.add(node) + # ... process current node here + if next_node in node.children(): + if not next_node in visited: + dfs(next node, visited) +``` + +```python +#非递归写法,实际上就是手动维护一个栈 +def DFS(self, tree): + if tree.root is None: + return [] + visited, stack = [], [tree.root] + while stack: + node = stack.pop() + visited.add(node) + process (node) + nodes = generate_related_nodes(node) + stack.push(nodes) + # ...other processing work +``` + + + +## **狄克斯特拉算法** + +用于找出两者之间的最快路径,最快路径问题,步骤如下 + +1. 找在最短时间内到达的节点 +2. 对于上述找到的节点的邻居,检查是否有前往他们的更短路径,如果有,更新该节点的邻居的开销, +3. 重复这个过程,直到对图中的每个节点都这样做了 +4. 计算最终路径 + +**使用场景** + +要计算非加权图中的最短路径,可使用广度优先搜索 + +要计算加权图中的最短路径,可使用狄克斯科拉算法 + +但如有负权边,就不能用狄克斯特拉算法 + +要计算包含负权边的图的最短路径,可使用贝尔曼-福德算法 + + + +## **贪心算法Greedy** + +每步都采取在当前状态下最好或最优的(最有利)选择做法,从而希望导致结果也是全局最好或最优的。 + +一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好方法。由于贪心法的高效性以及其所求得得答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些结果不那么精确的问题。 + +- 近似算法 + +- - 判断近似算法的优劣标准如下: + + - - 速度有多快 + - 得到的近似解与最优解的接近程度 + +可以用来求图中的最小生成树、哈夫曼编码等。 + +**使用贪心算法的场景** + +问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。这种子问题最优解称为最优子结构。 + +**贪心算法与动态规划的不同** + +贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存之前的运算结果,并根据之前的结果对当前进行选择,有回退功能。 + +- 贪心:当前局部最优判断 +- 回溯:能够回退 +- 动态规划:最优判断+回退 + + + +#### [33. 搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/) + +暴力解法 + +思路:将给定的数组还原成排序数组,再进行二分查找。 + +但如果单纯用暴力解法还原数组需要从头到尾遍历所以需要O(n)的时间复杂度,可用下面方法简化成O(logn) + +1.找到第一个无序的位置还原成排序数组,用O(logn)实现。 + +2.再用二分查找O(logn)。 + +```javascript +var search = function(nums, target) { + if(!nums.length) return -1; + + let l = 0; + let r = nums.length - 1; + let mid = 0; + + //暴力还原排序数组,即找到第一个无序的位置(用二分查找实现) + while (l<=r) { + mid = Math.floor((l+r)/2); + if (nums[mid]>nums[mid+1]) + break; + if (nums[mid]>=nums[l]) { + l = mid + 1; + }else { + r = mid - 1; + } + } + return -1; +} +``` + From f4e4e241fe77f0cc03d2eb80d72b6aaa9d3d4436 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Mon, 6 Jul 2020 17:10:58 +0800 Subject: [PATCH 07/12] week4 assignment-2 update NOTE.md&T33 --- Week04/33.search.js | 87 ++++++++++++++++++++++++++++++++++----------- Week04/NOTE.md | 44 ++++++++--------------- 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/Week04/33.search.js b/Week04/33.search.js index 2af93c42c..3920d64a5 100644 --- a/Week04/33.search.js +++ b/Week04/33.search.js @@ -42,39 +42,86 @@ * @param {number} target * @return {number} */ +/**@sponge + * 解法一:暴力 + * 思路:将给定的数组还原成排序数组,再进行二分查找。 + * 但如果单纯用暴力解法还原数组需要从头到尾遍历所以需要O(n)的时间复杂度,可用下面方法简化成O(logn) + * 1.找到第一个无序的位置还原成排序数组,用O(logn)实现, + * 2.再用二分查找O(logn) + * time:O(logn) + * space:O(1) + * runtime:72ms 56% + * memory usage:33.3MB 100% + */ +var search = function (nums, target) { + if (!nums.length) return -1; + + let l = 0; + let r = nums.length - 1; + let mid = 0; + //暴力还原排序数组,找到第一个无序的位置(用二分查找实现) + while (l < r) { + mid = Math.floor((l + r) / 2); + if (nums[mid] > nums[r]) { + l = mid + 1; + } else { + r = mid; + } + } + //找到第一个无序的位置,即最小的值 + let min = l; + l = 0; + r = nums.length - 1; + //判断target是否在min~r之间 + if (target <= nums[r]) l = min; + else r = min - 1; + //按二分查找模板搜索target + while (l <= r) { + mid = Math.floor((l + r) / 2); + if (nums[mid] == target) return mid; + if (nums[mid] < target) l = mid + 1; + else r = mid - 1; + } + return -1; +}; /**@sponge - * 解法一:二分查找 - * 根据nums[mid]与nums[l]的关系判断mid在左段还是右段 + * 解法二:二分查找 + * 根据nums[mid]与nums[r]的关系判断mid在左段还是右段, + * 或者nums[mid]与nums[l]的关系判断mid在左段还是右段 * 再判断target是在mid的左边还是右边 * time:O(logn) * space:O(1) + * runtime:72ms + * memory usage:33.1MB */ -var search = function(nums, target) { +var search = function (nums, target) { + if (!nums.length) return -1; + let l = 0; - let r = nums.length-1; + let r = nums.length - 1; - while (l<=r) { - let mid = Math.floor((l+r)/2); - if (target==nums[mid]) return mid; - // 先根据 nums[mid] 与 nums[lo] 的关系判断 mid 是在左段还是右段 - if (nums[mid] >= nums[l]) { - // 再判断 target 是在 mid 的左边还是右边,从而调整左右边界 lo 和 hi - if (target >= nums[l] && target < nums[mid]) { - r = mid - 1; + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (target == nums[mid]) return mid; + // 先根据 nums[mid] 与 nums[r] 的关系判断 mid 是在左段还是右段 + if (nums[mid] < nums[r]) { + // 再判断 target 是在 mid 的左边还是右边,从而调整左右边界 l 和 r + // mid ~ r 之间有序 + if (target > nums[mid] && target <= nums[r]) { + l = mid + 1; } else { - l = mid + 1; + r = mid - 1; } } else { - if (target > nums[mid] && target <= nums[r]) { - l = mid + 1; + if (target < nums[mid] && target >= nums[l]) { + r = mid - 1; } else { - r = mid - 1; + l = mid + 1; } - } - + } + } return -1; }; -// @lc code=end - +// @lc code=end \ No newline at end of file diff --git a/Week04/NOTE.md b/Week04/NOTE.md index 0a5dd0595..cc2d7e075 100644 --- a/Week04/NOTE.md +++ b/Week04/NOTE.md @@ -111,10 +111,8 @@ def DFS(self, tree): 一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好方法。由于贪心法的高效性以及其所求得得答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些结果不那么精确的问题。 - 近似算法 - -- - 判断近似算法的优劣标准如下: - - - - 速度有多快 + - 判断近似算法的优劣标准如下: + - 速度有多快 - 得到的近似解与最优解的接近程度 可以用来求图中的最小生成树、哈夫曼编码等。 @@ -133,38 +131,26 @@ def DFS(self, tree): -#### [33. 搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/) - -暴力解法 - -思路:将给定的数组还原成排序数组,再进行二分查找。 +#### **练习:** -但如果单纯用暴力解法还原数组需要从头到尾遍历所以需要O(n)的时间复杂度,可用下面方法简化成O(logn) +使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 +**思路:** -1.找到第一个无序的位置还原成排序数组,用O(logn)实现。 - -2.再用二分查找O(logn)。 +- 找到第一个无序的地方,即找到最小的值 ```javascript -var search = function(nums, target) { - if(!nums.length) return -1; - +var search = function(nums) { let l = 0; let r = nums.length - 1; - let mid = 0; - - //暴力还原排序数组,即找到第一个无序的位置(用二分查找实现) - while (l<=r) { + let mid = Math.floor((l+r)/2); + while (l < r) { + //如果nums[mid] > nums[r],说明mid~r之间无序,所以l要mid的右边移动继续查找最小值 + if (nums[mid] > nums[r]) l=mid+1; + //否则r + else r = mid; mid = Math.floor((l+r)/2); - if (nums[mid]>nums[mid+1]) - break; - if (nums[mid]>=nums[l]) { - l = mid + 1; - }else { - r = mid - 1; - } } - return -1; -} + return mid; +}; ``` From f630f6f31c105309800bf42515ac8429d3579c4b Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Mon, 20 Jul 2020 00:01:34 +0800 Subject: [PATCH 08/12] week06 assignment-1 --- ...57\345\276\204\346\200\273\345\222\214.js" | 24 +++ ...61\345\255\220\345\272\217\345\210\227.js" | 68 +++++++++ ...17\350\267\257\345\276\204\345\222\214.js" | 89 +++++++++++ ...15\345\220\214\350\267\257\345\276\204.js" | 105 +++++++++++++ ...0.\347\210\254\346\245\274\346\242\257.js" | 140 ++++++++++++++++++ 5 files changed, 426 insertions(+) create mode 100644 "Week06/112.\350\267\257\345\276\204\346\200\273\345\222\214.js" create mode 100644 "Week06/1143. \346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.js" create mode 100644 "Week06/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.js" create mode 100644 "Week06/62. \344\270\215\345\220\214\350\267\257\345\276\204.js" create mode 100644 "Week06/70.\347\210\254\346\245\274\346\242\257.js" diff --git "a/Week06/112.\350\267\257\345\276\204\346\200\273\345\222\214.js" "b/Week06/112.\350\267\257\345\276\204\346\200\273\345\222\214.js" new file mode 100644 index 000000000..a56d1452c --- /dev/null +++ "b/Week06/112.\350\267\257\345\276\204\346\200\273\345\222\214.js" @@ -0,0 +1,24 @@ +/* + * @lc app=leetcode.cn id=112 lang=javascript + * + * [112] 路径总和 + */ + +// @lc code=start +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @param {number} sum + * @return {boolean} + */ +var hasPathSum = function(root, sum) { + +}; +// @lc code=end + diff --git "a/Week06/1143. \346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.js" "b/Week06/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 000000000..23072893d --- /dev/null +++ "b/Week06/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,68 @@ +/** + * 难度:中等 + * 题目:1143. 最长公共子序列 + * 给定两个字符串 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 + 输入的字符串只含有小写英文字符。 + + */ +/** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + +/**@sponge + * 解法一:DP + * 1.绘制网格 + * 如果两个字母不同,就选择上方或左方邻居中较大的那个 + * 如果两个字母相同,就选择左上方单元格的值+1 + * 伪代码: + * if word_a[i] == word_b[j]: + * cell[i][j] = cell[i-1][j-1] + 1 + * else + * cell[i][j] = max(cell[i-1][j],cell[i][j-1]) + * + * time:O(n*m),n和m分别为text1和text2的长度. + * space:O(n*m),n和m分别为text1和text2的长度. + * runtime:180ms 8% + * memory usage:51.5MB 33% + */ +var longestCommonSubsequence = function(text1, text2) { + let n = text1.length; + let m = text2.length; + + let dp = Array.from(new Array(n+1),()=>new Array(m+1).fill(0)); + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= m; j++) { + dp[i][j] = text1[i-1]==text2[j-1] ? + dp[i-1][j-1] + 1 : + Math.max(dp[i-1][j],dp[i][j-1]); + } + } + return dp[n][m]; +}; \ No newline at end of file diff --git "a/Week06/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.js" "b/Week06/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.js" new file mode 100644 index 000000000..62ab5afc0 --- /dev/null +++ "b/Week06/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.js" @@ -0,0 +1,89 @@ +/** + * 难度:中等 + * 题目:120. 三角形最小路径和 + * 给定一个三角形,找出自顶向下的最小路径和。 + * 每一步只能移动到下一行中相邻的结点上。 + * 相邻的结点 在这里指的是 下标 与 上一层结点下标 相同 + * 或者等于 上一层结点下标 + 1 的两个结点。 + + 例如,给定三角形: + [ + [2], + [3,4], + [6,5,7], + [4,1,8,3] + ] + 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + + 说明: + 如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 + */ + +/**@other + * 解法一:递归(超时,自顶向下) + * time:O(n^2) + * space:O(n) + */ +var minimumTotal = function(triangle) { + function dfs(i,j){ + if(i==triangle.length) return 0; + return Math.min(dfs(i+1,j),dfs(i+1,j+1))+triangle[i][j]; + } + return dfs(0,0); +}; + +/**@other + * 解法二:递归+记忆化(自顶向下) + * time:O(n*n),n为三角形的行数 + * space:O(n*n),n为三角形的行数 + */ +var minimumTotal = function(triangle) { + let memo = Array.from(new Array(triangle.length),()=>new Array(triangle.length)); + function dfs(i,j){ + if(i==triangle.length) return 0; + if(memo[i][j] === undefined) memo[i][j] = Math.min(dfs(i+1,j),dfs(i+1,j+1))+triangle[i][j]; + return memo[i][j]; + } + return dfs(0,0); +}; + +/**@other + * 解法三:dp(自底向上的递推) + * time:O(n^2),n为三角形的行数 + * space:O(n^2),n为三角形的行数 + */ +var minimumTotal = function(triangle) { + let dp = Array.from(new Array(triangle.length),(item,index)=>new Array(triangle[index].length)); + // 初始化dp最后一行的值 + for (let j = 0; j < dp.length; j++) { + dp[dp.length-1][j] = triangle[dp.length-1][j]; + } + // 从倒数第二列开始迭代 + for (let i = dp.length - 2; i >= 0; i--) { + for (let j = 0; j <= i; j++) { + dp[i][j] = Math.min(dp[i+1][j],dp[i+1][j+1]) + triangle[i][j]; + } + } + return dp[0][0]; +}; + +/**@other + * 解法四:dp(自底向上的递推)+空间优化 + * time:O(n^2),n为三角形的行数 + * space:O(n),n为三角形的行数 + */ +var minimumTotal = function(triangle) { + const dp = new Array(triangle.length); + // base case 是最后一行 + for (let i = 0; i < dp.length; i++) { + dp[i] = triangle[triangle.length - 1][i]; + } + // 从倒数第二列开始迭代 + for (let i = dp.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]; +}; + diff --git "a/Week06/62. \344\270\215\345\220\214\350\267\257\345\276\204.js" "b/Week06/62. \344\270\215\345\220\214\350\267\257\345\276\204.js" new file mode 100644 index 000000000..b62ae60a4 --- /dev/null +++ "b/Week06/62. \344\270\215\345\220\214\350\267\257\345\276\204.js" @@ -0,0 +1,105 @@ +/** + * 难度:中等 + * 题目:62. 不同路径 + * 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + * 机器人每次只能向下或者向右移动一步。 + * 机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + * 问总共有多少条不同的路径? + * + 例如,上图是一个7 x 3 的网格。有多少可能的路径? + + 示例 1: + 输入: m = 3, n = 2 + 输出: 3 + 解释: + 从左上角开始,总共有 3 条路径可以到达右下角。 + 1. 向右 -> 向右 -> 向下 + 2. 向右 -> 向下 -> 向右 + 3. 向下 -> 向右 -> 向右 + + 示例 2: + 输入: m = 7, n = 3 + 输出: 28 + + 提示: + 1 <= m, n <= 100 + 题目数据保证答案小于等于 2 * 10 ^ 9 + + */ +/**@sponge + * 解法一:递归(超时) + * time:O(n*m) + * space:O(n) + */ +var uniquePaths = function(m, n) { + function dfs(i,j){ + if(i==m-1 || j==n-1) return 1; + return dfs(i+1,j) + dfs(i,j+1); + } + return dfs(0,0); +}; + +/**@sponge + * 解法二:递归 + 记忆化(超时) + * time:O(n*m) + * space:O(n) + */ +var uniquePaths = function(m, n) { + let memo = Array.from(new Array(m),()=>new Array(n)); + console.log(memo); + function dfs(i,j){ + if(i==m-1 || j==n-1) memo[i][j] = 1; + if(memo[i][j]===undefined) memo[i][j] = dfs(i+1,j) + dfs(i,j+1); + console.log(memo); + return memo[i][j]; + } + return dfs(0,0); +}; + +/**@sponge + * 解法三:dp + * time:O(n*m) + * space:O(n*m) + * time:104ms 6% + * memory usage:37.2MB 33% + */ +//方式一: +var uniquePaths = function(m, n) { + let dp = Array.from(new Array(m),()=>new Array(n)); + for (let i = m - 1; i >= 0; i--) { + for (let j = n - 1; j >= 0; j--) { + if(i==m-1||j==n-1) dp[i][j] = 1; + else dp[i][j] = dp[i+1][j] + dp[i][j+1]; + } + } + return dp[0][0]; +}; +//方式二: +var uniquePaths = function(m, n) { + let dp = Array.from(new Array(m),()=>new Array(n)); + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (i==0 || j==0) dp[i][j] = 1; + else dp[i][j] = dp[i-1][j] + dp[i][j-1]; + } + } + return dp[m-1][n-1]; +}; + +/**@sponge + * 解法四:dp + 空间优化 + * time:O(n*m) + * space:O(n) + * time:80ms 6% + * memory usage:37.5MB 33% + */ +var uniquePaths = function(m, n) { + let dp = new Array(n).fill(0); + dp[0] = 1; + for (let i = 0; i < m; i++) { + for (let j = 1; j < n; j++) { + dp[j] += dp[j-1]; + } + } + return dp[n-1]; +}; \ No newline at end of file diff --git "a/Week06/70.\347\210\254\346\245\274\346\242\257.js" "b/Week06/70.\347\210\254\346\245\274\346\242\257.js" new file mode 100644 index 000000000..783688328 --- /dev/null +++ "b/Week06/70.\347\210\254\346\245\274\346\242\257.js" @@ -0,0 +1,140 @@ +/* + * @lc app=leetcode.cn id=70 lang=javascript + * + * [70] 爬楼梯 + * + * https://leetcode-cn.com/problems/climbing-stairs/description/ + * + * algorithms + * Easy (48.72%) + * Likes: 1081 + * Dislikes: 0 + * Total Accepted: 224.3K + * Total Submissions: 450.8K + * 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} + */ +/* + 除了以下三种解法外,还有通项公式(Binets Formula)和矩阵快速幂的方法,这两个方法是最优解。 + time:O(log n) + space:O(1) +*/ + +/**@other + * 解法一:递归+记忆 + * 递归方法当n的数很大的时候,会造成超出时间限制的问题,所以要缓存冗余的数据,故用记忆化递归的方法 + * time:O(n) + * space:O(n) + * runtime:68ms 57.71% + * memory usage:32.2MB 100% + */ +var climbStairs = function (n) { + let memo = { + 1: 1, + 2: 2 + } + + function climbStairsMemo(n) { + if (memo[n] === undefined) + memo[n] = climbStairsMemo(n - 1) + climbStairsMemo(n - 2); + return memo[n]; + } + return climbStairsMemo(n); +}; + + + +/**@other + * 解法二:滚动数组(目前最优解) + * time:O(n) + * space:O(1) + * runtime:72ms 33.8% + * memory usage:32.3MB 100% + */ +var climbStairs = function (n) { + if (n == 0 || n == 1 || n == 2) return n; + let oneStepBefore = 2; + let twoStepBefore = 1; + let all_ways = 0; + + for (let i = 3; i <= n; i++) { + all_ways = oneStepBefore + twoStepBefore; + twoStepBefore = oneStepBefore; + oneStepBefore = all_ways; + } + return all_ways; +}; + +/**@other + * 解法三:动态规划(自底向上+迭代) + * time:O(n) + * space:O(n) + * runtime:56ms 96.53% + * memory usage:32.5MB 100% + */ +var climbStairs = function (n) { + let dp = [0,1,2]; + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +}; + +/**@other + * 解法四:尾递归 + * time:O(n) + * space:O(1) + * runtime:84ms 8.47% + * memory usage:37.7MB 9.52% + */ +var climbStairs = function(n) { + function fib(n,a,b){ + if(n<=1) return b; + return fib(n-1,b,a+b); + } + return fib(n,1,1); +}; + +/**@other + * 解法五:Binets Formula + * Fn =1/√5 * (((1+√5)/2)^n-((1-√5)/2)^n) + * + * time:O(logn),pow方法将会用去O(logn)的时间 + * space:O(1) + */ +var climbStairs = function(n) { + const sqrt5 = Math.sqrt(5); + const fibn = Math.pow((1 + sqrt5)/2,n+1) - Math.pow((1 - sqrt5)/2,n+1); + return Math.round(fibn / sqrt5); +}; +// @lc code=end \ No newline at end of file From c8126c4ea0fa857e6a51324b66bfa648b7c5d506 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Tue, 21 Jul 2020 11:52:25 +0800 Subject: [PATCH 09/12] week06 assignment-2 update NOTE,md --- Week06/NOTE.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/Week06/NOTE.md b/Week06/NOTE.md index 50de30414..9afb18545 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -1 +1,52 @@ -学习笔记 \ No newline at end of file +# **动态规划-Dynamic Programming** + +Simplifying a complicated problem by breaking it down into simpler sub-problems(in a recursive manner) + +可以利用分治 + 最优子结构的方式去解决动态规划的题目 + +- Divide & Conquer + Optimal substructure + +**解决动态规划题目的方式一:** + +1. 找出最优子结构 + + opt[n] = best_of(opt[n-1],opt[n-2],…) + +2. 储存中间状态opt[i] + +3. 递推公式(状态转移方程、DP方程) + + Fib:opt[i]=opt[n-1]+opt[n-2] + + 二维路径:opt[i,j]=opt[i+1]\[j]+opt\[i][j+1] (且判断a[i,j]是否是空地) + + + +**解决动态规划题目的方式二:** + +1. **每种动态规划解决方案都设计网格** + +2. - 单元格中的值通常就是你要优化的值 + - 每个单元格都是一个子问题,因此要考虑如何将问题分为子问题,有助于找出网格的坐标轴。 + +3. **绘制网格的时候要考虑如下问题:** + +4. - 单元格中的值是什么 + - 如何将这个问题划分为子问题 + - 网格的坐标轴是什么 + +5. **填充网格** + +6. - 费曼算法 + + - - 将问题写下来 + - 好好思考 + - 将答案写下来 + + + +**动态规划、递归、分治的区别的关键** + +- 是看有无最优子结构 +- 共性:找到重复的子问题 +- 差异性:最优子结构、中途可以淘汰次优解 \ No newline at end of file From d4c5f894c80d870ed6d07c305dbf48f14684ada8 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Sun, 2 Aug 2020 23:52:39 +0800 Subject: [PATCH 10/12] =?UTF-8?q?week07=E3=80=8108=20assignment-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week07/200. NumIslands.js | 97 +++++++++++++++++++++++++ Week07/208. Trie.js | 91 +++++++++++++++++++++++ Week07/22.generateParenthesis.js | 121 +++++++++++++++++++++++++++++++ Week07/547. FindCircleNum.js | 100 +++++++++++++++++++++++++ 4 files changed, 409 insertions(+) create mode 100644 Week07/200. NumIslands.js create mode 100644 Week07/208. Trie.js create mode 100644 Week07/22.generateParenthesis.js create mode 100644 Week07/547. FindCircleNum.js diff --git a/Week07/200. NumIslands.js b/Week07/200. NumIslands.js new file mode 100644 index 000000000..75d5402b1 --- /dev/null +++ b/Week07/200. NumIslands.js @@ -0,0 +1,97 @@ +/** + * 难度:中等 + * 题目:200. 岛屿数量 + * 相似题目: + * + 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格, + 请你计算网格中岛屿的数量。 + 岛屿总是被水包围,并且每座岛屿只能 + 由水平方向或竖直方向上相邻的陆地连接形成。 + 此外,你可以假设该网格的四条边均被水包围。 + + 示例 1: + 输入: + [ + ['1','1','1','1','0'], + ['1','1','0','1','0'], + ['1','1','0','0','0'], + ['0','0','0','0','0'] + ] + 输出: 1 + + 示例 2: + 输入: + [ + ['1','1','0','0','0'], + ['1','1','0','0','0'], + ['0','0','1','0','0'], + ['0','0','0','1','1'] + ] + 输出: 3 + 解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。 + + */ + +/** + * @param {character[][]} grid + * @return {number} + */ +/**@other + * 解法一:并查集 + * runtime:124ms 8% + * memory usage:41.9MB 16% + */ +class UnionFind{ + constructor(grid){ + let n = grid.length; + let m = grid[0].length; + this.count = 0; + this.parent = Array.from(new Array(n),()=>new Array(m)); + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + if (grid[i][j] == '1') { + // 二维变一维 + this.parent[i*m+j] = i*m+j; + this.count++; + } + } + } + } + union(p,q){ + let p_root = this.find(p); + let q_root = this.find(q); + if (p_root == q_root) return; + this.parent[q_root] = p_root; + this.count--; + } + find(p){ + while (this.parent[p] != p) { + this.parent[p] = this.parent[this.parent[p]]; + p = this.parent[p]; + } + return p; + } +} +var numIslands = function(grid) { + let n = grid.length; + if (!n) return 0; + let m = grid[0].length; + + let uf = new UnionFind(grid); + console.log(uf); + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + if (grid[i][j] == '1') { + //二维矩阵m*n,z在一维数组的位置是:(第几行×矩阵宽度)+ 在第几列 + //前面已经执行过,不用往回查 + if (i + 1 < n && grid[i+1][j] == '1') { + uf.union(i * m + j, (i+1) * m + j); + } + if (j + 1 < m && grid[i][j+1] == '1') { + uf.union(i * m + j, i * m + j + 1); + } + } + } + } + return uf.count; +}; \ No newline at end of file diff --git a/Week07/208. Trie.js b/Week07/208. Trie.js new file mode 100644 index 000000000..4b001b85f --- /dev/null +++ b/Week07/208. Trie.js @@ -0,0 +1,91 @@ +/** + * 难度:中等 + * 题目:208. 实现 Trie (前缀树) + * + 实现一个 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 构成的。 + 保证所有输入均为非空字符串。 + + */ +/** + * Initialize your data structure here. + */ +var Trie = function() { + this.root = new TrieNode(); +}; + +/** +* Inserts a word into the trie. +* @param {string} word +* @return {void} +*/ +Trie.prototype.insert = function(word) { + if (!word) return false; + + let node = this.root; + for (let i = 0; i < word.length; i++) { + if (!node.next[word[i]]) node.next[word[i]] = new TrieNode(); + node = node.next[word[i]]; + } + node.isEnd = true; + return true; +}; + +/** +* Returns if the word is in the trie. +* @param {string} word +* @return {boolean} +*/ +Trie.prototype.search = function(word) { + if (!word) return false; + + let node = this.root; + for (let i = 0; i < word.length; i++) { + if (node.next[word[i]]) node = node.next[word[i]]; + else return false; + } + return node.isEnd; +}; + +/** +* 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) { + if (!prefix) return false; + + let node = this.root; + for (let i = 0; i < prefix.length; i++) { + if (node.next[prefix[i]]) node = node.next[prefix[i]]; + else return false; + } + 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) +*/ +class TrieNode{ + constructor(){ + this.next = {}; + this.isEnd = false; + } +} \ No newline at end of file diff --git a/Week07/22.generateParenthesis.js b/Week07/22.generateParenthesis.js new file mode 100644 index 000000000..54962626a --- /dev/null +++ b/Week07/22.generateParenthesis.js @@ -0,0 +1,121 @@ +/* + * @lc app=leetcode.cn id=22 lang=javascript + * + * [22] 括号生成 + * + * https://leetcode-cn.com/problems/generate-parentheses/description/ + * + * algorithms + * Medium (75.46%) + * Likes: 1114 + * Dislikes: 0 + * Total Accepted: 139.7K + * Total Submissions: 184.9K + * Testcase Example: '3' + * + * 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 + * + * + * + * 示例: + * + * 输入:n = 3 + * 输出:[ + * ⁠ "((()))", + * ⁠ "(()())", + * ⁠ "(())()", + * ⁠ "()(())", + * ⁠ "()()()" + * ⁠ ] + * + * + */ + +// @lc code=start +/** + * @param {number} n + * @return {string[]} + */ + + /**@other 解法一:递归、深度优先遍历 + * @param left 表示左括号还有多少个没用掉 + * @param right 表示右括号还有多少个没用掉 + * @param result 返回的结果数组 + * @param str 当前递归得到的字符串n + * @param n 需要用到多少个左括号、右括号 + * + * time:O(2^n) + * space:O(n) + * runtime:68ms 74.28% + * memory usage:36.1MB 12.5% + */ +var generateParenthesis = function(n) { + let result = []; + if(n==0) return result; + //teminator + var generate = (left, right, n, str)=>{ + if( left==n && right==n ){ + result.push(str); + return ; + } + + //process logic in current level + if(leftright ) //因为left{ + + } + generate(0,0,n,str); + return result; +}; + + +/**@other 解法三:动态规划 + * + * + */ +function Node(str,left,right){ + this.str = str; + this.left = left; + this.right = right; +} +function ListNode(str,left,right){ + this.str = str; + this.left = left; + this.right = right; +} +var generateParenthesis = function(n) { + let result = []; + let str = ''; + var generate = (left, right, n, str)=>{ + + } + generate(0,0,n,str); + return result; +}; +// @lc code=end + diff --git a/Week07/547. FindCircleNum.js b/Week07/547. FindCircleNum.js new file mode 100644 index 000000000..cec882140 --- /dev/null +++ b/Week07/547. FindCircleNum.js @@ -0,0 +1,100 @@ +/** + * 难度:中等 + * 题目:547. 朋友圈 + * 相似题目: + * 200. 岛屿数量 + * 130. 被围绕的区域 + * + 班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。 + 如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。 + 所谓的朋友圈,是指所有朋友的集合。 + + 给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。 + 如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。 + 你必须输出所有学生中的已知的朋友圈总数。 + + 示例 1: + 输入: + [[1,1,0], + [1,1,0], + [0,0,1]] + 输出: 2 + 说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 + 第2个学生自己在一个朋友圈。所以返回2。 + + 示例 2: + 输入: + [[1,1,0], + [1,1,1], + [0,1,1]] + 输出: 1 + 说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友, + 所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 + + 注意: + N 在[1,200]的范围内。 + 对于所有学生,有M[i][i] = 1。 + 如果有M[i][j] = 1,则有M[j][i] = 1。 + */ + +/** + * @param {number[][]} M + * @return {number} + */ +/**@sponge + * 解法一:并查集 + * 找到集合的个数 + * time:O(n^3),访问整个矩阵一次,并查集操作需要最坏时间O(n); + * space:O(n),n为parent数组长度 + */ +var findCircleNum = function(M) { + if (!M.length) return 0; + + n = M.length; + let uf = new UnionFind(n); + console.log(uf); + for (let i = 0; i < n - 1; i++) { + for (let j = i + 1; j < n ; j++) { + if(M[i][j]==1) uf.union(i,j); + } + } + return uf.count; +}; +//并查集类 +class UnionFind{ + // 初始化并查集 + constructor(n){ + this.count = n; //一开始互不联通 + this.parent = new Array(n); + this.init(); + } + // 初始化parent数组,每个数的parent都指向自身 + init(){ + for (let i = 0; i < n; i++) { + this.parent[i] = i; + } + } + // 合并,让i,j任意一个节点的parent指向另一个节点的parent上 + union(i,j){ + let p1 = this.find(i); + let p2 = this.find(j); + if (p1 == p2) return; + this.parent[p1] = p2; //将两个集合合并为一个 + this.count--; //两个分量合二为一,所以集合数量-1 + } + // 查找,返回某个节点x的parent领头元素,time:O(n),树的最坏时间为0(n) + find(p){ + let root = p; + // 如果不是领头元素 + while (this.parent[root] != root) { + root = this.parent[root]; + } + // 路径压缩 + while (this.parent[p] != p) { + let x = p; + p = this.parent[p]; + this.parent[p] = root; + } + return root; + } +} \ No newline at end of file From b19b78f8b35d2101b2e250c3aa041494bb022a41 Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Sun, 2 Aug 2020 23:55:42 +0800 Subject: [PATCH 11/12] week08 assignment-1 --- Week08/146. LRUCache.js | 183 +++++++++++++++++++++++++++++++++++ Week08/190. ReverseBits.js | 86 ++++++++++++++++ Week08/191. hammingWeight.js | 90 +++++++++++++++++ Week08/231. IsPowerOfTwo.js | 65 +++++++++++++ Week08/338. CountBits.js | 51 ++++++++++ Week08/NOTE.md | 69 ++++++++++++- Week08/quick-sort.js | 5 + Week08/selection-sort.js | 25 +++++ 8 files changed, 573 insertions(+), 1 deletion(-) create mode 100644 Week08/146. LRUCache.js create mode 100644 Week08/190. ReverseBits.js create mode 100644 Week08/191. hammingWeight.js create mode 100644 Week08/231. IsPowerOfTwo.js create mode 100644 Week08/338. CountBits.js create mode 100644 Week08/quick-sort.js create mode 100644 Week08/selection-sort.js diff --git a/Week08/146. LRUCache.js b/Week08/146. LRUCache.js new file mode 100644 index 000000000..2c1e75863 --- /dev/null +++ b/Week08/146. LRUCache.js @@ -0,0 +1,183 @@ +/** + * 难度:中等 + * 题目:146. LRU缓存机制 + * + 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。 + 它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + 获取数据 get(key) - + 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。 + 写入数据 put(key, value) - + 如果关键字已经存在,则变更其数据值; + 如果关键字不存在,则插入该组「关键字/值」。 + 当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值, + 从而为新的数据值留出空间。 + + 进阶: + 你是否可以在 O(1) 时间复杂度内完成这两种操作? + + 示例: + LRUCache cache = new LRUCache( 2 ); //2为缓存容量 + + cache.put(1, 1); + cache.put(2, 2); + cache.get(1); // 返回 1 + cache.put(3, 3); // 该操作会使得关键字 2 作废 + cache.get(2); // 返回 -1 (未找到) + cache.put(4, 4); // 该操作会使得关键字 1 作废 + cache.get(1); // 返回 -1 (未找到) + cache.get(3); // 返回 3 + cache.get(4); // 返回 4 + */ + +/**@other + * 解法一:用Map对象实现,利用Map有序的特点实现 + * time:O(??) + * space:O(capacity) + * runtime:240ms 56% + * memory usage:50.2MB 73% + */ +/** + * @param {number} capacity + */ +var LRUCache = function(capacity) { + this.capacity = capacity; + // 缓存用一个Map对象存储 + this.cache = new Map(); +}; + +/** +* @param {number} key +* @return {number} +*/ +LRUCache.prototype.get = function(key) { + // 如果cache中不存在key + if (!this.cache.has(key)) return -1; + // cache中存在key + let value = this.cache.get(key); + // 先删除原来的key + this.cache.delete(key); + // 重新再添加原来的key、value,这样可以达到最近使用过的key值会放到最前面的目的 + this.cache.set(key,value); + return value; +}; + +/** +* @param {number} key +* @param {number} value +* @return {void} +*/ +LRUCache.prototype.put = function(key, value) { + // 如果cache中存在key,就删掉这个key,再重新set新的key和value,这样可以达到最近使用过的key值会放到最前面同时更新key、value值的目的 + if (this.cache.has(key)) { + this.cache.delete(key); + } else if (this.cache.size >= this.capacity) { + // 如果cache的不存在key,就需要插入新的key、value。同时如果cache容量超出了指定的缓存容量就要删除最近最少使用的key + // 获取最近最少使用的key + let leastUsed = this.cache.keys().next(); + // 并删除最近最少使用的key + this.cache.delete(leastUsed.value); + } + // set key、value + this.cache.set(key,value); +}; + +/** +* 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) +*/ + + + + + +/**@other + * 解法二:使用 哈希表 + 双端链表 实现 + * time:O(1) + * space:O(capacity) + * runtime:216ms 89% + * memory usage:50.9MB 7% + */ +class LinkedNode{ + constructor(key=0,value=0){ + this.key = key; + this.value = value; + this.prev = null; + this.next = null; + } +} +class DLinkedList{ + constructor(){ + this.head = new LinkedNode(); + this.tail = new LinkedNode(); + this.head.next = this.tail; + this.tail.prev = this.head; + } + insertFirst(node){ + node.prev = this.head; + node.next = this.head.next; + this.head.next.prev = node; + this.head.next = node; + } + remove(node){ + node.prev.next = node.next; + node.next.prev = node.prev; + } + removeLast(){ + if (this.head.next == this.tail) return null; + let last = this.tail.prev; + this.remove(last); + return last; + } +} +/** +* @param {number} capacity +*/ +var LRUCache = function(capacity) { + this.capacity = capacity; + // key用一个Map对象存储 + this.keyNodeMap = new Map(); + this.cacheLink = new DLinkedList(); +}; + +/** +* @param {number} key +* @return {number} +*/ +LRUCache.prototype.get = function(key) { + if (!this.keyNodeMap.has(key)) return -1; + let val = this.keyNodeMap.get(key).value; + this.put(key,val); + return val; +}; + +/** +* @param {number} key +* @param {number} value +* @return {void} +*/ +LRUCache.prototype.put = function(key, value) { + let size = this.keyNodeMap.size; + // 如果keyNodeMap中存在key,就删掉这个key,再重新set新的key和value + if (this.keyNodeMap.has(key)) { + this.cacheLink.remove(this.keyNodeMap.get(key)); + // this.keyNodeMap.delete(key); + size--; + } else if (size >= this.capacity) { + // 如果cache的不存在key,就需要插入新的key、value。同时如果cache容量超出了指定的缓存容量就要删除最近最少使用的key + // 并删除最近最少使用的key + this.keyNodeMap.delete(this.cacheLink.removeLast().key); + } + // set key、value + let firstNode = new LinkedNode(key,value); + this.keyNodeMap.set(key,firstNode); + this.cacheLink.insertFirst(firstNode); +}; + +/** +* 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) +*/ \ No newline at end of file diff --git a/Week08/190. ReverseBits.js b/Week08/190. ReverseBits.js new file mode 100644 index 000000000..5eb2adfd8 --- /dev/null +++ b/Week08/190. ReverseBits.js @@ -0,0 +1,86 @@ +/** + * 难度:简单 + * 题目:190. 颠倒二进制位 + * 相似题目: + * 231. 2的幂 + * 338. 比特位计数 + * 191. 位1的个数 + * + 颠倒给定的 32 位无符号整数的二进制位。 + + 示例 1: + 输入: 00000010100101000001111010011100 + 输出: 00111001011110000010100101000000 + 解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, + 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 + + 示例 2: + 输入:11111111111111111111111111111101 + 输出:10111111111111111111111111111111 + 解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293, +   因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。 +   + 提示: + 请注意,在某些语言(如 Java)中,没有无符号整数类型。 + 在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现, + 因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + 在 Java 中,编译器使用二进制补码记法来表示有符号整数。 + 因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。 +   + 进阶: + 如果多次调用这个函数,你将如何优化你的算法? + + */ +/** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +/**@sponge + * 解法一:位运算 + * 思路:从右到左的获取n的二进制形式的每一位 -> 将获取的每一个二进制位从左到右的赋值到ans的每一个二进制位 + * 步骤: + * 1.循环32次 + * 2.每次循环通过n&1获取n的二进制形式的最低位low, + * 3.并将ans左移一位之后(保留前面的位数)和low做 或操作,即将最低位low赋值给ans的最低位 + * 4.将n的二进制位向右移1位,丢弃本次循环获取的最低位low(以获得n的二进制形式的每一个二进制位),继续循环,重复2-4步骤,直至循环结束。 + * 5.将ans无符号右移0位(ans >>> 0),表示将ans变成正数。 + * 解释: + * 1.为什么要无符号右移0位? + * 根据MDN文档:即便右移 0 个比特,结果也是非负的。 + * 也就是说右移 0 个比特,结果就会变成正数。 + * 2.为什么要变成正数?(???不知道理解的对不对) + * 根据MDN文档:js中所有的按位操作符的操作数都会变成补码。 + * 正数的补码原码相同, + * 负数的补码是原码除符号位外全部取反+1, + * + * 如果n=11111111111111111111111111111101 + * 那么ans应该=10111111111111111111111111111111 + * n的最低位是1,所以ans的最高位就一定会是1,最高位是1(即符号位为1)就会变成负数, + * 又因为js是以补码显示的,所以负数的补码就会按补码转换规则将原码转成补码 + * 最后打印出来的ans就变成了(-1000000000000000000000000000001) + * 所以可以理解为什么不将ans无符号右移0位 得到的数不是想要的结果, + * 所以最后一定要把ans >>> 0转成正数。 + * + * time:O(1),共执行32次 + * space:O(1) + * runtime:92ms 44% + * memory usage:39.8MB 51% + */ +// 方式一: +var reverseBits = function(n) { + let ans = 0; + for (let i = 0; i < 32; i++) { + ans = (ans << 1) | (n & 1); + n >>= 1; + } + return ans >>> 0 ; +}; +// 方式二:将上述步骤3的或操作 改成 +操作 +var reverseBits = function(n) { + let ans = 0; + for (let i = 0; i < 32; i++) { + ans = (ans << 1) + (n & 1); + n >>= 1; + } + return ans >>> 0 ; +}; \ No newline at end of file diff --git a/Week08/191. hammingWeight.js b/Week08/191. hammingWeight.js new file mode 100644 index 000000000..6b9505314 --- /dev/null +++ b/Week08/191. hammingWeight.js @@ -0,0 +1,90 @@ +/** + * 难度:简单 + * 题目:191. 位1的个数 + * 相似题目: + * + 编写一个函数,输入是一个无符号整数, + 返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 + + 示例 1: + 输入:00000000000000000000000000001011 + 输出:3 + 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 + + 示例 2: + 输入:00000000000000000000000010000000 + 输出:1 + 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 + + 示例 3: + 输入:11111111111111111111111111111101 + 输出:31 + 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 + + 提示: + 请注意,在某些语言(如 Java)中,没有无符号整数类型。 + 在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现, + 因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。 + 在 Java 中,编译器使用二进制补码记法来表示有符号整数。 + 因此,在上面的 示例 3 中,输入表示有符号整数 -3。 +   + 进阶: + 如果多次调用这个函数,你将如何优化你的算法? + + */ + +/** + * @param {number} n - a positive integer + * @return {number} + */ +/**@other + * 解法一:循环+位运算移动 + * 1.给mask赋初值1,表示从最低位开始与1&, + * 如果n&mask!=0,表示当前位为1,所以count++ + * 2.当前位判断完后,mask左移一位,继续与n的左一位相与,看是否为1 + * 3.32位二进制比较完毕后退出循环 + * + * 任何数字跟掩码1进行逻辑与运算,都可以获得这个数字都最低位 + * 检查下一位时,将掩码左移一位 + * 0000 0000 0000 0000 0000 0000 0000 0001 => + * 0000 0000 0000 0000 0000 0000 0000 0010 + * + * time:O(1) + * space:O(1) + * runtime:200ms 5% + * memory usage:47.1MB 16% + */ +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; +}; + +/**@other + * 解法二:位操作技巧 + * n & (n-1): 清零最低位的1 + * 每次把数字最后一个二进制位1反转为0,sum++ + * 当没有1可反的时候,数字变成了0 + * + * n数字的二进制的最低位的1总是对应n-1数字的二进制的0 + * 相与后,其它位不变,当前位变成0 + * time:O(1), 最坏情况下n中所有位均为1. + * space:O(1) + * runtime:96ms 22% + * memory usage:38.3MB 68% + */ +var hammingWeight = function(n) { + let count = 0; + while (n!=0) { + // 清零最低位的1 + n = n & (n - 1); + count++; + } + return count; +}; \ No newline at end of file diff --git a/Week08/231. IsPowerOfTwo.js b/Week08/231. IsPowerOfTwo.js new file mode 100644 index 000000000..462786c60 --- /dev/null +++ b/Week08/231. IsPowerOfTwo.js @@ -0,0 +1,65 @@ +/** + * 难度:简单 + * 题目:231. 2的幂 + * 相似题目: + * + 给定一个整数,编写一个函数来判断它是否是 2 的幂次方。 + + 示例 1: + 输入: 1 + 输出: true + 解释: 20 = 1 + + 示例 2: + 输入: 16 + 输出: true + 解释: 24 = 16 + + 示例 3: + 输入: 218 + 输出: false + + */ + +/** + * @param {number} n + * @return {boolean} + */ +/**@sponge + * 解法一:位运算 + * 思路: + * 如果n是2的幂,代表它的二进制形式里面有且只有一个二进制位是1, + * 如果n是2的幂,n一定要大于0 + * 1.给count赋初值0,用来计算1出现的次数 + * 2.如果 n!=0 && n>0 的情况下, + * 说明n中的二进制位1未被完全清零, + * 进入循环将n重新赋值为清零后的数并count+1计数 + * 3.最后n被完全清零会跳出循环,看count是否为1,即二进制位1出现的次数是否为1 + * 为1返回true,否则false + * + * time:O(1),最好的情况是只有一个二进制位1,那么只用执行一次,最坏情况是有32个1,那么需要执行32次 + * space:O(1) + * runtime:92ms 56% + * memory usage:39.3MB 62% + */ +var isPowerOfTwo = function(n) { + let count = 0; + while (n!=0 && n>0) { + n = n & (n - 1); + count++; + } + return count == 1 ? true : false; +}; + +/**@sponge + * 解法二:位运算(最优解) + * 思路:清零最低位1之后 看是否为0,为0代表n的二进制位中只有一个1,所以清零后就为0了 + * 同时n必须大于0, + * time:O(1),只执行一次 + * space:O(1) + * runtime:96ms 42% + * memory usage:39.2MB 81% + */ +var isPowerOfTwo = function(n) { + return (n & (n - 1)) == 0 && n > 0 ? true : false; +}; \ No newline at end of file diff --git a/Week08/338. CountBits.js b/Week08/338. CountBits.js new file mode 100644 index 000000000..ea24902cc --- /dev/null +++ b/Week08/338. CountBits.js @@ -0,0 +1,51 @@ +/** + * 难度:中等 + * 题目:338. 比特位计数 + * 相似题目: + * + 给定一个非负整数 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)来执行此操作。 + */ +/** + * @param {number} num + * @return {number[]} + */ +/**@sponge + * 解法一:位运算 + * + * time:O(n*sizeof(integer)) + * space:O(n) + * runtime:1712ms 5% + * memory usage:52.2MB 6% + */ +var countBits = function(num) { + let arr = []; + for (let i = 0; i <= num; i++) { + let temp = i; + let count = 0; + while (temp != 0) { + temp = temp & (temp - 1); + count++; + console.log(count); + } + arr.push(count); + } + return arr; +}; \ No newline at end of file diff --git a/Week08/NOTE.md b/Week08/NOTE.md index 50de30414..29b6c38a0 100644 --- a/Week08/NOTE.md +++ b/Week08/NOTE.md @@ -1 +1,68 @@ -学习笔记 \ No newline at end of file +- # 位运算 + +- 机器里的数字表示方式和存储格式都是二进制 + +- | **含义** | **运算符** | **示例** | **备注** | + | -------- | ---------- | ------------------------------------------------------- | ----------------------------------------------- | + | 左移 | << | 0011=>0110 | 所有位数向左移一位,缺的位置补0 | + | 右移 | >> | 0110=>0011 | 所有位数向右移一位,缺的位置补0 | + | 按位或 | \| | 0011 \| 1011=>1011 0011 1011 = 1011 | 只要有一个是1那么结果那一位就是1,否则是0 | + | 按位与 | & | 0011 & 1011=>0011 0011 1011 = 0011 | 只要有一个是0那么结果那一位就是0,否则是1 | + | 按位取反 | ~ | 0011 =>1100 | 全部取反 | + | 按位异或 | ^ | 0011 ^ 1011=>1000 0011 1011 = 1000 | 相同位为0不同为1 也可用“不进位加法”来理解 | + +- **异或操作的一些特点** + +- - x^0=x + - x^1s=~x //注意1s=~0,1s为全1 + - x^(~x)=1s + - c=a^b=>a^c=b,b^c=a //交换两个数 + - a^b^c = a^(b^c) = (a^b) ^ c + +- **获取指定位置的位运算** + +- 1. 将x最右边的n位清零——先将0全部取反,全部为1后左移n位,不足位补0,最后和x与,只要有0那就是0,否则是1。 + + **x&(~0<>n)&1** + + 3. 获取x的第n位的幂值——先将一个1往左移移到高位去,最后和x与 + + **x&(1< (x&1)==1 + - X%2==0 => (x&1)==0 + + 3. x >> 1 => x/2 + + 即x=x/2 => x=x>>1; + + 例如:mid = (left+right)/2 => mid = (left+right) >> 1; + + 4. x = x & (x-1) =>清零最低位的1 + + 5. x & -x => 得到最低位的1 + + 6. x&~x => 0 + + + diff --git a/Week08/quick-sort.js b/Week08/quick-sort.js new file mode 100644 index 000000000..3c8144d41 --- /dev/null +++ b/Week08/quick-sort.js @@ -0,0 +1,5 @@ +function quicksort(arr){ + +} + +console.log(quicksort([33,15,10])) \ No newline at end of file diff --git a/Week08/selection-sort.js b/Week08/selection-sort.js new file mode 100644 index 000000000..b53404635 --- /dev/null +++ b/Week08/selection-sort.js @@ -0,0 +1,25 @@ +//选择排序 +function selectionSort(oldArr){ + let arr = [...oldArr]; + let newArr = []; + for(let i=0;iarr[i]){ + smallest = arr[i]; + smallIndex = i; + } + } + return smallIndex; +} +console.log(selectionSort([5,3,6,2,7])); \ No newline at end of file From 1799cfdcab8af3119cc72f0873a993778a2f79ad Mon Sep 17 00:00:00 2001 From: SpongeMa <455429745@qq.com> Date: Sun, 23 Aug 2020 21:05:49 +0800 Subject: [PATCH 12/12] add summary.md --- Summary.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Summary.md diff --git a/Summary.md b/Summary.md new file mode 100644 index 000000000..39f4bb77a --- /dev/null +++ b/Summary.md @@ -0,0 +1,5 @@ +# 总结 + +课程学下来发现原来自己也是可以AC题目的! + +报班之前除了在学校课上学习数据结构和算法之外没有去动手去刷过题,基本是小白一个。由于马上要毕业找工作的缘故,所以赶紧报了个班学习,希望能在面试的时候顺利一些。从课程学习至今只刷了80道题(忏愧T T),但是对于我来说还是觉得进步很大了,因为一开始做题真的头大不知道该从何下手,一度有点抗拒去刷题,但是还是听了覃超老师的话乖乖去看题解,翻高票题解一个个的看每种解法确保自己弄懂每道题并实践五毒神掌。五毒神掌真的有用,后面去刷原来的题的时候反应的速度也越来越快,对于多次反复用到的解法也能慢慢理解并记住。由于我的学习速度比较慢同时也不够自律,每周课程的作业也还有很多没刷完,今后要重新去回顾一遍并且要把题刷完,争取早日完成我的300道题!! \ No newline at end of file