From ac42472ec9654b859469443e5dd6071ff5c368e0 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Mon, 25 Jan 2021 13:03:54 +0800 Subject: [PATCH 01/22] =?UTF-8?q?=E6=B1=82=E8=A7=A3=E4=B8=A4=E6=95=B0?= =?UTF-8?q?=E4=B9=8B=E5=92=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_01/index.html | 12 ++++++++++++ Week_01/index.js | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 Week_01/index.html create mode 100644 Week_01/index.js diff --git a/Week_01/index.html b/Week_01/index.html new file mode 100644 index 00000000..0078568e --- /dev/null +++ b/Week_01/index.html @@ -0,0 +1,12 @@ + + + + + + Document + + + + + + \ No newline at end of file diff --git a/Week_01/index.js b/Week_01/index.js new file mode 100644 index 00000000..2cf4dde1 --- /dev/null +++ b/Week_01/index.js @@ -0,0 +1,25 @@ +// 两数之和 https://leetcode-cn.com/problems/two-sum/ +function twoSum(nums, target) { + let len = nums.length, + result = [], + map = new Map(); + + // 向map内添加元素并记录索引 + for (let i = 0; i < len; i++) { + map.set(nums[i], i) + } + + for (let j = 0; j < len; j++) { + let otherValue = target - nums[j]; + // ** map.has(nums[i]) -> map.has(otherValue) + // 判断map中是否有目标值与当前元素的差且不得重复 + if (map.has(otherValue) && map.get(otherValue) != j) { + result.push(j, map.get(otherValue)); + break; + } + } + + return result +} + +console.log(twoSum([2, 3, 7, 1, 5], 6)) \ No newline at end of file From f2b99c0b30ce484bfbc075ee613bc7257a968679 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Fri, 29 Jan 2021 14:34:05 +0800 Subject: [PATCH 02/22] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E5=91=A8=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E8=AF=BE=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_01/class_3_homework.js | 144 ++++++++++++++++++++++++++++++++++++ Week_01/index.js | 25 ------- 2 files changed, 144 insertions(+), 25 deletions(-) create mode 100644 Week_01/class_3_homework.js delete mode 100644 Week_01/index.js diff --git a/Week_01/class_3_homework.js b/Week_01/class_3_homework.js new file mode 100644 index 00000000..cb8160ff --- /dev/null +++ b/Week_01/class_3_homework.js @@ -0,0 +1,144 @@ +// 删除排序数组中的重复项 https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ +var removeDuplicates = function (nums) { + let p1 = 0, + p2 = 0, + len = nums.length; + + while (p2 < len) { + if (nums[p1] != nums[p2]) { + p1++; + nums[p1] = nums[p2]; + } + p2++ + } + return p1 + 1 +}; + + +// 旋转数组 https://leetcode-cn.com/problems/rotate-array/ +var rotate = function (nums, k) { + // 每次递减 + while (k--) { + // 将数组最后一位弹出并插入到头部 + nums.unshift(nums.pop()) + } + return nums +}; +console.log('旋转数组', rotate([1, 2, 3, 4, 5, 6, 7], 2)) + + +// 合并两个有序链表 https://leetcode-cn.com/problems/merge-two-sorted-lists/ +var mergeTwoLists = function (l1, l2) { + if (l1 === null) return l2; + if (l2 === null) return l1; + + if (l1.val < l2.val) { + l1.next = mergeTwoLists(l1.next, l2) + return l1 + } else { + l2.next = mergeTwoLists(l1, l2.next) + return l2 + } +}; +console.log('合并两个有序链表', mergeTwoLists([1, 2, 3], [1, 3, 4])) + + +// 合并两个有序数组 https://leetcode-cn.com/problems/merge-sorted-array/ +function merge(nums1, m, nums2, n) { + if (!n) return + + for (let i = 0; i < n; i++) { + nums1[m + 1] = nums2[i] + } + + return nums1.sort((a, b) => a - b) +} +console.log('合并有序数组', merge([1, 3, 5], 2, [2, 7, 0], 3)) + + +// 两数之和 https://leetcode-cn.com/problems/two-sum/ +function twoSum(nums, target) { + let len = nums.length, + result = [], + map = new Map(); + + // 向map内添加元素并记录索引 + for (let i = 0; i < len; i++) { + map.set(nums[i], i) + } + + for (let j = 0; j < len; j++) { + let otherValue = target - nums[j]; + // ** map.has(nums[i]) -> map.has(otherValue) + // 判断map中是否有目标值与当前元素的差且不得重复 + if (map.has(otherValue) && map.get(otherValue) != j) { + result.push(j, map.get(otherValue)); + break; + } + } + + return result +} +console.log('两数之和', twoSum([2, 3, 7, 1, 5], 6)) + + +// 移动零 https://leetcode-cn.com/problems/move-zeroes/ +function moveZero(nums) { + // 方法一 + let i = 0, + j = 0, + len = nums.length; + + while (i < len) { + if (nums[i] !== 0) { + [nums[i], nums[j]] = [nums[j], nums[i]] + i++; + j++; + } else { + i++; + } + } + return nums; + + // 方法二 + // for (let k = nums.length - 1; k--;) { + // if (nums[k] === 0) { + // nums.splice(k, 1); + // nums.push(0) + // } + // } + // return nums + + // 方法三 + // let left = [], right = []; + // for (let l = 0; l < nums.length; l++) { + // if (nums[l]) { + // left.push(nums[l]) + // } else { + // right.push(0) + // } + // } + // return left.concat(right) +} +console.log('移动零', moveZero([1, 0, 4, 5, 3])) + + +// 加一 https://leetcode-cn.com/problems/plus-one/ +function plusOne(nums) { + let len = nums.length; + + for (let i = len - 1; i >= 0; i--) { + // nums[i]++; + // nums[i] = nums[i] % 10; + // if (nums[i]) return nums + + if (++nums[i] > 9) nums[i] = 0 + else return nums + } + + // nums[0] = 1; + // nums.push(0); + nums.unshift(1); + return nums; +} +console.log('加一', plusOne([0, 2, 0, 3, 1, 4])) \ No newline at end of file diff --git a/Week_01/index.js b/Week_01/index.js deleted file mode 100644 index 2cf4dde1..00000000 --- a/Week_01/index.js +++ /dev/null @@ -1,25 +0,0 @@ -// 两数之和 https://leetcode-cn.com/problems/two-sum/ -function twoSum(nums, target) { - let len = nums.length, - result = [], - map = new Map(); - - // 向map内添加元素并记录索引 - for (let i = 0; i < len; i++) { - map.set(nums[i], i) - } - - for (let j = 0; j < len; j++) { - let otherValue = target - nums[j]; - // ** map.has(nums[i]) -> map.has(otherValue) - // 判断map中是否有目标值与当前元素的差且不得重复 - if (map.has(otherValue) && map.get(otherValue) != j) { - result.push(j, map.get(otherValue)); - break; - } - } - - return result -} - -console.log(twoSum([2, 3, 7, 1, 5], 6)) \ No newline at end of file From a7b56dbb31e1ad76a5b7b94deaa125cff90b6f2d Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Fri, 29 Jan 2021 16:59:41 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E7=AC=AC=E5=9B=9B=E8=AF=BE=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_01/class_4_homework.js | 157 ++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 Week_01/class_4_homework.js diff --git a/Week_01/class_4_homework.js b/Week_01/class_4_homework.js new file mode 100644 index 00000000..037b2591 --- /dev/null +++ b/Week_01/class_4_homework.js @@ -0,0 +1,157 @@ +// 设计循环双端队列 +var MyCircularDeque = function (k) { + // 队列的容量 + this.capacity = k; + // 使用数组存放队列元素,所有的初始值都是-1,取值的时候直接返回 + this.queue = new Array(k).fill(-1); + // 队列的头指针,即队列头元素的位置 + this.head = 0; + // 队列的尾指针,即尾部要插入元素的位置,也就是队列的尾元素的位置+1 + this.tail = 0; +}; + +// 将index-1,需要考虑index到达数组首尾时需要循环 +MyCircularDeque.prototype.reduceIndex = function (index) { + return (index + this.capacity - 1) % this.capacity; +}; + +// 将index+1,需要考虑index到达数组首尾时需要循环 +MyCircularDeque.prototype.addIndex = function (index) { + return (index + 1) % this.capacity; +}; + +/** + * Adds an item at the front of Deque. Return true if the operation is successful. + * @param {number} value + * @return {boolean} + */ +MyCircularDeque.prototype.insertFront = function (value) { + // 判断队列是否已满 + if (this.isFull()) { + return false; + } + + // 从头部插入元素时,要先将头指针向前移动一位 + this.head = this.reduceIndex(this.head); + // 在新的头指针位置插入元素 + this.queue[this.head] = value; + + return true; +}; + +/** + * Adds an item at the rear of Deque. Return true if the operation is successful. + * @param {number} value + * @return {boolean} + */ +MyCircularDeque.prototype.insertLast = function (value) { + // 判断队列是否已满 + if (this.isFull()) { + return false; + } + + // 在尾指针的位置插入元素 + this.queue[this.tail] = value; + // 将尾指针向后移动一位,指向下一次插入元素的位置 + this.tail = this.addIndex(this.tail); + + return true; +}; + +/** + * Deletes an item from the front of Deque. Return true if the operation is successful. + * @return {boolean} + */ +MyCircularDeque.prototype.deleteFront = function () { + // 判断队列是否为空 + if (this.isEmpty()) { + return false; + } + + // 将头指针的值置为-1,表示元素被删除 + this.queue[this.head] = -1; + // 删除元素后,要将头指针向后移动一位 + this.head = this.addIndex(this.head); + + return true; +}; + +/** + * Deletes an item from the rear of Deque. Return true if the operation is successful. + * @return {boolean} + */ +MyCircularDeque.prototype.deleteLast = function () { + // 判断队列是否为空 + if (this.isEmpty()) { + return false; + } + + // 先将尾指针向前移动一位,指向队尾元素 + this.tail = this.reduceIndex(this.tail); + // 将队尾元素设置为-1 + this.queue[this.tail] = -1; + + return true; +}; + +/** + * Get the front item from the deque. + * @return {number} + */ +MyCircularDeque.prototype.getFront = function () { + // 直接返回头指针的元素即可,由于初始值是-1,因此如果队列为空,会返回-1 + return this.queue[this.head]; +}; + +/** + * Get the last item from the deque. + * @return {number} + */ +MyCircularDeque.prototype.getRear = function () { + // 直接返回尾指针-1的元素即可,由于初始值是-1,因此如果队列为空,会返回-1 + return this.queue[this.reduceIndex(this.tail)]; +}; + +/** + * Checks whether the circular deque is empty or not. + * @return {boolean} + */ +MyCircularDeque.prototype.isEmpty = function () { + // 如果头尾指针的位置相同,且对应位置的值为-1,表示队列中已无元素,则为空 + return this.head === this.tail && this.queue[this.head] < 0; +}; + +/** + * Checks whether the circular deque is full or not. + * @return {boolean} + */ +MyCircularDeque.prototype.isFull = function () { + // 如果头尾指针的位置相同,且对应位置的值不为-1,此时无法再插入元素,则队列已满 + return this.head === this.tail && this.queue[this.head] >= 0; +}; + + +// 接雨水 https://leetcode-cn.com/problems/trapping-rain-water/ +var trap = function (height) { + + let left = 0, + right = height.length - 1, + result = 0, + leftMax = 0, + rightMax = 0; + + while (left < right) { + if (height[left] < height[right]) { + leftMax = Math.max(leftMax, height[left]); + result += leftMax - height[left]; + left++ + } else { + rightMax = Math.max(height[right], rightMax); + result += rightMax - height[right]; + right-- + } + } + + return result +}; +console.log('接雨水', trap([4, 2, 0, 3, 2, 5])) \ No newline at end of file From 91863ddd2d6851c9e217d59e884c1aee144c4bcf Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 30 Jan 2021 20:30:17 +0800 Subject: [PATCH 04/22] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E5=91=A8=E5=AD=A6?= =?UTF-8?q?=E4=B9=A0=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_01/README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Week_01/README.md b/Week_01/README.md index 50de3041..dcafd4fa 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -1 +1,24 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## 数组(Array) + +特性:是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。 +由上述特性可知数组才有了一个堪称“杀手锏”的特性:“随机访问”。但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。 + +## 链表 (Linked list) + +数组对内存的要求更高。因为数组需要一块连续内存空间来存放数据。不同于数组,链表并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用。我们习惯性地把链表的第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。 + +在进行数组的插入、删除操作时,为了保持内存数据的连续性,需要做大量的数据搬移,所以时间复杂度是 O(n)。而在链表中插入或者删除一个数据,我们并不需要为了保持内存的连续性而搬移结点,因为链表的存储空间本身就不是连续的。所以,在链表中插入和删除一个数据是非常快速的。但是,有利就有弊。链表要想随机访问第 k 个元素,就没有数组那么高效了。因为链表中的数据并非连续存储的,所以无法像数组那样,根据首地址和下标,通过寻址公式就能直接计算出对应的内存地址,而是需要根据指针一个结点一个结点地依次遍历,直到找到相应的结点。 + +除了单向链表还有循环链表与双向链表。其中循环链表与单向链表的不同之处在于尾结点指针是指向链表的头结点;单向链表只有一个方向,结点只有一个后继指针 next 指向后面的结点。而双向链表,顾名思义,它支持两个方向,每个结点不止有一个后继指针 next 指向后面的结点,还有一个前驱指针 prev 指向前面的结点。跳表: 对标平衡树和二分查找。原始的有序序列添加多级索引,升维+空间换时间 + +## 栈 (stack) + +又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 +特点:后进者先出,先进者后出,入栈、出栈的时间复杂度都为 O(1) + +## 队列 (Queue) + +队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 +特点:先进先出 From 7bdd148b031cf27b9adf0c20b729dfe742cda869 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 6 Feb 2021 15:41:09 +0800 Subject: [PATCH 05/22] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_02/class_5_homework.js | 45 +++++++++++++ Week_02/class_6_homework.js | 125 ++++++++++++++++++++++++++++++++++++ Week_02/index.html | 12 ++++ 3 files changed, 182 insertions(+) create mode 100644 Week_02/class_5_homework.js create mode 100644 Week_02/class_6_homework.js create mode 100644 Week_02/index.html diff --git a/Week_02/class_5_homework.js b/Week_02/class_5_homework.js new file mode 100644 index 00000000..f84c5148 --- /dev/null +++ b/Week_02/class_5_homework.js @@ -0,0 +1,45 @@ +// 有效字母异位词 +var isAnagram = function (s, t) { + return s.split('').sort().join('') === t.split('').sort().join('') +}; +console.log('有效字母异位词', isAnagram('car', 'rac')) + + +// 字母异位词分组 +var groupAnagrams = function (strs) { + let hash = new Map() + + for (let i = 0; i < strs.length; i++) { + let str = strs[i].split('').sort().join() + if (hash.has(str)) { + let temp = hash.get(str) + temp.push(strs[i]) + hash.set(str, temp) + } else { + hash.set(str, [strs[i]]) + } + } + + return [...hash.values()] +} +console.log('字母异位词分组', groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) + +// 两数之和 +var twoSum = function (nums, target) { + let result = [], len = nums.length, map = new Map(); + + for (let i = 0; i < len; i++) { + map.set(nums[i], i) + } + + for (let j = 0; j < len; j++) { + let otherValue = target - nums[j]; + + if (map.has(otherValue) && map.get(otherValue) !== j) { + result.push(j, map.get(otherValue)) + break + } + } + return result; +}; +console.log('两数之和', twoSum([2, 7, 11, 15], 9)) \ No newline at end of file diff --git a/Week_02/class_6_homework.js b/Week_02/class_6_homework.js new file mode 100644 index 00000000..5540e4be --- /dev/null +++ b/Week_02/class_6_homework.js @@ -0,0 +1,125 @@ +// 二叉树的中序遍历 +var inorderTraversal = function (root) { + const res = []; + const inorder = (root) => { + if (root == null) { + return; + } + inorder(root.left); + res.push(root.val); + inorder(root.right); + }; + inorder(root); + return res; +}; +console.log('二叉树的中序遍历', inorderTraversal([1, null, 2, 3])) + + +// 二叉树的前序遍历 +var preorderTraversal = function (root) { + let result = [] + var preOrderTraverseNode = (node) => { + if (node) { + result.push(node.val) + preOrderTraverseNode(node.left) + preOrderTraverseNode(node.right) + } + } + preOrderTraverseNode(root) + return result +}; +console.log('二叉树的前序遍历', preorderTraversal([1, null, 2, 3])) + + +// N叉树的后序遍历 +var postorder = function (root) { + if (!root) return [] + function houxu(root, res) { + if (!root) { + return [] + } + if (root.children) { + root.children.map(child => houxu(child, res)) + } + res.push(root.val) + return res + } + return houxu(root, []) +}; +console.log('N叉树的后序遍历', postorder([1, null, 3, 2, 4, null, 5, 6])) + + +// N叉树的前序遍历 +var preorder = function (root) { + let result = [] + var dfs = function (node) { + if (node === null) { + return + } + result.push(node.val) + for (let i = 0; i < node.children.length; i++) { + dfs(node.children[i]) + } + return + } + dfs(root) + return result +}; +console.log('N叉树的前序遍历', preorder([1, null, 3, 2, 4, null, 5, 6])) + + +// N叉树的层序遍历 +var levelOrder = function (root) { + if (!root) return [] + + let ans = [] + const dfs = (r = root, d = 0) => { + if (d >= ans.length) ans.push([r.val]) + else ans[d].push(r.val) + for (const child of r.children) + dfs(child, d + 1) + }; + dfs() + return ans +} +console.log('N叉树的层序遍历', levelOrder([1, null, 3, 2, 4, null, 5, 6])) + + +// 丑数 +var nthUglyNumber = function (n) { + const res = new Array(n); + res[0] = 1; + + let ptr2 = 0, // 下个数字永远 * 2 + ptr3 = 0, // 下个数字永远 * 3 + ptr5 = 0; // 下个数字永远 * 5 + + for (let i = 1; i < n; ++i) { + res[i] = Math.min(res[ptr2] * 2, res[ptr3] * 3, res[ptr5] * 5); + if (res[i] === res[ptr2] * 2) { + ++ptr2; + } + if (res[i] === res[ptr3] * 3) { + ++ptr3; + } + if (res[i] === res[ptr5] * 5) { + ++ptr5; + } + } + + return res[n - 1]; +} +console.log('丑数', nthUglyNumber(10)) + + +// 前K个高频元素 +var topKFrequent = function (nums, k) { + let map = new Map(), arr = [...new Set(nums)] + nums.map((num) => { + if (map.has(num)) map.set(num, map.get(num) + 1) + else map.set(num, 1) + }) + + return arr.sort((a, b) => map.get(b) - map.get(a)).slice(0, k); +}; +console.log('前K个高频元素', topKFrequent([1, 1, 1, 2, 2, 3], 2)) \ No newline at end of file diff --git a/Week_02/index.html b/Week_02/index.html new file mode 100644 index 00000000..b1ed6e15 --- /dev/null +++ b/Week_02/index.html @@ -0,0 +1,12 @@ + + + + + + Document + + + + + + \ No newline at end of file From 51852fd7cb1904436a929dd42d3664ddad9215fc Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 6 Feb 2021 16:00:16 +0800 Subject: [PATCH 06/22] =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_02/README.md | 277 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 276 insertions(+), 1 deletion(-) diff --git a/Week_02/README.md b/Week_02/README.md index 50de3041..813b680e 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -1 +1,276 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## Hash 表 + +Hash Table 是一种用于存储键值对(key value pair)的数据结构,因为 Hash Table 根据 key 查询 value 的速度很快,所以它常用于实现 Map、Dictinary、Object 等数据结构。如上图所示,Hash Table 内部使用一个 hash 函数将传入的键转换成一串数字,而这串数字将作为键值对实际的 key,通过这个 key 查询对应的 value 非常快,时间复杂度将达到 O(1)。Hash 函数要求相同输入对应的输出必须相等,而不同输入对应的输出必须不等,相当于对每对数据打上唯一的指纹。 + +一个 Hash Table 通常具有下列方法: + +1、add:增加一组键值对 +2、remove:删除一组键值对 +3、lookup:查找一个键对应的值 + +代码实现方式如下: + +````function hash(string, max) { + var hash = 0; + for (var i = 0; i < string.length; i++) { + hash += string.charCodeAt(i); + } + return hash % max; +} + +function HashTable() { + let storage = []; + const storageLimit = 4; + + this.add = function (key, value) { + var index = hash(key, storageLimit); + if (storage[index] === undefined) { + storage[index] = [ + [key, value] + ]; + } else { + var inserted = false; + for (var i = 0; i < storage[index].length; i++) { + if (storage[index][i][0] === key) { + storage[index][i][1] = value; + inserted = true; + } + } + if (inserted === false) { + storage[index].push([key, value]); + } + } + } + + this.remove = function (key) { + var index = hash(key, storageLimit); + if (storage[index].length === 1 && storage[index][0][0] === key) { + delete storage[index]; + } else { + for (var i = 0; i < storage[index]; i++) { + if (storage[index][i][0] === key) { + delete storage[index][i]; + } + } + } + } + + this.lookup = function (key) { + var index = hash(key, storageLimit); + if (storage[index] === undefined) { + return undefined; + } else { + for (var i = 0; i < storage[index].length; i++) { + if (storage[index][i][0] === key) { + return storage[index][i][1]; + } + } + } + } +}``` + + +## 树(Tree) + +Tree的数据结构和自然界中的树极其相似,有根、树枝、叶子,如上图所示。Tree是一种多层数据结构,与Array、Stack、Queue相比是一种非线性的数据结构,在进行插入和搜索操作时很高效。在描述一个Tree时经常会用到下列概念: + +1、Root(根):代表树的根节点,根节点没有父节点 +2、Parent Node(父节点):一个节点的直接上级节点,只有一个 +3、Child Node(子节点):一个节点的直接下级节点,可能有多个 +4、Siblings(兄弟节点):具有相同父节点的节点 +5、Leaf(叶节点):没有子节点的节点 +6、Edge(边):两个节点之间的连接线 +7、Path(路径):从源节点到目标节点的连续边 +8、Height of Node(节点的高度):表示节点与叶节点之间的最长路径上边的个数 +9、Height of Tree(树的高度):即根节点的高度 +10、Depth of Node(节点的深度):表示从根节点到该节点的边的个数 +11、Degree of Node(节点的度):表示子节点的个数 + +以二叉查找树为例,展示树在Javascript中的实现。在二叉查找树中,即每个节点最多只有两个子节点,而左侧子节点小于当前节点,而右侧子节点大于当前节点 + +一个二叉查找树应该具有以下常用方法: + +1、add:向树中插入一个节点 +2、findMin:查找树中最小的节点 +3、findMax:查找树中最大的节点 +4、find:查找树中的某个节点 +5、isPresent:判断某个节点在树中是否存在 +6、remove:移除树中的某个节点 + +代码实现如下: +```` + +class Node { +constructor(data, left = null, right = null) { +this.data = data; +this.left = left; +this.right = right; +} +} + +class BST { +constructor() { +this.root = null; +} + +add(data) { +const node = this.root; +if (node === null) { +this.root = new Node(data); +return; +} else { +const searchTree = function (node) { +if (data < node.data) { +if (node.left === null) { +node.left = new Node(data); +return; +} else if (node.left !== null) { +return searchTree(node.left); +} +} else if (data > node.data) { +if (node.right === null) { +node.right = new Node(data); +return; +} else if (node.right !== null) { +return searchTree(node.right); +} +} else { +return null; +} +}; +return searchTree(node); +} +} + +findMin() { +let current = this.root; +while (current.left !== null) { +current = current.left; +} +return current.data; +} + +findMax() { +let current = this.root; +while (current.right !== null) { +current = current.right; +} +return current.data; +} + +find(data) { +let current = this.root; +while (current.data !== data) { +if (data < current.data) { +current = current.left +} else { +current = current.right; +} +if (current === null) { +return null; +} +} +return current; +} + +isPresent(data) { +let current = this.root; +while (current) { +if (data === current.data) { +return true; +} +if (data < current.data) { +current = current.left; +} else { +current = current.right; +} +} +return false; +} + +remove(data) { +const removeNode = function (node, data) { +if (node == null) { +return null; +} +if (data == node.data) { +// node 没有子节点 +if (node.left == null && node.right == null) { +return null; +} +// node 没有左侧子节点 +if (node.left == null) { +return node.right; +} +// node 没有右侧子节点 +if (node.right == null) { +return node.left; +} +// node 有两个子节点 +var tempNode = node.right; +while (tempNode.left !== null) { +tempNode = tempNode.left; +} +node.data = tempNode.data; +node.right = removeNode(node.right, tempNode.data); +return node; +} else if (data < node.data) { +node.left = removeNode(node.left, data); +return node; +} else { +node.right = removeNode(node.right, data); +return node; +} +} +this.root = removeNode(this.root, data); +} +} + +``` + +## 图(Graph) +Graph是节点(或顶点)以及它们之间的连接(或边)的集合。Graph也可以称为Network(网络)。根据节点之间的连接是否有方向又可以分为Directed Graph(有向图)和Undrected Graph(无向图)。Graph在实际生活中有很多用途. + +在Javascript中,Graph可以用一个矩阵(二维数组)表示,广度优先搜索算法可以实现如下: +``` + +function bfs(graph, root) { +var nodesLen = {}; + +for (var i = 0; i < graph.length; i++) { +nodesLen[i] = Infinity; +} + +nodesLen[root] = 0; + +var queue = [root]; +var current; + +while (queue.length != 0) { +current = queue.shift(); + + var curConnected = graph[current]; + var neighborIdx = []; + var idx = curConnected.indexOf(1); + while (idx != -1) { + neighborIdx.push(idx); + idx = curConnected.indexOf(1, idx + 1); + } + + for (var j = 0; j < neighborIdx.length; j++) { + if (nodesLen[neighborIdx[j]] == Infinity) { + nodesLen[neighborIdx[j]] = nodesLen[current] + 1; + queue.push(neighborIdx[j]); + } + } + +} + +return nodesLen; +} + +``` + +``` From d04ac960a65a2809715c049481a61d052a4d5213 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 6 Feb 2021 16:09:48 +0800 Subject: [PATCH 07/22] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_02/README.md | 367 +++++++++++++++++++++++----------------------- 1 file changed, 182 insertions(+), 185 deletions(-) diff --git a/Week_02/README.md b/Week_02/README.md index 813b680e..6dfd74d4 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -12,69 +12,70 @@ Hash Table 是一种用于存储键值对(key value pair)的数据结构, 代码实现方式如下: -````function hash(string, max) { - var hash = 0; - for (var i = 0; i < string.length; i++) { - hash += string.charCodeAt(i); - } - return hash % max; +``` +function hash(string, max) { + var hash = 0; + for (var i = 0; i < string.length; i++) { + hash += string.charCodeAt(i); + } + return hash % max; } function HashTable() { - let storage = []; - const storageLimit = 4; - - this.add = function (key, value) { - var index = hash(key, storageLimit); - if (storage[index] === undefined) { - storage[index] = [ - [key, value] - ]; - } else { - var inserted = false; - for (var i = 0; i < storage[index].length; i++) { - if (storage[index][i][0] === key) { - storage[index][i][1] = value; - inserted = true; + let storage = []; + const storageLimit = 4; + + this.add = function (key, value) { + var index = hash(key, storageLimit); + if (storage[index] === undefined) { + storage[index] = [ + [key, value] + ]; + } else { + var inserted = false; + for (var i = 0; i < storage[index].length; i++) { + if (storage[index][i][0] === key) { + storage[index][i][1] = value; + inserted = true; + } + } + if (inserted === false) { + storage[index].push([key, value]); + } } - } - if (inserted === false) { - storage[index].push([key, value]); - } } - } - this.remove = function (key) { - var index = hash(key, storageLimit); - if (storage[index].length === 1 && storage[index][0][0] === key) { - delete storage[index]; - } else { - for (var i = 0; i < storage[index]; i++) { - if (storage[index][i][0] === key) { - delete storage[index][i]; + this.remove = function (key) { + var index = hash(key, storageLimit); + if (storage[index].length === 1 && storage[index][0][0] === key) { + delete storage[index]; + } else { + for (var i = 0; i < storage[index]; i++) { + if (storage[index][i][0] === key) { + delete storage[index][i]; + } + } } - } } - } - this.lookup = function (key) { - var index = hash(key, storageLimit); - if (storage[index] === undefined) { - return undefined; - } else { - for (var i = 0; i < storage[index].length; i++) { - if (storage[index][i][0] === key) { - return storage[index][i][1]; + this.lookup = function (key) { + var index = hash(key, storageLimit); + if (storage[index] === undefined) { + return undefined; + } else { + for (var i = 0; i < storage[index].length; i++) { + if (storage[index][i][0] === key) { + return storage[index][i][1]; + } + } } - } } - } -}``` - +} +``` ## 树(Tree) -Tree的数据结构和自然界中的树极其相似,有根、树枝、叶子,如上图所示。Tree是一种多层数据结构,与Array、Stack、Queue相比是一种非线性的数据结构,在进行插入和搜索操作时很高效。在描述一个Tree时经常会用到下列概念: +Tree 的数据结构和自然界中的树极其相似,有根、树枝、叶子,如上图所示。Tree 是一种多层数据结构,与 Array、Stack、Queue 相比是一种非线性的数据结构,在进行插入和搜索操作时很高效。在描述一个 Tree 时经常会用到下列概念: 1、Root(根):代表树的根节点,根节点没有父节点 2、Parent Node(父节点):一个节点的直接上级节点,只有一个 @@ -88,7 +89,7 @@ Tree的数据结构和自然界中的树极其相似,有根、树枝、叶子 10、Depth of Node(节点的深度):表示从根节点到该节点的边的个数 11、Degree of Node(节点的度):表示子节点的个数 -以二叉查找树为例,展示树在Javascript中的实现。在二叉查找树中,即每个节点最多只有两个子节点,而左侧子节点小于当前节点,而右侧子节点大于当前节点 +以二叉查找树为例,展示树在 Javascript 中的实现。在二叉查找树中,即每个节点最多只有两个子节点,而左侧子节点小于当前节点,而右侧子节点大于当前节点 一个二叉查找树应该具有以下常用方法: @@ -100,177 +101,173 @@ Tree的数据结构和自然界中的树极其相似,有根、树枝、叶子 6、remove:移除树中的某个节点 代码实现如下: -```` +``` class Node { -constructor(data, left = null, right = null) { -this.data = data; -this.left = left; -this.right = right; -} + constructor(data, left = null, right = null) { + this.data = data; + this.left = left; + this.right = right; + } } class BST { -constructor() { -this.root = null; + constructor() { + this.root = null; } add(data) { -const node = this.root; -if (node === null) { -this.root = new Node(data); -return; -} else { -const searchTree = function (node) { -if (data < node.data) { -if (node.left === null) { -node.left = new Node(data); -return; -} else if (node.left !== null) { -return searchTree(node.left); -} -} else if (data > node.data) { -if (node.right === null) { -node.right = new Node(data); -return; -} else if (node.right !== null) { -return searchTree(node.right); -} -} else { -return null; -} -}; -return searchTree(node); -} + const node = this.root; + if (node === null) { + this.root = new Node(data); + return; + } else { + const searchTree = function (node) { + if (data < node.data) { + if (node.left === null) { + node.left = new Node(data); + return; + } else if (node.left !== null) { + return searchTree(node.left); + } + } else if (data > node.data) { + if (node.right === null) { + node.right = new Node(data); + return; + } else if (node.right !== null) { + return searchTree(node.right); + } + } else { + return null; + } + }; + return searchTree(node); + } } findMin() { -let current = this.root; -while (current.left !== null) { -current = current.left; -} -return current.data; + let current = this.root; + while (current.left !== null) { + current = current.left; + } + return current.data; } findMax() { -let current = this.root; -while (current.right !== null) { -current = current.right; -} -return current.data; + let current = this.root; + while (current.right !== null) { + current = current.right; + } + return current.data; } find(data) { -let current = this.root; -while (current.data !== data) { -if (data < current.data) { -current = current.left -} else { -current = current.right; -} -if (current === null) { -return null; -} -} -return current; + let current = this.root; + while (current.data !== data) { + if (data < current.data) { + current = current.left + } else { + current = current.right; + } + if (current === null) { + return null; + } + } + return current; } isPresent(data) { -let current = this.root; -while (current) { -if (data === current.data) { -return true; -} -if (data < current.data) { -current = current.left; -} else { -current = current.right; -} -} -return false; + let current = this.root; + while (current) { + if (data === current.data) { + return true; + } + if (data < current.data) { + current = current.left; + } else { + current = current.right; + } + } + return false; } remove(data) { -const removeNode = function (node, data) { -if (node == null) { -return null; -} -if (data == node.data) { -// node 没有子节点 -if (node.left == null && node.right == null) { -return null; -} -// node 没有左侧子节点 -if (node.left == null) { -return node.right; -} -// node 没有右侧子节点 -if (node.right == null) { -return node.left; -} -// node 有两个子节点 -var tempNode = node.right; -while (tempNode.left !== null) { -tempNode = tempNode.left; -} -node.data = tempNode.data; -node.right = removeNode(node.right, tempNode.data); -return node; -} else if (data < node.data) { -node.left = removeNode(node.left, data); -return node; -} else { -node.right = removeNode(node.right, data); -return node; -} -} -this.root = removeNode(this.root, data); -} + const removeNode = function (node, data) { + if (node == null) { + return null; + } + if (data == node.data) { + // node 没有子节点 + if (node.left == null && node.right == null) { + return null; + } + // node 没有左侧子节点 + if (node.left == null) { + return node.right; + } + // node 没有右侧子节点 + if (node.right == null) { + return node.left; + } + // node 有两个子节点 + var tempNode = node.right; + while (tempNode.left !== null) { + tempNode = tempNode.left; + } + node.data = tempNode.data; + node.right = removeNode(node.right, tempNode.data); + return node; + } else if (data < node.data) { + node.left = removeNode(node.left, data); + return node; + } else { + node.right = removeNode(node.right, data); + return node; + } + } + this.root = removeNode(this.root, data); + } } - ``` ## 图(Graph) -Graph是节点(或顶点)以及它们之间的连接(或边)的集合。Graph也可以称为Network(网络)。根据节点之间的连接是否有方向又可以分为Directed Graph(有向图)和Undrected Graph(无向图)。Graph在实际生活中有很多用途. -在Javascript中,Graph可以用一个矩阵(二维数组)表示,广度优先搜索算法可以实现如下: -``` +Graph 是节点(或顶点)以及它们之间的连接(或边)的集合。Graph 也可以称为 Network(网络)。根据节点之间的连接是否有方向又可以分为 Directed Graph(有向图)和 Undrected Graph(无向图)。Graph 在实际生活中有很多用途. -function bfs(graph, root) { -var nodesLen = {}; +在 Javascript 中,Graph 可以用一个矩阵(二维数组)表示,广度优先搜索算法可以实现如下: -for (var i = 0; i < graph.length; i++) { -nodesLen[i] = Infinity; -} +``` +function bfs(graph, root) { + var nodesLen = {}; -nodesLen[root] = 0; + for (var i = 0; i < graph.length; i++) { + nodesLen[i] = Infinity; + } -var queue = [root]; -var current; + nodesLen[root] = 0; -while (queue.length != 0) { -current = queue.shift(); + var queue = [root]; + var current; - var curConnected = graph[current]; - var neighborIdx = []; - var idx = curConnected.indexOf(1); - while (idx != -1) { - neighborIdx.push(idx); - idx = curConnected.indexOf(1, idx + 1); - } + while (queue.length != 0) { + current = queue.shift(); - for (var j = 0; j < neighborIdx.length; j++) { - if (nodesLen[neighborIdx[j]] == Infinity) { - nodesLen[neighborIdx[j]] = nodesLen[current] + 1; - queue.push(neighborIdx[j]); - } - } + var curConnected = graph[current]; + var neighborIdx = []; + var idx = curConnected.indexOf(1); + while (idx != -1) { + neighborIdx.push(idx); + idx = curConnected.indexOf(1, idx + 1); + } -} + for (var j = 0; j < neighborIdx.length; j++) { + if (nodesLen[neighborIdx[j]] == Infinity) { + nodesLen[neighborIdx[j]] = nodesLen[current] + 1; + queue.push(neighborIdx[j]); + } + } -return nodesLen; + } + return nodesLen; } - -``` - ``` From 0f0fbb09644705ce7a2d45194585d9c61077fa51 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 6 Feb 2021 16:12:54 +0800 Subject: [PATCH 08/22] g --- Week_02/README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Week_02/README.md b/Week_02/README.md index 6dfd74d4..fc8a5419 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -6,9 +6,9 @@ Hash Table 是一种用于存储键值对(key value pair)的数据结构, 一个 Hash Table 通常具有下列方法: -1、add:增加一组键值对 -2、remove:删除一组键值对 -3、lookup:查找一个键对应的值 +1.add:增加一组键值对 +2.remove:删除一组键值对 +3.lookup:查找一个键对应的值 代码实现方式如下: @@ -77,28 +77,28 @@ function HashTable() { Tree 的数据结构和自然界中的树极其相似,有根、树枝、叶子,如上图所示。Tree 是一种多层数据结构,与 Array、Stack、Queue 相比是一种非线性的数据结构,在进行插入和搜索操作时很高效。在描述一个 Tree 时经常会用到下列概念: -1、Root(根):代表树的根节点,根节点没有父节点 -2、Parent Node(父节点):一个节点的直接上级节点,只有一个 -3、Child Node(子节点):一个节点的直接下级节点,可能有多个 -4、Siblings(兄弟节点):具有相同父节点的节点 -5、Leaf(叶节点):没有子节点的节点 -6、Edge(边):两个节点之间的连接线 -7、Path(路径):从源节点到目标节点的连续边 -8、Height of Node(节点的高度):表示节点与叶节点之间的最长路径上边的个数 -9、Height of Tree(树的高度):即根节点的高度 -10、Depth of Node(节点的深度):表示从根节点到该节点的边的个数 -11、Degree of Node(节点的度):表示子节点的个数 +1.Root(根):代表树的根节点,根节点没有父节点 +2.Parent Node(父节点):一个节点的直接上级节点,只有一个 +3.Child Node(子节点):一个节点的直接下级节点,可能有多个 +4.Siblings(兄弟节点):具有相同父节点的节点 +5.Leaf(叶节点):没有子节点的节点 +6.Edge(边):两个节点之间的连接线 +7.Path(路径):从源节点到目标节点的连续边 +8.Height of Node(节点的高度):表示节点与叶节点之间的最长路径上边的个数 +9.Height of Tree(树的高度):即根节点的高度 +10.Depth of Node(节点的深度):表示从根节点到该节点的边的个数 +11.Degree of Node(节点的度):表示子节点的个数 以二叉查找树为例,展示树在 Javascript 中的实现。在二叉查找树中,即每个节点最多只有两个子节点,而左侧子节点小于当前节点,而右侧子节点大于当前节点 一个二叉查找树应该具有以下常用方法: -1、add:向树中插入一个节点 -2、findMin:查找树中最小的节点 -3、findMax:查找树中最大的节点 -4、find:查找树中的某个节点 -5、isPresent:判断某个节点在树中是否存在 -6、remove:移除树中的某个节点 +1.add:向树中插入一个节点 +2.findMin:查找树中最小的节点 +3.findMax:查找树中最大的节点 +4.find:查找树中的某个节点 +5.isPresent:判断某个节点在树中是否存在 +6.remove:移除树中的某个节点 代码实现如下: From 3a81314c03acbcf8a38b38cd88545ac294119a01 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Mon, 8 Feb 2021 11:31:59 +0800 Subject: [PATCH 09/22] =?UTF-8?q?=E7=AC=AC=E4=B8=89=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_03/class_7_homework.js | 152 ++++++++++++++++++++++++++++++++++++ Week_03/index.html | 13 +++ 2 files changed, 165 insertions(+) create mode 100644 Week_03/class_7_homework.js create mode 100644 Week_03/index.html diff --git a/Week_03/class_7_homework.js b/Week_03/class_7_homework.js new file mode 100644 index 00000000..c4c6fad6 --- /dev/null +++ b/Week_03/class_7_homework.js @@ -0,0 +1,152 @@ +// 二叉树的最近公共祖先 https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +/** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ +var lowestCommonAncestor = function (root, p, q) { + let ans; + const dfs = (root, p, q) => { + if (root === null) 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))) { + ans = root; + } + return lson || rson || (root.val === p.val || root.val === q.val); + } + dfs(root, p, q); + return ans; +}; +// console.log('二叉树的最近公共祖先', lowestCommonAncestor([3, 5, 1, 6, 2, 0, 8, null, null, 7, 4], 5, 1)) + + +// 从前序与中序遍历序列构造二叉树 https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ +/** + * Definition for a binary tree node. + */ +function TreeNode(val, left, right) { + this.val = (val === undefined ? 0 : val) + this.left = (left === undefined ? null : left) + this.right = (right === undefined ? null : right) +} +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +var buildTree = function (preorder, inorder) { + let pre = 0, i = 0; + let build = function (stop) { + if (inorder[i] != stop) { + var root = new TreeNode(preorder[pre++]) + root.left = build(root.val) + i++ + root.right = build(stop) + return root + } + return null + } + return build() +}; +// console.log('从前序与中序遍历序列构造二叉树', buildTree([4, 2, 3], [5, 1, 8])) + + +// 组合 https://leetcode-cn.com/problems/combinations/submissions/ +/** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ +var combine = function (n, k) { + const result = []; + + const helper = (n, k, path) => { + if (n < k || k === 0) { + if (k === 0) { + result.push(path.slice()) + } + return; + } + helper(n - 1, k - 1, path.concat(n)); + helper(n - 1, k, path); + } + + helper(n, k, []) + return result; +}; +console.log('组合', combine(5, 2)) + + +// 全排列 https://leetcode-cn.com/problems/permutations/submissions/ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permute = function (nums) { + const result = [], + hash = {}; + + function dfs(path) { + if (path.length === nums.length) { + result.push(path.slice()); + return; + } + + for (const num of nums) { + if (hash[num]) continue; + path.push(num) + hash[num] = true; + dfs(path); + path.pop(); + hash[num] = false; + } + } + + dfs([]); + return result +}; +console.log('全排列', permute([1, 2, 3])) + + +// 全排列II https://leetcode-cn.com/problems/permutations-ii/ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var permuteUnique = function (nums) { + const len = nums.length; + nums = nums.sort((a, b) => a - b); + + let result = [], + tmpPath = [], + hash = {}; + + let backtrack = path => { + if (path.length === len) { + result.push(path); + return; + } + + for (let i = 0; i < len; i++) { + if (hash[i] || (i > 0 && !hash[i - 1] && (nums[i - 1] === nums[i]))) continue; + hash[i] = true; + path.push(nums[i]); + backtrack(path.slice()); + hash[i] = false; + path.pop(); + } + } + + backtrack(tmpPath); + return result; +}; +console.log('全排列II', permuteUnique([1, 1, 2])) \ No newline at end of file diff --git a/Week_03/index.html b/Week_03/index.html new file mode 100644 index 00000000..545558e3 --- /dev/null +++ b/Week_03/index.html @@ -0,0 +1,13 @@ + + + + + + Document + + + + + \ No newline at end of file From a610d804b53ee8674ed63d2bd598106e51fa9083 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 20 Feb 2021 08:06:29 +0800 Subject: [PATCH 10/22] =?UTF-8?q?=E7=AC=AC=E4=B8=89=E5=91=A8=E7=AC=94?= =?UTF-8?q?=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_03/README.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Week_03/README.md b/Week_03/README.md index 50de3041..2bd5b1eb 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,34 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## 递归 + +程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。 + +递归需要满足的三个条件: + +1. 一个问题的解可以分解为几个子问题的解 +2. 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样 +3. 存在递归终止条件把问题分解为子问题,把子问题再分解为子子问题,一层一层分解下去,不能存在无限循环,这就需要有终止条件 + +## 分治 + +分治算法(divide and conquer)的核心思想其实就是四个字,分而治之 ,也就是将原问题划分成 n 个规模较小,并且结构与原问题相似的子问题,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。 + +分治算法能解决的问题,一般需要满足下面这几个条件: + +1. 原问题与分解成的小问题具有相同的模式; +2. 原问题分解成的子问题可以独立求解,子问题之间没有相关性; +3. 具有分解终止条件,也就是说,当问题足够小时,可以直接求解; +4. 可以将子问题合并成原问题,而这个合并操作的复杂度不能太高,否则就起不到减小算法总体复杂度的效果了。 + +## 回溯 + +回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。 + +用回溯算法解决问题的一般步骤: + +1. 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解; +2. 确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间; +3. 以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。 + +回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第 2 个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第 2 个皇后的位置,直到找到符合条件的位置就可以了。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。 From 7e2cdfa0fb55bced769e7082064bb9e81f5c6202 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Fri, 26 Feb 2021 09:30:18 +0800 Subject: [PATCH 11/22] =?UTF-8?q?=E7=AC=AC4=E5=91=A8=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_04/class_9_homework.js | 413 ++++++++++++++++++++++++++++++++++++ Week_04/index.html | 14 ++ 2 files changed, 427 insertions(+) create mode 100644 Week_04/class_9_homework.js create mode 100644 Week_04/index.html diff --git a/Week_04/class_9_homework.js b/Week_04/class_9_homework.js new file mode 100644 index 00000000..21f9bbff --- /dev/null +++ b/Week_04/class_9_homework.js @@ -0,0 +1,413 @@ +// 柠檬水找零 https://leetcode-cn.com/problems/lemonade-change/ +var lemonadeChange = function (bills) { + let n5 = 0, + n10 = 0; + + for (const bill of bills) { + if (bill === 5) { + n5++; + } else if (bill === 10) { + if (n5 === 0) { + return false; + } + n5--; + n10++; + } else { + if (n5 > 0 && n10 > 0) { + n5--; + n10--; + } else if (n5 >= 3) { + n5 -= 3; + } else { + return false; + } + } + } + return true +}; + + + +// 买卖股票的最佳时机II https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/ +var maxProfit = function (prices) { + const n = prices.length; + const dp = new Array(n).fill(0).map(v => new Array(2).fill(0)); + dp[0][0] = 0, dp[0][1] = -prices[0]; + for (let i = 1; i < n; ++i) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); + } + return dp[n - 1][0]; +} + + + +// 分发饼干 https://leetcode-cn.com/problems/assign-cookies/ +var findContentChildren = function (g, s) { + g.sort((a, b) => a - b); + s.sort((a, b) => a - b); + const numOfChildren = g.length, numOfCookies = s.length; + let count = 0; + for (let i = 0, j = 0; i < numOfChildren && j < numOfCookies; i++, j++) { + while (j < numOfCookies && g[i] > s[j]) { + j++; + } + if (j < numOfCookies) { + count++; + } + } + return count; +}; + + + + +// 模拟行走机器人 https://leetcode-cn.com/problems/walking-robot-simulation/ +var robotSim = function (commands, obstacles) { + var dx = [0, 1, 0, -1]; + var dy = [1, 0, -1, 0]; + var di = 0; + var endX = 0; + var endY = 0; + var result = 0; + var hashObstacle = {}; + for (var r = 0; r < obstacles.length; r++) { + hashObstacle[obstacles[r][0] + '-' + obstacles[r][1]] = true; + } + for (var s = 0; s < commands.length; s++) { + if (commands[s] == -2) { + di = (di + 3) % 4; + } else if (commands[s] == -1) { + di = (di + 1) % 4; + } else { + // 每次走一步 + for (var z = 1; z <= commands[s]; z++) { + var nextX = endX + dx[di]; + var nextY = endY + dy[di]; + // 判断下一步是否为障碍物 + if (hashObstacle[nextX + '-' + nextY]) { + break; + } + endX = nextX; + endY = nextY; + result = Math.max(result, endX * endX + endY * endY); + } + } + } + return result; +}; + + + +// 单词接龙 https://leetcode-cn.com/problems/word-ladder/ +var ladderLength = function (beginWord, endWord, wordList) { + let wordListSet = new Set(wordList); + if (!wordListSet.has(endWord)) { + return 0; + } + let beginSet = new Set(); + beginSet.add(beginWord); + let endSet = new Set(); + endSet.add(endWord) + let level = 1; + // BFS + while (beginSet.size > 0) { + let next_beginSet = new Set(); + for (let key of beginSet) { + for (let i = 0; i < key.length; i++) { + for (let j = 0; j < 26; j++) { + let s = String.fromCharCode(97 + j); + if (s != key[i]) { + let new_word = key.slice(0, i) + s + key.slice(i + 1); + if (endSet.has(new_word)) { + return level + 1; + } + if (wordListSet.has(new_word)) { + next_beginSet.add(new_word); + wordListSet.delete(new_word); + } + } + } + } + } + beginSet = next_beginSet; + level++; + if (beginSet.size > endSet.size) { + let tmp = beginSet; + beginSet = endSet; + endSet = tmp; + } + } + return 0; +} + + + +// 岛屿数量 https://leetcode-cn.com/problems/number-of-islands/ +var numIslands = function (grid) { + let m = grid.length; + if (m == 0) { + return 0; + } + let n = grid[0].length; + let count = 0; + let parent = []; + let rank = []; + + + let find = (p) => { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + let union = (p, q) => { + let rootP = find(p); + let rootQ = find(q); + if (rootP == rootQ) { + return; + } + if (rank[rootP] > rank[rootQ]) { + parent[rootQ] = rootP; + } else if (rank[rootP] < rank[rootQ]) { + parent[rootP] = rootQ; + } else { + parent[rootP] = rootQ; + rank[rootQ]++; + } + count--; + } + + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] == 1) { + parent[i * n + j] = i * n + j; + count++; + } + rank[i * n + j] = 0; + } + } + + for (var i = 0; i < m; i++) { + for (var j = 0; j < n; j++) { + if (grid[i][j] == 1) { + grid[i][j] = 0; + i - 1 >= 0 && grid[i - 1][j] == 1 && union(i * n + j, (i - 1) * n + j); + j - 1 >= 0 && grid[i][j - 1] == 1 && union(i * n + j, i * n + j - 1); + i + 1 < m && grid[i + 1][j] == 1 && union(i * n + j, (i + 1) * n + j); + j + 1 < n && grid[i][j + 1] == 1 && union(i * n + j, i * n + j + 1); + } + } + } + return count; +}; + + + +// 扫雷游戏 https://leetcode-cn.com/problems/minesweeper/ +var updateBoard = (board, click) => { + const m = board.length; + const n = board[0].length; + const dx = [1, 1, 1, -1, -1, -1, 0, 0]; + const dy = [1, 0, -1, 0, 1, -1, 1, -1]; + const inBound = (x, y) => x >= 0 && x < m && y >= 0 && y < n; // 辅助函数 + + const update = (x, y) => { + if (!inBound(x, y) || board[x][y] != 'E') return; // 不在界内或不是E,直接返回 + let count = 0; + for (let i = 0; i < 8; i++) { // 统计周围雷的个数 + const nX = x + dx[i]; + const nY = y + dy[i]; + if (inBound(nX, nY) && board[nX][nY] == 'M') { + count++; + } + } + if (count == 0) { // 如果周围没有雷,标记B,递归周围的点 + board[x][y] = 'B'; + for (let i = 0; i < 8; i++) { + update(x + dx[i], y + dy[i]); + } + } else { + board[x][y] = count + ''; + } + }; + + const [cX, cY] = click; + if (board[cX][cY] == 'M') { // 第一下就踩雷了 + board[cX][cY] = 'X'; + } else { + update(cX, cY); // 开启dfs + } + return board; +} + + + +// 跳跃游戏 https://leetcode-cn.com/problems/jump-game/ +var canJump = function (nums) { + for (var i = 0, max = 0; i < nums.length; i++) + if (i <= max) max = Math.max(max, i + nums[i]) + else return false + return true +} + + + +// 搜索旋转排序数组 https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ +var search = function (nums, target) { + let start = 0; + let end = nums.length - 1; + + while (start <= end) { + const mid = start + ((end - start) >> 1); + if (nums[mid] === target) return mid; + + if (nums[mid] >= nums[start]) { + if (target >= nums[start] && target <= nums[mid]) { + end = mid - 1; + } else { + start = mid + 1; + } + } else { + if (target >= nums[mid] && target <= nums[end]) { + start = mid + 1; + } else { + end = mid - 1; + } + } + } + + return -1; +} + + + +// 搜索二维矩阵 https://leetcode-cn.com/problems/search-a-2d-matrix/ +var searchMatrix = function (matrix, target) { + if (!matrix || !matrix.length) return false; + + const rows = matrix.length; + const cols = matrix[0].length; + let l = 0, + r = rows * cols - 1, + mid = 0; + + while (l <= r) { + mid = ((l + r) / 2) << 0; + const [x, y] = getCoordFromPos(mid); + const num = matrix[x][y]; + if (num < target) l = mid + 1; + else if (num > target) r = mid - 1; + else return true; + } + + return false; + + function getCoordFromPos(pos) { + const x = (pos / cols) << 0; + const y = pos % cols; + return [x, y]; + } +}; + + + + +// 寻找旋转排序数组中的最小值 https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ +var findMin = function (nums) { + var left = 0; + var right = nums.length - 1; + while (left < right) { + var mid = (left + right) >> 1; + if (nums[mid] > nums[right]) { + left = mid + 1; + } else { + right = mid; + } + } + return nums[left]; +}; + +// 单词接龙II https://leetcode-cn.com/problems/word-ladder-ii/ +var findLadders = function (beginWord, endWord, wordList) { + if (wordList.indexOf(endWord) < 0) return []; + if (wordList.indexOf(beginWord) == -1) wordList.push(beginWord); + + let allCombDict = new Map(); + let wordLevel = new Map(); + let wordConnection = new Map(); + let L = beginWord.length; + //建图 + for (let word of wordList) { + for (let i = 0; i < L; ++i) { + let key = word.slice(0, i) + "*" + word.slice(i + 1); + if (allCombDict.has(key)) allCombDict.get(key).push(word); + else allCombDict.set(key, [word]); + } + } + let queue = [beginWord]; + let wordUsed = new Set(); + let step = 1; + let flag = 1; + //帮助我们判断是否能从beginWord到endWord,如果可以则转为0,这可以帮助我们提前结束循环,并且如果不能到达endWord,则不需要再进行DFS 直接返回[]; + //BFS + while (queue.length && flag) { + let len = queue.length; + for (let t = 0; t < len; ++t) { + let word = queue.shift(); + if (!wordUsed.has(word)) { + wordUsed.add(word); + wordLevel.set(word, step); + if (word == endWord) flag = 0; + for (let i = 0; i < L; ++i) { + let key = word.slice(0, i) + "*" + word.slice(i + 1); + if (allCombDict.has(key)) { + let connected = allCombDict.get(key).filter((d) => d != word);//这里要去除自身,两个原因:1.connected里面要保存的是该节点的邻居节点,自身不属于;2.如果将自身这个节点加进去会产生重复; + if (wordConnection.has(word)) + wordConnection.get(word).push(...connected); + else wordConnection.set(word, [...connected]); + queue.push(...connected); + } + } + } + } + step++; + } + if (flag) return []; + let res = []; + //DFS + function dfs(list, word, connection, level) { + let lev = level.get(word); + if (lev == 1) { + res.push([...list]); + return; + } + for (let node of connection.get(word)) { + if (level.get(node) == lev - 1) { + list.unshift(node); + dfs(list, node, connection, level); + list.shift(); + } + } + } + dfs([endWord], endWord, wordConnection, wordLevel); + return res; +}; + + + +// 跳跃游戏II https://leetcode-cn.com/problems/jump-game-ii/ +var jump = function (nums) { + var steps = 0 + var end = 0 + var maxPos = 0 + for (var i = 0; i < nums.length - 1; ++i) { + maxPos = Math.max(maxPos, nums[i] + i) + if (i == end) { + end = maxPos + ++steps + } + } + return steps +}; \ No newline at end of file diff --git a/Week_04/index.html b/Week_04/index.html new file mode 100644 index 00000000..f635ae71 --- /dev/null +++ b/Week_04/index.html @@ -0,0 +1,14 @@ + + + + + + + Document + + + + + \ No newline at end of file From 6c1e58fb29af783b3c05f7643a738f279b869205 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Fri, 26 Feb 2021 11:09:26 +0800 Subject: [PATCH 12/22] =?UTF-8?q?=E7=AC=AC=E5=9B=9B=E5=91=A8=E5=AD=A6?= =?UTF-8?q?=E4=B9=A0=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_04/README.md | 107 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/Week_04/README.md b/Week_04/README.md index 50de3041..d0f79689 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,106 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## 深度和广度优先搜索算法 + +深度优先搜索算法和广度优先搜索算法就是作用于图这种数据结构的搜索算法,图上的搜索算法,就是从图中的一个顶点出发,到另一个顶点的路径。图有两种存储方法,邻接矩阵和邻接表,在这里我们用邻接表来存储图,并以无向图作为例子,但这两种算法也同样都可以应用在有向图中。 + +### 深度优先搜索 (DFS) + +``` +def dfs(node): + if node in visited: + return; + visited.add(node); + + dfs(node.left); + dfs(node.right); +``` + +上述是 dfs 的代码模板 ; +dfs 首先是在 visited 数组中判断节点是否存在,存在的情况 , 需要 return 出去; 不存在的情况,需要使用递归; + +### 广度优先搜索(BFS) + +一种地毯式层层推进的搜索策略,即先查找离起始顶点最近的,然后是次近的,依次往外搜索。 + +BFS 的代码模板是: + +``` +// 使用队列进行实现; +const bfs = (root) => { + let result = []; + let queue = [root]; + + while(queue.length > 0) { + let level = []; + let n = queue.length; + for (let i = 0 ;i < n;i++){ + let node = queue.pop(); + level.push(node.val); + } + if(node.left) queue.unshift(node.left) + if(node.right) queue.unshift(node.right) + } + + result.push(level); + return result; +} +``` + +## 贪心算法 + +贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。 + +贪心算法一般按如下步骤进行: + +1. 建立数学模型来描述问题; +2. 把求解的问题分成若干个子问题; +3. 对每个子问题求解,得到子问题的局部最优解; +4. 把子问题的解局部最优解合成原来解问题的一个解。 + +贪心算法可解决的问题通常大部分都有如下的特性: + +1. 有一个以最优方式来解决的问题。为了构造问题的解决方案,有一个候选的对象的集合:比如不同面值的硬币; +2. 随着算法的进行,将积累起其他两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象; +3. 有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优; +4. 还有一个函数检查是否一个候选对象的集合是可行的,即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性; +5. 选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解; +6. 最后,目标函数给出解的值。 + +## 二分查找 + +二分查找针对的是一个有序的数据集合,查找思想有点类似分治思想。每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为 0 + +二分查找算法的原理如下: + +1. 如果待查序列为空,那么就返回-1,并退出算法;这表示查找不到目标元素。 +2. 如果待查序列不为空,则将它的中间元素与要查找的目标元素进行匹配,看它们是否相等。 +3. 如果相等,则返回该中间元素的索引,并退出算法;此时就查找成功了。 +4. 如果不相等,就再比较这两个元素的大小。 +5. 如果该中间元素大于目标元素,那么就将当前序列的前半部分作为新的待查序列;这是因为后半部分的所有元素都大于目标元素,它们全都被排除了。 +6. 如果该中间元素小于目标元素,那么就将当前序列的后半部分作为新的待查序列;这是因为前半部分的所有元素都小于目标元素,它们全都被排除了。 +7. 在新的待查序列上重新开始第 1 步的工作。 + +二分查找之所以快速,是因为它在匹配不成功的时候,每次都能排除剩余元素中一半的元素。因此可能包含目标元素的有效范围就收缩得很快,而不像顺序查找那样,每次仅能排除一个元素。 + +问题:使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 + +解法: 通过二分查找的手段,以双指针方式对头和尾元素做比较,以此判别无序点位置 + +``` +var findIndex = (arr) => { + let low = 0; + let high = arr.length - 1; + while(low <= high){ + let middle = low + ((high - low) >> 1); + if( arr[middle] <= arr[0] || arr[middle] >= arr[high]) { + return middle; + }else if(arr[middle] > arr[0]) { + middle = low + 1;// 向左偏移; + }else { + high = middle - 1; + } + } + return -1; +} +``` From 6455e53bb976c863a1e88fa6c8735843a8c59ce5 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 13 Mar 2021 21:33:55 +0800 Subject: [PATCH 13/22] =?UTF-8?q?=E7=AC=AC=E5=85=AD=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_06/README.md | 26 ++- Week_06/class_12_homework.js | 376 +++++++++++++++++++++++++++++++++++ Week_06/index.html | 12 ++ 3 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 Week_06/class_12_homework.js create mode 100644 Week_06/index.html diff --git a/Week_06/README.md b/Week_06/README.md index 50de3041..c84b9e4e 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -1 +1,25 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## 动态规划 + +### 定义 + +动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。 + +### 基本思想 + +动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。 + +### 基本思路 + +动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。如图所示。动态规划的设计都有着一定的模式,一般要经历以下几个步骤:初始状态 →│ 决策1 │→│ 决策2 │→…→│ 决策n │→ 结束状态 + +1. 划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。 +2. 确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。 +3. 确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。 +4. 寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。 + 一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。实际应用中可以按以下几个简化的步骤进行设计: +5. 分析最优解的性质,并刻画其结构特征。 +6. 递归的定义最优解。 +7. 以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值 +8. 根据计算最优值时得到的信息,构造问题的最优解 diff --git a/Week_06/class_12_homework.js b/Week_06/class_12_homework.js new file mode 100644 index 00000000..a66bcb07 --- /dev/null +++ b/Week_06/class_12_homework.js @@ -0,0 +1,376 @@ +// 最小路径和 https://leetcode-cn.com/problems/minimum-path-sum/ +/** + * @param {number[][]} grid + * @return {number} + */ +var minPathSum = function (grid) { + let row = grid.length, + col = grid[0].length; + + // calc boundary + for (let i = 1; i < row; i++) + // calc first col + grid[i][0] += grid[i - 1][0] + + for (let j = 1; j < col; j++) + // calc first row + grid[0][j] += grid[0][j - 1] + + for (let i = 1; i < row; i++) + for (let j = 1; j < col; j++) + grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]) + + return grid[row - 1][col - 1] +}; + +// 解码方法 https://leetcode-cn.com/problems/decode-ways/ +/** + * @param {string} s + * @return {number} + */ +var numDecodings = function (s) { + if (s[0] === '0') return 0 + + const len = s.length, dp = [1, 1, ...new Array(len - 1).fill(0)] + + for (let i = 2; i <= len; i++) { + let lastOne = s.slice(i - 1, i), lastTwo = s.slice(i - 2, i) + + if (lastOne > 0 && lastOne < 10) dp[i] += dp[i - 1] + + if (lastTwo >= 10 && lastTwo <= 26) dp[i] += dp[i - 2] + } + + return dp[len] +}; + +// 最大正方形 https://leetcode-cn.com/problems/maximal-square/ +/** + * @param {character[][]} matrix + * @return {number} + */ +var maximalSquare = function (matrix) { + let maxSize = 0; + let dp = new Array(matrix.length); + for (let i = 0; i < dp.length; i++) { + dp[i] = new Array(matrix[i].length).fill(0); + } + for (let i = 0; i < matrix.length; i++) { + for (let j = 0; j < matrix[i].length; j++) { + if (matrix[i][j] === '1') { + if (i == 0 || j === 0) dp[i][j] = 1; + else + dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1; + maxSize = Math.max(maxSize, dp[i][j]); + } + } + } + return maxSize ** 2; +}; + +// 任务调度器 https://leetcode-cn.com/problems/task-scheduler/ +/** + * @param {character[]} tasks + * @param {number} n + * @return {number} + */ +var leastInterval = function (tasks, n) { + if (n === 0) return tasks.length + + var i = -1, + maxCount = 0, + h = new Uint16Array(26); + + while (++i < tasks.length) h[t = tasks[i].charCodeAt() - 65]++ + h.sort((a, b) => b - a) + while (h[maxCount + 1] === h[maxCount++]) { } + return Math.max((h[0] - 1) * (n + 1) + maxCount, i) +}; + +// 回文子串 https://leetcode-cn.com/problems/palindromic-substrings/ +/** + * @param {string} s + * @return {number} + */ +var countSubstrings = function (s) { + const n = s.length; + let ans = 0; + for (let i = 0; i < 2 * n - 1; ++i) { + let l = i / 2, r = i / 2 + i % 2; + while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) { + --l; + ++r; + ++ans; + } + } + return ans; +}; + +// 最长有效括号 https://leetcode-cn.com/problems/longest-valid-parentheses/ +/** + * @param {string} s + * @return {number} + */ +var longestValidParentheses = function (s) { + let maxLen = 0; + const stack = []; + stack.push(-1); + for (let i = 0; i < s.length; i++) { + const c = s[i]; + if (c == '(') { + stack.push(i); + } else { + stack.pop(); + if (stack.length) { + const curMaxLen = i - stack[stack.length - 1]; + maxLen = Math.max(maxLen, curMaxLen); + } else { + stack.push(i); + } + } + } + return maxLen; +}; + +// 编辑距离 https://leetcode-cn.com/problems/edit-distance/ +/** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ +let minDistance = (word1, word2) => { + let n = word1.length, m = word2.length + let dp = new Array(n + 1).fill(0).map(() => new Array(m + 1).fill(0)) + for (let i = 0; i <= n; i++) { + dp[i][0] = i + } + for (let j = 0; j <= m; j++) { + dp[0][j] = j + } + + for (let i = 0; i <= n; i++) { + for (let j = 0; j <= m; j++) { + if (i * j) { + dp[i][j] = word1[i - 1] == word2[j - 1] ? dp[i - 1][j - 1] : (Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1) + } else { + dp[i][j] = i + j + } + } + } + return dp[n][m] +}; + +// 矩形区域不超过 K 的最大数值和 https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k/ +/** + * @param {number[][]} matrix + * @param {number} k + * @return {number} + */ +var maxSumSubmatrix = function (matrix, k) { + const row = matrix.length; + if (!row) return 0; + const col = matrix[0].length; + if (!col) return 0; + + let max = -Infinity; + for (let l = 0; l < col; l++) { + const list = new Array(row).fill(0); + for (let r = l; r < col; r++) { + for (let k = 0; k < row; k++) list[k] += matrix[k][r]; + const m = maxSubarraySumNoMoreThanK(list, k); + max = Math.max(max, m); + } + } + + function maxSubarraySumNoMoreThanK(list, k) { + let max = -Infinity; + const preSum = [0]; + let accu = 0; + for (let i = 0; i < list.length; i++) { + accu += list[i]; + const index = findLowerBound(preSum, accu - k); + const sum = accu - preSum[index]; + if (sum <= k) max = Math.max(max, sum); + insert(preSum, accu); + } + return max; + } + + function insert(nums, target) { + if (target >= nums[nums.length - 1]) { + nums.push(target); + return; + } + const index = findLowerBound(nums, target); + nums.splice(index, 0, target); + } + + function findLowerBound(nums, target) { + let l = 0; + let r = nums.length - 1; + while (l < r) { + const mid = l + r >>> 1; + if (nums[mid] >= target) r = mid; + else l = mid + 1; + } + return l; + } + + if (max === -Infinity) return 0; + return max; +}; + +// 青蛙过河 https://leetcode-cn.com/problems/frog-jump/ +/** + * @param {number[]} stones + * @return {boolean} + */ +var canCross = function (stones) { + let len = stones.length; + let no_way = {}; + return jump(0, 0); + + function jump(curr, jumpLength) { + if (curr == len - 1) return true; + if (no_way[curr + ":" + jumpLength]) return false; + + for (let i = curr + 1; i < len; i++) { + let length = stones[i] - stones[curr]; + if (length > jumpLength + 1) break; + else if (length < jumpLength - 1) continue; + else if (jump(i, length)) return true; + } + no_way[curr + ":" + jumpLength] = true; + return false; + } +}; + +// 分割数组的最大值 https://leetcode-cn.com/problems/split-array-largest-sum/ +/** + * @param {number[]} nums + * @param {number} m + * @return {number} + */ +var splitArray = function (nums, m) { + let left = 0, right = 0; + let len = nums.length; + for (let i = 0; i < len; i++) { + right += nums[i]; + if (left < nums[i]) { + left = nums[i] + } + } + function check(mid, m) { + let sum = 0; + let cnt = 1; + for (let i = 0; i < len; i++) { + if (sum + nums[i] > mid) { + cnt++; + sum = nums[i] + } else { + sum += nums[i] + } + } + return cnt <= m; + } + while (left < right) { + let mid = Math.floor((left + right) / 2) + if (check(mid, m)) { + right = mid; + } else { + left = mid + 1; + } + } + return left +}; + +// 学生出勤记录 II https://leetcode-cn.com/problems/student-attendance-record-ii/ +/** + * @param {number} n + * @return {number} + */ +var checkRecord = function (n) { + let P = 1, // 不含A以P结尾的数量 + L = 1, // 不含A以L结尾不以LL结尾的数量 + LL = 0, // 不含A以LL结尾的数量 + A = 1, // 含有A并且以A结尾的数量 + AP = 0, // 含有A并且以P结尾的数量 + AL = 0, // 含有A并且以L结尾不以LL结尾的数量 + ALL = 0;// 含有A并且以LL结尾的数量 + for (let i = 1; i < n; ++i) { + [P, L, LL, A, AP, AL, ALL] = [ + (P + L + LL) % 1000000007, + P, + L, + (P + L + LL) % 1000000007, + (A + AP + AL + ALL) % 1000000007, + (A + AP) % 1000000007, + AL + ] + } + return (P + L + LL + A + AP + AL + ALL) % 1000000007 +}; + +// 最小覆盖子串 https://leetcode-cn.com/problems/minimum-window-substring/ +/** + * @param {string} s + * @param {string} t + * @return {string} + */ +var minWindow = (s, t) => { + let minLen = s.length + 1; + let start = s.length; // 结果子串的起始位置 + let map = {}; // 存储目标字符和对应的缺失个数 + let missingType = 0; // 当前缺失的字符种类数 + for (const c of t) { // t为baac的话,map为{a:2,b:1,c:1} + if (!map[c]) { + missingType++; // 需要找齐的种类数 +1 + map[c] = 1; + } else { + map[c]++; + } + } + let l = 0, r = 0; // 左右指针 + for (; r < s.length; r++) { // 主旋律扩张窗口,超出s串就结束 + let rightChar = s[r]; // 获取right指向的新字符 + if (map[rightChar] !== undefined) map[rightChar]--; // 是目标字符,它的缺失个数-1 + if (map[rightChar] == 0) missingType--; // 它的缺失个数新变为0,缺失的种类数就-1 + while (missingType == 0) { // 当前窗口包含所有字符的前提下,尽量收缩窗口 + if (r - l + 1 < minLen) { // 窗口宽度如果比minLen小,就更新minLen + minLen = r - l + 1; + start = l; // 更新最小窗口的起点 + } + let leftChar = s[l]; // 左指针要右移,左指针指向的字符要被丢弃 + if (map[leftChar] !== undefined) map[leftChar]++; // 被舍弃的是目标字符,缺失个数+1 + if (map[leftChar] > 0) missingType++; // 如果缺失个数新变为>0,缺失的种类+1 + l++; // 左指针要右移 收缩窗口 + } + } + if (start == s.length) return ""; + return s.substring(start, start + minLen); // 根据起点和minLen截取子串 +}; + +// 戳气球 https://leetcode-cn.com/problems/burst-balloons/ +/** + * @param {number[]} nums + * @return {number} + */ +var maxCoins = function (nums) { + let n = nums.length; + // 添加两侧的虚拟气球 + let points = [1, ...nums, 1]; + let dp = Array.from(Array(n + 2), () => Array(n + 2).fill(0)); + // 最后一行开始遍历,从下往上 + for (let i = n; i >= 0; i--) { + // 从左往右 + for (let j = i + 1; j < n + 2; j++) { + for (let k = i + 1; k < j; k++) { + dp[i][j] = Math.max( + dp[i][j], + points[j] * points[k] * points[i] + dp[i][k] + dp[k][j] + ); + } + } + } + return dp[0][n + 1]; +}; \ No newline at end of file diff --git a/Week_06/index.html b/Week_06/index.html new file mode 100644 index 00000000..3d4dadb6 --- /dev/null +++ b/Week_06/index.html @@ -0,0 +1,12 @@ + + + + + + + 第六周作业 + + + + + \ No newline at end of file From 4b3ca0812b9ac6e6a9b1b7df295ba60251fafa57 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 13 Mar 2021 21:41:21 +0800 Subject: [PATCH 14/22] readme --- Week_06/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Week_06/README.md b/Week_06/README.md index c84b9e4e..8fa1b760 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -18,7 +18,8 @@ 2. 确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。 3. 确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。 4. 寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。 - 一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。实际应用中可以按以下几个简化的步骤进行设计: + 一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。 + 实际应用中可以按以下几个简化的步骤进行设计: 5. 分析最优解的性质,并刻画其结构特征。 6. 递归的定义最优解。 7. 以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值 From 25e0bcaa452cd1436c21c304b7e9253bf0e52619 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 13 Mar 2021 21:42:46 +0800 Subject: [PATCH 15/22] read --- Week_06/README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Week_06/README.md b/Week_06/README.md index 8fa1b760..13f0a0a0 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -18,9 +18,12 @@ 2. 确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。 3. 确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。 4. 寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。 - 一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。 - 实际应用中可以按以下几个简化的步骤进行设计: -5. 分析最优解的性质,并刻画其结构特征。 -6. 递归的定义最优解。 -7. 以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值 -8. 根据计算最优值时得到的信息,构造问题的最优解 + +一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。 + +实际应用中可以按以下几个简化的步骤进行设计: + +1. 分析最优解的性质,并刻画其结构特征。 +2. 递归的定义最优解。 +3. 以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值 +4. 根据计算最优值时得到的信息,构造问题的最优解 From e9bdbaf8d6f3eacfd2068b5a18310c0cddba0ca2 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Sat, 20 Mar 2021 13:55:02 +0800 Subject: [PATCH 16/22] =?UTF-8?q?=E7=AC=AC=E4=B8=83=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_07/README.md | 12 +- Week_07/class_13_homework.js | 230 +++++++++++++++++++++++++++++++++++ Week_07/index.html | 12 ++ 3 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 Week_07/class_13_homework.js create mode 100644 Week_07/index.html diff --git a/Week_07/README.md b/Week_07/README.md index 50de3041..9c8a6b67 100644 --- a/Week_07/README.md +++ b/Week_07/README.md @@ -1 +1,11 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## 搜索算法 + +### 定义 + +搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。现阶段一般有枚举算法、深度优先搜索、广度优先搜索、A\*算法、回溯算法、蒙特卡洛树搜索、散列函数等算法。在大规模实验环境中,通常通过在搜索前,根据条件降低搜索规模;根据问题的约束条件进行剪枝;利用搜索过程中的中间解,避免重复计算这几种方法进行优化。 + +#### 运算原理 + +搜索算法实际上是根据初始条件和扩展规则构造一棵“解答树”并寻找符合目标状态的节点的过程。所有的搜索算法从最终的算法实现上来看,都可以划分成两个部分——控制结构(扩展节点的方式)和产生系统(扩展节点),而所有的算法优化和改进主要都是通过修改其控制结构来完成的。其实,在这样的思考过程中,我们已经不知不觉地将一个具体的问题抽象成了一个图论的模型——树,即搜索算法的使用第一步在于搜索树的建立。完成搜索的过程就是找到一条从根结点到目标结点的路径,找出一个最优的解。这种搜索算法的实现类似于图或树的遍历,通常可以有两种不同的实现方法,即深度优先搜索(DFS——Depth First search)和广度优先搜索(BFS——Breadth First Search)。 diff --git a/Week_07/class_13_homework.js b/Week_07/class_13_homework.js new file mode 100644 index 00000000..d8b2483b --- /dev/null +++ b/Week_07/class_13_homework.js @@ -0,0 +1,230 @@ +// 爬楼梯 https://leetcode-cn.com/problems/climbing-stairs/ +/** + * @param {number} n + * @return {number} + */ +const climbStairs = function (n) { + if (n <= 0) return 0; + if (n <= 2) return n; + + let arr = [1, 1] + + for (let i = 2; i <= n; i++) { + arr.push(arr[i - 1] + arr[i - 2]) + } + return arr[n] +}; + +// 括号生成 https://leetcode-cn.com/problems/generate-parentheses/ +/** + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = function (n) { + const res = []; + + const dfs = (lRemain, rRemain, str) => { // 左右括号所剩的数量,str是当前构建的字符串 + if (str.length == 2 * n) { // 字符串构建完成 + res.push(str); // 加入解集 + return; // 结束当前递归分支 + } + if (lRemain > 0) { // 只要左括号有剩,就可以选它,然后继续做选择(递归) + dfs(lRemain - 1, rRemain, str + "("); + } + if (lRemain < rRemain) { // 右括号比左括号剩的多,才能选右括号 + dfs(lRemain, rRemain - 1, str + ")"); // 然后继续做选择(递归) + } + }; + + dfs(n, n, ""); // 递归的入口,剩余数量都是n,初始字符串是空串 + return res; +}; + +// 有效的数独 https://leetcode-cn.com/problems/valid-sudoku/ +/** + * @param {character[][]} board + * @return {boolean} + */ +var isValidSudoku = function (board) { + let box_val = new Array(9).fill(0).map(() => new Map()) + let row_val = new Array(9).fill(0).map(() => new Map()) + let col_val = new Array(9).fill(0).map(() => new Map()) + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + if (board[i][j] === '.') continue + let num = board[i][j] + let box_idx = Math.floor(i / 3) * 3 + Math.floor(j / 3) + if (box_val[box_idx].has(board[i][j])) return false + if (row_val[i].has(board[i][j])) return false + if (col_val[j].has(board[i][j])) return false + box_val[box_idx].set(board[i][j], 1) + row_val[i].set(board[i][j], 1) + col_val[j].set(board[i][j], 1) + } + } + return true +}; + +// 单词接龙 https://leetcode-cn.com/problems/word-ladder/ +/** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ +var ladderLength = function (beginWord, endWord, wordList) { + let wordListSet = new Set(wordList); + if (!wordListSet.has(endWord)) { + return 0; + } + let beginSet = new Set(); + beginSet.add(beginWord); + let endSet = new Set(); + endSet.add(endWord) + let level = 1; + // BFS + while (beginSet.size > 0) { + let next_beginSet = new Set(); + for (let key of beginSet) { + for (let i = 0; i < key.length; i++) { + for (let j = 0; j < 26; j++) { + let s = String.fromCharCode(97 + j); + if (s != key[i]) { + let new_word = key.slice(0, i) + s + key.slice(i + 1); + if (endSet.has(new_word)) { + return level + 1; + } + if (wordListSet.has(new_word)) { + next_beginSet.add(new_word); + wordListSet.delete(new_word); + } + } + } + } + } + beginSet = next_beginSet; + level++; + if (beginSet.size > endSet.size) { + let tmp = beginSet; + beginSet = endSet; + endSet = tmp; + } + } + return 0; +} + +// 最小基因变化 https://leetcode-cn.com/problems/minimum-genetic-mutation/ +/** + * @param {string} start + * @param {string} end + * @param {string[]} bank + * @return {number} + */ +var minMutation = function (start, end, bank) { + let bankSet = new Set(bank); + if (!bankSet.has(end)) return -1; + let queue = [[start, 0]]; + let dna = ["A", "C", "G", "T"]; + while (queue.length) { + let [node, count] = queue.shift(); + if (node === end) return count; + for (let i = 0; i < node.length; i++) { + for (let j = 0; j < dna.length; j++) { + let d = node.slice(0, i) + dna[j] + node.slice(i + 1); + if (bankSet.has(d)) { + queue.push([d, count + 1]); + bankSet.delete(d); + } + } + } + } + return -1; +}; + +// N皇后 https://leetcode-cn.com/problems/n-queens/submissions/ +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = (n) => { + const board = new Array(n); + for (let i = 0; i < n; i++) { // 棋盘的初始化 + board[i] = new Array(n).fill('.'); + } + const res = []; + const isValid = (row, col) => { + for (let i = 0; i < row; i++) { // 之前的行 + for (let j = 0; j < n; j++) { // 所有的列 + if (board[i][j] == 'Q' && // 发现了皇后,并且和自己同列/对角线 + (j == col || i + j === row + col || i - j === row - col)) { + return false; // 不是合法的选择 + } + } + } + return true; + }; + const helper = (row) => { // 放置当前行的皇后 + if (row == n) { // 递归的出口,超出了最后一行 + const stringsBoard = board.slice(); // 拷贝一份board + for (let i = 0; i < n; i++) { + stringsBoard[i] = stringsBoard[i].join(''); // 将每一行拼成字符串 + } + res.push(stringsBoard); // 推入res数组 + return; + } + for (let col = 0; col < n; col++) { // 枚举出所有选择 + if (isValid(row, col)) { // 剪掉无效的选择 + board[row][col] = "Q"; // 作出选择,放置皇后 + helper(row + 1); // 继续选择,往下递归 + board[row][col] = '.'; // 撤销当前选择 + } + } + }; + helper(0); // 从第0行开始放置 + return res; +}; + +// 解数独 https://leetcode-cn.com/problems/sudoku-solver/ +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solveSudoku = (board) => { + const hasConflit = (r, c, val) => { // 判断是否有行列和框框的冲突 + for (let i = 0; i < 9; i++) { + if (board[i][c] == val || board[r][i] == val) { // 行或列里有冲突 + return true; + } + } + const subRowStart = Math.floor(r / 3) * 3; // 对于小框,行有三种起始索引 0、3、6 + const subColStart = Math.floor(c / 3) * 3; // 对于小框,列有三种起始索引 0、3、6 + for (let i = 0; i < 3; i++) { // 遍历所在的小框 + for (let j = 0; j < 3; j++) { + if (val == board[subRowStart + i][subColStart + j]) { // 发现了重复数 + return true; + } + } + } + return false; // 没有发生冲突 + }; + + const fill = (i, j) => { + if (j == 9) { // 列越界,填完一行,填下一行 + i++; + j = 0; + if (i == 9) return true; // 都填完了,返回true + } + if (board[i][j] != ".") return fill(i, j + 1); // 不是空白格,递归填下一格 + + for (let num = 1; num <= 9; num++) { // 枚举出当前格的所有可填的选择 + if (hasConflit(i, j, String(num))) continue; // 如果存在冲突,跳过这个选择 + board[i][j] = String(num); // 作出一个选择 + if (fill(i, j + 1)) return true; // 如果基于它,填下一格,最后可以解出数独,直接返回true + board[i][j] = "."; // 如果基于它,填下一格,填1-9都不行,回溯,恢复为空白格 + } + return false; // 尝试了1-9,每个都往下递归,都不能做完,返回false + }; + + fill(0, 0); // 从第一个格子开始填 + return board; +}; \ No newline at end of file diff --git a/Week_07/index.html b/Week_07/index.html new file mode 100644 index 00000000..b03b42cc --- /dev/null +++ b/Week_07/index.html @@ -0,0 +1,12 @@ + + + + + + + 第七周作业 + + + + + \ No newline at end of file From 9511d276a0c74164579a78249d99d219a0b2bf41 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Wed, 24 Mar 2021 15:10:10 +0800 Subject: [PATCH 17/22] =?UTF-8?q?=E7=AC=AC=E5=85=AB=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_08/README.md | 75 ++++++- Week_08/class_16_homework.js | 394 +++++++++++++++++++++++++++++++++++ Week_08/index.html | 12 ++ 3 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 Week_08/class_16_homework.js create mode 100644 Week_08/index.html diff --git a/Week_08/README.md b/Week_08/README.md index 50de3041..85d2ff3d 100644 --- a/Week_08/README.md +++ b/Week_08/README.md @@ -1 +1,74 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +## 字典树 + +又称单词查找树,Trie 树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。 + +### 性质 + +1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符; +2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; +3. 每个节点的所有子节点包含的字符都不相同。 + +### 操作与实现方法 + +其基本操作有:查找、插入和删除,当然删除操作比较少见。 + +搜索字典项目的方法为: + +1. 从根结点开始一次搜索; +2. 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索; +3. 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。 +4. 迭代过程…… +5. 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。 + 其他操作类似处理 + +## 并查集 + +并查集,在一些有 N 个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1 ~ 3 秒)内计算出试题需要的结果,只能用并查集来描述。 +并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。常常在使用中以森林来表示。 + +### 主要操作 + +1. 初始化 + 把每个点所在集合初始化为其自身。 + 通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为 O(N)。 +2. 查找 + 查找元素所在的集合,即根节点。 +3. 合并 + 将两个元素所在的集合合并为一个集合。 + 通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。 + +## 红黑树 + +红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。 +红黑树是一种特定类型的二叉树,它是在计算机科学中用来组织数据比如数字的块的一种结构。若一棵二叉查找树是红黑树,则它的任一子树必为红黑树。 [4] +红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。 [2] +由于每一棵红黑树都是一颗二叉排序树,因此,在对红黑树进行查找时,可以采用运用于普通二叉排序树上的查找算法,在查找过程中不需要颜色信息。 + +### 特征 + +红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。 在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求: + +1. 结点是红色或黑色。 +2. 根结点是黑色。 +3. 所有叶子都是黑色。(叶子是 NIL 结点) +4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点) +5. 从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点。 + 这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。 + +## AVL 树 + +AVL 树是最先发明的自平衡二叉查找树。在 AVL 树中任何节点的两个子树的高度最大差别为 1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。 + +### 特点 + +AVL 树本质上还是一棵二叉搜索树,它的特点是: + +1. 本身首先是一棵二叉搜索树。 +2. 带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为 1。 + 也就是说,AVL 树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。 + +## 位运算 + +程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。比如,and 运算本来是一个逻辑运算符,但整数与整数之间也可以进行 and 运算。举个例子,6 的二进制是 110,11 的二进制是 1011,那么 6 and 11 的结果就是 2,它是二进制对应位进行逻辑运算的结果(0 表示 False,1 表示 True,空位都当 0 处理)。 diff --git a/Week_08/class_16_homework.js b/Week_08/class_16_homework.js new file mode 100644 index 00000000..714bed42 --- /dev/null +++ b/Week_08/class_16_homework.js @@ -0,0 +1,394 @@ +// 位1的个数 https://leetcode-cn.com/problems/number-of-1-bits/ +/** + * @param {number} n - a positive integer + * @return {number} + */ +var hammingWeight = function (n) { + let ret = 0; + for (let i = 0; i < 32; i++) { + if ((n & (1 << i)) !== 0) { + ret++; + } + } + return ret; +}; + +// 2 的幂 https://leetcode-cn.com/problems/power-of-two/ +/** + * @param {number} n + * @return {boolean} + */ +var isPowerOfTwo = function (n) { + return n > 0 && (n & (n - 1)) == 0; +}; + +// 颠倒二进制位 https://leetcode-cn.com/problems/reverse-bits/ +/** + * @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 = (res << 1) + (n & 1); // 取末尾 + n >>= 1; + } + return res >>> 0 // 无符号右移 +}; + +// 实现 Trie (前缀树) https://leetcode-cn.com/problems/implement-trie-prefix-tree/ +/** + * Initialize your data structure here. + */ +var Trie = function () { + this.h = {}; +}; + +/** + * Inserts a word into the trie. + * @param {string} word + * @return {void} + */ +Trie.prototype.insert = function (word) { + let h = this.h; + for (const w of word) { + !h[w] && (h[w] = {}), h = h[w] + } + h.isEnd = 1 +}; + +/** + * Returns if the word is in the trie. + * @param {string} word + * @return {boolean} + */ +Trie.prototype.search = function (word) { + return (h = this.s(word)) && h.isEnd !== undefined +}; + +Trie.prototype.s = function (word) { + let h = this.h + for (const w of word) { + if (!h[w]) return false + h = h[w] + } + return h +} + +/** + * 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) { + return this.s(prefix) !== false +}; + +/** + * 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) + */ + +// 省份数量 https://leetcode-cn.com/problems/number-of-provinces/ +/** + * @param {number[][]} isConnected + * @return {number} + */ +var findCircleNum = function (isConnected) { + let res = 0, i = -1, n = isConnected.length, visited = new Uint8Array(n), + q = new Uint8Array(n ** 2), p1 = p2 = 0 + while (++i < n) + if (visited[i] === 0) { + res++ + q[p1++] = i + while (p2 < p1 && p2 < q.length) { + const k = q[p2++] + visited[k] = 1 + for (let j = 0; j < isConnected[k].length; j++) + if (isConnected[k][j] && visited[j] === 0) q[p1++] = j + } + } + return res +}; + +// 岛屿数量 https://leetcode-cn.com/problems/number-of-islands/ +/** + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + let m = grid.length; + if (m == 0) { + return 0; + } + let n = grid[0].length; + let count = 0; + let parent = []; + let rank = []; + + + let find = (p) => { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + return p; + } + let union = (p, q) => { + let rootP = find(p); + let rootQ = find(q); + if (rootP == rootQ) { + return; + } + if (rank[rootP] > rank[rootQ]) { + parent[rootQ] = rootP; + } else if (rank[rootP] < rank[rootQ]) { + parent[rootP] = rootQ; + } else { + parent[rootP] = rootQ; + rank[rootQ]++; + } + count--; + } + + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] == 1) { + parent[i * n + j] = i * n + j; + count++; + } + rank[i * n + j] = 0; + } + } + + for (var i = 0; i < m; i++) { + for (var j = 0; j < n; j++) { + if (grid[i][j] == 1) { + grid[i][j] = 0; + i - 1 >= 0 && grid[i - 1][j] == 1 && union(i * n + j, (i - 1) * n + j); + j - 1 >= 0 && grid[i][j - 1] == 1 && union(i * n + j, i * n + j - 1); + i + 1 < m && grid[i + 1][j] == 1 && union(i * n + j, (i + 1) * n + j); + j + 1 < n && grid[i][j + 1] == 1 && union(i * n + j, i * n + j + 1); + } + } + } + return count; +}; + +// 被围绕的区域 https://leetcode-cn.com/problems/surrounded-regions/ +/** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ +var solve = (board) => { + const m = board.length; + if (m == 0) return; + const n = board[0].length; + const dfs = (i, j) => { + if (i < 0 || i == m || j < 0 || j == n) return; + if (board[i][j] == 'O') { + board[i][j] = 'NO'; + dfs(i + 1, j); + dfs(i - 1, j); + dfs(i, j + 1); + dfs(i, j - 1); + } + }; + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (i == 0 || i == m - 1 || j == 0 || j == n - 1) { + if (board[i][j] == 'O') dfs(i, j); + } + } + } + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (board[i][j] === 'NO') board[i][j] = 'O'; + else if (board[i][j] === 'O') board[i][j] = 'X'; + } + } +}; + +// 单词搜索 II https://leetcode-cn.com/problems/word-search-ii/ +/** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ +var findWords = function (board, words) { + // 构建字典树 + class TrieNode { + constructor() { + this.END = false; + this.children = new Array(26); + } + containsKey(letter) { + return this.children[letter.charCodeAt() - 97] != undefined; + } + put(letter, newTrieNode) { + this.children[letter.charCodeAt() - 97] = newTrieNode; + } + getNext(letter) { + return this.children[letter.charCodeAt() - 97]; + } + setEnd() { + this.END = true; + } + isEnd() { + return this.END; + } + } + let root = null; + let Trie = function () { + root = new TrieNode(); + } + Trie.prototype.insert = (word) => { + let currNode = root; + for (let i = 0; i < word.length; i++) { + if (!currNode.containsKey(word[i])) { + currNode.put(word[i], new TrieNode()); + } + currNode = currNode.getNext(word[i]); + } + currNode.setEnd(); + } + let searchPrefix = (word) => { + let currNode = root; + for (let i = 0; i < word.length; i++) { + if (currNode.containsKey(word[i])) { + currNode = currNode.getNext(word[i]); + } else { + return null; + } + } + return currNode; + } + Trie.prototype.search = (word) => { + let currNode = searchPrefix(word); + return currNode != null && currNode.isEnd(); + } + Trie.prototype.startsWith = (prefix) => { + let currNode = searchPrefix(prefix); + return currNode != null; + } + // 初始化变量 + let m = board.length; + let n = board[0].length; + // 初始化字典树 + let wordsTrie = new Trie(); + for (let i = 0; i < words.length; i++) { + wordsTrie.insert(words[i]); + } + // 搜索方向向量 + let dx = [-1, 1, 0, 0]; + let dy = [0, 0, -1, 1]; + // DFS 搜索 + let boardDFS = (i, j, curStr) => { + let restore = board[i][j]; + curStr += restore; + // 字典树中找到了 + if (wordsTrie.search(curStr) && result.indexOf(curStr) == -1) { + result.push(curStr); + } + // 减枝 - 拼接字符判断是否存在于字典树中,如果前缀都不是,直接false + if (!wordsTrie.startsWith(curStr)) { + return; + } + // 前进 + board[i][j] = '#'; + for (let r = 0; r < 4; r++) { + let tmp_i = dx[r] + i; + let tmp_j = dy[r] + j; + // 边界情况处理 + if (tmp_i >= 0 && tmp_i < m && tmp_j >= 0 && tmp_j < n && board[tmp_i][tmp_j] != '#') { + boardDFS(tmp_i, tmp_j, curStr); + } + } + // 还原(回溯) + board[i][j] = restore; + } + // 寻找结果 + let result = []; + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + boardDFS(i, j, ''); + } + } + return result; +}; + +// N 皇后 https://leetcode-cn.com/problems/n-queens/ +/** + * @param {number} n + * @return {string[][]} + */ +var solveNQueens = (n) => { + const board = new Array(n); + for (let i = 0; i < n; i++) { // 棋盘的初始化 + board[i] = new Array(n).fill('.'); + } + const res = []; + const isValid = (row, col) => { + for (let i = 0; i < row; i++) { // 之前的行 + for (let j = 0; j < n; j++) { // 所有的列 + if (board[i][j] == 'Q' && // 发现了皇后,并且和自己同列/对角线 + (j == col || i + j === row + col || i - j === row - col)) { + return false; // 不是合法的选择 + } + } + } + return true; + }; + const helper = (row) => { // 放置当前行的皇后 + if (row == n) { // 递归的出口,超出了最后一行 + const stringsBoard = board.slice(); // 拷贝一份board + for (let i = 0; i < n; i++) { + stringsBoard[i] = stringsBoard[i].join(''); // 将每一行拼成字符串 + } + res.push(stringsBoard); // 推入res数组 + return; + } + for (let col = 0; col < n; col++) { // 枚举出所有选择 + if (isValid(row, col)) { // 剪掉无效的选择 + board[row][col] = "Q"; // 作出选择,放置皇后 + helper(row + 1); // 继续选择,往下递归 + board[row][col] = '.'; // 撤销当前选择 + } + } + }; + helper(0); // 从第0行开始放置 + return res; +}; + +// N 皇后 II https://leetcode-cn.com/problems/n-queens-ii/ +/** + * @param {number} n + * @return {number} + */ +var totalNQueens = function (n) { + let count = 0 + + const dfs = (x, y, arr) => { + for (let [x0, y0] of arr) { + if (y === y0 || Math.abs(x - x0) === Math.abs(y - y0)) { + return + } + } + if (x === n - 1) { + count++ + return + } + for (let i = 0; i < n; i++) { + arr.push([x, y]) + dfs(x + 1, i, arr) + arr.pop() + } + } + for (let i = 0; i < n; i++) { + dfs(0, i, []) + } + return count +}; \ No newline at end of file diff --git a/Week_08/index.html b/Week_08/index.html new file mode 100644 index 00000000..58996c2d --- /dev/null +++ b/Week_08/index.html @@ -0,0 +1,12 @@ + + + + + + + 第八周作业 + + + + + \ No newline at end of file From 5cc72bef1653190b1a37e7f20da018183e395f70 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Mon, 29 Mar 2021 11:17:27 +0800 Subject: [PATCH 18/22] =?UTF-8?q?=E7=AC=AC=E4=B9=9D=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_09/class_19_homework.js | 295 +++++++++++++++++++++++++++++++++++ Week_09/index.html | 12 ++ 2 files changed, 307 insertions(+) create mode 100644 Week_09/class_19_homework.js create mode 100644 Week_09/index.html diff --git a/Week_09/class_19_homework.js b/Week_09/class_19_homework.js new file mode 100644 index 00000000..b3bf9a9a --- /dev/null +++ b/Week_09/class_19_homework.js @@ -0,0 +1,295 @@ +// 数组的相对排序 https://leetcode-cn.com/problems/relative-sort-array/ +/** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ +var relativeSortArray = function (arr1, arr2) { + return arr1.sort((a, b) => { + let indexA = arr2.indexOf(a), + indexB = arr2.indexOf(b) + + if (~indexA && ~indexB) { + return indexA - indexB + } else if (~indexA ^ ~indexB) { + return indexB - indexA + } else { + return a - b + } + }) +}; + +// 有效的字母异位词 https://leetcode-cn.com/problems/valid-anagram/ +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + return s.length == t.length && [...s].sort().join('') === [...t].sort().join('') +}; + +// 翻转字符串里的单词 https://leetcode-cn.com/problems/reverse-words-in-a-string/ +/** + * @param {string} s + * @return {string} + */ +var reverseWords = function (s) { + return s.trim().replace(/\s+/g, ' ').split(' ').reverse().join(' ') +}; + +// 同构字符串 https://leetcode-cn.com/problems/isomorphic-strings/ +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isIsomorphic = function (s, t) { + for (let i = 0; i < s.length; i++) + if (s.indexOf(s[i]) !== t.indexOf(t[i])) return false + return true +}; + +// LRU 缓存机制 https://leetcode-cn.com/problems/lru-cache/ +var LRUCache = function (capacity) { + this.capacity = capacity; + this.cache = new Map(); +}; + +/** + * @param {number} key + * @return {number} + */ +LRUCache.prototype.get = function (key) { + if (this.cache.has(key)) { + var val = this.cache.get(key); + this.cache.delete(key); + this.cache.set(key, val); + return val; + } else { + return -1; + } + +}; + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +LRUCache.prototype.put = function (key, value) { + if (this.cache.size < this.capacity && !this.cache.has(key)) { + this.cache.set(key, value); + } else if (this.cache.has(key)) { + this.cache.delete(key); + this.cache.set(key, value); + } else if (this.cache.size = this.capacity) { + this.cache.delete(this.cache.keys().next().value); + this.cache.set(key, value); + } +}; + +// 力扣排行榜 https://leetcode-cn.com/problems/design-a-leaderboard/ +var Leaderboard = function () { + this.board = {}; +}; + +/** + * @param {number} playerId + * @param {number} score + * @return {void} + */ +Leaderboard.prototype.addScore = function (playerId, score) { + this.board[playerId] = (this.board[playerId] || 0) + score; +}; + +/** + * @param {number} K + * @return {number} + */ +Leaderboard.prototype.top = function (K) { + return Object.values(this.board).sort((a, b) => b - a).slice(0, K).reduce((acc, curr) => acc + curr); +}; + +/** + * @param {number} playerId + * @return {void} + */ +Leaderboard.prototype.reset = function (playerId) { + delete this.board[playerId] +}; + +// 合并区间 https://leetcode-cn.com/problems/merge-intervals/ +/** + * @param {number[][]} intervals + * @return {number[][]} + */ +var merge = function (intervals) { + let res = []; + intervals.sort((a, b) => a[0] - b[0]); + + let prev = intervals[0]; + + for (let i = 1; i < intervals.length; i++) { + let cur = intervals[i]; + if (prev[1] >= cur[0]) { // 有重合 + prev[1] = Math.max(cur[1], prev[1]); + } else { // 不重合,prev推入res数组 + res.push(prev); + prev = cur; // 更新 prev + } + } + + res.push(prev); + return res; +}; + +// 最长递增子序列 https://leetcode-cn.com/problems/longest-increasing-subsequence/ +/** + * @param {number[]} nums + * @return {number} + */ +var lengthOfLIS = function (nums, dp = [1]) { + for (var i = 1; dp[i] = 1, i < nums.length; i++) + for (var j = 0; j < i; j++) + nums[i] > nums[j] && (dp[i] = Math.max(dp[i], dp[j] + 1)) + return nums.length < 2 ? nums.length : Math.max(...dp) +}; + +// 解码方法 https://leetcode-cn.com/problems/decode-ways/ +/** + * @param {string} s + * @return {number} + */ +var numDecodings = function (s) { + if (!s) return 0 + let len = s.length; + let dp = Array(len + 1).fill(0); + dp[0] = 1; + dp[1] = s[0] === '0' ? 0 : 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]; +}; + +// 翻转对 https://leetcode-cn.com/problems/reverse-pairs/ +/** + * @param {number[]} nums + * @return {number} + */ +var reversePairs = function (nums) { + let count = 0; + let mergeArr = (left, right) => { + let result = []; + let left_i = 0; + let right_j = 0; + let tmpI = 0, tmpJ = 0; + while (tmpI < left.length && tmpJ < right.length) { + if (left[tmpI] / 2 > right[tmpJ]) { + count += left.length - tmpI; + tmpJ++; + } else { + tmpI++; + } + } + while (left_i < left.length && right_j < right.length) { + if (left[left_i] < right[right_j]) { + result.push(left[left_i]); + left_i++; + } else { + result.push(right[right_j]); + right_j++; + } + } + return [...result, ...left.slice(left_i), ...right.slice(right_j)]; + } + let mergeSort = (arr) => { + if (arr.length <= 1) { + return arr; + } + let mid = arr.length >> 1; + let left = arr.slice(0, mid); + let right = arr.slice(mid); + return mergeArr(mergeSort(left), mergeSort(right)); + } + mergeSort(nums); + return count; +}; + +// 最长有效括号 https://leetcode-cn.com/problems/longest-valid-parentheses/ +/** + * @param {string} s + * @return {number} + */ +var longestValidParentheses = function (s) { + const dp = Array(s.length).fill(0); + + for (let i = 1; i < s.length; i++) { + if (s[i] === ')') { + + if (i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] === '(') { + dp[i] = dp[i - 1] + 2; + + if (i - dp[i - 1] - 2 > 0) { + dp[i] += dp[i - dp[i - 1] - 2]; + } + } + } + } + return Math.max(...dp, 0); +}; + +// 赛车 https://leetcode-cn.com/problems/race-car/ +/** + * @param {number} target + * @return {number} + */ +var racecar = function (target) { + let dp = []; + for (let i = 1; i <= target; i++) { + dp[i] = Number.MAX_VALUE; + let j = 1, cnt1 = 1; + for (; j < i; j = (1 << ++cnt1) - 1) { + for (let k = 0, cnt2 = 0; k < j; k = (1 << ++cnt2) - 1) { + dp[i] = Math.min(dp[i], cnt1 + 1 + cnt2 + 1 + dp[i - (j - k)]); + } + } + + dp[i] = Math.min(dp[i], cnt1 + (i == j ? 0 : 1 + dp[j - i])) + } + + return dp[target]; +}; + +// 不同的子序列 https://leetcode-cn.com/problems/distinct-subsequences/ +/** + * @param {string} s + * @param {string} t + * @return {number} + */ +var numDistinct = function (s, t) { + const m = s.length, n = t.length; + if (m < n) { + return 0; + } + const dp = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0)); + for (let i = 0; i <= m; i++) { + dp[i][n] = 1; + } + for (let i = m - 1; i >= 0; i--) { + for (let j = n - 1; j >= 0; j--) { + if (s[i] == t[j]) { + dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j]; + } else { + dp[i][j] = dp[i + 1][j]; + } + } + } + return dp[0][0]; +}; \ No newline at end of file diff --git a/Week_09/index.html b/Week_09/index.html new file mode 100644 index 00000000..87fe02fd --- /dev/null +++ b/Week_09/index.html @@ -0,0 +1,12 @@ + + + + + + + 第九周作业 + + + + + \ No newline at end of file From 64eabc0265cac9d03cc9a5ceb5cbd656f875d5e4 Mon Sep 17 00:00:00 2001 From: jinlolo <718071681@qq.com> Date: Sun, 4 Apr 2021 17:52:30 +0800 Subject: [PATCH 19/22] =?UTF-8?q?=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_09/README.md | 308 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 307 insertions(+), 1 deletion(-) diff --git a/Week_09/README.md b/Week_09/README.md index 50de3041..a8f4ae0e 100644 --- a/Week_09/README.md +++ b/Week_09/README.md @@ -1 +1,307 @@ -学习笔记 \ No newline at end of file +## 学习笔记 + +### 排序算法代码实现 + +1. 冒泡排序 +``` +function bubbleSort(arr) { + var len = arr.length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len - 1 - i; j++) { + if (arr[j] > arr[j+1]) { //相邻元素两两对比 + var temp = arr[j+1]; //元素交换 + arr[j+1] = arr[j]; + arr[j] = temp; + } + } + } + return arr; +} +``` + +2. 选择排序 +``` +function selectionSort(arr) { + var len = arr.length; + var minIndex, temp; + for (var i = 0; i < len - 1; i++) { + minIndex = i; + for (var j = i + 1; j < len; j++) { + if (arr[j] < arr[minIndex]) { //寻找最小的数 + minIndex = j; //将最小数的索引保存 + } + } + temp = arr[i]; + arr[i] = arr[minIndex]; + arr[minIndex] = temp; + } + return arr; +} +``` + +3. 插入排序 +``` +function insertionSort(arr) { + var len = arr.length; + var preIndex, current; + for (var i = 1; i < len; i++) { + preIndex = i - 1; + current = arr[i]; + while(preIndex >= 0 && arr[preIndex] > current) { + arr[preIndex+1] = arr[preIndex]; + preIndex--; + } + arr[preIndex+1] = current; + } + return arr; +} +``` + +4. 希尔排序 +``` +function shellSort(arr) { + var len = arr.length, + temp, + gap = 1; + while(gap < len/3) { //动态定义间隔序列 + gap =gap*3+1; + } + for (gap; gap > 0; gap = Math.floor(gap/3)) { + for (var i = gap; i < len; i++) { + temp = arr[i]; + for (var j = i-gap; j >= 0 && arr[j] > temp; j-=gap) { + arr[j+gap] = arr[j]; + } + arr[j+gap] = temp; + } + } + return arr; +} +``` + +5. 归并排序 +``` +function mergeSort(arr) { //采用自上而下的递归方法 + var len = arr.length; + if(len < 2) { + return arr; + } + var middle = Math.floor(len / 2), + left = arr.slice(0, middle), + right = arr.slice(middle); + return merge(mergeSort(left), mergeSort(right)); +} + +function merge(left, right) +{ + var result = []; + + while (left.length && right.length) { + if (left[0] <= right[0]) { + result.push(left.shift()); + } else { + result.push(right.shift()); + } + } + + while (left.length) + result.push(left.shift()); + + while (right.length) + result.push(right.shift()); + + return result; +} +``` + +6. 快速排序 +``` +function quickSort(arr, left, right) { + var len = arr.length, + partitionIndex, + left = typeof left != 'number' ? 0 : left, + right = typeof right != 'number' ? len - 1 : right; + + if (left < right) { + partitionIndex = partition(arr, left, right); + quickSort(arr, left, partitionIndex-1); + quickSort(arr, partitionIndex+1, right); + } + return arr; +} + +function partition(arr, left ,right) { //分区操作 + var pivot = left, //设定基准值(pivot) + index = pivot + 1; + for (var i = index; i <= right; i++) { + if (arr[i] < arr[pivot]) { + swap(arr, i, index); + index++; + } + } + swap(arr, pivot, index - 1); + return index-1; +} + +function swap(arr, i, j) { + var temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; +} +``` + +7. 堆排序 +``` +var len; //因为声明的多个函数都需要数据长度,所以把len设置成为全局变量 + +function buildMaxHeap(arr) { //建立大顶堆 + len = arr.length; + for (var i = Math.floor(len/2); i >= 0; i--) { + heapify(arr, i); + } +} + +function heapify(arr, i) { //堆调整 + var left = 2 * i + 1, + right = 2 * i + 2, + largest = i; + + if (left < len && arr[left] > arr[largest]) { + largest = left; + } + + if (right < len && arr[right] > arr[largest]) { + largest = right; + } + + if (largest != i) { + swap(arr, i, largest); + heapify(arr, largest); + } +} + +function swap(arr, i, j) { + var temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; +} + +function heapSort(arr) { + buildMaxHeap(arr); + + for (var i = arr.length-1; i > 0; i--) { + swap(arr, 0, i); + len--; + heapify(arr, 0); + } + return arr; +} +``` + +8. 计数排序 +``` +function countingSort(arr, maxValue) { + var bucket = new Array(maxValue+1), + sortedIndex = 0; + arrLen = arr.length, + bucketLen = maxValue + 1; + + for (var i = 0; i < arrLen; i++) { + if (!bucket[arr[i]]) { + bucket[arr[i]] = 0; + } + bucket[arr[i]]++; + } + + for (var j = 0; j < bucketLen; j++) { + while(bucket[j] > 0) { + arr[sortedIndex++] = j; + bucket[j]--; + } + } + + return arr; +} +``` + +9. 桶排序 +``` +function bucketSort(arr, bucketSize) { + if (arr.length === 0) { + return arr; + } + + var i; + var minValue = arr[0]; + var maxValue = arr[0]; + for (i = 1; i < arr.length; i++) { + if (arr[i] < minValue) { + minValue = arr[i]; //输入数据的最小值 + } else if (arr[i] > maxValue) { + maxValue = arr[i]; //输入数据的最大值 + } + } + + //桶的初始化 + var DEFAULT_BUCKET_SIZE = 5; //设置桶的默认数量为5 + bucketSize = bucketSize || DEFAULT_BUCKET_SIZE; + var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; + var buckets = new Array(bucketCount); + for (i = 0; i < buckets.length; i++) { + buckets[i] = []; + } + + //利用映射函数将数据分配到各个桶中 + for (i = 0; i < arr.length; i++) { + buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]); + } + + arr.length = 0; + for (i = 0; i < buckets.length; i++) { + insertionSort(buckets[i]); //对每个桶进行排序,这里使用了插入排序 + for (var j = 0; j < buckets[i].length; j++) { + arr.push(buckets[i][j]); + } + } + + return arr; +} +``` + +10. 基数排序 +``` +//LSD Radix Sort +var counter = []; +function radixSort(arr, maxDigit) { + var mod = 10; + var dev = 1; + for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) { + for(var j = 0; j < arr.length; j++) { + var bucket = parseInt((arr[j] % mod) / dev); + if(counter[bucket]==null) { + counter[bucket] = []; + } + counter[bucket].push(arr[j]); + } + var pos = 0; + for(var j = 0; j < counter.length; j++) { + var value = null; + if(counter[j]!=null) { + while ((value = counter[j].shift()) != null) { + arr[pos++] = value; + } + } + } + } + return arr; +} +``` + +#### 不同路径II状态转移方程 + +解题思路:使用动态规划,用一个二维数组dp[i][j]存储状态变量。 +其中dp[i][j]表示从起始点到第i行第j列的不同路径的个数; +状态转移方程: +1. 如果网格矩阵a[i-1][j]==1 || a[i][j-1]==1,状态转移方程为dp[i][j] = max(dp[i-1][j], dp[i][j-1]); +2. 如果网格矩阵a[i][j ==1,状态转移方程为dp[i][j]=0; +3. 如果网格矩阵a[i-1][j]==0 && a[i][j-1]==0,状态转移方程为dp[i][j] = dp[i-1][j] + dp[i][j-1]; \ No newline at end of file From a7fd16c96706ff0af097f98dfe10affdb777680c Mon Sep 17 00:00:00 2001 From: jinlolo <718071681@qq.com> Date: Sun, 11 Apr 2021 18:12:49 +0800 Subject: [PATCH 20/22] =?UTF-8?q?=E7=AC=AC=E5=8D=81=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../class_20_homework.js" | 283 ++++++++++++++++++ .../index.html" | 14 + 2 files changed, 297 insertions(+) create mode 100644 "Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/class_20_homework.js" create mode 100644 "Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/index.html" diff --git "a/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/class_20_homework.js" "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/class_20_homework.js" new file mode 100644 index 00000000..d388c3c3 --- /dev/null +++ "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/class_20_homework.js" @@ -0,0 +1,283 @@ +// 字符串中的第一个唯一字符(https://leetcode-cn.com/problems/first-unique-character-in-a-string/) +/** + * @param {string} s + * @return {number} + */ + var firstUniqChar = function(s) { + let h = new Uint16Array(26), i = s.length + while (i--) h[s.charCodeAt(i) - 97]++ + i = -1 + while (++i < s.length) + if (h[s.charCodeAt(i) - 97] === 1) + return i + return -1 +}; + +// 反转字符串 II(https://leetcode-cn.com/problems/reverse-string-ii/) +/** + * @param {string} s + * @param {number} k + * @return {string} + */ + var reverseStr = function(s, k) { + if(k == 1) return s + let result = '' + let temp = '' + let dobulek = 2 * k + for (let i = 0; i < s.length; i++) { + const element = s[i]; + let kyu = i % dobulek + if(kyu == 0){ + result += temp + temp = '' + } + if(kyu < k){ + temp = element + temp + }else { + temp = temp + element + } + } + + return result + temp +}; + +// 翻转字符串里的单词(https://leetcode-cn.com/problems/reverse-words-in-a-string/) +/** + * @param {string} s + * @return {string} + */ + var reverseWords = function(s) { + return s.trim().split(/\s+/).reverse().join(' '); +}; + + +// 反转字符串中的单词 III(https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/) +/** + * @param {string} s + * @return {string} + */ + var reverseWords = function(s) { + const ret = []; + const length = s.length; + let i = 0; + while (i < length) { + let start = i; + while (i < length && s.charAt(i) != ' ') { + i++; + } + for (let p = start; p < i; p++) { + ret.push(s.charAt(start + i - 1 - p)); + } + while (i < length && s.charAt(i) == ' ') { + i++; + ret.push(' '); + } + } + return ret.join(''); +}; + +// 仅仅反转字母(https://leetcode-cn.com/problems/reverse-only-letters/) +/** + * @param {string} S + * @return {string} + */ + var reverseOnlyLetters = function(S) { + var arr = S.match(/[a-zA-Z]/g) + if (arr === null) return S + return S.replace(/[a-zA-Z]/g, () => arr.pop()) +} + +// 同构字符串(https://leetcode-cn.com/problems/isomorphic-strings/) +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + var isIsomorphic = function(s, t) { + const s2t = {}; + const t2s = {}; + const len = s.length; + for (let i = 0; i < len; ++i) { + const x = s[i], y = t[i]; + if ((s2t[x] && s2t[x] !== y) || (t2s[y] && t2s[y] !== x)) { + return false; + } + s2t[x] = y; + t2s[y] = x; + } + return true; +}; + +// 验证回文字符串Ⅱ(https://leetcode-cn.com/problems/valid-palindrome-ii/) +/** + * @param {string} s + * @return {boolean} + */ + var validPalindrome = function(s) { + let n = s.length; + if(n < 2){ + return s; + } + let isPalindrome = (left,right)=> { + while(left < right){ + if(s[left++] != s[right--]){ + return false; + } + } + return true; + } + for(let i = 0;i < n;i++){ + if(s[i] != s[n-i-1]){ + return isPalindrome(i+1,n-i-1) || isPalindrome(i,n-1-i-1); + } + } + return true; +}; + +// 字符串转换整数 (atoi)(https://leetcode-cn.com/problems/string-to-integer-atoi/) +/** + * @param {string} str + * @return {number} + */ + var myAtoi = function(str) { + const number = parseInt(str, 10); + + if(isNaN(number)) { + return 0; + } else if (number < Math.pow(-2, 31) || number > Math.pow(2, 31) - 1) { + return number < Math.pow(-2, 31) ? Math.pow(-2, 31) : Math.pow(2, 31) - 1; + } else { + return number; + } +}; + +// 找到字符串中所有字母异位词(https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/) +/** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + var findAnagrams = function (s, p) { + const res = [], win = {}, need = {}, pLen = p.length; + let len = 0, val = 0; + for (const x of p) { + if (need[x] === undefined) { + need[x] = win[x] = 0; + len++; + } + need[x]++; + } + for (let i = 0; i < s.length; i++) { + const j = i - pLen; + if (s[i] in need && ++win[s[i]] === need[s[i]]) val++; + if (s[j] in need && win[s[j]]-- === need[s[j]]) val--; + if (val === len) res.push(j + 1); + } + return res; +}; + +// 最长回文子串(https://leetcode-cn.com/problems/longest-palindromic-substring/) +/** + * @param {string} s + * @return {string} + */ + var longestPalindrome = function(s) { + if(!s || s.length < 2){ + return s; + } + var s_f = s.split('').reverse().join(''); + var resultStr = s[0]; + var maxLen = 1; + var tmpLen = 1; + var maxStrIndex = 0; + var len = s.length; + //判断字符串是否回文 + function isPalinerome(i,r){ + if(len - i - 1 == r -tmpLen + 1){ + return true + } + return false; + } + //初始化二维数组 + var len = s.length; + var arr = new Array(len); + for(var i = 0;i maxLen && isPalinerome(i,r)){ + maxStrIndex = r; + maxLen = tmpLen; + resultStr = s.substring(i-tmpLen+1,i+1); + } + } + } + } + return resultStr; +}; + +// 通配符匹配(https://leetcode-cn.com/problems/wildcard-matching/) +/** + * @param {string} s + * @param {string} p + * @return {boolean} + */ + var isMatch = (s, p) => { + const sLen = s.length; + const pLen = p.length; + const dp = new Array(sLen + 1); + for (let i = 0; i < sLen + 1; i++) { + dp[i] = new Array(pLen + 1).fill(false); + } + dp[0][0] = true; + for (let j = 1; j <= pLen; j++) { + dp[0][j] = p[j - 1] == '*' && dp[0][j - 1]; + } + for (let i = 1; i <= sLen; i++) { + for (let j = 1; j <= pLen; j++) { + if (p[j - 1] == '?' || s[i - 1] == p[j - 1]) + dp[i][j] = dp[i - 1][j - 1]; + else if (p[j - 1] == '*' && (dp[i - 1][j] || dp[i][j - 1])) + dp[i][j] = true; + } + } + return dp[sLen][pLen]; + }; + +// 不同的子序列(https://leetcode-cn.com/problems/distinct-subsequences/) +/** + * @param {string} s + * @param {string} t + * @return {number} + */ + var numDistinct = function(s, t) { + const sLen = s.length, tLen = t.length + + function helper(i, j) { + if (j < 0) { + return 1 + } + if (i < 0) { + return 0 + } + + if (s[i] == t[j]) { + return helper(i-1, j) + helper(i-1, j-1) + } else { + return helper(i-1, j) + } + } + return helper(sLen-1, tLen-1) +}; + diff --git "a/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/index.html" "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/index.html" new file mode 100644 index 00000000..8338f0fe --- /dev/null +++ "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/index.html" @@ -0,0 +1,14 @@ + + + + + + + 第十周作业 + + + + + \ No newline at end of file From 7dec72cabd26c17fb95f420871d59947c853f836 Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Wed, 14 Apr 2021 14:39:39 +0800 Subject: [PATCH 21/22] =?UTF-8?q?=E6=AF=95=E4=B8=9A=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\346\257\225\344\270\232\346\200\273\347\273\223.md" | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 "Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" diff --git "a/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" new file mode 100644 index 00000000..91096b9c --- /dev/null +++ "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" @@ -0,0 +1,3 @@ +## 毕业总结 + +不知不觉十周的算法学习就这么匆匆忙忙的过去,学习的过程中也了解了不少全新的知识,尽管只用了短短的十周学习完了算法课程,但这既是结束也是新挑战的开始,个人以后还有很长的道路需要去摸索,也会持续的关注算法相关内容 From 62847a03f67757d104aa8a692f1c23438001881c Mon Sep 17 00:00:00 2001 From: jinlong12 Date: Wed, 14 Apr 2021 14:56:34 +0800 Subject: [PATCH 22/22] =?UTF-8?q?=E6=AF=95=E4=B8=9A=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\346\257\225\344\270\232\346\200\273\347\273\223.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" index 91096b9c..7d5d467c 100644 --- "a/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" +++ "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" @@ -1,3 +1,3 @@ ## 毕业总结 -不知不觉十周的算法学习就这么匆匆忙忙的过去,学习的过程中也了解了不少全新的知识,尽管只用了短短的十周学习完了算法课程,但这既是结束也是新挑战的开始,个人以后还有很长的道路需要去摸索,也会持续的关注算法相关内容 +不知不觉十周的算法学习就这么匆匆忙忙的过去,学习的过程中也了解了不少全新的知识,最主要的还是对于学习方法的理解与运用,尽管只用了短短的十周学习完了算法课程,但这既是结束也是新挑战的开始,课程结束不代表知识的学习结束,个人以后还有很长的道路需要去摸索,也会持续的关注算法相关内容。